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   $Log: midas.c,v $
00009   Revision 1.198  2003/11/24 08:22:46  midas
00010   Changed timeouts from INT to DWORD, added ignore_timeout to cm_cleanup, adde '-f' flag to ODBEdit 'cleanup'
00011 
00012   Revision 1.197  2003/11/20 11:29:44  midas
00013   Implemented db_check_record and use it in most places instead of db_create_record
00014 
00015   Revision 1.196  2003/11/01 01:27:58  olchansk
00016   abort if cannot read /runinfo/run number
00017 
00018   Revision 1.195  2003/10/30 12:03:11  midas
00019   Removed tabs
00020 
00021   Revision 1.194  2003/10/13 00:07:40  olchansk
00022   refuse run number zero and abort on corrupted run numbers
00023 
00024   Revision 1.193  2003/10/12 22:56:33  olchansk
00025   when submitting new Elog message, add the message text to the outgoing email.
00026   add traps for some array overruns (see http://midas.triumf.ca/forum/Development%20Area/12)
00027 
00028   Revision 1.192  2003/09/04 11:47:48  midas
00029   Fixed problem with hKey in cm_transition
00030 
00031   Revision 1.191  2003/05/09 07:40:05  midas
00032   Added extra parameter to cm_get_environment
00033 
00034   Revision 1.190  2003/05/08 19:36:32  midas
00035   Changed size_t into INT
00036 
00037   Revision 1.189  2003/05/02 09:03:01  midas
00038   Fixed buffer overflows by strlcpy()
00039 
00040   Revision 1.188  2003/04/25 13:54:04  midas
00041   Fixed compiler warnings
00042 
00043   Revision 1.187  2003/04/22 12:01:29  midas
00044   Added graceful shutdown of odbedit->frontend connection
00045 
00046   Revision 1.186  2003/04/22 10:09:06  midas
00047   Added RPC_NODELAY option
00048 
00049   Revision 1.185  2003/04/16 19:34:42  pierre
00050   mv stdio.h, ctype.h into midasinc.h
00051 
00052   Revision 1.184  2003/04/15 08:37:04  midas
00053   Removed error message in rpc_client_connect on broken connection, since this is normal if the frontend for example is restarted
00054 
00055   Revision 1.183  2003/04/14 11:01:04  midas
00056   Added bk_find(), added htonl(INADDR_ANY) for bind()
00057 
00058   Revision 1.182  2003/04/09 13:42:47  midas
00059   Made file compile under C++
00060 
00061   Revision 1.181  2003/03/28 07:59:50  midas
00062   Removed old code
00063 
00064   Revision 1.180  2003/03/27 19:40:27  olchansk
00065   fix infinite loop in cm_scan_experiments(). Why feof() does not work is a mystery, but I definitely saw fgets() return NULL and the subsequent feof() return 0.
00066 
00067   Revision 1.179  2003/03/22 07:06:47  olchansk
00068   prevent infinite loop in hs_read() and hs_dump() when reading broken history files.
00069 
00070   Revision 1.178  2003/01/14 12:19:23  midas
00071   Removed unnecessary code
00072 
00073   Revision 1.177  2003/01/14 08:14:32  midas
00074   Removed unnecessary bind()
00075 
00076   Revision 1.176  2003/01/13 17:07:01  midas
00077   Fixed problem with missing history in recent files
00078 
00079   Revision 1.175  2002/11/27 12:54:49  midas
00080   Removed unnecessary bind()
00081 
00082   Revision 1.174  2002/10/21 00:14:38  olchansk
00083   add missing error reporting
00084 
00085   Revision 1.173  2002/10/15 19:14:06  olchansk
00086   disallow recursive rpc_server_disconnect() (->rpc_call->rpc_server_disconnect)
00087 
00088   Revision 1.172  2002/10/04 09:05:39  midas
00089   Set timeout to 0 in cm_enable_watchdog(FALSE)
00090 
00091   Revision 1.171  2002/09/23 18:13:49  pierre
00092   correct cm_cleanup() rpc_call arg list
00093 
00094   Revision 1.170  2002/09/23 09:50:23  midas
00095   Fixed problem with odbedit 'cleanup' command
00096 
00097   Revision 1.169  2002/09/18 16:39:16  pierre
00098   add bk_list()
00099 
00100   Revision 1.168  2002/09/17 08:58:43  midas
00101   Fix for watchdog timeout after tape operations
00102 
00103   Revision 1.167  2002/09/13 07:32:47  midas
00104   Added client name to cm_cleanup()
00105 
00106   Revision 1.166  2002/09/12 10:42:20  midas
00107   Added note to cm_cleanup()
00108 
00109   Revision 1.165  2002/09/09 17:57:14  pierre
00110   #if !defined(OS_VXWORKS) for eb_ & hs_ section
00111 
00112   Revision 1.164  2002/06/25 19:39:48  pierre
00113   doc++ functions  strstr
00114 
00115   Revision 1.163  2002/06/25 19:00:36  pierre
00116   doc++ functions
00117 
00118   Revision 1.162  2002/05/29 18:49:37  midas
00119   Fixed bug with 'shutdown all' in odbedit
00120 
00121   Revision 1.161  2002/05/29 07:25:13  midas
00122   Fixed bug with shutting down programs
00123 
00124   Revision 1.160  2002/05/28 12:47:46  midas
00125   Shut down client connection in FTCP mode
00126 
00127   Revision 1.159  2002/05/27 14:29:10  midas
00128   Improved rpc timeout error reports
00129 
00130   Revision 1.158  2002/05/22 06:07:01  midas
00131   Call bm_defragment_event for both EVENTID_FRAG1 and EVENTID_FRAG
00132 
00133   Revision 1.157  2002/05/22 05:43:32  midas
00134   Added extra variables to hs_enum_vars for mhist to display array size
00135 
00136   Revision 1.156  2002/05/22 05:26:38  midas
00137   Fixed problem with empty history files
00138 
00139   Revision 1.155  2002/05/16 18:01:13  midas
00140   Added subdir creation in logger and improved program restart scheme
00141 
00142   Revision 1.154  2002/05/15 23:43:39  midas
00143   Added bm_defragment_event()
00144 
00145   Revision 1.153  2002/05/14 04:24:53  midas
00146   Fixed bug on nonexisting message file
00147 
00148   Revision 1.152  2002/05/11 01:22:48  midas
00149   Improved malloc/free debugging
00150 
00151   Revision 1.151  2002/05/10 16:49:37  midas
00152   Moved 'execute on start' before TR_PRESTART
00153 
00154   Revision 1.150  2002/05/10 01:41:19  midas
00155   Added optional debug output to cm_transition
00156 
00157   Revision 1.149  2002/05/10 00:17:05  midas
00158   Run start abort causes logger to delete old data file on next run start
00159 
00160   Revision 1.148  2002/05/08 22:15:24  pierre
00161   add db_get_value arg doc
00162 
00163   Revision 1.147  2002/05/08 19:54:40  midas
00164   Added extra parameter to function db_get_value()
00165 
00166   Revision 1.146  2002/05/07 22:27:56  midas
00167   Fixed bug that history files did not get closed on hs_read
00168 
00169   Revision 1.145  2002/03/13 08:38:00  midas
00170   Added periodic alarms
00171 
00172   Revision 1.144  2002/02/02 11:33:45  midas
00173   Fixed bug in hs_read with small history files
00174 
00175   Revision 1.143  2002/01/30 13:03:34  midas
00176   Fixed small bug in history function
00177 
00178   Revision 1.142  2001/12/12 18:27:03  pierre
00179   add doc++, fix comments
00180 
00181   Revision 1.141  2001/12/12 17:46:21  pierre
00182   1.8.3-2 doc++ comments
00183 
00184   Revision 1.140  2001/12/05 11:29:51  midas
00185   Changed creation of "/Loger/xxx dir"
00186 
00187   Revision 1.139  2001/11/20 14:42:15  midas
00188   Added "/logger/history dir" and "/logger/elog dir"
00189 
00190   Revision 1.138  2001/10/25 22:18:48  pierre
00191   added doc++ comments
00192 
00193   Revision 1.137  2001/10/05 22:35:46  pierre
00194   - change doc \_ to _
00195   - Change MALLOC to M_MALLOC, FREE to M_FREE macros.
00196 
00197   Revision 1.136  2001/08/07 13:07:01  midas
00198   Return error if subprocess creation in rpc_server_accept fails
00199 
00200   Revision 1.135  2001/08/07 08:07:09  midas
00201   Fixed bug in el_retrieve with attachment decoding
00202 
00203   Revision 1.134  2001/06/27 11:55:50  midas
00204   Fixed compiler warnings (came from IRIX)
00205 
00206   Revision 1.133  2001/06/15 08:49:56  midas
00207   Fixed bug when query gave no result if only messages from today are present
00208 
00209   Revision 1.132  2001/05/22 09:27:13  midas
00210   Fixed bug when searching old messages
00211 
00212   Revision 1.131  2001/04/10 01:17:44  midas
00213   Fixed bug in cm_msg_retrieve which screwed up message display in mhttpd
00214 
00215   Revision 1.130  2001/02/19 11:29:05  midas
00216   Set run stop time in ODB before run is stopped in order to have the proper
00217   value in the runxxx.odb file
00218 
00219   Revision 1.129  2001/01/30 08:28:13  midas
00220   Use va_arg(..., double) for float numbers
00221 
00222   Revision 1.128  2000/12/06 02:58:24  midas
00223   Put extended error information for bind() call
00224 
00225   Revision 1.127  2000/11/14 12:19:24  midas
00226   Fixed bug in cm_msg_retrieve, added improved "more" feature in message display
00227 
00228   Revision 1.126  2000/11/14 08:17:04  midas
00229   Added number of messages for cm_msg_retrieve and in odbedit "old" command
00230 
00231   Revision 1.125  2000/11/06 14:19:19  midas
00232   Don't return from hs_read if variable not found (could be present later...)
00233 
00234   Revision 1.124  2000/10/21 12:26:12  midas
00235   Fixed bug with cache pointer in hs_read
00236 
00237   Revision 1.123  2000/09/29 13:31:12  midas
00238   ODBEdit cleanup now deletes open record with no client attached to
00239 
00240   Revision 1.122  2000/08/21 14:18:39  midas
00241   bk_close returns bank size
00242 
00243   Revision 1.121  2000/08/21 07:05:48  midas
00244   Added cm_msg_log1(...,facility) to be compatible with older programs
00245 
00246   Revision 1.120  2000/08/11 12:16:44  midas
00247   Fixed bug with "facility" being NULL
00248 
00249   Revision 1.119  2000/08/11 11:43:51  midas
00250   Added cm_msg1 to produce messages which go to a differnt logging file
00251 
00252   Revision 1.118  2000/08/10 08:04:56  midas
00253   Create default /runinfo structure in cm_connect_experiment
00254 
00255   Revision 1.117  2000/05/16 10:38:17  midas
00256   - Set MIDAS_DIR as the default /logger/data dir on cm_connect_experiment
00257   - Remove elog file if all messages are deleted
00258 
00259   Revision 1.116  2000/05/09 09:06:12  midas
00260   Added MIDAS_EXPTAB environment variable and hashmark comments in exptab
00261 
00262   Revision 1.115  2000/05/08 14:29:38  midas
00263   Added delete option in ELog
00264 
00265   Revision 1.114  2000/05/05 14:20:05  midas
00266   Do online mode check in al_trigger_alarm
00267 
00268   Revision 1.113  2000/04/26 20:27:06  pierre
00269   -Added doc++ comments on some functions.
00270 
00271   Revision 1.112  2000/04/25 11:55:42  midas
00272   Adjusted tabs for history functions
00273 
00274   Revision 1.111  2000/04/17 16:28:21  pierre
00275   - Added arg "BOOL binary_time" to hs_dump(), hs_fdump() for mhist -b
00276 
00277   Revision 1.110  2000/03/29 09:14:47  midas
00278   Fixed bug with original message tagging having the wrong offset
00279 
00280   Revision 1.109  2000/03/17 13:00:06  midas
00281   Frontends use default timeout fo 60 sec.
00282 
00283   Revision 1.108  2000/03/17 10:55:15  midas
00284   Don't trigger internal alarms if alarm system is off
00285 
00286   Revision 1.107  2000/03/04 00:42:29  midas
00287   Delete elog & alarm mutexes correctly
00288 
00289   Revision 1.106  2000/03/03 22:46:07  midas
00290   Remove elog and alarm mutex on exit
00291 
00292   Revision 1.105  2000/03/03 01:45:13  midas
00293   Added web password for mhttpd, added webpasswd command in odbedit
00294 
00295   Revision 1.104  2000/03/01 23:06:19  midas
00296   bk_xxx functions now don't use global variable _pbk
00297 
00298   Revision 1.103  2000/02/29 21:59:05  midas
00299   Fixec bug with order of actions in cm_transition
00300 
00301   Revision 1.102  2000/02/29 02:10:26  midas
00302   Added cm_is_ctrlc_pressed and cm_ack_ctrlc_pressed
00303 
00304   Revision 1.101  2000/02/25 22:49:29  midas
00305   Increased timeouts
00306 
00307   Revision 1.100  2000/02/25 22:19:09  midas
00308   Improved Ctrl-C handling
00309 
00310   Revision 1.99  2000/02/24 23:58:29  midas
00311   Fixed problem with _requested_transition being update by hotlink too late
00312 
00313   Revision 1.98  2000/02/24 22:29:25  midas
00314   Added deferred transitions
00315 
00316   Revision 1.97  2000/02/23 21:07:44  midas
00317   Changed spaces and tabulators
00318 
00319   Revision 1.96  2000/02/15 11:07:51  midas
00320   Changed GET_xxx to bit flags
00321 
00322   Revision 1.95  2000/02/09 08:03:52  midas
00323   Fixed bracket indention
00324 
00325   Revision 1.94  1999/12/08 16:10:43  midas
00326   Fixed another watchdog bug causing remote clients to crash
00327 
00328   Revision 1.93  1999/12/08 11:44:25  midas
00329   Fixed bug with watchdog timeout
00330 
00331   Revision 1.92  1999/12/08 10:00:41  midas
00332   Changed error string to single line
00333 
00334   Revision 1.91  1999/12/08 00:25:20  pierre
00335   - add cm_get_path in cm_msg_retrieve for midas.log location.
00336   - mod dm_buffer_create for arg "max user event size".
00337   - fix dm_area_flush.
00338   - fix other compilation warnings for OSF/1
00339 
00340   Revision 1.90  1999/11/26 08:31:58  midas
00341   midas.log is now places in the same directory as the .SHM files in case
00342   there is no data dir in the ODB
00343 
00344   Revision 1.89  1999/11/25 13:29:55  midas
00345   Fixed bug in cm_msg_retrieve
00346 
00347   Revision 1.88  1999/11/23 15:52:40  midas
00348   If an event is larger than the buffer read or write cache, it bypasses the
00349   cache.
00350 
00351   Revision 1.87  1999/11/19 09:49:58  midas
00352   Fixed bug with wrong default watchdog timeout in cm_connect_experiment1
00353 
00354   Revision 1.86  1999/11/12 10:04:59  midas
00355   Fixed bug with WATCHDOG_INTERVAL
00356 
00357   Revision 1.85  1999/11/10 15:05:16  midas
00358   Did some additional database locking
00359 
00360   Revision 1.84  1999/11/10 13:56:12  midas
00361   Alarm record only gets created when old one mismatches
00362 
00363   Revision 1.83  1999/11/10 10:39:11  midas
00364   Changed initialization of alarms
00365 
00366   Revision 1.82  1999/11/10 08:30:44  midas
00367   Fixed bug when editing the last elog message
00368 
00369   Revision 1.81  1999/11/09 14:44:08  midas
00370   Changed ODB locking in cm_cleanup
00371 
00372   Revision 1.80  1999/11/09 13:17:25  midas
00373   Added secure ODB feature
00374 
00375   Revision 1.79  1999/11/08 13:56:09  midas
00376   Added different alarm types
00377 
00378   Revision 1.78  1999/10/27 15:13:56  midas
00379   Added "access(<key>)" in alarm system
00380 
00381   Revision 1.77  1999/10/27 13:37:57  midas
00382   Added event size check in bm_send_event
00383 
00384   Revision 1.76  1999/10/18 15:52:12  midas
00385   Use "alarm count" to declare programs dead if inactive for 5 minutes
00386 
00387   Revision 1.75  1999/10/18 14:41:51  midas
00388   Use /programs/<name>/Watchdog timeout in all programs as timeout value. The
00389   default value can be submitted by calling cm_connect_experiment1(..., timeout)
00390 
00391   Revision 1.74  1999/10/13 08:03:28  midas
00392   Fixed bug displaying executed message as %d
00393 
00394   Revision 1.73  1999/10/11 14:14:03  midas
00395   Use ss_system in certain places
00396 
00397   Revision 1.72  1999/10/11 13:01:22  midas
00398   Produce system message when executing an alarm script
00399 
00400   Revision 1.71  1999/10/08 22:15:03  midas
00401   Added ftruncate for LINUX
00402 
00403   Revision 1.70  1999/10/08 22:00:30  midas
00404   Finished editing of elog messages
00405 
00406   Revision 1.69  1999/10/08 15:07:06  midas
00407   Program check creates new internal alarm when triggered
00408 
00409   Revision 1.68  1999/10/08 13:21:20  midas
00410   Alarm system disabled when running offline
00411 
00412   Revision 1.67  1999/10/07 13:50:49  midas
00413   Fixed bug with date in el_submit
00414 
00415   Revision 1.66  1999/10/07 13:31:18  midas
00416   Fixed truncated date in el_submit, cut off @host in author search
00417 
00418   Revision 1.65  1999/10/06 06:56:02  midas
00419   Include weekday in elog
00420 
00421   Revision 1.64  1999/10/05 13:16:10  midas
00422   Added global alarm flag "/alarms/alarm system active"
00423 
00424   Revision 1.63  1999/10/04 11:54:14  midas
00425   Submit full alarm string to execute command
00426 
00427   Revision 1.62  1999/09/30 22:59:06  pierre
00428   - fix bk_close for BK32
00429 
00430   Revision 1.61  1999/09/29 19:23:33  pierre
00431   - Fix bk_iterate,swap,locate for bank32
00432 
00433   Revision 1.60  1999/09/27 13:49:04  midas
00434   Added bUnique parameter to cm_shutdown
00435 
00436   Revision 1.59  1999/09/27 12:54:08  midas
00437   Finished alarm system
00438 
00439   Revision 1.58  1999/09/27 08:56:53  midas
00440   Fixed bug with missing run number in elog
00441 
00442   Revision 1.57  1999/09/23 14:00:48  midas
00443   Used capital names for mutexes
00444 
00445   Revision 1.56  1999/09/23 12:45:49  midas
00446   Added 32 bit banks
00447 
00448   Revision 1.55  1999/09/22 08:57:08  midas
00449   Implemented auto start and auto stop in /programs
00450 
00451   Revision 1.54  1999/09/21 14:57:39  midas
00452   Added "execute on start/stop" under /programs
00453 
00454   Revision 1.53  1999/09/21 14:15:04  midas
00455   Replaces cm_execute by system()
00456 
00457   Revision 1.52  1999/09/21 13:48:04  midas
00458   Added programs check in al_check
00459 
00460   Revision 1.51  1999/09/17 15:59:03  midas
00461   Added internal alarms
00462 
00463   Revision 1.50  1999/09/17 15:06:48  midas
00464   Moved al_check into cm_yield() and rpc_server_thread
00465 
00466   Revision 1.49  1999/09/17 11:50:53  midas
00467   Added al_xxx functions
00468 
00469   Revision 1.48  1999/09/17 11:48:06  midas
00470   Alarm system half finished
00471 
00472   Revision 1.47  1999/09/15 13:33:34  midas
00473   Added remote el_submit functionality
00474 
00475   Revision 1.46  1999/09/14 15:15:45  midas
00476   Moved el_xxx funtions into midas.c
00477 
00478   Revision 1.45  1999/09/13 11:08:24  midas
00479   Check NULL as experiment in cm_connect_experiment
00480 
00481   Revision 1.44  1999/09/10 06:11:15  midas
00482   Used %100 for year in tms structure
00483 
00484   Revision 1.43  1999/08/03 14:41:09  midas
00485   Lock buffer in bm_skip_event
00486 
00487   Revision 1.42  1999/08/03 11:15:07  midas
00488   Added bm_skip_event
00489 
00490   Revision 1.41  1999/07/21 09:22:01  midas
00491   Added Ctrl-C handler to cm_connect_experiment and cm_yield
00492 
00493   Revision 1.40  1999/06/28 12:01:21  midas
00494   Added hs_fdump
00495 
00496   Revision 1.39  1999/06/25 12:01:54  midas
00497   Added bk_delete function
00498 
00499   Revision 1.38  1999/06/23 09:36:24  midas
00500   - Fixed "too many connections" bug
00501   - incorporated PAAs dm_xxx changes
00502 
00503   Revision 1.37  1999/05/05 12:02:33  midas
00504   Added and modified history functions, added db_set_num_values
00505 
00506   Revision 1.36  1999/04/30 14:22:01  midas
00507   Send buffer name via bm_notify_client to java application
00508 
00509   Revision 1.35  1999/04/30 13:19:54  midas
00510   Changed inter-process communication (ss_resume, bm_notify_clients, etc)
00511   to strings so that server process can receive it's own watchdog produced
00512   messages (pass buffer name insteas buffer handle)
00513 
00514   Revision 1.34  1999/04/30 10:58:58  midas
00515   Added -D debug to screen for mserver
00516 
00517   Revision 1.33  1999/04/29 10:48:02  midas
00518   Implemented "/System/Client Notify" key
00519 
00520   Revision 1.32  1999/04/28 15:27:28  midas
00521   Made hs_read working for Java
00522 
00523   Revision 1.31  1999/04/27 15:16:14  midas
00524   Increased ASCII_BUFFER_SIZE to 64500
00525 
00526   Revision 1.30  1999/04/27 11:11:26  midas
00527   Added rpc_register_client
00528 
00529   Revision 1.29  1999/04/23 11:42:52  midas
00530   Made db_get_data_index working for Java
00531 
00532   Revision 1.28  1999/04/19 07:47:00  midas
00533   Added cm_msg_retrieve
00534 
00535   Revision 1.27  1999/04/16 15:13:28  midas
00536   bm_notify_client notifies ASCII client (Java) always
00537 
00538   Revision 1.26  1999/04/15 15:43:06  midas
00539   Added functionality for bm_receive_event in ASCII mode
00540 
00541   Revision 1.25  1999/04/15 09:58:42  midas
00542   Switched if (rpc_list[i].id == 0) statements
00543 
00544   Revision 1.24  1999/04/13 12:20:43  midas
00545   Added db_get_data1 (for Java)
00546 
00547   Revision 1.23  1999/04/08 15:26:05  midas
00548   Worked on rpc_execute_ascii
00549 
00550   Revision 1.22  1999/03/23 10:37:39  midas
00551   Fixed bug in cm_set_watchdog_params which causes mtape report ODB errors
00552 
00553   Revision 1.21  1999/02/12 10:55:03  midas
00554   Accepted PAA's modification in cm_set_watchdog_params()
00555 
00556   Revision 1.20  1999/02/11 13:14:46  midas
00557   Basic ASCII protocol implemented in server
00558 
00559   Revision 1.19  1999/02/09 14:38:23  midas
00560   Added debug logging facility
00561 
00562   Revision 1.18  1999/02/06 00:17:12  pierre
00563   - Fix local watchdog timeout in cm_set_watchdog_params()
00564   - Touch dm_xxx functions for OS_WINNT
00565 
00566   Revision 1.17  1999/02/02 07:42:22  midas
00567   Only print warning about zero length bank in bk_close if bank has type TID_STRUCT
00568 
00569   Revision 1.16  1999/02/01 15:41:23  midas
00570   Added warning for zero length bank in bk_close
00571 
00572   Revision 1.15  1999/02/01 13:03:49  midas
00573   Added /system/clients/xxx/link timeout to show current TCP timeout value
00574 
00575   Revision 1.14  1999/01/22 09:31:16  midas
00576   Fixed again status return from ss_mutex_create in bm_open_buffer
00577 
00578   Revision 1.13  1999/01/21 23:09:17  pierre
00579   - Incorporate dm_semaphore_...() functionality into ss_mutex_...()
00580   - Remove dm_semaphore_...(), adjust dm_...() accordingly.
00581   - Incorporate taskSpawn into ss_thread_create (system.c).
00582   - Adjust status value returnd from ss_mutex_create().
00583 
00584   Revision 1.12  1999/01/20 08:55:44  midas
00585   - Renames ss_xxx_mutex to ss_mutex_xxx
00586   - Added timout flag to ss_mutex_wait_for
00587 
00588   Revision 1.11  1999/01/19 19:58:56  pierre
00589   - Fix compiler warning in dm_buffer_send
00590 
00591   Revision 1.10  1999/01/18 17:50:35  pierre
00592   - Added dm_...() functions for Dual Memory buffer handling.
00593 
00594   Revision 1.9  1998/12/11 17:00:02  midas
00595   Fixed a few typos
00596 
00597   Revision 1.8  1998/10/28 12:05:57  midas
00598   Fixed minor compiler warning
00599 
00600   Revision 1.7  1998/10/28 12:01:30  midas
00601   Added version number to run start notification
00602 
00603   Revision 1.6  1998/10/27 10:53:48  midas
00604   - Added run start notification
00605   - Added ss_shell() for NT
00606 
00607   Revision 1.5  1998/10/23 14:21:50  midas
00608   - Modified version scheme from 1.06 to 1.6.0
00609   - cm_get_version() now returns versino as string
00610 
00611   Revision 1.4  1998/10/13 07:34:42  midas
00612   Reopened database in case of wrong password
00613 
00614   Revision 1.3  1998/10/12 12:19:02  midas
00615   Added Log tag in header
00616 
00617   Revision 1.2  1998/10/12 11:59:10  midas
00618   Added Log tag in header
00619 
00620 \********************************************************************/
00621 
00622 #include "midas.h"
00623 #include "msystem.h"
00624 #include <assert.h>
00625 
00626 /********************************************************************/
00627 /** \file midas.c
00628 The main core C-code for Midas.
00629 */
00630 
00631 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00632  */
00633 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00634  */
00635 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00636  */
00637 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00638  */
00639 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00640  */
00641 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00642  */
00643 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00644  */
00645 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00646  */
00647 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00648  */
00649 
00650 /** @addtogroup midasincludecode
00651  *  
00652  *  @{  */
00653 
00654 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00655 /********************************************************************/
00656 /* data type sizes */
00657 INT tid_size[] = {
00658   0,                            /* tid == 0 not defined                               */
00659   1,                            /* TID_BYTE      unsigned byte         0       255    */
00660   1,                            /* TID_SBYTE     signed byte         -128      127    */
00661   1,                            /* TID_CHAR      single character      0       255    */
00662   2,                            /* TID_WORD      two bytes             0      65535   */
00663   2,                            /* TID_SHORT     signed word        -32768    32767   */
00664   4,                            /* TID_DWORD     four bytes            0      2^32-1  */
00665   4,                            /* TID_INT       signed dword        -2^31    2^31-1  */
00666   4,                            /* TID_BOOL      four bytes bool       0        1     */
00667   4,                            /* TID_FLOAT     4 Byte float format                  */
00668   8,                            /* TID_DOUBLE    8 Byte float format                  */
00669   1,                            /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00670   0,                            /* TID_STRING    zero terminated string               */
00671   0,                            /* TID_ARRAY     variable length array of unkown type */
00672   0,                            /* TID_STRUCT    C structure                          */
00673   0,                            /* TID_KEY       key in online database               */
00674   0                             /* TID_LINK      link in online database              */
00675 };
00676 
00677 
00678 /* data type names */
00679 char *tid_name[] =
00680     { "NULL", "BYTE", "SBYTE", "CHAR", "WORD", "SHORT", "DWORD",
00681   "INT", "BOOL", "FLOAT", "DOUBLE", "BITFIELD", "STRING", "ARRAY",
00682   "STRUCT", "KEY", "LINK"
00683 };
00684 
00685 /* Globals */
00686 #ifdef OS_MSDOS
00687 extern unsigned _stklen = 60000U;
00688 
00689 #endif                          /*  */
00690 extern DATABASE *_database;
00691 extern INT _database_entries;
00692 static BUFFER *_buffer;
00693 static INT _buffer_entries = 0;
00694 static INT _msg_buffer = 0;
00695 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00696 static REQUEST_LIST *_request_list;
00697 static INT _request_list_entries = 0;
00698 static EVENT_HEADER *_event_buffer;
00699 static INT _event_buffer_size = 0;
00700 static char *_net_recv_buffer;
00701 static INT _net_recv_buffer_size = 0;
00702 static char *_net_send_buffer;
00703 static INT _net_send_buffer_size = 0;
00704 static char *_tcp_buffer = NULL;
00705 static INT _tcp_wp = 0;
00706 static INT _tcp_rp = 0;
00707 static INT _send_sock;
00708 static void (*_debug_print) (char *) = NULL;
00709 static INT _debug_mode = 0;
00710 static INT _watchdog_last_called = 0;
00711 
00712 /* table for transition functions */
00713 typedef struct {
00714   INT transition;
00715    INT(*func) (INT, char *);
00716 } TRANS_TABLE;
00717 TRANS_TABLE _trans_table[] = {
00718   {
00719    TR_START, NULL}, {
00720                      TR_STOP, NULL}, {
00721                                       TR_PAUSE, NULL}, {
00722                                                         TR_RESUME,
00723                                                         NULL}, {
00724                                                                 TR_PRESTART,
00725                                                                 NULL},
00726   {
00727    TR_POSTSTART, NULL}, {
00728                          TR_PRESTOP, NULL}, {
00729                                              TR_POSTSTOP, NULL}, {
00730                                                                   TR_PREPAUSE,
00731                                                                   NULL}, {
00732                                                                           TR_POSTPAUSE,
00733                                                                           NULL},
00734   {
00735    TR_PRERESUME, NULL}, {
00736                          TR_POSTRESUME, NULL}, {
00737                                                 0, NULL}
00738 };
00739 
00740 TRANS_TABLE _deferred_trans_table[] = {
00741   {
00742    TR_START, NULL}, {
00743                      TR_STOP, NULL}, {
00744                                       TR_PAUSE, NULL}, {
00745                                                         TR_RESUME,
00746                                                         NULL}, {
00747                                                                 TR_PRESTART,
00748                                                                 NULL},
00749   {
00750    TR_POSTSTART, NULL}, {
00751                          TR_PRESTOP, NULL}, {
00752                                              TR_POSTSTOP, NULL}, {
00753                                                                   TR_PREPAUSE,
00754                                                                   NULL}, {
00755                                                                           TR_POSTPAUSE,
00756                                                                           NULL},
00757   {
00758    TR_PRERESUME, NULL}, {
00759                          TR_POSTRESUME, NULL}, {
00760                                                 0, NULL}
00761 };
00762 static BOOL _server_registered = FALSE;
00763 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00764 void cm_ctrlc_handler(int sig);
00765 typedef struct {
00766   INT code;
00767   char *string;
00768 } ERROR_TABLE;
00769 ERROR_TABLE _error_table[] = {
00770   {
00771    CM_WRONG_PASSWORD, "Wrong password"}, {
00772                                           CM_UNDEF_EXP,
00773                                           "Experiment not defined"}, {
00774                                                                       CM_UNDEF_ENVIRON,
00775                                                                       "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00776   {
00777    RPC_NET_ERROR, "Cannot connect to remote host"}, {
00778                                                      0, NULL}
00779 };
00780 typedef struct {
00781   void *adr;
00782   int size;
00783   char file[80];
00784   int line;
00785 } DBG_MEM_LOC;
00786 DBG_MEM_LOC *_mem_loc = NULL;
00787 INT _n_mem = 0;
00788 void *dbg_malloc(size_t size, char *file, int line)
00789 {
00790   FILE *f;
00791   void *adr;
00792   int i;
00793 
00794   adr = malloc(size);
00795 
00796   /* search for deleted entry */
00797   for (i = 0; i < _n_mem; i++)
00798     if (_mem_loc[i].adr == NULL)
00799       break;
00800   if (i == _n_mem) {
00801     _n_mem++;
00802     if (!_mem_loc)
00803       _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00804 
00805     else
00806       _mem_loc =
00807           (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00808   }
00809   _mem_loc[i].adr = adr;
00810   _mem_loc[i].size = size;
00811   strcpy(_mem_loc[i].file, file);
00812   _mem_loc[i].line = line;
00813   f = fopen("mem.txt", "w");
00814   for (i = 0; i < _n_mem; i++)
00815     if (_mem_loc[i].adr)
00816       fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file,
00817               _mem_loc[i].line, _mem_loc[i].size,
00818               (unsigned int) _mem_loc[i].adr);
00819   fclose(f);
00820   return adr;
00821 }
00822 
00823 void *dbg_calloc(size_t size, size_t count, char *file, int line)
00824 {
00825   void *adr;
00826 
00827   adr = dbg_malloc(size * count, file, line);
00828   if (adr)
00829     memset(adr, 0, size * count);
00830   return adr;
00831 }
00832 void dbg_free(void *adr, char *file, int line)
00833 {
00834   FILE *f;
00835   int i;
00836 
00837   free(adr);
00838   for (i = 0; i < _n_mem; i++)
00839     if (_mem_loc[i].adr == adr)
00840       break;
00841   if (i < _n_mem)
00842     _mem_loc[i].adr = NULL;
00843   f = fopen("mem.txt", "w");
00844   for (i = 0; i < _n_mem; i++)
00845     if (_mem_loc[i].adr)
00846       fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file,
00847               _mem_loc[i].line, _mem_loc[i].size,
00848               (unsigned int) _mem_loc[i].adr);
00849   fclose(f);
00850 }
00851 
00852 /*---- strlcpy and strlcat to avoid buffer overflow ----------------*/
00853 
00854 /*
00855  * Copy src to string dst of size siz.  At most siz-1 characters
00856  * will be copied.  Always NUL terminates (unless size == 0).
00857  * Returns strlen(src); if retval >= siz, truncation occurred.
00858  */
00859 INT strlcpy(char *dst, const char *src, INT size)
00860 {
00861   char *d = dst;
00862   const char *s = src;
00863   INT n = size;
00864 
00865   /* Copy as many bytes as will fit */
00866   if (n != 0 && --n != 0) {
00867 
00868     do {
00869       if ((*d++ = *s++) == 0)
00870         break;
00871     } while (--n != 0);
00872   }
00873 
00874   /* Not enough room in dst, add NUL and traverse rest of src */
00875   if (n == 0) {
00876     if (size != 0)
00877       *d = '\0';                /* NUL-terminate dst */
00878     while (*s++);
00879   }
00880   return (s - src - 1);         /* count does not include NUL */
00881 }
00882 
00883 
00884 /*
00885  * Appends src to string dst of size siz (unlike strncat, siz is the
00886  * full size of dst, not space left).  At most siz-1 characters
00887  * will be copied.  Always NUL terminates (unless size <= strlen(dst)).
00888  * Returns strlen(src) + MIN(size, strlen(initial dst)).
00889  * If retval >= size, truncation occurred.
00890  */
00891 INT strlcat(char *dst, const char *src, INT size)
00892 {
00893   char *d = dst;
00894   const char *s = src;
00895   INT n = size;
00896   INT dlen;
00897 
00898   /* Find the end of dst and adjust bytes left but don't go past end */
00899   while (n-- != 0 && *d != '\0')
00900     d++;
00901   dlen = d - dst;
00902   n = size - dlen;
00903   if (n == 0)
00904     return (dlen + strlen(s));
00905   while (*s != '\0') {
00906     if (n != 1) {
00907       *d++ = *s;
00908       n--;
00909     }
00910     s++;
00911   }
00912   *d = '\0';
00913   return (dlen + (s - src));    /* count does not include NUL */
00914 }
00915 
00916 
00917 /********************************************************************\
00918 *                                                                    *
00919 *              Common message functions                              *
00920 *                                                                    *
00921 \********************************************************************/
00922 static int (*_message_print) (const char *) = puts;
00923 static INT _message_mask_system = MT_ALL;
00924 static INT _message_mask_user = MT_ALL;
00925 
00926 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00927 
00928 /** @addtogroup msgfunctionc
00929  *  
00930  *  @{  */
00931 
00932 /********************************************************************/
00933 /**
00934 Convert error code to string. Used after cm_connect_experiment to print
00935 error string in command line programs or windows programs.
00936 @param code Error code as defined in midas.h
00937 @param string Error string
00938 @return CM_SUCCESS
00939 */
00940 INT cm_get_error(INT code, char *string)
00941 {
00942   INT i;
00943 
00944   for (i = 0; _error_table[i].code; i++)
00945     if (_error_table[i].code == code) {
00946       strcpy(string, _error_table[i].string);
00947       return CM_SUCCESS;
00948     }
00949   sprintf(string, "Unexpected error #%d", code);
00950   return CM_SUCCESS;
00951 }
00952 
00953 
00954 /********************************************************************/
00955 /** 
00956 Set message masks. When a message is generated by calling cm_msg(),
00957 it can got to two destinatinons. First a user defined callback routine
00958 and second to the "SYSMSG" buffer.
00959 
00960 A user defined callback receives all messages which satisfy the user_mask.
00961 
00962 \code
00963 int message_print(const char *msg)
00964 {
00965   char str[160];
00966 
00967   memset(str, ' ', 159);
00968   str[159] = 0;
00969   if (msg[0] == '[')
00970     msg = strchr(msg, ']')+2;
00971   memcpy(str, msg, strlen(msg));
00972   ss_printf(0, 20, str);
00973   return 0;
00974 }
00975 ...
00976   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
00977 ...
00978 \endcode
00979 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
00980 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
00981 @param func Function which receives all printout. By setting "puts",
00982        messages are just printed to the screen.
00983 @return CM_SUCCESS
00984 */
00985 INT cm_set_msg_print(INT system_mask,
00986                      INT user_mask, int (*func) (const char *))
00987 {
00988   _message_mask_system = system_mask;
00989   _message_mask_user = user_mask;
00990   _message_print = func;
00991   return BM_SUCCESS;
00992 }
00993 
00994 
00995 /********************************************************************/
00996 /**
00997 Write message to logging file. Called by cm_msg.
00998 @attention May burn your fingers
00999 @param message_type      Message type
01000 @param message          Message string
01001 @return CM_SUCCESS
01002 */
01003 INT cm_msg_log(INT message_type, const char *message)
01004 {
01005   char dir[256];
01006   char filename[256];
01007   char path[256];
01008   char str[256];
01009   FILE *f;
01010   INT status, size;
01011   HNDLE hDB, hKey;
01012 
01013   if (rpc_is_remote())
01014     return rpc_call(RPC_CM_MSG_LOG, message_type, message);
01015   if (message_type != MT_DEBUG) {
01016     cm_get_experiment_database(&hDB, NULL);
01017     if (hDB) {
01018       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01019       if (status == DB_SUCCESS) {
01020         size = sizeof(dir);
01021         memset(dir, 0, size);
01022         db_get_value(hDB, 0, "/Logger/Data dir", dir,
01023                      &size, TID_STRING, TRUE);
01024         if (dir[0] != 0)
01025           if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01026             strcat(dir, DIR_SEPARATOR_STR);
01027         strcpy(filename, "midas.log");
01028         db_get_value(hDB, 0, "/Logger/Message file",
01029                      filename, &size, TID_STRING, TRUE);
01030         strcpy(path, dir);
01031         strcat(path, filename);
01032       }
01033 
01034       else {
01035         cm_get_path(dir);
01036         if (dir[0] != 0)
01037           if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01038             strcat(dir, DIR_SEPARATOR_STR);
01039         strcpy(path, dir);
01040         strcat(path, "midas.log");
01041       }
01042     }
01043 
01044     else
01045       strcpy(path, "midas.log");
01046     f = fopen(path, "a");
01047     if (f == NULL) {
01048       printf("Cannot open message log file %s\n", path);
01049     }
01050 
01051     else {
01052       strcpy(str, ss_asctime());
01053       fprintf(f, str);
01054       fprintf(f, " %s\n", message);
01055       fclose(f);
01056     }
01057   }
01058   return CM_SUCCESS;
01059 }
01060 
01061 
01062 /********************************************************************/
01063 /**
01064 Write message to logging file. Called by cm_msg().
01065 @internal 
01066 @param message_type      Message type
01067 @param message          Message string
01068 @param facility         Message facility, filename in which messages will be written
01069 @return CM_SUCCESS
01070 */
01071 INT cm_msg_log1(INT message_type,
01072                 const char *message, const char *facility)
01073 /********************************************************************\
01074 
01075   Routine: cm_msg_log1
01076 
01077   Purpose: Write message to logging file. Called by cm_msg.
01078            Internal use only
01079 
01080   Input:
01081     INT    message_type      Message type
01082     char   *message          Message string
01083     char   *
01084 
01085   Output:
01086     none
01087 
01088   Function value:
01089     CM_SUCCESS
01090 
01091 \********************************************************************/
01092 {
01093   char dir[256];
01094   char filename[256];
01095   char path[256];
01096   char str[256];
01097   FILE *f;
01098   INT status, size;
01099   HNDLE hDB, hKey;
01100 
01101   if (rpc_is_remote())
01102     return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
01103   if (message_type != MT_DEBUG) {
01104     cm_get_experiment_database(&hDB, NULL);
01105     if (hDB) {
01106       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01107       if (status == DB_SUCCESS) {
01108         size = sizeof(dir);
01109         memset(dir, 0, size);
01110         db_get_value(hDB, 0, "/Logger/Data dir", dir,
01111                      &size, TID_STRING, TRUE);
01112         if (dir[0] != 0)
01113           if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01114             strcat(dir, DIR_SEPARATOR_STR);
01115         if (facility[0]) {
01116           strcpy(filename, facility);
01117           strcat(filename, ".log");
01118         }
01119 
01120         else {
01121           strcpy(filename, "midas.log");
01122           db_get_value(hDB, 0, "/Logger/Message file",
01123                        filename, &size, TID_STRING, TRUE);
01124         }
01125         strcpy(path, dir);
01126         strcat(path, filename);
01127       }
01128 
01129       else {
01130         cm_get_path(dir);
01131         if (dir[0] != 0)
01132           if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01133             strcat(dir, DIR_SEPARATOR_STR);
01134         strcpy(path, dir);
01135         if (facility[0]) {
01136           strcat(path, facility);
01137           strcat(path, ".log");
01138         }
01139 
01140         else
01141           strcat(path, "midas.log");
01142       }
01143     }
01144 
01145     else {
01146       if (facility[0]) {
01147         strcpy(path, facility);
01148         strcat(path, ".log");
01149       }
01150 
01151       else
01152         strcpy(path, "midas.log");
01153     }
01154     f = fopen(path, "a");
01155     if (f == NULL) {
01156       printf("Cannot open message log file %s\n", path);
01157     }
01158 
01159     else {
01160       strcpy(str, ss_asctime());
01161       fprintf(f, str);
01162       fprintf(f, " %s\n", message);
01163       fclose(f);
01164     }
01165   }
01166   return CM_SUCCESS;
01167 }
01168 
01169 
01170 /********************************************************************/
01171 /** 
01172 This routine can be called whenever an internal error occurs
01173 or an informative message is produced. Different message
01174 types can be enabled or disabled by setting the type bits
01175 via cm_set_msg_print().
01176 @attention Do not add the "\n" escape carriage control at the end of the
01177 formated line as it is already added by the client on the receiving side.
01178 \code
01179    ...
01180    cm_msg(MINFO, "my program", "This is a information message only);
01181    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
01182    cm_msg(MTALK, "my_program", My program is Done!");
01183    ...
01184 \endcode
01185 @param message_type (See @ref midas_macro).
01186 @param filename Name of source file where error occured
01187 @param line Line number where error occured
01188 @param routine Routine name.
01189 @param format message to printout, ... Parameters like for printf()
01190 @return CM_SUCCESS
01191 */
01192 INT cm_msg(INT message_type,
01193            char *filename,
01194            INT line, const char *routine, const char *format, ...)
01195 {
01196   va_list argptr;
01197   char event[1000], str[256], local_message[256], send_message[256], *pc;
01198   EVENT_HEADER *pevent;
01199   INT status;
01200   static BOOL in_routine = FALSE;
01201 
01202   /* avoid recursive calls */
01203   if (in_routine)
01204     return 0;
01205   in_routine = TRUE;
01206 
01207   /* strip path */
01208   pc = filename + strlen(filename);
01209   while (*pc != '\\' && *pc != '/' && pc != filename)
01210     pc--;
01211   if (pc != filename)
01212     pc++;
01213 
01214   /* print client name into string */
01215   if (message_type == MT_USER)
01216     sprintf(send_message, "[%s] ", routine);
01217 
01218   else {
01219     rpc_get_name(str);
01220     if (str[0])
01221       sprintf(send_message, "[%s] ", str);
01222 
01223     else
01224       send_message[0] = 0;
01225   }
01226   local_message[0] = 0;
01227 
01228   /* preceed error messages with file and line info */
01229   if (message_type == MT_ERROR) {
01230     sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01231     strcat(send_message, str);
01232     strcat(local_message, str);
01233   }
01234 
01235   /* print argument list into message */
01236   va_start(argptr, format);
01237   vsprintf(str, (char *) format, argptr);
01238   va_end(argptr);
01239   strcat(send_message, str);
01240   strcat(local_message, str);
01241 
01242   /* call user function if set via cm_set_msg_print */
01243   if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01244     _message_print(local_message);
01245 
01246   /* return if system mask is not set */
01247   if ((message_type & _message_mask_system) == 0) {
01248     in_routine = FALSE;
01249     return CM_SUCCESS;
01250   }
01251 
01252   /* copy message to event */
01253   pevent = (EVENT_HEADER *) event;
01254   strcpy(event + sizeof(EVENT_HEADER), send_message);
01255 
01256   /* send event if not of type MLOG */
01257   if (message_type != MT_LOG) {
01258 
01259     /* if no message buffer already opened, do so now */
01260     if (_msg_buffer == 0) {
01261       status =
01262           bm_open_buffer(MESSAGE_BUFFER_NAME,
01263                          MESSAGE_BUFFER_SIZE, &_msg_buffer);
01264       if (status != BM_SUCCESS && status != BM_CREATED) {
01265         in_routine = FALSE;
01266         return status;
01267       }
01268     }
01269 
01270     /* setup the event header and send the message */
01271     bm_compose_event(pevent, EVENTID_MESSAGE,
01272                      (WORD) message_type,
01273                      strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01274     bm_send_event(_msg_buffer, event,
01275                   pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01276   }
01277 
01278   /* log message */
01279   cm_msg_log(message_type, send_message);
01280   in_routine = FALSE;
01281   return CM_SUCCESS;
01282 }
01283 
01284 
01285 /********************************************************************/
01286 /**
01287 This routine is similar to @ref cm_msg().
01288 It differs from cm_msg() only by the logging destination being a file
01289 given through the argument list i.e:\b facility
01290 @internal
01291 @attention Do not add the "\n" escape carriage control at the end of the
01292 formated line as it is already added by the client on the receiving side.
01293 The first arg in the following example uses the predefined
01294 macro MINFO which handles automatically the first 3 arguments of the function
01295 (see @ref midas_macro).
01296 \code   ...
01297    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
01298    ...
01299 //----- File my_log_file.log
01300 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
01301 \endcode
01302 @param message_type See @ref midas_macro.
01303 @param filename Name of source file where error occured
01304 @param line Line number where error occured
01305 @param facility Logging file name
01306 @param routine Routine name
01307 @param format message to printout, ... Parameters like for printf()
01308 @return CM_SUCCESS
01309 */
01310 INT cm_msg1(INT message_type,
01311             char *filename,
01312             INT line,
01313             const char *facility,
01314             const char *routine, const char *format, ...)
01315 {
01316   va_list argptr;
01317   char event[1000], str[256], local_message[256], send_message[256], *pc;
01318   EVENT_HEADER *pevent;
01319   INT status;
01320   static BOOL in_routine = FALSE;
01321 
01322   /* avoid recursive calles */
01323   if (in_routine)
01324     return 0;
01325   in_routine = TRUE;
01326 
01327   /* strip path */
01328   pc = filename + strlen(filename);
01329   while (*pc != '\\' && *pc != '/' && pc != filename)
01330     pc--;
01331   if (pc != filename)
01332     pc++;
01333 
01334   /* print client name into string */
01335   if (message_type == MT_USER)
01336     sprintf(send_message, "[%s] ", routine);
01337 
01338   else {
01339     rpc_get_name(str);
01340     if (str[0])
01341       sprintf(send_message, "[%s] ", str);
01342 
01343     else
01344       send_message[0] = 0;
01345   }
01346   local_message[0] = 0;
01347 
01348   /* preceed error messages with file and line info */
01349   if (message_type == MT_ERROR) {
01350     sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01351     strcat(send_message, str);
01352     strcat(local_message, str);
01353   }
01354 
01355   /* print argument list into message */
01356   va_start(argptr, format);
01357   vsprintf(str, (char *) format, argptr);
01358   va_end(argptr);
01359   if (facility)
01360     sprintf(local_message + strlen(local_message), "{%s} ", facility);
01361   strcat(send_message, str);
01362   strcat(local_message, str);
01363 
01364   /* call user function if set via cm_set_msg_print */
01365   if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01366     _message_print(local_message);
01367 
01368   /* return if system mask is not set */
01369   if ((message_type & _message_mask_system) == 0) {
01370     in_routine = FALSE;
01371     return CM_SUCCESS;
01372   }
01373 
01374   /* copy message to event */
01375   pevent = (EVENT_HEADER *) event;
01376   strcpy(event + sizeof(EVENT_HEADER), send_message);
01377 
01378   /* send event if not of type MLOG */
01379   if (message_type != MT_LOG) {
01380 
01381     /* if no message buffer already opened, do so now */
01382     if (_msg_buffer == 0) {
01383       status =
01384           bm_open_buffer(MESSAGE_BUFFER_NAME,
01385                          MESSAGE_BUFFER_SIZE, &_msg_buffer);
01386       if (status != BM_SUCCESS && status != BM_CREATED) {
01387         in_routine = FALSE;
01388         return status;
01389       }
01390     }
01391 
01392     /* setup the event header and send the message */
01393     bm_compose_event(pevent, EVENTID_MESSAGE,
01394                      (WORD) message_type,
01395                      strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01396     bm_send_event(_msg_buffer, event,
01397                   pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01398   }
01399 
01400   /* log message */
01401   cm_msg_log1(message_type, send_message, facility);
01402   in_routine = FALSE;
01403   return CM_SUCCESS;
01404 }
01405 
01406 
01407 /********************************************************************/
01408 /** 
01409 Register a dispatch function for receiving system messages.
01410 - example code from mlxspeaker.c
01411 \code
01412 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
01413 {
01414   char str[256], *pc, *sp;
01415   // print message
01416   printf("%s\n", (char *)(message));
01417 
01418   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
01419                  ,header->event_id
01420                  ,header->trigger_mask
01421                  ,header->serial_number
01422                  ,header->data_size);
01423   pc = strchr((char *)(message),']')+2;
01424   ...
01425   // skip none talking message
01426   if (header->trigger_mask == MT_TALK ||
01427       header->trigger_mask == MT_USER)
01428    ...
01429 }
01430 
01431 int main(int argc, char *argv[])
01432 {
01433   ...
01434   // now connect to server
01435   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
01436   if (status != CM_SUCCESS)
01437     return 1;
01438   // Register callback for messages
01439   cm_msg_register(receive_message);
01440   ...
01441 }
01442 \endcode
01443 @param func Dispatch function.
01444 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
01445 */
01446 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
01447 {
01448   INT status, id;
01449 
01450   /* if no message buffer already opened, do so now */
01451   if (_msg_buffer == 0) {
01452     status =
01453         bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE,
01454                        &_msg_buffer);
01455     if (status != BM_SUCCESS && status != BM_CREATED)
01456       return status;
01457   }
01458   _msg_dispatch = func;
01459   status =
01460       bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL,
01461                        GET_SOME, &id, func);
01462   return status;
01463 }
01464 
01465 
01466 /********************************************************************/
01467 /**
01468 Retrieve old messages from log file 
01469 @param  n_message        Number of messages to retrieve
01470 @param  message          buf_size bytes of messages, separated
01471                          by \n characters. The returned number
01472                          of bytes is normally smaller than the
01473                          initial buf_size, since only full
01474                          lines are returned.
01475 @param *buf_size         Size of message buffer to fill
01476 @return CM_SUCCESS
01477 */
01478 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
01479 {
01480   char dir[256];
01481   char filename[256];
01482   char path[256], *p;
01483   FILE *f;
01484   INT status, size, offset, i;
01485   HNDLE hDB, hKey;
01486 
01487   if (rpc_is_remote())
01488     return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
01489   cm_get_experiment_database(&hDB, NULL);
01490   if (hDB) {
01491     status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01492     if (status == DB_SUCCESS) {
01493       size = sizeof(dir);
01494       memset(dir, 0, size);
01495       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size,
01496                    TID_STRING, TRUE);
01497       if (dir[0] != 0)
01498         if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01499           strcat(dir, DIR_SEPARATOR_STR);
01500       strcpy(filename, "midas.log");
01501       db_get_value(hDB, 0, "/Logger/Message file", filename,
01502                    &size, TID_STRING, TRUE);
01503       strcpy(path, dir);
01504       strcat(path, filename);
01505     }
01506 
01507     else {
01508       cm_get_path(dir);
01509       if (dir[0] != 0)
01510         if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01511           strcat(dir, DIR_SEPARATOR_STR);
01512       strcpy(path, dir);
01513       strcat(path, "midas.log");
01514     }
01515   }
01516 
01517   else
01518     strcpy(path, "midas.log");
01519   f = fopen(path, "rb");
01520   if (f == NULL) {
01521     sprintf(message, "Cannot open message log file %s\n", path);
01522     *buf_size = strlen(message);
01523     return CM_DB_ERROR;
01524   }
01525 
01526   else {
01527 
01528     /* position buf_size bytes before the EOF */
01529     fseek(f, -(*buf_size - 1), SEEK_END);
01530     offset = ftell(f);
01531     if (offset != 0) {
01532 
01533       /* go to end of line */
01534       fgets(message, *buf_size - 1, f);
01535       offset = ftell(f) - offset;
01536       *buf_size -= offset;
01537     }
01538     memset(message, 0, *buf_size);
01539     fread(message, 1, *buf_size - 1, f);
01540     message[*buf_size - 1] = 0;
01541     fclose(f);
01542     p = message + (*buf_size - 2);
01543 
01544     /* goto end of buffer */
01545     while (p != message && *p == 0)
01546       p--;
01547 
01548     /* strip line break */
01549     while (p != message && (*p == '\n' || *p == '\r'))
01550       *(p--) = 0;
01551 
01552     /* trim buffer so that last n_messages remain */
01553     for (i = 0; i < n_message; i++) {
01554       while (p != message && *p != '\n')
01555         p--;
01556       while (p != message && (*p == '\n' || *p == '\r'))
01557         p--;
01558     }
01559     if (p != message) {
01560       p++;
01561       while (*p == '\n' || *p == '\r')
01562         p++;
01563     }
01564     *buf_size = (*buf_size - 1) - ((PTYPE) p - (PTYPE) message);
01565     memmove(message, p, *buf_size);
01566     message[*buf_size] = 0;
01567   }
01568   return CM_SUCCESS;
01569 }
01570 
01571 
01572 /** @} */// end of msgfunctionc
01573 
01574 /********************************************************************/
01575 /** @addtogroup cmfunctionc
01576  *  
01577  *  @{  */
01578 
01579 /********************************************************************/
01580 /**
01581 Get time from MIDAS server and set local time.
01582 @param    seconds         Time in seconds
01583 @return CM_SUCCESS
01584 */
01585 INT cm_synchronize(DWORD * seconds)
01586 {
01587   INT sec, status;
01588 
01589   /* if connected to server, get time from there */
01590   if (rpc_is_remote()) {
01591     status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01592 
01593     /* set local time */
01594     if (status == CM_SUCCESS)
01595       ss_settime(sec);
01596   }
01597 
01598   /* return time to caller */
01599   if (seconds != NULL) {
01600     *seconds = ss_time();
01601   }
01602   return CM_SUCCESS;
01603 }
01604 
01605 
01606 /********************************************************************/
01607 /**
01608 Get time from MIDAS server and set local time.
01609 @param    str            return time string
01610 @param    buf_size       Maximum size of str
01611 @return   CM_SUCCESS
01612 */
01613 INT cm_asctime(char *str, INT buf_size)
01614 {
01615 
01616   /* if connected to server, get time from there */
01617   if (rpc_is_remote())
01618     return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01619 
01620   /* return local time */
01621   strcpy(str, ss_asctime());
01622   return CM_SUCCESS;
01623 }
01624 
01625 
01626 /********************************************************************/
01627 /**
01628 Get time from ss_time on server.
01629 @param    time string
01630 @return   CM_SUCCESS
01631 */
01632 INT cm_time(DWORD * time)
01633 {
01634 
01635   /* if connected to server, get time from there */
01636   if (rpc_is_remote())
01637     return rpc_call(RPC_CM_TIME, time);
01638 
01639   /* return local time */
01640   *time = ss_time();
01641   return CM_SUCCESS;
01642 }
01643 
01644 
01645 /** @} */// end of cmfunctionc
01646 
01647 /********************************************************************\
01648 *                                                                    *
01649 *           cm_xxx  -  Common Functions to buffer & database         *
01650 *                                                                    *
01651 \********************************************************************/
01652 
01653 /* Globals */
01654 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01655 static HNDLE _hDB = 0;          /* Database handle */
01656 static char _client_name[NAME_LENGTH];
01657 static char _path_name[MAX_STRING_LENGTH];
01658 static INT _call_watchdog = TRUE;
01659 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01660 INT _mutex_alarm, _mutex_elog;
01661 
01662 /********************************************************************/
01663 /** @addtogroup cmfunctionc
01664  *  
01665  *  @{  */
01666 
01667 /**
01668 Return version number of current MIDAS library as a string
01669 @return version number * 100
01670 */
01671 char *cm_get_version()
01672 {
01673   return MIDAS_VERSION;
01674 }
01675 
01676 
01677 /********************************************************************/
01678 /**
01679 Set path to actual experiment. This function gets called
01680 by cm_connect_experiment if the connection is established
01681 to a local experiment (not through the TCP/IP server).
01682 The path is then used for all shared memory routines.
01683 @param  path             Pathname
01684 @return CM_SUCCESS
01685 */
01686 INT cm_set_path(char *path)
01687 {
01688   strcpy(_path_name, path);
01689 
01690   /* check for trailing directory seperator */
01691   if (strlen(_path_name) > 0
01692       && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01693     strcat(_path_name, DIR_SEPARATOR_STR);
01694   return CM_SUCCESS;
01695 }
01696 
01697 
01698 /********************************************************************/
01699 /**
01700 Return the path name previously set with cm_set_path.
01701 @param  path             Pathname
01702 @return CM_SUCCESS
01703 */
01704 INT cm_get_path(char *path)
01705 {
01706   strcpy(path, _path_name);
01707   return CM_SUCCESS;
01708 }
01709 
01710 
01711 /** @} */// end of cmfunctionc
01712 
01713 /********************************************************************/
01714 /** @addtogroup cmfunctionc
01715  *  
01716  *  @{  */
01717 
01718 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01719 typedef struct {
01720   char name[NAME_LENGTH];
01721   char directory[MAX_STRING_LENGTH];
01722   char user[NAME_LENGTH];
01723 } experiment_table;
01724 static experiment_table exptab[MAX_EXPERIMENT];
01725 
01726 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01727 
01728 /**
01729 Scan the "exptab" file for MIDAS experiment names and save them
01730 for later use by rpc_server_accept(). The file is first searched
01731 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01732 @return CM_SUCCESS<br>
01733         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01734 */
01735 INT cm_scan_experiments(void)
01736 {
01737   INT i;
01738   FILE *f;
01739   char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01740 
01741   for (i = 0; i < MAX_EXPERIMENT; i++)
01742     exptab[i].name[0] = 0;
01743 
01744   /* MIDAS_DIR overrides exptab */
01745   if (getenv("MIDAS_DIR")) {
01746     strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01747     strcpy(exptab[0].name, "Default");
01748     strlcpy(exptab[0].directory, getenv("MIDAS_DIR"),
01749             sizeof(exptab[0].directory));
01750     exptab[0].user[0] = 0;
01751     return CM_SUCCESS;
01752   }
01753 
01754   /* default directory for different OSes */
01755 #if defined (OS_WINNT)
01756   if (getenv("SystemRoot"))
01757     strlcpy(str, getenv("SystemRoot"), sizeof(str));
01758 
01759   else if (getenv("windir"))
01760     strlcpy(str, getenv("windir"), sizeof(str));
01761 
01762   else
01763     strcpy(str, "");
01764   strcpy(alt_str, str);
01765   strcat(str, "\\system32\\exptab");
01766   strcat(alt_str, "\\system\\exptab");
01767 
01768 #elif defined (OS_UNIX)
01769   strcpy(str, "/etc/exptab");
01770   strcpy(alt_str, "/exptab");
01771 
01772 #else                           /*  */
01773   strcpy(str, "exptab");
01774   strcpy(alt_str, "exptab");
01775 
01776 #endif                          /*  */
01777 
01778   /* MIDAS_EXPTAB overrides default directory */
01779   if (getenv("MIDAS_EXPTAB")) {
01780     strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01781     strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01782   }
01783 
01784   /* read list of available experiments */
01785   f = fopen(str, "r");
01786   if (f == NULL) {
01787     f = fopen(alt_str, "r");
01788     if (f == NULL)
01789       return CM_UNDEF_ENVIRON;
01790   }
01791   i = 0;
01792   if (f != NULL) {
01793 
01794     do {
01795       str[0] = 0;
01796       if (fgets(str, 100, f) == NULL)
01797         break;
01798       if (str[0] && str[0] != '#') {
01799         sscanf(str, "%s %s %s", exptab[i].name,
01800                exptab[i].directory, exptab[i].user);
01801 
01802         /* check for trailing directory separator */
01803         pdir = exptab[i].directory;
01804         if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01805           strcat(pdir, DIR_SEPARATOR_STR);
01806         i++;
01807       }
01808     } while (!feof(f));
01809     fclose(f);
01810   }
01811 
01812   /*
01813      for (j=0 ; j<i ; j++)
01814      {
01815      sprintf(str, "Scanned experiment %s", exptab[j].name);
01816      cm_msg(MINFO, str);
01817      }
01818    */
01819   return CM_SUCCESS;
01820 }
01821 
01822 
01823 /********************************************************************/
01824 /**
01825 Delete client info from database
01826 @param hDB               Database handle
01827 @param pid               PID of entry to delete, zero for this process.
01828 @return CM_SUCCESS
01829 */
01830 INT cm_delete_client_info(HNDLE hDB, INT pid)
01831 {
01832 
01833 #ifdef LOCAL_ROUTINES
01834 
01835   /* only do it if local */
01836   if (!rpc_is_remote()) {
01837     INT status;
01838     HNDLE hKey;
01839     char str[256];
01840 
01841     if (!pid)
01842       pid = ss_gettid();
01843 
01844     /* don't delete info from a closed database */
01845     if (_database_entries == 0)
01846       return CM_SUCCESS;
01847 
01848     /* make operation atomic by locking database */
01849     db_lock_database(hDB);
01850     sprintf(str, "System/Clients/%0d", pid);
01851     status = db_find_key1(hDB, 0, str, &hKey);
01852     if (status != DB_SUCCESS) {
01853       db_unlock_database(hDB);
01854       return status;
01855     }
01856 
01857     /* unlock client entry and delete it without locking DB */
01858     db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01859     db_delete_key1(hDB, hKey, 1, TRUE);
01860     db_unlock_database(hDB);
01861 
01862     /* touch notify key to inform others */
01863     status = 0;
01864     db_set_value(hDB, 0, "/System/Client Notify", &status,
01865                  sizeof(status), 1, TID_INT);
01866   }
01867 #endif                          /*LOCAL_ROUTINES */
01868   return CM_SUCCESS;
01869 }
01870 
01871 
01872 /********************************************************************/
01873 /**
01874 Check if a client with a /system/client/xxx entry has
01875 a valid entry in the ODB client table. If not, remove
01876 that client from the /system/client tree. 
01877 @param   hDB               Handle to online database
01878 @param   hKeyClient        Handle to client key
01879 @return  CM_SUCCESS, CM_NO_CLIENT
01880 */
01881 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01882 {
01883 
01884 #ifdef LOCAL_ROUTINES
01885   KEY key;
01886   DATABASE_HEADER *pheader;
01887   DATABASE_CLIENT *pclient;
01888   INT i, client_pid, status;
01889   char name[NAME_LENGTH];
01890 
01891   db_get_key(hDB, hKeyClient, &key);
01892   client_pid = atoi(key.name);
01893   i = sizeof(name);
01894   db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01895   db_lock_database(hDB);
01896   if (_database[hDB - 1].attached) {
01897     pheader = _database[hDB - 1].database_header;
01898     pclient = pheader->client;
01899 
01900     /* loop through clients */
01901     for (i = 0; i < pheader->max_client_index; i++, pclient++)
01902       if (pclient->tid == client_pid)
01903         break;
01904     if (i == pheader->max_client_index) {
01905 
01906       /* client not found : delete ODB stucture */
01907       db_unlock_database(hDB);
01908       status = cm_delete_client_info(hDB, client_pid);
01909       if (status != CM_SUCCESS)
01910         cm_msg(MERROR, "cm_check_client", "cannot delete client info");
01911 
01912       else
01913         cm_msg(MINFO, "cm_check_clinet",
01914                "Deleted /System/Clients/%d entry for client %s\n",
01915                client_pid, name);
01916       return CM_NO_CLIENT;
01917     }
01918   }
01919   db_unlock_database(hDB);
01920 
01921 #endif                          /*LOCAL_ROUTINES */
01922   return CM_SUCCESS;
01923 }
01924 
01925 
01926 /********************************************************************/
01927 /**
01928 Set client information in online database and return handle 
01929 @param  hDB              Handle to online database  
01930 @param  hKeyClient       returned key
01931 @param  host_name        server name 
01932 @param  client_name      Name of this program as it will be seen
01933                          by other clients.
01934 @param  hw_type          Type of byte order
01935 @param  password         MIDAS password  
01936 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01937                          by ODB setting /programs/<name>/Watchdog timeout
01938 @return   CM_SUCCESS
01939 */
01940 INT cm_set_client_info(HNDLE hDB,
01941                        HNDLE * hKeyClient,
01942                        char *host_name,
01943                        char *client_name,
01944                        INT hw_type, char *password, DWORD watchdog_timeout)
01945 {
01946   if (rpc_is_remote())
01947     return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01948                     host_name, client_name, hw_type, password,
01949                     watchdog_timeout);
01950 
01951 #ifdef LOCAL_ROUTINES
01952   {
01953     INT status, pid, data, i, index, size;
01954     HNDLE hKey, hSubkey;
01955     char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH],
01956         pwd[NAME_LENGTH];
01957     BOOL call_watchdog, allow;
01958 
01959     PROGRAM_INFO_STR(program_info_str);
01960 
01961     /* check security if password is presend */
01962     status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01963     if (hKey) {
01964 
01965       /* get password */
01966       size = sizeof(pwd);
01967       db_get_data(hDB, hKey, pwd, &size, TID_STRING);
01968 
01969       /* first check allowed hosts list */
01970       allow = FALSE;
01971       db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
01972       if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
01973         allow = TRUE;
01974 
01975       /* check allowed programs list */
01976       db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
01977       if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
01978         allow = TRUE;
01979 
01980       /* now check password */
01981       if (!allow && strcmp(password, pwd) != 0
01982           && strcmp(password, "mid7qBxsNMHux") != 0) {
01983         if (password[0])
01984           cm_msg(MINFO, "cm_set_client_info",
01985                  "Wrong password for host %s", host_name);
01986         db_close_all_databases();
01987         bm_close_all_buffers();
01988         _msg_buffer = 0;
01989         return CM_WRONG_PASSWORD;
01990       }
01991     }
01992 
01993     /* check if entry with this pid exists already */
01994     pid = ss_gettid();
01995     sprintf(str, "System/Clients/%0d", pid);
01996     status = db_find_key(hDB, 0, str, &hKey);
01997     if (status == DB_SUCCESS) {
01998       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
01999       db_delete_key(hDB, hKey, TRUE);
02000     }
02001     if (strlen(client_name) >= NAME_LENGTH)
02002       client_name[NAME_LENGTH] = 0;
02003     strcpy(name, client_name);
02004     strcpy(orig_name, client_name);
02005 
02006     /* check if client name already exists */
02007     status = db_find_key(hDB, 0, "System/Clients", &hKey);
02008     for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
02009       for (i = 0;; i++) {
02010         status = db_enum_key(hDB, hKey, i, &hSubkey);
02011         if (status == DB_NO_MORE_SUBKEYS)
02012           break;
02013         if (status == DB_SUCCESS) {
02014           size = sizeof(str);
02015           status =
02016               db_get_value(hDB, hSubkey, "Name", str,
02017                            &size, TID_STRING, TRUE);
02018         }
02019 
02020         /* check if client is living */
02021         if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
02022           continue;
02023         if (equal_ustring(str, name)) {
02024           sprintf(name, "%s%d", client_name, index);
02025           break;
02026         }
02027       }
02028     }
02029 
02030     /* set name */
02031     sprintf(str, "System/Clients/%0d/Name", pid);
02032     status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
02033     if (status != DB_SUCCESS) {
02034       cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
02035       return status;
02036     }
02037 
02038     /* copy new client name */
02039     strcpy(client_name, name);
02040     db_set_client_name(hDB, client_name);
02041 
02042     /* set also as rpc name */
02043     rpc_set_name(client_name);
02044 
02045     /* use /system/clients/PID as root */
02046     sprintf(str, "System/Clients/%0d", pid);
02047     db_find_key(hDB, 0, str, &hKey);
02048 
02049     /* set host name */
02050     status =
02051         db_set_value(hDB, hKey, "Host", host_name,
02052                      HOST_NAME_LENGTH, 1, TID_STRING);
02053     if (status != DB_SUCCESS)
02054       return status;
02055 
02056     /* set computer id */
02057     status =
02058         db_set_value(hDB, hKey, "Hardware type", &hw_type,
02059                      sizeof(hw_type), 1, TID_INT);
02060     if (status != DB_SUCCESS)
02061       return status;
02062 
02063     /* set server port */
02064     data = 0;
02065     status =
02066         db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT),
02067                      1, TID_INT);
02068     if (status != DB_SUCCESS)
02069       return status;
02070 
02071     /* set transition mask */
02072     data = 0;
02073     status =
02074         db_set_value(hDB, hKey, "Transition Mask", &data,
02075                      sizeof(DWORD), 1, TID_DWORD);
02076     if (status != DB_SUCCESS)
02077       return status;
02078 
02079     /* set deferred transition mask */
02080     data = 0;
02081     status =
02082         db_set_value(hDB, hKey, "Deferred Transition Mask", &data,
02083                      sizeof(DWORD), 1, TID_DWORD);
02084     if (status != DB_SUCCESS)
02085       return status;
02086 
02087     /* lock client entry */
02088     db_set_mode(hDB, hKey, MODE_READ, TRUE);
02089 
02090     /* get (set) default watchdog timeout */
02091     size = sizeof(watchdog_timeout);
02092     sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
02093     db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02094 
02095     /* define /programs entry */
02096     sprintf(str, "/Programs/%s", orig_name);
02097     db_create_record(hDB, 0, str, strcomb(program_info_str));
02098 
02099     /* save handle for ODB and client */
02100     rpc_set_server_option(RPC_ODB_HANDLE, hDB);
02101     rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
02102 
02103     /* save watchdog timeout */
02104     cm_get_watchdog_params(&call_watchdog, NULL);
02105     cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02106     if (call_watchdog)
02107       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02108 
02109     /* touch notify key to inform others */
02110     data = 0;
02111     db_set_value(hDB, 0, "/System/Client Notify", &data,
02112                  sizeof(data), 1, TID_INT);
02113     *hKeyClient = hKey;
02114   }
02115 
02116 #endif                          /* LOCAL_ROUTINES */
02117   return CM_SUCCESS;
02118 }
02119 
02120 
02121 /********************************************************************/
02122 /**
02123 Get info about the current client 
02124 @param  *client_name       Client name.  
02125 @return   CM_SUCCESS, CM_UNDEF_EXP  
02126 */
02127 INT cm_get_client_info(char *client_name)
02128 {
02129   INT status, length;
02130   HNDLE hDB, hKey;
02131 
02132   /* get root key of client */
02133   cm_get_experiment_database(&hDB, &hKey);
02134   if (!hDB) {
02135     client_name[0] = 0;
02136     return CM_UNDEF_EXP;
02137   }
02138   status = db_find_key(hDB, hKey, "Name", &hKey);
02139   if (status != DB_SUCCESS)
02140     return status;
02141   length = NAME_LENGTH;
02142   status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
02143   if (status != DB_SUCCESS)
02144     return status;
02145   return CM_SUCCESS;
02146 }
02147 
02148 
02149 /********************************************************************/
02150 /**
02151 Returns MIDAS environment variables. 
02152 @attention This function can be used to evaluate the standard MIDAS
02153            environment variables before connecting to an experiment
02154            (see @ref Environment_variables).
02155            The usual way is that the host name and experiment name are first derived
02156            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
02157            They can then be superseded by command line parameters with -h and -e flags.
02158 \code
02159 #include <stdio.h>
02160 #include <midas.h>
02161 main(int argc, char *argv[])
02162 {
02163   INT  status, i;
02164   char host_name[256],exp_name[32];
02165 
02166   // get default values from environment
02167   cm_get_environment(host_name, exp_name);
02168 
02169   // parse command line parameters
02170   for (i=1 ; i<argc ; i++)
02171     {
02172     if (argv[i][0] == '-')
02173       {
02174       if (i+1 >= argc || argv[i+1][0] == '-')
02175         goto usage;
02176       if (argv[i][1] == 'e')
02177         strcpy(exp_name, argv[++i]);
02178       else if (argv[i][1] == 'h')
02179         strcpy(host_name, argv[++i]);
02180       else
02181         {
02182 usage:
02183         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02184         return 1;
02185         }
02186       }
02187     }
02188   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02189   if (status != CM_SUCCESS)
02190     return 1;
02191     ...do anyting...
02192   cm_disconnect_experiment();
02193 }
02194 \endcode
02195 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
02196 @param host_name_size     string length
02197 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
02198 @param exp_name_size      string length
02199 @return CM_SUCCESS
02200 */
02201 INT cm_get_environment(char *host_name,
02202                        int host_name_size,
02203                        char *exp_name, int exp_name_size)
02204 {
02205   host_name[0] = exp_name[0] = 0;
02206   if (getenv("MIDAS_SERVER_HOST"))
02207     strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
02208   if (getenv("MIDAS_EXPT_NAME"))
02209     strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
02210   return CM_SUCCESS;
02211 }
02212 
02213 
02214 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02215 
02216 /********************************************************************/
02217 void cm_check_connect(void)
02218 {
02219   if (_hKeyClient)
02220     cm_msg(MERROR, "",
02221            "cm_disconnect_experiment not called at end of program");
02222 }
02223 
02224 
02225 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02226 
02227 /********************************************************************/
02228 /**
02229 This function connects to an existing MIDAS experiment.
02230 This must be the first call in a MIDAS application.
02231 It opens three TCP connection to the remote host (one for RPC calls,
02232 one to send events and one for hot-link notifications from the remote host)
02233 and writes client information into the ODB under /System/Clients.
02234 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
02235 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
02236 experiment name (see @ref Environment_variables).
02237 For that purpose, the function cm_get_environment()
02238 should be called prior to cm_connect_experiment(). If command line
02239 parameters -h and -e are used, the evaluation should be done between
02240 cm_get_environment() and cm_connect_experiment(). The function
02241 cm_disconnect_experiment() must be called before a MIDAS application exits.
02242 \code
02243 #include <stdio.h>
02244 #include <midas.h>
02245 main(int argc, char *argv[])
02246 {
02247   INT  status, i;
02248   char host_name[256],exp_name[32];
02249 
02250   // get default values from environment
02251   cm_get_environment(host_name, exp_name);
02252 
02253   // parse command line parameters
02254   for (i=1 ; i<argc ; i++)
02255     {
02256     if (argv[i][0] == '-')
02257       {
02258       if (i+1 >= argc || argv[i+1][0] == '-')
02259         goto usage;
02260       if (argv[i][1] == 'e')
02261         strcpy(exp_name, argv[++i]);
02262       else if (argv[i][1] == 'h')
02263         strcpy(host_name, argv[++i]);
02264       else
02265         {
02266 usage:
02267         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02268         return 1;
02269         }
02270       }
02271     }
02272   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02273   if (status != CM_SUCCESS)
02274     return 1;
02275   ...do operations...
02276   cm_disconnect_experiment();
02277 }
02278 \endcode
02279 @param host_name Specifies host to connect to. Must be a valid IP host name.
02280   The string can be empty ("") if to connect to the local computer.
02281 @param exp_name Specifies the experiment to connect to.
02282   If this string is empty, the number of defined experiments in exptab is checked.
02283   If only one experiment is defined, the function automatically connects to this
02284   one. If more than one experiment is defined, a list is presented and the user
02285   can interactively select one experiment.
02286 @param client_name Client name of the calling program as it can be seen by
02287   others (like the scl command in ODBEdit).
02288 @param func Callback function to read in a password if security has
02289   been enabled. In all command line applications this function is NULL which
02290   invokes an internal ss_gets() function to read in a password.
02291   In windows environments (MS Windows, X Windows) a function can be supplied to
02292   open a dialog box and read in the password. The argument of this function must
02293   be the returned password.
02294 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
02295 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
02296 */
02297 INT cm_connect_experiment(char *host_name,
02298                           char *exp_name,
02299                           char *client_name, void (*func) (char *))
02300 {
02301   INT status;
02302   char str[256];
02303 
02304   status =
02305       cm_connect_experiment1(host_name, exp_name, client_name, func,
02306                              DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
02307   if (status != CM_SUCCESS) {
02308     cm_get_error(status, str);
02309     puts(str);
02310   }
02311   return status;
02312 }
02313 
02314 
02315 /********************************************************************/
02316 /**
02317 Connect to a MIDAS experiment (to the online database) on
02318            a specific host.
02319 @internal
02320 */
02321 INT cm_connect_experiment1(char *host_name,
02322                            char *exp_name,
02323                            char *client_name,
02324                            void (*func) (char *),
02325                            INT odb_size, DWORD watchdog_timeout)
02326 {
02327   INT status, i, mutex_elog, mutex_alarm, size;
02328   char local_host_name[HOST_NAME_LENGTH];
02329   char client_name1[NAME_LENGTH];
02330   char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
02331   HNDLE hDB, hKeyClient;
02332   BOOL call_watchdog;
02333 
02334   RUNINFO_STR(runinfo_str);
02335   if (_hKeyClient)
02336     cm_disconnect_experiment();
02337   rpc_set_name(client_name);
02338 
02339   /* check for local host */
02340   if (equal_ustring(host_name, "local"))
02341     host_name[0] = 0;
02342 
02343 #ifdef OS_WINNT
02344   {
02345     WSADATA WSAData;
02346 
02347     /* Start windows sockets */
02348     if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02349       return RPC_NET_ERROR;
02350   }
02351 
02352 #endif                          /*  */
02353 
02354   /* search for experiment name in exptab */
02355   if (exp_name == NULL)
02356     exp_name = "";
02357   strcpy(exp_name1, exp_name);
02358   if (exp_name1[0] == 0) {
02359     status = cm_select_experiment(host_name, exp_name1);
02360     if (status != CM_SUCCESS)
02361       return status;
02362   }
02363 
02364   /* connect to MIDAS server */
02365   if (host_name[0]) {
02366     status = rpc_server_connect(host_name, exp_name1);
02367     if (status != RPC_SUCCESS)
02368       return status;
02369 
02370     /* register MIDAS library functions */
02371     status = rpc_register_functions(rpc_get_internal_list(1), NULL);
02372     if (status != RPC_SUCCESS)
02373       return status;
02374   }
02375 
02376   else {
02377 
02378     /* lookup path for *SHM files and save it */
02379     status = cm_scan_experiments();
02380     if (status != CM_SUCCESS)
02381       return status;
02382     for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
02383       if (equal_ustring(exp_name1, exptab[i].name))
02384         break;
02385 
02386     /* return if experiment not defined */
02387     if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
02388 
02389       /* message should be displayed by application
02390          sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
02391          cm_msg(MERROR, str);
02392        */
02393       return CM_UNDEF_EXP;
02394     }
02395     cm_set_path(exptab[i].directory);
02396 
02397     /* create alarm and elog mutexes */
02398     status = ss_mutex_create("ALARM", &mutex_alarm);
02399     if (status != SS_CREATED && status != SS_SUCCESS) {
02400       cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
02401       return status;
02402     }
02403     status = ss_mutex_create("ELOG", &mutex_elog);
02404     if (status != SS_CREATED && status != SS_SUCCESS) {
02405       cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
02406       return status;
02407     }
02408     cm_set_experiment_mutex(mutex_alarm, mutex_elog);
02409   }
02410 
02411   /* open ODB */
02412   if (odb_size == 0)
02413     odb_size = DEFAULT_ODB_SIZE;
02414   status = db_open_database("ODB", odb_size, &hDB, client_name);
02415   if (status != DB_SUCCESS && status != DB_CREATED) {
02416     cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02417     return status;
02418   }
02419 
02420   /* now setup client info */
02421   gethostname(local_host_name, sizeof(local_host_name));
02422 
02423   /* check watchdog timeout */
02424   if (watchdog_timeout == 0)
02425     watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
02426   strcpy(client_name1, client_name);
02427   password[0] = 0;
02428   status =
02429       cm_set_client_info(hDB, &hKeyClient, local_host_name,
02430                          client_name1, rpc_get_option(0,
02431                                                       RPC_OHW_TYPE),
02432                          password, watchdog_timeout);
02433   if (status == CM_WRONG_PASSWORD) {
02434     if (func == NULL)
02435       strcpy(str, ss_getpass("Password: "));
02436 
02437     else
02438       func(str);
02439 
02440     /* re-open database */
02441     status = db_open_database("ODB", odb_size, &hDB, client_name);
02442     if (status != DB_SUCCESS && status != DB_CREATED) {
02443       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02444       return status;
02445     }
02446     strcpy(password, ss_crypt(str, "mi"));
02447     status =
02448         cm_set_client_info(hDB, &hKeyClient, local_host_name,
02449                            client_name1, rpc_get_option(0,
02450                                                         RPC_OHW_TYPE),
02451                            password, watchdog_timeout);
02452     if (status != CM_SUCCESS) {
02453 
02454       /* disconnect */
02455       if (rpc_is_remote())
02456         rpc_server_disconnect();
02457       return status;
02458     }
02459   }
02460   cm_set_experiment_database(hDB, hKeyClient);
02461 
02462   /* set experiment name in ODB */
02463   db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH,
02464                1, TID_STRING);
02465 
02466   /* set data dir in ODB */
02467   cm_get_path(str);
02468   size = sizeof(str);
02469   db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
02470 
02471   /* check /runinfo structure */
02472   status =
02473       db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), FALSE);
02474   if (status == DB_STRUCT_MISMATCH) {
02475     cm_msg(MERROR, "cm_connect_experiment1",
02476            "Aborting on mismatching /Runinfo structure");
02477     cm_disconnect_experiment();
02478     abort();
02479   }
02480 
02481   /* register server to be able to be called by other clients */
02482   status = cm_register_server();
02483   if (status != CM_SUCCESS)
02484     return status;
02485 
02486   /* set watchdog timeout */
02487   cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
02488   size = sizeof(watchdog_timeout);
02489   sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
02490   db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02491   cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02492 
02493   /* send startup notification */
02494   if (strchr(local_host_name, '.'))
02495     *strchr(local_host_name, '.') = 0;
02496 
02497   /* startup message is not displayed */
02498   _message_print = NULL;
02499   cm_msg(MINFO, "cm_connect_experiment",
02500          "Program %s on host %s started", client_name, local_host_name);
02501 
02502   /* enable system and user messages to stdout as default */
02503   cm_set_msg_print(MT_ALL, MT_ALL, puts);
02504 
02505   /* call cm_check_connect when exiting */
02506   atexit((void (*)(void)) cm_check_connect);
02507 
02508   /* register ctrl-c handler */
02509   ss_ctrlc_handler(cm_ctrlc_handler);
02510   return CM_SUCCESS;
02511 }
02512 
02513 
02514 /********************************************************************/
02515 /** 
02516 Connect to a MIDAS server and return all defined
02517            experiments in *exp_name[MAX_EXPERIMENTS]
02518 @param  host_name         Internet host name.
02519 @param  exp_name          list of experiment names
02520 @return CM_SUCCESS, RPC_NET_ERROR
02521 */
02522 INT cm_list_experiments(char *host_name,
02523                         char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
02524 {
02525   INT i, status;
02526   struct sockaddr_in bind_addr;
02527   INT sock;
02528   char str[MAX_EXPERIMENT * NAME_LENGTH];
02529   struct hostent *phe;
02530 
02531   if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
02532     status = cm_scan_experiments();
02533     if (status != CM_SUCCESS)
02534       return status;
02535     for (i = 0; i < MAX_EXPERIMENT; i++)
02536       strcpy(exp_name[i], exptab[i].name);
02537     return CM_SUCCESS;
02538   }
02539 #ifdef OS_WINNT
02540   {
02541     WSADATA WSAData;
02542 
02543     /* Start windows sockets */
02544     if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02545       return RPC_NET_ERROR;
02546   }
02547 
02548 #endif                          /*  */
02549 
02550   /* create a new socket for connecting to remote server */
02551   sock = socket(AF_INET, SOCK_STREAM, 0);
02552   if (sock == -1) {
02553     cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02554     return RPC_NET_ERROR;
02555   }
02556 
02557   /* connect to remote node */
02558   memset(&bind_addr, 0, sizeof(bind_addr));
02559   bind_addr.sin_family = AF_INET;
02560   bind_addr.sin_addr.s_addr = 0;
02561   bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
02562 
02563 #ifdef OS_VXWORKS
02564   {
02565     INT host_addr;
02566 
02567     host_addr = hostGetByName(host_name);
02568     memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02569   }
02570 #else                           /*  */
02571   phe = gethostbyname(host_name);
02572   if (phe == NULL) {
02573     cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
02574     return RPC_NET_ERROR;
02575   }
02576   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02577 
02578 #endif                          /*  */
02579 
02580 #ifdef OS_UNIX
02581   do {
02582     status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02583 
02584     /* don't return if an alarm signal was cought */
02585   } while (status == -1 && errno == EINTR);
02586 
02587 #else                           /*  */
02588   status =
02589       connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02590 
02591 #endif                          /*  */
02592   if (status != 0) {
02593 
02594 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02595     return RPC_NET_ERROR;
02596   }
02597 
02598   /* request experiment list */
02599   send(sock, "I", 2, 0);
02600   for (i = 0; i < MAX_EXPERIMENT; i++) {
02601     exp_name[i][0] = 0;
02602     status = recv_string(sock, str, sizeof(str), 1000);
02603     if (status < 0)
02604       return RPC_NET_ERROR;
02605     if (status == 0)
02606       break;
02607     strcpy(exp_name[i], str);
02608   }
02609   exp_name[i][0] = 0;
02610   closesocket(sock);
02611   return CM_SUCCESS;
02612 }
02613 
02614 
02615 /********************************************************************/
02616 /**
02617 Connect to a MIDAS server and select an experiment
02618            from the experiments available on this server
02619 @internal
02620 @param  host_name         Internet host name.
02621 @param  exp_name          list of experiment names
02622 @return CM_SUCCESS, RPC_NET_ERROR
02623 */
02624 INT cm_select_experiment(char *host_name, char *exp_name)
02625 {
02626   INT status, i;
02627   char expts[MAX_EXPERIMENT][NAME_LENGTH];
02628   char str[32];
02629 
02630   /* retrieve list of experiments and make selection */
02631   status = cm_list_experiments(host_name, expts);
02632   if (status != CM_SUCCESS)
02633     return status;
02634   if (expts[1][0]) {
02635     if (host_name[0])
02636       printf("Available experiments on server %s:\n", host_name);
02637 
02638     else
02639       printf("Available experiments on local computer:\n");
02640     for (i = 0; expts[i][0]; i++)
02641       printf("%d : %s\n", i, expts[i]);
02642     printf("Select number: ");
02643     ss_gets(str, 32);
02644     i = atoi(str);
02645     strcpy(exp_name, expts[i]);
02646   }
02647 
02648   else
02649     strcpy(exp_name, expts[0]);
02650   return CM_SUCCESS;
02651 }
02652 
02653 
02654 /********************************************************************/
02655 /**
02656 Connect to a MIDAS client of the current experiment
02657 @internal
02658 @param  client_name       Name of client to connect to. This name
02659                             is set by the other client via the
02660                             cm_connect_experiment call.
02661 @param  hConn            Connection handle
02662 @return CM_SUCCESS, CM_NO_CLIENT
02663 */
02664 INT cm_connect_client(char *client_name, HNDLE * hConn)
02665 {
02666   HNDLE hDB, hKeyRoot, hSubkey, hKey;
02667   INT status, i, length, port;
02668   char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02669 
02670   /* find client entry in ODB */
02671   cm_get_experiment_database(&hDB, &hKey);
02672   status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02673   if (status != DB_SUCCESS)
02674     return status;
02675   i = 0;
02676 
02677   do {
02678 
02679     /* search for client with specific name */
02680     status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02681     if (status == DB_NO_MORE_SUBKEYS)
02682       return CM_NO_CLIENT;
02683     status = db_find_key(hDB, hSubkey, "Name", &hKey);
02684     if (status != DB_SUCCESS)
02685       return status;
02686     length = NAME_LENGTH;
02687     status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02688     if (status != DB_SUCCESS)
02689       return status;
02690     if (equal_ustring(name, client_name)) {
02691       status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02692       if (status != DB_SUCCESS)
02693         return status;
02694       length = sizeof(INT);
02695       status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02696       if (status != DB_SUCCESS)
02697         return status;
02698       status = db_find_key(hDB, hSubkey, "Host", &hKey);
02699       if (status != DB_SUCCESS)
02700         return status;
02701       length = sizeof(host_name);
02702       status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02703       if (status != DB_SUCCESS)
02704         return status;
02705 
02706       /* client found -> connect to its server port */
02707       return rpc_client_connect(host_name, port, client_name, hConn);
02708     }
02709   } while (TRUE);
02710 }
02711 
02712 
02713 /********************************************************************/
02714 /**
02715 Disconnect from a MIDAS client 
02716 @param   hConn             Connection handle obtained via
02717                              cm_connect_client()
02718 @param   bShutdown         If TRUE, disconnect from client and
02719                              shut it down (exit the client program)
02720                              by sending a RPC_SHUTDOWN message
02721 @return   see rpc_client_disconnect()
02722 */
02723 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02724 {
02725   return rpc_client_disconnect(hConn, bShutdown);
02726 }
02727 
02728 
02729 /********************************************************************/
02730 /**
02731 Disconnect from a MIDAS experiment.
02732 @attention Should be the last call to a MIDAS library function in an
02733 application before it exits. This function removes the client information
02734 from the ODB, disconnects all TCP connections and frees all internal
02735 allocated memory. See cm_connect_experiment() for example.
02736 @return CM_SUCCESS
02737 */
02738 INT cm_disconnect_experiment(void)
02739 {
02740   HNDLE hDB, hKey;
02741   char local_host_name[HOST_NAME_LENGTH], client_name[80];
02742 
02743   /* send shutdown notification */
02744   rpc_get_name(client_name);
02745   gethostname(local_host_name, sizeof(local_host_name));
02746   if (strchr(local_host_name, '.'))
02747     *strchr(local_host_name, '.') = 0;
02748 
02749   /* disconnect message not displayed */
02750   _message_print = NULL;
02751   cm_msg(MINFO, "cm_disconnect_experiment",
02752          "Program %s on host %s stopped", client_name, local_host_name);
02753   if (rpc_is_remote()) {
02754 
02755     /* close open records */
02756     db_close_all_records();
02757     rpc_client_disconnect(-1, FALSE);
02758     rpc_server_disconnect();
02759   }
02760 
02761   else {
02762     rpc_client_disconnect(-1, FALSE);
02763 
02764 #ifdef LOCAL_ROUTINES
02765     ss_alarm(0, cm_watchdog);
02766     _watchdog_last_called = 0;
02767 
02768 #endif                          /* LOCAL_ROUTINES */
02769 
02770     /* delete client info */
02771     cm_get_experiment_database(&hDB, &hKey);
02772     if (hDB)
02773       cm_delete_client_info(hDB, 0);
02774     bm_close_all_buffers();
02775     db_close_all_databases();
02776   }
02777   if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02778     rpc_server_shutdown();
02779 
02780   /* free RPC list */
02781   rpc_deregister_functions();
02782   cm_set_experiment_database(0, 0);
02783   _msg_buffer = 0;
02784 
02785   /* free memory buffers */
02786   if (_event_buffer_size > 0) {
02787     M_FREE(_event_buffer);
02788     _event_buffer_size = 0;
02789   }
02790   if (_net_recv_buffer_size > 0) {
02791     M_FREE(_net_recv_buffer);
02792     _net_recv_buffer_size = 0;
02793   }
02794   if (_net_send_buffer_size > 0) {
02795     M_FREE(_net_send_buffer);
02796     _net_send_buffer_size = 0;
02797   }
02798   if (_tcp_buffer != NULL) {
02799     M_FREE(_tcp_buffer);
02800     _tcp_buffer = NULL;
02801   }
02802   return CM_SUCCESS;
02803 }
02804 
02805 
02806 /********************************************************************/
02807 /**
02808 Set the handle to the ODB for the currently connected experiment
02809 @param hDB              Database handle
02810 @param hKeyClient       Key handle of client structure
02811 @return CM_SUCCESS
02812 */
02813 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02814 {
02815   _hDB = hDB;
02816   _hKeyClient = hKeyClient;
02817   return CM_SUCCESS;
02818 }
02819 
02820 
02821 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02822 
02823 /********************************************************************/
02824 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02825 /********************************************************************\
02826 
02827   Routine: cm_set_experiment_mutex
02828 
02829   Purpose: Set the handle to the experiment wide mutexes
02830 
02831   Input:
02832     INT    mutex_alarm      Alarm mutex
02833     INT    mutex_elog       Elog mutex
02834 
02835   Output:
02836     none
02837 
02838   Function value:
02839     CM_SUCCESS              Successful completion
02840 
02841 \********************************************************************/
02842 {
02843   _mutex_alarm = mutex_alarm;
02844   _mutex_elog = mutex_elog;
02845   return CM_SUCCESS;
02846 }
02847 
02848 
02849 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02850 
02851 /********************************************************************/
02852 /** 
02853 Get the handle to the ODB from the currently connected experiment.
02854 
02855 @attention This function returns the handle of the online database (ODB) which
02856 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02857 to access the client information in the ODB. If the client key handle is not needed,
02858 the parameter can be NULL.
02859 \code
02860 HNDLE hDB, hkeyclient;
02861  char  name[32];
02862  int   size;
02863  db_get_experiment_database(&hdb, &hkeyclient);
02864  size = sizeof(name);
02865  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02866  printf("My name is %s\n", name);
02867 \endcode
02868 @param hDB Database handle.
02869 @param hKeyClient Handle for key where search starts, zero for root.
02870 @return CM_SUCCESS
02871 */
02872 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02873 {
02874   if (_hDB) {
02875     if (hDB != NULL)
02876       *hDB = _hDB;
02877     if (hKeyClient != NULL)
02878       *hKeyClient = _hKeyClient;
02879   }
02880 
02881   else {
02882     if (hDB != NULL)
02883       *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02884     if (hKeyClient != NULL)
02885       *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02886   }
02887   return CM_SUCCESS;
02888 }
02889 
02890 
02891 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02892 /********************************************************************/
02893 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02894 /********************************************************************\
02895 
02896   Routine: cm_get_experiment_mutex
02897 
02898   Purpose: Get the handle to the experiment wide mutexes
02899 
02900   Input:
02901     none
02902 
02903   Output:
02904     INT    mutex_alarm      Alarm mutex
02905     INT    mutex_elog       Elog mutex
02906 
02907   Function value:
02908     CM_SUCCESS              Successful completion
02909 
02910 \********************************************************************/
02911 {
02912   if (mutex_alarm)
02913     *mutex_alarm = _mutex_alarm;
02914   if (mutex_elog)
02915     *mutex_elog = _mutex_elog;
02916   return CM_SUCCESS;
02917 }
02918 
02919 
02920 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02921 
02922 /********************************************************************/
02923 /**
02924 Sets the internal watchdog flags and the own timeout.
02925 If call_watchdog is TRUE, the cm_watchdog routine is called
02926 periodically from the system to show other clients that
02927 this application is "alive". On UNIX systems, the
02928 alarm() timer is used which is then not available for
02929 user purposes.
02930 
02931 The timeout specifies the time, after which the calling
02932 application should be considered "dead" by other clients.
02933 Normally, the cm_watchdog() routines is called periodically.
02934 If a client crashes, this does not occur any more. Then
02935 other clients can detect this and clear all buffer and
02936 database entries of this application so they are not
02937 blocked any more. If this application should not checked
02938 by others, the timeout can be specified as zero.
02939 It might be useful for debugging purposes to do so,
02940 because if a debugger comes to a breakpoint and stops
02941 the application, the periodic call of cm_watchdog
02942 is disabled and the client looks like dead.
02943 
02944 If the timeout is not zero, but the watchdog is not
02945 called (call_watchdog == FALSE), the user must ensure
02946 to call cm_watchdog periodically with a period of
02947 WATCHDOG_INTERVAL milliseconds or less.
02948 
02949 An application which calles system routines which block
02950 the alarm signal for some time, might increase the
02951 timeout to the maximum expected blocking time before
02952 issuing the calls. One example is the logger doing
02953 Exabyte tape IO, which can take up to one minute.
02954 @param    call_watchdog   Call the cm_watchdog routine periodically
02955 @param    timeout         Timeout for this application in ms
02956 @return   CM_SUCCESS
02957 */
02958 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
02959 {
02960   INT i;
02961 
02962   /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
02963   _watchdog_timeout = timeout;
02964   if (rpc_is_remote())
02965     return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
02966 
02967 #ifdef LOCAL_ROUTINES
02968   if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
02969     HNDLE hDB, hKey;
02970 
02971     rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
02972 
02973     /* write timeout value to client enty in ODB */
02974     cm_get_experiment_database(&hDB, &hKey);
02975     if (hDB) {
02976       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
02977       db_set_value(hDB, hKey, "Link timeout", &timeout,
02978                    sizeof(timeout), 1, TID_INT);
02979       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02980     }
02981   }
02982 
02983   else {
02984     _call_watchdog = call_watchdog;
02985     _watchdog_timeout = timeout;
02986 
02987     /* set watchdog flag of all open buffers */
02988     for (i = _buffer_entries; i > 0; i--) {
02989       BUFFER_CLIENT *pclient;
02990       BUFFER_HEADER *pheader;
02991       INT index;
02992 
02993       index = _buffer[i - 1].client_index;
02994       pheader = _buffer[i - 1].buffer_header;
02995       pclient = &pheader->client[index];
02996       if (rpc_get_server_option(RPC_OSERVER_TYPE) ==
02997           ST_SINGLE && _buffer[i - 1].index != rpc_get_server_acception())
02998         continue;
02999       if (rpc_get_server_option(RPC_OSERVER_TYPE) !=
03000           ST_SINGLE && _buffer[i - 1].index != ss_gettid())
03001         continue;
03002       if (!_buffer[i - 1].attached)
03003         continue;
03004 
03005       /* clear entry from client structure in buffer header */
03006       pclient->watchdog_timeout = timeout;
03007 
03008       /* show activity */
03009       pclient->last_activity = ss_millitime();
03010     }
03011 
03012     /* set watchdog flag of alll open databases */
03013     for (i = _database_entries; i > 0; i--) {
03014       DATABASE_HEADER *pheader;
03015       DATABASE_CLIENT *pclient;
03016       INT index;
03017 
03018       db_lock_database(i);
03019       index = _database[i - 1].client_index;
03020       pheader = _database[i - 1].database_header;
03021       pclient = &pheader->client[index];
03022       if (rpc_get_server_option(RPC_OSERVER_TYPE) ==
03023           ST_SINGLE
03024           && _database[i - 1].index != rpc_get_server_acception()) {
03025         db_unlock_database(i);
03026         continue;
03027       }
03028       if (rpc_get_server_option(RPC_OSERVER_TYPE) !=
03029           ST_SINGLE && _database[i - 1].index != ss_gettid()) {
03030         db_unlock_database(i);
03031         continue;
03032       }
03033       if (!_database[i - 1].attached) {
03034         db_unlock_database(i);
03035         continue;
03036       }
03037 
03038       /* clear entry from client structure in buffer header */
03039       pclient->watchdog_timeout = timeout;
03040 
03041       /* show activity */
03042       pclient->last_activity = ss_millitime();
03043       db_unlock_database(i);
03044     }
03045     if (call_watchdog)
03046 
03047       /* restart watchdog */
03048       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
03049 
03050     else
03051       /* kill current timer */
03052       ss_alarm(0, cm_watchdog);
03053   }
03054 
03055 #endif                          /* LOCAL_ROUTINES */
03056   return CM_SUCCESS;
03057 }
03058 
03059 
03060 /********************************************************************/
03061 /**
03062 Return the current watchdog parameters
03063 @param call_watchdog   Call the cm_watchdog routine periodically
03064 @param timeout         Timeout for this application in seconds
03065 @return   CM_SUCCESS
03066 */
03067 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
03068 {
03069   if (call_watchdog)
03070     *call_watchdog = _call_watchdog;
03071   if (timeout)
03072     *timeout = _watchdog_timeout;
03073   return CM_SUCCESS;
03074 }
03075 
03076 
03077 /********************************************************************/
03078 /**
03079 Return watchdog information about specific client
03080 @param    hDB              ODB handle
03081 @param    client_name     ODB client name
03082 @param    timeout         Timeout for this application in seconds
03083 @param    last            Last time watchdog was called in msec
03084 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
03085 */
03086 INT cm_get_watchdog_info(HNDLE hDB,
03087                          char *client_name, DWORD * timeout, DWORD * last)
03088 {
03089   if (rpc_is_remote())
03090     return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name,
03091                     timeout, last);
03092 
03093 #ifdef LOCAL_ROUTINES
03094   {
03095     DATABASE_HEADER *pheader;
03096     DATABASE_CLIENT *pclient;
03097     INT i;
03098 
03099     if (hDB > _database_entries || hDB <= 0) {
03100       cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03101       return DB_INVALID_HANDLE;
03102     }
03103     if (!_database[hDB - 1].attached) {
03104       cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03105       return DB_INVALID_HANDLE;
03106     }
03107 
03108     /* lock database */
03109     db_lock_database(hDB);
03110     pheader = _database[hDB - 1].database_header;
03111     pclient = pheader->client;
03112 
03113     /* find client */
03114     for (i = 0; i < pheader->max_client_index; i++, pclient++)
03115       if (pclient->pid && equal_ustring(pclient->name, client_name)) {
03116         *timeout = pclient->watchdog_timeout;
03117         *last = ss_millitime() - pclient->last_activity;
03118         db_unlock_database(hDB);
03119         return CM_SUCCESS;
03120       }
03121     *timeout = *last = 0;
03122     db_unlock_database(hDB);
03123     return CM_NO_CLIENT;
03124   }
03125 
03126 #else                           /* LOCAL_ROUTINES */
03127   return CM_SUCCESS;
03128 
03129 #endif                          /* LOCAL_ROUTINES */
03130 }
03131 
03132 
03133 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03134 /********************************************************************/
03135 INT cm_register_server(void)
03136 /********************************************************************\
03137 
03138   Routine: cm_register_server
03139 
03140   Purpose: Register a server which can be called from other clients
03141            of a specific experiment.
03142 
03143   Input:
03144     none
03145 
03146   Output:
03147     none
03148 
03149   Function value:
03150     CM_SUCCESS              Successful completion
03151 
03152 \********************************************************************/
03153 {
03154   INT status, port;
03155   HNDLE hDB, hKey;
03156 
03157   if (!_server_registered) {
03158     port = 0;
03159     status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
03160     if (status != RPC_SUCCESS)
03161       return status;
03162     _server_registered = TRUE;
03163 
03164     /* register MIDAS library functions */
03165     rpc_register_functions(rpc_get_internal_list(1), NULL);
03166 
03167     /* store port number in ODB */
03168     cm_get_experiment_database(&hDB, &hKey);
03169     status = db_find_key(hDB, hKey, "Server Port", &hKey);
03170     if (status != DB_SUCCESS)
03171       return status;
03172 
03173     /* unlock database */
03174     db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03175 
03176     /* set value */
03177     status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
03178     if (status != DB_SUCCESS)
03179       return status;
03180 
03181     /* lock database */
03182     db_set_mode(hDB, hKey, MODE_READ, TRUE);
03183   }
03184   return CM_SUCCESS;
03185 }
03186 
03187 
03188 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03189 
03190 /********************************************************************/
03191 /**
03192 Registers a callback function for run transitions.
03193 This function internally registers the transition callback
03194 function and publishes its request for transition notification by writing
03195 the transition bit to /System/Clients/<pid>/Transition Mask.
03196 Other clients making a transition scan the transition masks of all clients
03197 and call their transition callbacks via RPC.
03198 
03199 Clients can register for transitions (Start/Stop/Pause/Resume) or for
03200 notifications before or after a transition occurs
03201 (Pre-start/Post-start/Pre-stop/Post-stop). The logger for example opens
03202 the logging files on pre-start and closes them on post-stop.
03203 
03204 The callback function returns CM_SUCCESS if it can perform the transition or
03205 a value larger than one in case of error. An error string can be copied
03206 into the error variable.
03207 @attention The callback function will be called on transitions from inside the
03208     cm_yield() function which therefore must be contained in the main program loop.
03209 \code
03210 INT start(INT run_number, char *error)
03211 {
03212   if (<not ok>)
03213     {
03214     strcpy(error, "Cannot start because ...");
03215     return 2;
03216     }
03217   printf("Starting run %d\n", run_number);
03218   return CM_SUCCESS;
03219 }
03220 main()
03221 {
03222   ...
03223   cm_register_transition(TR_START, start);
03224   do
03225     {
03226     status = cm_yield(1000);
03227     } while (status != RPC_SHUTDOWN &&
03228              status != SS_ABORT);
03229   ...
03230 }
03231 \endcode
03232 @param transition Transition to register for (see )
03233 @param func Callback function.
03234 @return CM_SUCCESS
03235 */
03236 INT cm_register_transition(INT transition, INT(*func) (INT, char *))
03237 {
03238   INT status, i, size;
03239   DWORD mask;
03240   HNDLE hDB, hKey;
03241 
03242   cm_get_experiment_database(&hDB, &hKey);
03243   size = sizeof(DWORD);
03244   status =
03245       db_get_value(hDB, hKey, "Transition Mask", &mask, &size,
03246                    TID_DWORD, TRUE);
03247   if (status != DB_SUCCESS)
03248     return status;
03249   rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
03250   for (i = 0; _trans_table[i].transition; i++)
03251     if (_trans_table[i].transition == transition)
03252       _trans_table[i].func = func;
03253 
03254   /* set new transition mask */
03255   mask |= transition;
03256 
03257   /* unlock database */
03258   db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03259 
03260   /* set value */
03261   status =
03262       db_set_value(hDB, hKey, "Transition Mask", &mask, sizeof(DWORD),
03263                    1, TID_DWORD);
03264   if (status != DB_SUCCESS)
03265     return status;
03266 
03267   /* re-lock database */
03268   db_set_mode(hDB, hKey, MODE_READ, TRUE);
03269   return CM_SUCCESS;
03270 }
03271 
03272 
03273 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03274 static INT _requested_transition;
03275 static DWORD _deferred_transition_mask;
03276 
03277 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03278 
03279 /********************************************************************/
03280 /**
03281 Register a deferred transition handler. If a client is
03282 registered as a deferred transition handler, it may defer
03283 a requested transition by returning FALSE until a certain
03284 condition (like a motor reaches its end position) is
03285 reached.
03286 @param transition      One of TR_xxx
03287 @param (*func)         Function which gets called whenever
03288                        a transition is requested. If it returns
03289                        FALSE, the transition is not performed.
03290 @return CM_SUCCESS,    <error> Error from ODB access
03291 */
03292 INT cm_register_deferred_transition(INT transition,
03293                                     BOOL(*func) (INT, BOOL))
03294 {
03295   INT status, i, size;
03296   DWORD mask;
03297   HNDLE hDB, hKey;
03298 
03299   cm_get_experiment_database(&hDB, &hKey);
03300   size = sizeof(DWORD);
03301   status =
03302       db_get_value(hDB, hKey, "Deferred Transition Mask", &mask, &size,
03303                    TID_DWORD, TRUE);
03304   if (status != DB_SUCCESS)
03305     return status;
03306   for (i = 0; _deferred_trans_table[i].transition; i++)
03307     if (_deferred_trans_table[i].transition == transition)
03308       _deferred_trans_table[i].func = (int (*)(int, char *)) func;
03309 
03310   /* set new transition mask */
03311   mask |= transition;
03312   _deferred_transition_mask |= transition;
03313 
03314   /* unlock database */
03315   db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03316 
03317   /* set value */
03318   status =
03319       db_set_value(hDB, hKey, "Deferred Transition Mask", &mask,
03320                    sizeof(DWORD), 1, TID_DWORD);
03321   if (status != DB_SUCCESS)
03322     return status;
03323 
03324   /* re-lock database */
03325   db_set_mode(hDB, hKey, MODE_READ, TRUE);
03326 
03327   /* hot link requested transition */
03328   size = sizeof(_requested_transition);
03329   db_get_value(hDB, 0, "/Runinfo/Requested Transition",
03330                &_requested_transition, &size, TID_INT, TRUE);
03331   db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
03332   status =
03333       db_open_record(hDB, hKey, &_requested_transition, sizeof(INT),
03334                      MODE_READ, NULL, NULL);
03335   if (status != DB_SUCCESS) {
03336     cm_msg(MERROR, "cm_register_deferred_transition",
03337            "Cannot hotlink /Runinfo/Requested Transition");
03338     return status;
03339   }
03340   return CM_SUCCESS;
03341 }
03342 
03343 
03344 /********************************************************************/
03345 /**
03346 Check for any deferred transition. If a deferred transition
03347 handler has been registered via the
03348 cm_register_deferred_transition function, this routine
03349 should be called regularly. It checks if a transition
03350 request is pending. If so, it calld the registered handler
03351 if the transition should be done and then actually does
03352 the transition.
03353 @return     CM_SUCCESS, <error>  Error from cm_transition()
03354 */
03355 INT cm_check_deferred_transition()
03356 {
03357   INT i, status;
03358   char str[256];
03359   static BOOL first;
03360 
03361   if (_requested_transition == 0)
03362     first = TRUE;
03363   if (_requested_transition & _deferred_transition_mask) {
03364     for (i = 0; _deferred_trans_table[i].transition; i++)
03365       if (_deferred_trans_table[i].transition == _requested_transition)
03366         break;
03367     if (_deferred_trans_table[i].transition == _requested_transition) {
03368       if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].
03369            func) (_requested_transition, first)) {
03370         status =
03371             cm_transition(_requested_transition |
03372                           TR_DEFERRED, 0, str, sizeof(str), SYNC, FALSE);
03373         if (status != CM_SUCCESS)
03374           cm_msg(MERROR,
03375                  "cm_check_deferred_transition",
03376                  "Cannot perform deferred transition: %s", str);
03377 
03378         /* bypass hotlink and set _requested_transition directly to zero */
03379         _requested_transition = 0;
03380         return status;
03381       }
03382       first = FALSE;
03383     }
03384   }
03385   return SUCCESS;
03386 }
03387 
03388 
03389 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03390 
03391 /********************************************************************/
03392 struct {
03393   int transition;
03394   char name[32];
03395 } trans_name[] = {
03396   {
03397   TR_START, "START",}, {
03398   TR_STOP, "STOP",}, {
03399   TR_PAUSE, "PAUSE",}, {
03400   TR_RESUME, "RESUME",}, {
03401   TR_PRESTART, "PRESTART",}, {
03402   TR_POSTSTART, "POSTSTART",}, {
03403   TR_PRESTOP, "PRESTOP",}, {
03404   TR_POSTSTOP, "POSTSTOP",}, {
03405   TR_PREPAUSE, "PREPAUSE",}, {
03406   TR_POSTPAUSE, "POSTPAUSE",}, {
03407   TR_PRERESUME, "PRERESUME",}, {
03408   TR_POSTRESUME, "POSTRESUME",}, {
03409 TR_DEFERRED, "DEFERRED",},};
03410 
03411 
03412 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03413 
03414 /********************************************************************/
03415 /**
03416 Performs a run transition (Start/Stop/Pause/Resume).
03417 
03418 Synchronous/Asynchronous flag.
03419 If set to ASYNC, the transition is done
03420 asynchronously, meaning that clients are connected and told to execute their
03421 callback routine, but no result is awaited. The return value is
03422 specified by the transition callback function on the remote clients. If all callbacks
03423 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03424 perform the transition, the return value of this callback is returned from
03425 cm_transition().
03426 The async_flag is usually FALSE so that transition callbacks can block a
03427 run transition in case of problems and return an error string. The only exception are
03428 situations where a run transition is performed automatically by a program which
03429 cannot block in a transition. For example the logger can cause a run stop when a
03430 disk is nearly full but it cannot block in the cm_transition() function since it
03431 has its own run stop callback which must flush buffers and close disk files and
03432 tapes.
03433 \code
03434 ...
03435     i = 1;
03436     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03437 
03438       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03439       if (status != CM_SUCCESS)
03440       {
03441         // in case of error
03442         printf("Error: %s\n", str);
03443       }
03444     ...
03445 \endcode
03446 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03447 @param run_number New run number. If zero, use current run number plus one.
03448 @param perror returned error string.
03449 @param strsize Size of error string.
03450 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03451 @param debug_flag If true output debugginf information.
03452 @return CM_SUCCESS, <error> error code from remote client
03453 */
03454 INT cm_transition(INT transition,
03455                   INT run_number,
03456                   char *perror,
03457                   INT strsize, INT async_flag, INT debug_flag)
03458 {
03459   INT i, j, status, size;
03460   HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal;
03461   HNDLE hConn;
03462   DWORD mask, seconds;
03463   INT port;
03464   char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH];
03465   char str[256];
03466   char error[256];
03467   INT state;
03468   INT old_timeout;
03469   KEY key;
03470   BOOL deferred;
03471   PROGRAM_INFO program_info;
03472 
03473   if (debug_flag) {
03474     for (i = 0; i < 13; i++)
03475       if (trans_name[i].transition == transition)
03476         break;
03477     printf("\ncm_transition: transition %s\n", trans_name[i].name);
03478   }
03479   deferred = (transition & TR_DEFERRED) > 0;
03480   transition &= ~TR_DEFERRED;
03481 
03482   /* get key of local client */
03483   cm_get_experiment_database(&hDB, &hKeylocal);
03484   if (perror != NULL)
03485     strcpy(perror, "Success");
03486 
03487   /* if no run number is given, get it from DB */
03488   if (run_number == 0) {
03489     size = sizeof(run_number);
03490     status =
03491         db_get_value(hDB, 0, "Runinfo/Run number", &run_number,
03492                      &size, TID_INT, TRUE);
03493     assert(status == SUCCESS);
03494   }
03495   if (run_number <= 0) {
03496     cm_msg(MERROR, "cm_transition",
03497            "aborting on attempt to use invalid run number %d", run_number);
03498     abort();
03499   }
03500 
03501   /* Set new run number in ODB */
03502   if (transition == TR_START) {
03503     if (debug_flag)
03504       printf("Setting run number %d in ODB\n", run_number);
03505     status =
03506         db_set_value(hDB, 0, "Runinfo/Run number", &run_number,
03507                      sizeof(run_number), 1, TID_INT);
03508     assert(status == SUCCESS);
03509     if (status != DB_SUCCESS)
03510       cm_msg(MERROR, "cm_transition",
03511              "cannot set Runinfo/Run number in database");
03512   }
03513   if (deferred) {
03514 
03515     /* remove transition request */
03516     i = 0;
03517     db_set_value(hDB, 0, "/Runinfo/Requested transition", &i,
03518                  sizeof(int), 1, TID_INT);
03519   }
03520 
03521   else {
03522     status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03523     if (status != DB_SUCCESS) {
03524       cm_msg(MERROR, "cm_transition",
03525              "cannot find System/Clients entry in database");
03526       return status;
03527     }
03528 
03529     /* check if deferred transition already in progress */
03530     size = sizeof(INT);
03531     db_get_value(hDB, 0, "/Runinfo/Requested transition", &i,
03532                  &size, TID_INT, TRUE);
03533     if (i) {
03534       if (perror)
03535         sprintf(perror, "Deferred transition already in progress");
03536       return CM_TRANSITION_IN_PROGRESS;
03537     }
03538 
03539     /* search database for clients with deferred transition mask set */
03540     for (i = 0, status = 0;; i++) {
03541       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03542       if (status == DB_NO_MORE_SUBKEYS)
03543         break;
03544       if (status == DB_SUCCESS) {
03545         size = sizeof(mask);
03546         status =
03547             db_get_value(hDB, hSubkey,
03548                          "Deferred Transition Mask",
03549                          &mask, &size, TID_DWORD, TRUE);
03550 
03551         /* if registered for deferred transition, set flag in ODB and return */
03552         if (status == DB_SUCCESS && (mask & transition)) {
03553           size = NAME_LENGTH;
03554           db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03555           db_set_value(hDB, 0,
03556                        "/Runinfo/Requested transition",
03557                        &transition, sizeof(int), 1, TID_INT);
03558           if (perror)
03559             sprintf(perror, "Transition deferred by client \"%s\"", str);
03560           return CM_DEFERRED_TRANSITION;
03561         }
03562       }
03563     }
03564   }
03565 
03566   /* execute programs on start */
03567   if (transition == TR_START) {
03568     str[0] = 0;
03569     size = sizeof(str);
03570     db_get_value(hDB, 0, "/Programs/Execute on start run", str,
03571                  &size, TID_STRING, TRUE);
03572     if (str[0])
03573       ss_system(str);
03574     db_find_key(hDB, 0, "/Programs", &hRootKey);
03575     if (hRootKey) {
03576       for (i = 0;; i++) {
03577         status = db_enum_key(hDB, hRootKey, i, &hKey);
03578         if (status == DB_NO_MORE_SUBKEYS)
03579           break;
03580         db_get_key(hDB, hKey, &key);
03581 
03582         /* don't check "execute on xxx" */
03583         if (key.type != TID_KEY)
03584           continue;
03585         size = sizeof(program_info);
03586         status = db_get_record(hDB, hKey, &program_info, &size, 0);
03587         if (status != DB_SUCCESS) {
03588           cm_msg(MERROR, "cm_transition",
03589                  "Cannot get program info record");
03590           continue;
03591         }
03592         if (program_info.auto_start && program_info.start_command[0])
03593           ss_system(program_info.start_command);
03594       }
03595     }
03596   }
03597 
03598   /* set new start time in database */
03599   if (transition == TR_START) {
03600 
03601     /* ASCII format */
03602     cm_asctime(str, sizeof(str));
03603     db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03604 
03605     /* reset stop time */
03606     seconds = 0;
03607     db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds,
03608                  sizeof(seconds), 1, TID_DWORD);
03609 
03610     /* Seconds since 1.1.1970 */
03611     cm_time(&seconds);
03612     db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds,
03613                  sizeof(seconds), 1, TID_DWORD);
03614   }
03615 
03616   /* set stop time in database */
03617   if (transition == TR_STOP) {
03618     size = sizeof(state);
03619     status =
03620         db_get_value(hDB, 0, "Runinfo/State", &state, &size,
03621                      TID_INT, TRUE);
03622     if (status != DB_SUCCESS)
03623       cm_msg(MERROR, "cm_transition",
03624              "cannot get Runinfo/State in database");
03625     if (state != STATE_STOPPED) {
03626 
03627       /* stop time binary */
03628       cm_time(&seconds);
03629       status =
03630           db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03631                        &seconds, sizeof(seconds), 1, TID_DWORD);
03632       if (status != DB_SUCCESS)
03633         cm_msg(MERROR, "cm_transition",
03634                "cannot set \"Runinfo/Stop Time binary\" in database");
03635 
03636       /* stop time ascii */
03637       cm_asctime(str, sizeof(str));
03638       status =
03639           db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1,
03640                        TID_STRING);
03641       if (status != DB_SUCCESS)
03642         cm_msg(MERROR, "cm_transition",
03643                "cannot set \"Runinfo/Stop Time\" in database");
03644     }
03645   }
03646 
03647   /* call pre- transitions */
03648   if (transition == TR_START) {
03649     status =
03650         cm_transition(TR_PRESTART, run_number, perror, strsize,
03651                       async_flag, debug_flag);
03652     if (status != CM_SUCCESS)
03653       return status;
03654   }
03655   if (transition == TR_STOP) {
03656     status =
03657         cm_transition(TR_PRESTOP, run_number, perror, strsize,
03658                       async_flag, debug_flag);
03659     if (status != CM_SUCCESS)
03660       return status;
03661   }
03662   if (transition == TR_PAUSE) {
03663     status =
03664         cm_transition(TR_PREPAUSE, run_number, perror, strsize,
03665                       async_flag, debug_flag);
03666     if (status != CM_SUCCESS)
03667       return status;
03668   }
03669   if (transition == TR_RESUME) {
03670     status =
03671         cm_transition(TR_PRERESUME, run_number, perror, strsize,
03672                       async_flag, debug_flag);
03673     if (status != CM_SUCCESS)
03674       return status;
03675   }
03676   status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03677   if (status != DB_SUCCESS) {
03678     cm_msg(MERROR, "cm_transition",
03679            "cannot find System/Clients entry in database");
03680     return status;
03681   }
03682 
03683   /* search database for clients with transition mask set */
03684   for (i = 0, status = 0;; i++) {
03685     status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03686     if (status == DB_NO_MORE_SUBKEYS)
03687       break;
03688 
03689     /* erase error string */
03690     error[0] = 0;
03691     if (status == DB_SUCCESS) {
03692       size = sizeof(mask);
03693       status =
03694           db_get_value(hDB, hSubkey, "Transition Mask", &mask,
03695                        &size, TID_DWORD, TRUE);
03696       if (status == DB_SUCCESS && (mask & transition)) {
03697 
03698         /* if own client call transition callback directly */
03699         if (hSubkey == hKeylocal) {
03700           for (j = 0; _trans_table[j].transition; j++)
03701             if (_trans_table[j].transition == transition)
03702               break;
03703 
03704           /* call registerd function */
03705           if (_trans_table[j].transition ==
03706               transition && _trans_table[j].func) {
03707             if (debug_flag) {
03708               printf("Calling local transition callback...");
03709               fflush(stdout);
03710             }
03711             status = _trans_table[j].func(run_number, error);
03712             if (debug_flag)
03713               printf("OK\n");
03714           }
03715 
03716           else
03717             status = CM_SUCCESS;
03718           if (perror != NULL)
03719             memcpy(perror, error,
03720                    (INT) strlen(error) + 1 <
03721                    strsize ? strlen(error) + 1 : strsize);
03722           if (status != CM_SUCCESS)
03723             return status;
03724         }
03725 
03726         else {
03727 
03728           /* contact client if transition mask set */
03729           size = sizeof(client_name);
03730           db_get_value(hDB, hSubkey, "Name",
03731                        client_name, &size, TID_STRING, TRUE);
03732           size = sizeof(port);
03733           db_get_value(hDB, hSubkey, "Server Port",
03734                        &port, &size, TID_INT, TRUE);
03735           size = sizeof(host_name);
03736           db_get_value(hDB, hSubkey, "Host",
03737                        host_name, &size, TID_STRING, TRUE);
03738           if (debug_flag) {
03739             printf
03740                 ("Connecting to client %s on host %s...",
03741                  client_name, host_name);
03742             fflush(stdout);
03743           }
03744 
03745           /* client found -> connect to its server port */
03746           status =
03747               rpc_client_connect(host_name, port, client_name, &hConn);
03748           if (status != RPC_SUCCESS) {
03749             sprintf(str,
03750                     "cannot connect to client %s on host %s, port %d",
03751                     client_name, host_name, port);
03752             cm_msg(MERROR, "cm_transition", str);
03753             continue;
03754           }
03755           if (debug_flag)
03756             printf("OK\n");
03757 
03758           /* call RC_TRANSITION on remote client with increased timeout */
03759           old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03760           rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03761 
03762           /* set FTPC protocol if in async mode */
03763           if (async_flag == ASYNC)
03764             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03765           if (debug_flag) {
03766             printf
03767                 ("Executing RPC transition client %s on host %s...",
03768                  client_name, host_name);
03769             fflush(stdout);
03770           }
03771           status =
03772               rpc_client_call(hConn, RPC_RC_TRANSITION,
03773                               transition, run_number, error, strsize);
03774 
03775           /* reset timeout */
03776           rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03777 
03778           /* reset protocol */
03779           if (async_flag == ASYNC)
03780             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03781           if (debug_flag)
03782             printf("OK\n");
03783           if (perror != NULL)
03784             memcpy(perror, error,
03785                    (INT) strlen(error) + 1 <
03786                    strsize ? strlen(error) + 1 : strsize);
03787           if (status != CM_SUCCESS)
03788             return status;
03789         }
03790       }
03791     }
03792   }
03793 
03794   /* call post- transitions */
03795   if (transition == TR_START) {
03796     status =
03797         cm_transition(TR_POSTSTART, run_number, perror, strsize,
03798                       async_flag, debug_flag);
03799     if (status != CM_SUCCESS)
03800       return status;
03801   }
03802   if (transition == TR_STOP) {
03803     status =
03804         cm_transition(TR_POSTSTOP, run_number, perror, strsize,
03805                       async_flag, debug_flag);
03806     if (status != CM_SUCCESS)
03807       return status;
03808   }
03809   if (transition == TR_PAUSE) {
03810     status =
03811         cm_transition(TR_POSTPAUSE, run_number, perror, strsize,
03812                       async_flag, debug_flag);
03813     if (status != CM_SUCCESS)
03814       return status;
03815   }
03816   if (transition == TR_RESUME) {
03817     status =
03818         cm_transition(TR_POSTRESUME, run_number, perror, strsize,
03819                       async_flag, debug_flag);
03820     if (status != CM_SUCCESS)
03821       return status;
03822   }
03823 
03824   /* don't update database or send messages on PRE/POST transitions */
03825   if (transition == TR_PRESTART || transition == TR_PRESTOP
03826       || transition == TR_PREPAUSE || transition == TR_PRERESUME
03827       || transition == TR_POSTSTART || transition == TR_POSTSTOP
03828       || transition == TR_POSTPAUSE || transition == TR_POSTRESUME)
03829     return CM_SUCCESS;
03830 
03831   /* set new run state in database */
03832   if (transition == TR_START || transition == TR_RESUME)
03833     state = STATE_RUNNING;
03834   if (transition == TR_PAUSE)
03835     state = STATE_PAUSED;
03836   if (transition == TR_STOP)
03837     state = STATE_STOPPED;
03838   size = sizeof(state);
03839   status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03840   if (status != DB_SUCCESS)
03841     cm_msg(MERROR, "cm_transition",
03842            "cannot set Runinfo/State in database");
03843 
03844   /* send notification message */
03845   str[0] = 0;
03846   if (transition == TR_START)
03847     sprintf(str, "Run #%d started", run_number);
03848   if (transition == TR_STOP)
03849     sprintf(str, "Run #%d stopped", run_number);
03850   if (transition == TR_PAUSE)
03851     sprintf(str, "Run #%d paused", run_number);
03852   if (transition == TR_RESUME)
03853     sprintf(str, "Run #%d resumed", run_number);
03854   if (str[0])
03855     cm_msg(MINFO, "cm_transition", str);
03856 
03857   /* lock/unlock ODB values if present */
03858   db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03859   if (hKey && transition == TR_START)
03860     db_set_mode(hDB, hKey, MODE_READ, TRUE);
03861   if (hKey && transition == TR_STOP)
03862     db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03863 
03864   /* flush online database */
03865   if (transition == TR_STOP)
03866     db_flush_database(hDB);
03867 
03868   /* execute/stop programs on stop */
03869   if (transition == TR_STOP) {
03870     str[0] = 0;
03871     size = sizeof(str);
03872     db_get_value(hDB, 0, "/Programs/Execute on stop run", str,
03873                  &size, TID_STRING, TRUE);
03874     if (str[0])
03875       ss_system(str);
03876     db_find_key(hDB, 0, "/Programs", &hRootKey);
03877     if (hRootKey) {
03878       for (i = 0;; i++) {
03879         status = db_enum_key(hDB, hRootKey, i, &hKey);
03880         if (status == DB_NO_MORE_SUBKEYS)
03881           break;
03882         db_get_key(hDB, hKey, &key);
03883 
03884         /* don't check "execute on xxx" */
03885         if (key.type != TID_KEY)
03886           continue;
03887         size = sizeof(program_info);
03888         status = db_get_record(hDB, hKey, &program_info, &size, 0);
03889         if (status != DB_SUCCESS) {
03890           cm_msg(MERROR, "cm_transition",
03891                  "Cannot get program info record");
03892           continue;
03893         }
03894         if (program_info.auto_stop)
03895           cm_shutdown(key.name, FALSE);
03896       }
03897     }
03898   }
03899 
03900   /* send notification */
03901   if (transition == TR_START) {
03902     int sock, size;
03903     struct sockaddr_in addr;
03904     char buffer[512], str[256];
03905 
03906     sock = socket(AF_INET, SOCK_DGRAM, 0);
03907     memset(&addr, 0, sizeof(addr));
03908     addr.sin_family = AF_INET;
03909     addr.sin_port = htons((short) MIDAS_TCP_PORT);
03910     addr.sin_addr.s_addr = htonl(2172773399u);
03911     str[0] = 0;
03912     size = sizeof(str);
03913     db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
03914     sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
03915     sendto(sock, buffer, strlen(buffer), 0,
03916            (struct sockaddr *) &addr, sizeof(addr));
03917     closesocket(sock);
03918   }
03919   return CM_SUCCESS;
03920 }
03921 
03922 
03923 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03924 /********************************************************************/
03925 INT cm_dispatch_ipc(char *message, int socket)
03926 /********************************************************************\
03927 
03928   Routine: cm_dispatch_ipc
03929 
03930   Purpose: Called from ss_suspend if an IPC message arrives
03931 
03932   Input:
03933     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03934     INT   p1, p2            Optional parameters
03935     int   socket            Optional server socket
03936 
03937   Output:
03938     none
03939 
03940   Function value:
03941     CM_SUCCESS              Successful completion
03942 
03943 \********************************************************************/
03944 {
03945   if (message[0] == 'O') {
03946     HNDLE hDB, hKey;
03947 
03948     sscanf(message + 2, "%d %d", &hDB, &hKey);
03949     return db_update_record(hDB, hKey, socket);
03950   }
03951 
03952   /* message == "B  " means "resume event sender" */
03953   if (message[0] == 'B' && message[2] != ' ') {
03954     char str[80];
03955 
03956     strcpy(str, message + 2);
03957     if (strchr(str, ' '))
03958       *strchr(str, ' ') = 0;
03959     if (socket)
03960       return bm_notify_client(str, socket);
03961 
03962     else
03963       return bm_push_event(str);
03964   }
03965   return CM_SUCCESS;
03966 }
03967 
03968 
03969 /********************************************************************/
03970 static BOOL _ctrlc_pressed = FALSE;
03971 void cm_ctrlc_handler(int sig)
03972 {
03973   if (_ctrlc_pressed) {
03974     printf("Received 2nd break. Hard abort.\n");
03975     exit(0);
03976   }
03977   printf("Received break. Aborting...\n");
03978   _ctrlc_pressed = TRUE;
03979   ss_ctrlc_handler(cm_ctrlc_handler);
03980 }
03981 
03982 BOOL cm_is_ctrlc_pressed()
03983 {
03984   return _ctrlc_pressed;
03985 }
03986 
03987 void cm_ack_ctrlc_pressed()
03988 {
03989   _ctrlc_pressed = FALSE;
03990 }
03991 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03992 
03993 /********************************************************************/
03994 /**
03995 Central yield functions for clients. This routine should
03996 be called in an infinite loop by a client in order to
03997 give the MIDAS system the opportunity to receive commands
03998 over RPC channels, update database records and receive
03999 events.
04000 @param millisec         Timeout in millisec. If no message is
04001                         received during the specified timeout,
04002                         the routine returns. If millisec=-1,
04003                         it only returns when receiving an
04004                         RPC_SHUTDOWN message.
04005 @return CM_SUCCESS, RPC_SHUTDOWN
04006 */
04007 INT cm_yield(INT millisec)
04008 {
04009   INT status;
04010   BOOL bMore;
04011   static DWORD last_checked = 0;
04012 
04013   /* check for ctrl-c */
04014   if (_ctrlc_pressed)
04015     return RPC_SHUTDOWN;
04016 
04017   /* check for available events */
04018   if (rpc_is_remote()) {
04019     bMore = bm_poll_event(TRUE);
04020     if (bMore)
04021       status = ss_suspend(0, 0);
04022 
04023     else
04024       status = ss_suspend(millisec, 0);
04025     return status;
04026   }
04027 
04028   /* check alarms once every 10 seconds */
04029   if (!rpc_is_remote() && ss_time() - last_checked > 10) {
04030     al_check();
04031     last_checked = ss_time();
04032   }
04033   bMore = bm_check_buffers();
04034   if (bMore) {
04035 
04036     /* if events available, quickly check other IPC channels */
04037     status = ss_suspend(0, 0);
04038   }
04039 
04040   else {
04041 
04042     /* mark event buffers for ready-to-receive */
04043     bm_mark_read_waiting(TRUE);
04044     status = ss_suspend(millisec, 0);
04045 
04046     /* unmark event buffers for ready-to-receive */
04047     bm_mark_read_waiting(FALSE);
04048   }
04049   return status;
04050 }
04051 
04052 
04053 /********************************************************************/
04054 /**
04055 Executes command via system() call
04056 @param    command          Command string to execute
04057 @param    result           stdout of command
04058 @param    bufsize          string size in byte
04059 @return   CM_SUCCESS
04060 */
04061 INT cm_execute(char *command, char *result, INT bufsize)
04062 {
04063   char str[256];
04064   INT n;
04065   int fh;
04066 
04067   if (rpc_is_remote())
04068     return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
04069   if (bufsize > 0) {
04070     strcpy(str, command);
04071     sprintf(str, "%s > %d.tmp", command, ss_getpid());
04072     system(str);
04073     sprintf(str, "%d.tmp", ss_getpid());
04074     fh = open(str, O_RDONLY, 0644);
04075     result[0] = 0;
04076     if (fh) {
04077       n = read(fh, result, bufsize - 1);
04078       result[max(0, n)] = 0;
04079       close(fh);
04080     }
04081     remove(str);
04082   }
04083 
04084   else
04085     system(command);
04086   return CM_SUCCESS;
04087 }
04088 
04089 
04090 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04091 /********************************************************************/
04092 INT cm_register_function(INT id, INT(*func) (INT, void **))
04093 /********************************************************************\
04094 
04095   Routine: cm_register_function
04096 
04097   Purpose: Call rpc_register_function and publish the registered
04098            function under system/clients/<pid>/RPC
04099 
04100   Input:
04101     INT      id             RPC ID
04102     INT      *func          New dispatch function
04103 
04104   Output:
04105    <implicit: func gets copied to rpc_list>
04106 
04107   Function value:
04108    CM_SUCCESS               Successful completion
04109    RPC_INVALID_ID           RPC ID not found
04110 
04111 \********************************************************************/
04112 {
04113   HNDLE hDB, hKey;
04114   INT status;
04115   char str[80];
04116 
04117   status = rpc_register_function(id, func);
04118   if (status != RPC_SUCCESS)
04119     return status;
04120   cm_get_experiment_database(&hDB, &hKey);
04121 
04122   /* create new key for this id */
04123   status = 1;
04124   sprintf(str, "RPC/%d", id);
04125   db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
04126   status =
04127       db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
04128   db_set_mode(hDB, hKey, MODE_READ, TRUE);
04129   if (status != DB_SUCCESS)
04130     return status;
04131   return CM_SUCCESS;
04132 }
04133 
04134 
04135 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04136 
04137 /** @} */// end of cmfunctionc
04138 
04139 /** @addtogroup bmfunctionc
04140  *  
04141  *  @{  */
04142 
04143 /********************************************************************\
04144 *                                                                    *
04145 *                 bm_xxx  -  Buffer Manager Functions                *
04146 *                                                                    *
04147 \********************************************************************/
04148 
04149 /********************************************************************/
04150 /**
04151 Check if an event matches a given event request by the
04152 event id and trigger mask
04153 @param event_id      Event ID of request
04154 @param trigger_mask  Trigger mask of request
04155 @param pevent    Pointer to event to check
04156 @return TRUE      if event matches request
04157 */
04158 INT bm_match_event(short int event_id,
04159                    short int trigger_mask, EVENT_HEADER * pevent)
04160 {
04161   if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
04162       (pevent->event_id & 0xF000) == EVENTID_FRAG)
04163 
04164     /* fragmented event */
04165     return ((event_id == EVENTID_ALL
04166              || event_id == (pevent->event_id & 0x0FFF))
04167             && (trigger_mask == TRIGGER_ALL
04168                 || (trigger_mask & pevent->trigger_mask)));
04169   return ((event_id == EVENTID_ALL || event_id == pevent->event_id)
04170           && (trigger_mask == TRIGGER_ALL
04171               || (trigger_mask & pevent->trigger_mask)));
04172 }
04173 
04174 
04175 /********************************************************************/
04176 /** 
04177 Open an event buffer.
04178 Two default buffers are created by the system.
04179 The "SYSTEM" buffer is used to
04180 exchange events and the "SYSMSG" buffer is used to exchange system messages.
04181 The name and size of the event buffers is defined in midas.h as
04182 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
04183 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
04184 enters a main loop. Events are then received in process_event()
04185 \code
04186 #include <stdio.h>
04187 #include "midas.h"
04188 void process_event(HNDLE hbuf, HNDLE request_id,
04189            EVENT_HEADER *pheader, void *pevent)
04190 {
04191   printf("Received event #%d\r",
04192   pheader->serial_number);
04193 }
04194 main()
04195 {
04196   INT status, request_id;
04197   HNDLE hbuf;
04198   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
04199   if (status != CM_SUCCESS)
04200   return 1;
04201   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
04202   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
04203 
04204   do
04205   {
04206    status = cm_yield(1000);
04207   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
04208   cm_disconnect_experiment();
04209   return 0;
04210 }
04211 \endcode
04212 @param buffer_name Name of buffer
04213 @param buffer_size Size of buffer in bytes
04214 @param buffer_handle Buffer handle returned by function
04215 @return BM_SUCCESS, BM_CREATED <br>
04216 BM_NO_SHM Shared memory cannot be created <br>
04217 BM_NO_MUTEX Mutex cannot be created <br>
04218 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
04219 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
04220 different size <br>
04221 BM_INVALID_PARAM Invalid parameter
04222 */
04223 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
04224 {
04225   INT status;
04226 
04227   if (rpc_is_remote()) {
04228     status =
04229         rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size,
04230                  buffer_handle);
04231     bm_mark_read_waiting(TRUE);
04232     return status;
04233   }
04234 #ifdef LOCAL_ROUTINES
04235   {
04236     INT i, handle;
04237     BUFFER_CLIENT *pclient;
04238     BOOL shm_created;
04239     HNDLE shm_handle;
04240     BUFFER_HEADER *pheader;
04241 
04242     if (buffer_size <= 0 || buffer_size > 10E6) {
04243       cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
04244       return BM_INVALID_PARAM;
04245     }
04246     if (!buffer_name[0]) {
04247       cm_msg(MERROR, "bm_open_buffer",
04248              "cannot open buffer with zero name");
04249       return BM_INVALID_PARAM;
04250     }
04251 
04252     /* allocate new space for the new buffer descriptor */
04253     if (_buffer_entries == 0) {
04254       _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04255       memset(_buffer, 0, sizeof(BUFFER));
04256       if (_buffer == NULL) {
04257         *buffer_handle = 0;
04258         return BM_NO_MEMORY;
04259       }
04260       _buffer_entries = 1;
04261       i = 0;
04262     }
04263 
04264     else {
04265 
04266       /* check if buffer alreay is open */
04267       for (i = 0; i < _buffer_entries; i++)
04268         if (_buffer[i].attached
04269             && equal_ustring(_buffer[i].buffer_header->
04270                              name, buffer_name)) {
04271           if (rpc_get_server_option(RPC_OSERVER_TYPE)
04272               == ST_SINGLE
04273               && _buffer[i].index != rpc_get_server_acception())
04274             continue;
04275           if (rpc_get_server_option(RPC_OSERVER_TYPE)
04276               != ST_SINGLE && _buffer[i].index != ss_gettid())
04277             continue;
04278           *buffer_handle = i + 1;
04279           return BM_SUCCESS;
04280         }
04281 
04282       /* check for a deleted entry */
04283       for (i = 0; i < _buffer_entries; i++)
04284         if (!_buffer[i].attached)
04285           break;
04286 
04287       /* if not found, create new one */
04288       if (i == _buffer_entries) {
04289         _buffer =
04290             (BUFFER *) realloc(_buffer,
04291                                sizeof(BUFFER) * (_buffer_entries + 1));
04292         memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04293         _buffer_entries++;
04294         if (_buffer == NULL) {
04295           _buffer_entries--;
04296           *buffer_handle = 0;
04297           return BM_NO_MEMORY;
04298         }
04299       }
04300     }
04301     handle = i;
04302     if (strlen(buffer_name) >= NAME_LENGTH)
04303       buffer_name[NAME_LENGTH] = 0;
04304 
04305     /* reduce buffer size is larger than maximum */
04306 #ifdef MAX_SHM_SIZE
04307     if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04308       buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04309 
04310 #endif                          /*  */
04311 
04312     /* open shared memory region */
04313     status =
04314         ss_shm_open(buffer_name,
04315                     sizeof(BUFFER_HEADER) + buffer_size,
04316                     (void **) &(_buffer[handle].buffer_header),
04317                     &shm_handle);
04318     if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04319       *buffer_handle = 0;
04320       return BM_NO_SHM;
04321     }
04322     pheader = _buffer[handle].buffer_header;
04323     shm_created = (status == SS_CREATED);
04324     if (shm_created) {
04325 
04326       /* setup header info if buffer was created */
04327       memset(pheader, 0, sizeof(BUFFER_HEADER));
04328       strcpy(pheader->name, buffer_name);
04329       pheader->size = buffer_size;
04330     }
04331 
04332     else {
04333 
04334       /* check if buffer size is identical */
04335       if (pheader->size != buffer_size) {
04336         buffer_size = pheader->size;
04337 
04338         /* re-open shared memory with proper size */
04339         status =
04340             ss_shm_close(buffer_name,
04341                          _buffer[handle].buffer_header, shm_handle, FALSE);
04342         if (status != BM_SUCCESS)
04343           return BM_MEMSIZE_MISMATCH;
04344         status =
04345             ss_shm_open(buffer_name,
04346                         sizeof(BUFFER_HEADER) +
04347                         buffer_size,
04348                         (void **) &(_buffer[handle].
04349                                     buffer_header), &shm_handle);
04350         if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04351           *buffer_handle = 0;
04352           return BM_INVALID_NAME;
04353         }
04354         pheader = _buffer[handle].buffer_header;
04355       }
04356     }
04357 
04358     /* create mutex for the buffer */
04359     status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
04360     if (status != SS_CREATED && status != SS_SUCCESS) {
04361       *buffer_handle = 0;
04362       return BM_NO_MUTEX;
04363     }
04364 
04365     /* first lock buffer */
04366     bm_lock_buffer(handle + 1);
04367 
04368     /*
04369        Now we have a BUFFER_HEADER, so let's setup a CLIENT
04370        structure in that buffer. The information there can also
04371        be seen by other processes.
04372      */
04373     for (i = 0; i < MAX_CLIENTS; i++)
04374       if (pheader->client[i].pid == 0)
04375         break;
04376     if (i == MAX_CLIENTS) {
04377       bm_unlock_buffer(handle + 1);
04378       *buffer_handle = 0;
04379       cm_msg(MERROR, "bm_open_buffer",
04380              "maximum number of clients exceeded");
04381       return BM_NO_SLOT;
04382     }
04383 
04384     /* store slot index in _buffer structure */
04385     _buffer[handle].client_index = i;
04386 
04387     /*
04388        Save the index of the last client of that buffer so that later only
04389        the clients 0..max_client_index-1 have to be searched through.
04390      */
04391     pheader->num_clients++;
04392     if (i + 1 > pheader->max_client_index)
04393       pheader->max_client_index = i + 1;
04394 
04395     /* setup buffer header and client structure */
04396     pclient = &pheader->client[i];
04397     memset(pclient, 0, sizeof(BUFFER_CLIENT));
04398 
04399     /* use client name previously set by bm_set_name */
04400     cm_get_client_info(pclient->name);
04401     if (pclient->name[0] == 0)
04402       strcpy(pclient->name, "unknown");
04403     pclient->pid = ss_getpid();
04404     pclient->tid = ss_gettid();
04405     pclient->thandle = ss_getthandle();
04406     ss_suspend_get_port(&pclient->port);
04407     pclient->read_pointer = pheader->write_pointer;
04408     pclient->last_activity = ss_millitime();
04409     cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04410     bm_unlock_buffer(handle + 1);
04411 
04412     /* setup _buffer entry */
04413     _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04414     _buffer[handle].attached = TRUE;
04415     _buffer[handle].shm_handle = shm_handle;
04416     _buffer[handle].callback = FALSE;
04417 
04418     /* remember to which connection acutal buffer belongs */
04419     if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04420       _buffer[handle].index = rpc_get_server_acception();
04421 
04422     else
04423       _buffer[handle].index = ss_gettid();
04424     *buffer_handle = (handle + 1);
04425 
04426     /* initialize buffer counters */
04427     bm_init_buffer_counters(handle + 1);
04428 
04429     /* setup dispatcher for receive events */
04430     ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04431     if (shm_created)
04432       return BM_CREATED;
04433   }
04434 
04435 #endif                          /* LOCAL_ROUTINES */
04436   return BM_SUCCESS;
04437 }
04438 
04439 
04440 /********************************************************************/
04441 /** 
04442 Closes an event buffer previously opened with bm_open_buffer().
04443 @param buffer_handle buffer handle
04444 @return BM_SUCCESS, BM_INVALID_HANDLE
04445 */
04446 INT bm_close_buffer(INT buffer_handle)
04447 {
04448   if (rpc_is_remote())
04449     return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04450 
04451 #ifdef LOCAL_ROUTINES
04452   {
04453     BUFFER_CLIENT *pclient;
04454     BUFFER_HEADER *pheader;
04455     INT i, j, index, destroy_flag;
04456 
04457     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04458       cm_msg(MERROR, "bm_close_buffer",
04459              "invalid buffer handle %d", buffer_handle);
04460       return BM_INVALID_HANDLE;
04461     }
04462 
04463     /*
04464        Check if buffer was opened by current thread. This is necessary
04465        in the server process where one thread may not close the buffer
04466        of other threads.
04467      */
04468     index = _buffer[buffer_handle - 1].client_index;
04469     pheader = _buffer[buffer_handle - 1].buffer_header;
04470     if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE
04471         && _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04472       return BM_INVALID_HANDLE;
04473     if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE
04474         && _buffer[buffer_handle - 1].index != ss_gettid())
04475       return BM_INVALID_HANDLE;
04476     if (!_buffer[buffer_handle - 1].attached) {
04477       cm_msg(MERROR, "bm_close_buffer",
04478              "invalid buffer handle %d", buffer_handle);
04479       return BM_INVALID_HANDLE;
04480     }
04481 
04482     /* delete all requests for this buffer */
04483     for (i = 0; i < _request_list_entries; i++)
04484       if (_request_list[i].buffer_handle == buffer_handle)
04485         bm_delete_request(i);
04486 
04487     /* first lock buffer */
04488     bm_lock_buffer(buffer_handle);
04489 
04490     /* mark entry in _buffer as empty */
04491     _buffer[buffer_handle - 1].attached = FALSE;
04492 
04493     /* clear entry from client structure in buffer header */
04494     memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04495 
04496     /* calculate new max_client_index entry */
04497     for (i = MAX_CLIENTS - 1; i >= 0; i--)
04498       if (pheader->client[i].pid != 0)
04499         break;
04500     pheader->max_client_index = i + 1;
04501 
04502     /* count new number of clients */
04503     for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04504       if (pheader->client[i].pid != 0)
04505         j++;
04506     pheader->num_clients = j;
04507     destroy_flag = (pheader->num_clients == 0);
04508 
04509     /* free cache */
04510     if (_buffer[buffer_handle - 1].read_cache_size > 0)
04511       M_FREE(_buffer[buffer_handle - 1].read_cache);
04512     if (_buffer[buffer_handle - 1].write_cache_size > 0)
04513       M_FREE(_buffer[buffer_handle - 1].write_cache);
04514 
04515     /* check if anyone is waiting and wake him up */
04516     pclient = pheader->client;
04517     for (i = 0; i < pheader->max_client_index; i++, pclient++)
04518       if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04519         ss_resume(pclient->port, "B  ");
04520 
04521     /* unmap shared memory, delete it if we are the last */
04522     ss_shm_close(pheader->name,
04523                  _buffer[buffer_handle - 1].buffer_header,
04524                  _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04525 
04526     /* unlock buffer */
04527     bm_unlock_buffer(buffer_handle);
04528 
04529     /* delete mutex */
04530     ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04531 
04532     /* update _buffer_entries */
04533     if (buffer_handle == _buffer_entries)
04534       _buffer_entries--;
04535     if (_buffer_entries > 0)
04536       _buffer =
04537           (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04538 
04539     else {
04540       M_FREE(_buffer);
04541       _buffer = NULL;
04542     }
04543   }
04544 
04545 #endif                          /* LOCAL_ROUTINES */
04546   return BM_SUCCESS;
04547 }
04548 
04549 
04550 /********************************************************************/
04551 /**
04552 Close all open buffers
04553 @return BM_SUCCESS
04554 */
04555 INT bm_close_all_buffers(void)
04556 {
04557   if (rpc_is_remote())
04558     return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04559 
04560 #ifdef LOCAL_ROUTINES
04561   {
04562     INT i;
04563 
04564     for (i = _buffer_entries; i > 0; i--)
04565       bm_close_buffer(i);
04566   }
04567 
04568 #endif                          /* LOCAL_ROUTINES */
04569   return BM_SUCCESS;
04570 }
04571 
04572 
04573 /** @} */// end of bmfunctionc
04574 
04575 /** @addtogroup cmfunctionc
04576  *  
04577  *  @{  */
04578 
04579 /*-- Watchdog routines ---------------------------------------------*/
04580 #ifdef LOCAL_ROUTINES
04581 
04582 /********************************************************************/
04583 /**
04584 Called at periodic intervals, checks if all clients are
04585 alive. If one process died, its client entries are cleaned up.
04586 @param dummy unused!
04587 */
04588 void cm_watchdog(int dummy)
04589 {
04590   BUFFER_HEADER *pheader;
04591   BUFFER_CLIENT *pbclient, *pbctmp;
04592   DATABASE_HEADER *pdbheader;
04593   DATABASE_CLIENT *pdbclient;
04594   KEY *pkey;
04595   DWORD actual_time, interval;
04596   INT client_pid;
04597   INT i, j, k, nc, status;
04598   BOOL bDeleted, time_changed, wrong_interval;
04599   char str[256];
04600 
04601   /* return immediately if watchdog has been disabled in meantime */
04602   if (!_call_watchdog)
04603     return;
04604 
04605   /* tell system services that we are in async mode ... */
04606   ss_set_async_flag(TRUE);
04607 
04608   /* Calculate the time since last watchdog call. Kill clients if they
04609      are inactive for more than the timeout they specified */
04610   actual_time = ss_millitime();
04611   if (_watchdog_last_called == 0)
04612     _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04613   interval = actual_time - _watchdog_last_called;
04614 
04615   /* check if system time has been changed more than 10 min */
04616   time_changed = interval < 0 || interval > 600000;
04617   wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04618       || interval > 1.2 * WATCHDOG_INTERVAL;
04619   if (time_changed)
04620     cm_msg(MINFO, "cm_watchdog",
04621            "System time has been changed! last:%dms  now:%dms  delta:%dms",
04622            _watchdog_last_called, actual_time, interval);
04623 
04624   /* check buffers */
04625   for (i = 0; i < _buffer_entries; i++)
04626     if (_buffer[i].attached) {
04627 
04628       /* update the last_activity entry to show that we are alive */
04629       pheader = _buffer[i].buffer_header;
04630       pbclient = pheader->client;
04631       pbclient[_buffer[i].client_index].last_activity = actual_time;
04632 
04633       /* don't check other clients if interval is stange */
04634       if (wrong_interval)
04635         continue;
04636 
04637       /* now check other clients */
04638       for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04639 
04640         /* If client process has no activity, clear its buffer entry. */
04641         if (pbclient->pid
04642             && pbclient->watchdog_timeout > 0
04643             && actual_time - pbclient->last_activity >
04644             pbclient->watchdog_timeout) {
04645           bm_lock_buffer(i + 1);
04646           str[0] = 0;
04647 
04648           /* now make again the check with the buffer locked */
04649           if (pbclient->pid
04650               && pbclient->watchdog_timeout > 0
04651               && actual_time -
04652               pbclient->last_activity > pbclient->watchdog_timeout) {
04653             sprintf(str,
04654                     "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04655                     pbclient->name, pheader->name,
04656                     (actual_time -
04657                      pbclient->last_activity) / 1000.0,
04658                     pbclient->watchdog_timeout / 1000.0);
04659 
04660             /* clear entry from client structure in buffer header */
04661             memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04662 
04663             /* calculate new max_client_index entry */
04664             for (k = MAX_CLIENTS - 1; k >= 0; k--)
04665               if (pheader->client[k].pid != 0)
04666                 break;
04667             pheader->max_client_index = k + 1;
04668 
04669             /* count new number of clients */
04670             for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04671               if (pheader->client[k].pid != 0)
04672                 nc++;
04673             pheader->num_clients = nc;
04674 
04675             /* check if anyone is wating and wake him up */
04676             pbctmp = pheader->client;
04677             for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04678               if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04679                 ss_resume(pbctmp->port, "B  ");
04680           }
04681           bm_unlock_buffer(i + 1);
04682 
04683           /* display info message after unlocking buffer */
04684           if (str[0])
04685             cm_msg(MINFO, "cm_watchdog", str);
04686         }
04687     }
04688 
04689   /* check online databases */
04690   for (i = 0; i < _database_entries; i++)
04691     if (_database[i].attached) {
04692 
04693       /* update the last_activity entry to show that we are alive */
04694       pdbheader = _database[i].database_header;
04695       pdbclient = pdbheader->client;
04696       pdbclient[_database[i].client_index].last_activity = actual_time;
04697 
04698       /* don't check other clients if interval is stange */
04699       if (wrong_interval)
04700         continue;
04701 
04702       /* now check other clients */
04703       for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04704 
04705         /* If client process has no activity, clear its buffer entry. */
04706         if (pdbclient->pid
04707             && pdbclient->watchdog_timeout > 0
04708             && actual_time -
04709             pdbclient->last_activity > pdbclient->watchdog_timeout) {
04710           client_pid = pdbclient->tid;
04711           bDeleted = FALSE;
04712           db_lock_database(i + 1);
04713           str[0] = 0;
04714 
04715           /* now make again the check with the buffer locked */
04716           if (pdbclient->pid
04717               && pdbclient->watchdog_timeout
04718               && actual_time -
04719               pdbclient->last_activity > pdbclient->watchdog_timeout) {
04720             sprintf(str,
04721                     "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04722                     pdbclient->name, client_pid,
04723                     pdbheader->name,
04724                     (actual_time -
04725                      pdbclient->last_activity) /
04726                     1000.0, pdbclient->watchdog_timeout / 1000.0);
04727 
04728             /* decrement notify_count for open records and clear exclusive mode */
04729             for (k = 0; k < pdbclient->max_index; k++)
04730               if (pdbclient->open_record[k].handle) {
04731                 pkey = (KEY *) ((char *)
04732                                 pdbheader +
04733                                 pdbclient->open_record[k].handle);
04734                 if (pkey->notify_count > 0)
04735                   pkey->notify_count--;
04736                 if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04737                   db_set_mode(i + 1,
04738                               pdbclient->open_record[k].handle, (WORD)
04739                               (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04740               }
04741 
04742             /* clear entry from client structure in buffer header */
04743             memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04744 
04745             /* calculate new max_client_index entry */
04746             for (k = MAX_CLIENTS - 1; k >= 0; k--)
04747               if (pdbheader->client[k].pid != 0)
04748                 break;
04749             pdbheader->max_client_index = k + 1;
04750 
04751             /* count new number of clients */
04752             for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04753               if (pdbheader->client[k].pid != 0)
04754                 nc++;
04755             pdbheader->num_clients = nc;
04756             bDeleted = TRUE;
04757           }
04758           db_unlock_database(i + 1);
04759 
04760           /* display info message after unlocking db */
04761           if (str[0])
04762             cm_msg(MINFO, "cm_watchdog", str);
04763 
04764           /* delete client entry after unlocking db */
04765           if (bDeleted) {
04766             status = cm_delete_client_info(i + 1, client_pid);
04767             if (status != CM_SUCCESS)
04768               cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04769           }
04770         }
04771     }
04772   _watchdog_last_called = actual_time;
04773   ss_set_async_flag(FALSE);
04774 
04775   /* Schedule next watchdog call */
04776   if (_call_watchdog)
04777     ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04778 }
04779 
04780 
04781 /********************************************************************/
04782 /**
04783 Temporarily disable watchdog calling. Used for tape IO
04784 not to interrupt lengthy operations like mount.
04785 @param flag FALSE for disable, TRUE for re-enable
04786 @return CM_SUCCESS
04787 */
04788 INT cm_enable_watchdog(BOOL flag)
04789 {
04790   static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04791   static BOOL call_flag = FALSE;
04792 
04793   if (flag) {
04794     if (call_flag)
04795       cm_set_watchdog_params(TRUE, timeout);
04796   }
04797 
04798   else {
04799     call_flag = _call_watchdog;
04800     timeout = _watchdog_timeout;
04801     if (call_flag)
04802       cm_set_watchdog_params(FALSE, 0);
04803   }
04804   return CM_SUCCESS;
04805 }
04806 
04807 
04808 #endif                          /* local routines */
04809 
04810 /********************************************************************/
04811 /**
04812 Shutdown (exit) other MIDAS client
04813 @param name           Client name or "all" for all clients
04814 @param bUnique        If true, look for the exact client name.
04815                       If false, look for namexxx where xxx is
04816                       a any number.
04817 
04818 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
04819 */
04820 INT cm_shutdown(char *name, BOOL bUnique)
04821 {
04822   INT status, return_status, i, size;
04823   HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04824   KEY key;
04825   char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04826   INT port;
04827   DWORD start_time;
04828 
04829   cm_get_experiment_database(&hDB, &hKeyClient);
04830   status = db_find_key(hDB, 0, "System/Clients", &hKey);
04831   if (status != DB_SUCCESS)
04832     return DB_NO_KEY;
04833   return_status = CM_NO_CLIENT;
04834 
04835   /* loop over all clients */
04836   for (i = 0;; i++) {
04837     status = db_enum_key(hDB, hKey, i, &hSubkey);
04838     if (status == DB_NO_MORE_SUBKEYS)
04839       break;
04840 
04841     /* don't shutdown ourselves */
04842     if (hSubkey == hKeyClient)
04843       continue;
04844     if (status == DB_SUCCESS) {
04845       db_get_key(hDB, hSubkey, &key);
04846 
04847       /* contact client */
04848       size = sizeof(client_name);
04849       db_get_value(hDB, hSubkey, "Name", client_name, &size,
04850                    TID_STRING, TRUE);
04851       if (!bUnique)
04852         client_name[strlen(name)] = 0;  /* strip number */
04853 
04854       /* check if individual client */
04855       if (!equal_ustring("all", name)
04856           && !equal_ustring(client_name, name))
04857         continue;
04858       size = sizeof(port);
04859       db_get_value(hDB, hSubkey, "Server Port", &port, &size,
04860                    TID_INT, TRUE);
04861       size = sizeof(remote_host);
04862       db_get_value(hDB, hSubkey, "Host", remote_host, &size,
04863                    TID_STRING, TRUE);
04864 
04865       /* client found -> connect to its server port */
04866       status = rpc_client_connect(remote_host, port, client_name, &hConn);
04867       if (status != RPC_SUCCESS) {
04868         return_status = CM_NO_CLIENT;
04869         sprintf(str,
04870                 "cannot connect to client %s on host %s, port %d",
04871                 client_name, remote_host, port);
04872         cm_msg(MERROR, "cm_shutdown", str);
04873       }
04874 
04875       else {
04876 
04877         /* call disconnect with shutdown=TRUE */
04878         rpc_client_disconnect(hConn, TRUE);
04879 
04880         /* wait until client has shut down */
04881         start_time = ss_millitime();
04882 
04883         do {
04884           ss_sleep(100);
04885           status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04886         } while (status == DB_SUCCESS
04887                  && (ss_millitime() - start_time < 5000));
04888         if (status == DB_SUCCESS) {
04889           cm_msg(MINFO, "cm_shutdown",
04890                  "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04891                  client_name);
04892           return_status = CM_NO_CLIENT;
04893         }
04894 
04895         else {
04896           return_status = CM_SUCCESS;
04897           i--;
04898         }
04899       }
04900     }
04901   }
04902   return return_status;
04903 }
04904 
04905 
04906 /********************************************************************/
04907 /**
04908 Check if a MIDAS client exists in current experiment
04909 @param    name            Client name
04910 @param    bUnique         If true, look for the exact client name.
04911                           If false, look for namexxx where xxx is
04912                           a any number
04913 @return   CM_SUCCESS, CM_NO_CLIENT 
04914 */
04915 INT cm_exist(char *name, BOOL bUnique)
04916 {
04917   INT status, i, size;
04918   HNDLE hDB, hKeyClient, hKey, hSubkey;
04919   char client_name[NAME_LENGTH];
04920 
04921   if (rpc_is_remote())
04922     return rpc_call(RPC_CM_EXIST, name, bUnique);
04923   cm_get_experiment_database(&hDB, &hKeyClient);
04924   status = db_find_key(hDB, 0, "System/Clients", &hKey);
04925   if (status != DB_SUCCESS)
04926     return DB_NO_KEY;
04927 
04928   /* loop over all clients */
04929   for (i = 0;; i++) {
04930     status = db_enum_key(hDB, hKey, i, &hSubkey);
04931     if (status == DB_NO_MORE_SUBKEYS)
04932       break;
04933     if (hSubkey == hKeyClient)
04934       continue;
04935     if (status == DB_SUCCESS) {
04936 
04937       /* get client name */
04938       size = sizeof(client_name);
04939       db_get_value(hDB, hSubkey, "Name", client_name, &size,
04940                    TID_STRING, TRUE);
04941       if (equal_ustring(client_name, name))
04942         return CM_SUCCESS;
04943       if (!bUnique) {
04944         client_name[strlen(name)] = 0;  /* strip number */
04945         if (equal_ustring(client_name, name))
04946           return CM_SUCCESS;
04947       }
04948     }
04949   }
04950   return CM_NO_CLIENT;
04951 }
04952 
04953 
04954 /********************************************************************/
04955 /**
04956 Remove hanging clients independent of their watchdog
04957            timeout.
04958 
04959 Since this function does not obey the client watchdog
04960 timeout, it should be only called to remove clients which
04961 have their watchdog checking turned off or which are
04962 known to be dead. The normal client removement is done
04963 via cm_watchdog().
04964 
04965 Currently (Sept. 02) there are two applications for that:
04966 -# The ODBEdit command "cleanup", which can be used to
04967 remove clients which have their watchdog checking off,
04968 like the analyzer started with the "-d" flag for a
04969 debugging session.
04970 -# The frontend init code to remove previous frontends.
04971 This can be helpful if a frontend dies. Normally,
04972 one would have to wait 60 sec. for a crashed frontend
04973 to be removed. Only then one can start again the
04974 frontend. Since the frontend init code contains a
04975 call to cm_cleanup(<frontend_name>), one can restart
04976 a frontend immediately.
04977 
04978 Added ignore_timeout on Nov.03. A logger might have an
04979 increased tiemout of up to 60 sec. because of tape
04980 operations. If ignore_timeout is FALSE, the logger is
04981 then not killed if its inactivity is less than 60 sec., 
04982 while in the previous implementation it was always
04983 killed after 2*WATCHDOG_INTERVAL.
04984 @param    client_name      Client name, if zero check all clients
04985 @param    ignore_timeout   If TRUE, ignore a possible increased
04986                            timeout defined by each client.
04987 @return   CM_SUCCESS
04988 */
04989 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
04990 {
04991   if (rpc_is_remote())
04992     return rpc_call(RPC_CM_CLEANUP, client_name);
04993 
04994 #ifdef LOCAL_ROUTINES
04995   {
04996     BUFFER_HEADER *pheader = NULL;
04997     BUFFER_CLIENT *pbclient, *pbctmp;
04998     DATABASE_HEADER *pdbheader;
04999     DATABASE_CLIENT *pdbclient;
05000     KEY *pkey;
05001     INT client_pid;
05002     INT i, j, k, status, nc;
05003     BOOL bDeleted;
05004     char str[256];
05005     DWORD interval;
05006 
05007     /* check buffers */
05008     for (i = 0; i < _buffer_entries; i++)
05009       if (_buffer[i].attached) {
05010 
05011         /* update the last_activity entry to show that we are alive */
05012         pheader = _buffer[i].buffer_header;
05013         pbclient = pheader->client;
05014         pbclient[_buffer[i].client_index].last_activity = ss_millitime();
05015 
05016         /* now check other clients */
05017         for (j = 0; j < pheader->max_client_index; j++, pbclient++)
05018           if (j != _buffer[i].client_index && pbclient->pid
05019               && (client_name[0] == 0
05020                   || strncmp(pbclient->name, client_name,
05021                              strlen(client_name)) == 0)) {
05022             if (ignore_timeout)
05023               interval = 2 * WATCHDOG_INTERVAL;
05024 
05025             else
05026               interval = pbclient->watchdog_timeout;
05027 
05028             /* If client process has no activity, clear its buffer entry. */
05029             if (ss_millitime() - pbclient->last_activity > interval) {
05030               bm_lock_buffer(i + 1);
05031               str[0] = 0;
05032 
05033               /* now make again the check with the buffer locked */
05034               if (ss_millitime() - pbclient->last_activity > interval) {
05035                 sprintf(str,
05036                         "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05037                         pbclient->name,
05038                         pheader->name,
05039                         (ss_millitime() -
05040                          pbclient->last_activity) /
05041                         1000.0, interval / 1000.0);
05042 
05043                 /* clear entry from client structure in buffer header */
05044                 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
05045 
05046                 /* calculate new max_client_index entry */
05047                 for (k = MAX_CLIENTS - 1; k >= 0; k--)
05048                   if (pheader->client[k].pid != 0)
05049                     break;
05050                 pheader->max_client_index = k + 1;
05051 
05052                 /* count new number of clients */
05053                 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05054                   if (pheader->client[k].pid != 0)
05055                     nc++;
05056                 pheader->num_clients = nc;
05057 
05058                 /* check if anyone is wating and wake him up */
05059                 pbctmp = pheader->client;
05060                 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
05061                   if (pbctmp->pid
05062                       && (pbctmp->write_wait || pbctmp->read_wait))
05063                     ss_resume(pbctmp->port, "B  ");
05064               }
05065               bm_unlock_buffer(i + 1);
05066 
05067               /* display info message after unlocking buffer */
05068               if (str[0])
05069                 cm_msg(MINFO, "cm_cleanup", str);
05070 
05071               /* go again through whole list */
05072               j = 0;
05073             }
05074           }
05075       }
05076 
05077     /* check online databases */
05078     for (i = 0; i < _database_entries; i++)
05079       if (_database[i].attached) {
05080 
05081         /* update the last_activity entry to show that we are alive */
05082         db_lock_database(i + 1);
05083         pdbheader = _database[i].database_header;
05084         pdbclient = pdbheader->client;
05085         pdbclient[_database[i].client_index].
05086             last_activity = ss_millitime();
05087 
05088         /* now check other clients */
05089         for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
05090           if (j != _database[i].client_index
05091               && pdbclient->pid && (client_name[0] == 0
05092                                     || strncmp(pdbclient->
05093                                                name,
05094                                                client_name,
05095                                                strlen(client_name))
05096                                     == 0)) {
05097             client_pid = pdbclient->tid;
05098             if (ignore_timeout)
05099               interval = 2 * WATCHDOG_INTERVAL;
05100 
05101             else
05102               interval = pdbclient->watchdog_timeout;
05103 
05104             /* If client process has no activity, clear its buffer entry. */
05105             if (ss_millitime() - pdbclient->last_activity > interval) {
05106               bDeleted = FALSE;
05107               str[0] = 0;
05108 
05109               /* now make again the check with the buffer locked */
05110               if (ss_millitime() - pdbclient->last_activity > interval) {
05111                 sprintf(str,
05112                         "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05113                         pdbclient->name,
05114                         pdbheader->name,
05115                         (ss_millitime() -
05116                          pdbclient->
05117                          last_activity) / 1000.0, interval / 1000.0);
05118 
05119                 /* decrement notify_count for open records and clear exclusive mode */
05120                 for (k = 0; k < pdbclient->max_index; k++)
05121                   if (pdbclient->open_record[k].handle) {
05122                     pkey = (KEY *) ((char *)
05123                                     pdbheader
05124                                     + pdbclient->open_record[k].handle);
05125                     if (pkey->notify_count > 0)
05126                       pkey->notify_count--;
05127                     if (pdbclient->open_record[k].access_mode & MODE_WRITE)
05128                       db_set_mode(i + 1,
05129                                   pdbclient->open_record[k].handle, (WORD)
05130                                   (pkey->
05131                                    access_mode & ~MODE_EXCLUSIVE), 2);
05132                   }
05133 
05134                 /* clear entry from client structure in buffer header */
05135                 memset(&
05136                        (pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
05137 
05138                 /* calculate new max_client_index entry */
05139                 for (k = MAX_CLIENTS - 1; k >= 0; k--)
05140                   if (pdbheader->client[k].pid != 0)
05141                     break;
05142                 pdbheader->max_client_index = k + 1;
05143 
05144                 /* count new number of clients */
05145                 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05146                   if (pheader->client[k].pid != 0)
05147                     nc++;
05148                 pdbheader->num_clients = nc;
05149                 bDeleted = TRUE;
05150               }
05151 
05152               /* delete client entry after unlocking db */
05153               if (bDeleted) {
05154                 db_unlock_database(i + 1);
05155 
05156                 /* display info message after unlocking buffer */
05157                 cm_msg(MINFO, "cm_cleanup", str);
05158                 status = cm_delete_client_info(i + 1, client_pid);
05159                 if (status != CM_SUCCESS)
05160                   cm_msg(MERROR,
05161                          "cm_cleanup", "cannot delete client info");
05162 
05163                 /* re-lock database */
05164                 db_lock_database(i + 1);
05165                 pdbheader = _database[i].database_header;
05166                 pdbclient = pdbheader->client;
05167 
05168                 /* go again though whole list */
05169                 j = 0;
05170               }
05171             }
05172           }
05173         db_unlock_database(i + 1);
05174       }
05175   }
05176 
05177 #endif                          /* LOCAL_ROUTINES */
05178   return CM_SUCCESS;
05179 }
05180 
05181 
05182 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05183 
05184 /********************************************************************/
05185 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
05186 /********************************************************************\
05187 
05188   Routine: bm_buffer_info
05189 
05190   Purpose: Copies the current buffer header referenced by buffer_handle
05191            into the *buffer_header structure which must be supplied
05192            by the calling routine.
05193 
05194   Input:
05195     INT buffer_handle       Handle of the buffer to get the header from
05196 
05197   Output:
05198     BUFFER_HEADER *buffer_header   Destination address which gets a copy
05199                                    of the buffer header structure.
05200 
05201   Function value:
05202     BM_SUCCESS              Successful completion
05203     BM_INVALID_HANDLE       Buffer handle is invalid
05204     RPC_NET_ERROR           Network error
05205 
05206 \********************************************************************/
05207 {
05208   if (rpc_is_remote())
05209     return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
05210 
05211 #ifdef LOCAL_ROUTINES
05212   if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05213     cm_msg(MERROR, "bm_get_buffer_info",
05214            "invalid buffer handle %d", buffer_handle);
05215     return BM_INVALID_HANDLE;
05216   }
05217   if (!_buffer[buffer_handle - 1].attached) {
05218     cm_msg(MERROR, "bm_get_buffer_info",
05219            "invalid buffer handle %d", buffer_handle);
05220     return BM_INVALID_HANDLE;
05221   }
05222   bm_lock_buffer(buffer_handle);
05223   memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header,
05224          sizeof(BUFFER_HEADER));
05225   bm_unlock_buffer(buffer_handle);
05226 
05227 #endif                          /* LOCAL_ROUTINES */
05228   return BM_SUCCESS;
05229 }
05230 
05231 
05232 /********************************************************************/
05233 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
05234 /********************************************************************\
05235 
05236   Routine: bm_get_buffer_level
05237 
05238   Purpose: Return number of bytes in buffer or in cache
05239 
05240   Input:
05241     INT buffer_handle       Handle of the buffer to get the info
05242 
05243   Output:
05244     INT *n_bytes              Number of bytes in buffer
05245 
05246   Function value:
05247     BM_SUCCESS              Successful completion
05248     BM_INVALID_HANDLE       Buffer handle is invalid
05249     RPC_NET_ERROR           Network error
05250 
05251 \********************************************************************/
05252 {
05253   if (rpc_is_remote())
05254     return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
05255 
05256 #ifdef LOCAL_ROUTINES
05257   {
05258     BUFFER *pbuf;
05259     BUFFER_HEADER *pheader;
05260     BUFFER_CLIENT *pclient;
05261 
05262     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05263       cm_msg(MERROR, "bm_get_buffer_level",
05264              "invalid buffer handle %d", buffer_handle);
05265       return BM_INVALID_HANDLE;
05266     }
05267     pbuf = &_buffer[buffer_handle - 1];
05268     pheader = pbuf->buffer_header;
05269     if (!pbuf->attached) {
05270       cm_msg(MERROR, "bm_get_buffer_level",
05271              "invalid buffer handle %d", buffer_handle);
05272       return BM_INVALID_HANDLE;
05273     }
05274     bm_lock_buffer(buffer_handle);
05275     pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
05276     *n_bytes = pheader->write_pointer - pclient->read_pointer;
05277     if (*n_bytes < 0)
05278       *n_bytes += pheader->size;
05279     bm_unlock_buffer(buffer_handle);
05280 
05281     /* add bytes in cache */
05282     if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05283       *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05284   }
05285 
05286 #endif                          /* LOCAL_ROUTINES */
05287   return BM_SUCCESS;
05288 }
05289 
05290 
05291 #ifdef LOCAL_ROUTINES
05292 
05293 /********************************************************************/
05294 INT bm_lock_buffer(INT buffer_handle)
05295 /********************************************************************\
05296 
05297   Routine: bm_lock_buffer
05298 
05299   Purpose: Lock a buffer for exclusive access via system mutex calls.
05300 
05301   Input:
05302     INT    bufer_handle     Handle to the buffer to lock
05303   Output:
05304     none
05305 
05306   Function value:
05307     BM_SUCCESS              Successful completion
05308     BM_INVALID_HANDLE       Buffer handle is invalid
05309 
05310 \********************************************************************/
05311 {
05312   if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05313     cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d",
05314            buffer_handle);
05315     return BM_INVALID_HANDLE;
05316   }
05317   ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 0);
05318   return BM_SUCCESS;
05319 }
05320 
05321 
05322 /********************************************************************/
05323 INT bm_unlock_buffer(INT buffer_handle)
05324 /********************************************************************\
05325 
05326   Routine: bm_unlock_buffer
05327 
05328   Purpose: Unlock a buffer via system mutex calls.
05329 
05330   Input:
05331     INT    bufer_handle     Handle to the buffer to lock
05332   Output:
05333     none
05334 
05335   Function value:
05336     BM_SUCCESS              Successful completion
05337     BM_INVALID_HANDLE       Buffer handle is invalid
05338 
05339 \********************************************************************/
05340 {
05341   if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05342     cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d",
05343            buffer_handle);
05344     return BM_INVALID_HANDLE;
05345   }
05346   ss_mutex_release(_buffer[buffer_handle - 1].mutex);
05347   return BM_SUCCESS;
05348 }
05349 
05350 
05351 #endif                          /* LOCAL_ROUTINES */
05352 
05353 /********************************************************************/
05354 INT bm_init_buffer_counters(INT buffer_handle)
05355 /********************************************************************\
05356 
05357   Routine: bm_init_event_counters
05358 
05359   Purpose: Initialize counters for a specific buffer. This routine
05360            should be called at the beginning of a run.
05361 
05362   Input:
05363     INT    buffer_handle    Handle to the buffer to be
05364                             initialized.
05365   Output:
05366     none
05367 
05368   Function value:
05369     BM_SUCCESS              Successful completion
05370     BM_INVALID_HANDLE       Buffer handle is invalid
05371 
05372 \********************************************************************/
05373 {
05374   if (rpc_is_remote())
05375     return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05376 
05377 #ifdef LOCAL_ROUTINES
05378   if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05379     cm_msg(MERROR, "bm_init_buffer_counters",
05380            "invalid buffer handle %d", buffer_handle);
05381     return BM_INVALID_HANDLE;
05382   }
05383   if (!_buffer[buffer_handle - 1].attached) {
05384     cm_msg(MERROR, "bm_init_buffer_counters",
05385            "invalid buffer handle %d", buffer_handle);
05386     return BM_INVALID_HANDLE;
05387   }
05388   _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05389   _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05390 
05391 #endif                          /* LOCAL_ROUTINES */
05392   return BM_SUCCESS;
05393 }
05394 
05395 
05396 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05397 
05398 /** @} */// end of cmfunctionc
05399 
05400 /********************************************************************/
05401 /** @addtogroup bmfunctionc
05402  *  
05403  *  @{  */
05404 
05405 /********************************************************************/
05406 /**
05407 Modifies buffer cache size.
05408 Without a buffer cache, events are copied to/from the shared
05409 memory event by event.
05410 
05411 To protect processed from accessing the shared memory simultaneously,
05412 semaphores are used. Since semaphore operations are CPU consuming (typically
05413 50-100us) this can slow down the data transfer especially for small events.
05414 By using a cache the number of semaphore operations is reduced dramatically.
05415 Instead writing directly to the shared memory, the events are copied to a
05416 local cache buffer. When this buffer is full, it is copied to the shared
05417 memory in one operation. The same technique can be used when receiving events.
05418 
05419 The drawback of this method is that the events have to be copied twice, once to the
05420 cache and once from the cache to the shared memory. Therefore it can happen that the
05421 usage of a cache even slows down data throughput on a given environment (computer
05422 type, OS type, event size).
05423 The cache size has therefore be optimized manually to maximize data throughput.
05424 @param buffer_handle buffer handle obtained via bm_open_buffer()
05425 @param read_size cache size for reading events in bytes, zero for no cache
05426 @param write_size cache size for writing events in bytes, zero for no cache
05427 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05428 */
05429 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05430 /*------------------------------------------------------------------*/
05431 {
05432   if (rpc_is_remote())
05433     return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle,
05434                     read_size, write_size);
05435 
05436 #ifdef LOCAL_ROUTINES
05437   {
05438     BUFFER *pbuf;
05439 
05440     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05441       cm_msg(MERROR, "bm_set_cache_size",
05442              "invalid buffer handle %d", buffer_handle);
05443       return BM_INVALID_HANDLE;
05444     }
05445     if (!_buffer[buffer_handle - 1].attached) {
05446       cm_msg(MERROR, "bm_set_cache_size",
05447              "invalid buffer handle %d", buffer_handle);
05448       return BM_INVALID_HANDLE;
05449     }
05450     if (read_size < 0 || read_size > 1E6) {
05451       cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05452       return BM_INVALID_PARAM;
05453     }
05454     if (write_size < 0 || write_size > 1E6) {
05455       cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05456       return BM_INVALID_PARAM;
05457     }
05458 
05459     /* manage read cache */
05460     pbuf = &_buffer[buffer_handle - 1];
05461     if (pbuf->read_cache_size > 0)
05462       M_FREE(pbuf->read_cache);
05463     if (read_size > 0) {
05464       pbuf->read_cache = (char *) M_MALLOC(read_size);
05465       if (pbuf->read_cache == NULL) {
05466         cm_msg(MERROR, "bm_set_cache_size",
05467                "not enough memory to allocate cache buffer");
05468         return BM_NO_MEMORY;
05469       }
05470     }
05471     pbuf->read_cache_size = read_size;
05472     pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05473 
05474     /* manage write cache */
05475     if (pbuf->write_cache_size > 0)
05476       M_FREE(pbuf->write_cache);
05477     if (write_size > 0) {
05478       pbuf->write_cache = (char *) M_MALLOC(write_size);
05479       if (pbuf->write_cache == NULL) {
05480         cm_msg(MERROR, "bm_set_cache_size",
05481                "not enough memory to allocate cache buffer");
05482         return BM_NO_MEMORY;
05483       }
05484     }
05485     pbuf->write_cache_size = write_size;
05486     pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05487   }
05488 
05489 #endif                          /* LOCAL_ROUTINES */
05490   return BM_SUCCESS;
05491 }
05492 
05493 
05494 /********************************************************************/
05495 /**
05496 Compose a Midas event header.
05497 An event header can usually be set-up manually or
05498 through this routine. If the data size of the event is not known when
05499 the header is composed, it can be set later with event_header->data-size = <...>
05500 Following structure is created at the beginning of an event
05501 \code
05502 typedef struct {
05503  short int     event_id;
05504  short int     trigger_mask;
05505  DWORD         serial_number;
05506  DWORD         time_stamp;
05507  DWORD         data_size;
05508 } EVENT_HEADER;
05509 
05510 char event[1000];
05511  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05512  *(event+sizeof(EVENT_HEADER)) = <...>
05513 \endcode
05514 @param event_header pointer to the event header
05515 @param event_id event ID of the event
05516 @param trigger_mask trigger mask of the event
05517 @param size size if the data part of the event in bytes
05518 @param serial serial number
05519 @return BM_SUCCESS
05520 */
05521 INT bm_compose_event(EVENT_HEADER * event_header,
05522                      short int event_id,
05523                      short int trigger_mask, DWORD size, DWORD serial)
05524 {
05525   event_header->event_id = event_id;
05526   event_header->trigger_mask = trigger_mask;
05527   event_header->data_size = size;
05528   event_header->time_stamp = ss_time();
05529   event_header->serial_number = serial;
05530   return BM_SUCCESS;
05531 }
05532 
05533 
05534 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05535 /********************************************************************/
05536 INT bm_add_event_request(INT buffer_handle,
05537                          short int event_id,
05538                          short int trigger_mask,
05539                          INT sampling_type,
05540                          void (*func) (HNDLE,
05541                                        HNDLE,
05542                                        EVENT_HEADER *,
05543                                        void *), INT request_id)
05544 /********************************************************************\
05545 
05546   Routine:  bm_add_event_request
05547 
05548   Purpose:  Place a request for a specific event type in the client
05549             structure of the buffer refereced by buffer_handle.
05550 
05551   Input:
05552     INT          buffer_handle  Handle to the buffer where the re-
05553                                 quest should be placed in
05554 
05555     short int    event_id       Event ID      \
05556     short int    trigger_mask   Trigger mask  / Event specification
05557 
05558     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05559 
05560 
05561                  Note: to request all types of events, use
05562                    event_id = 0 (all others should be !=0 !)
05563                    trigger_mask = TRIGGER_ALL
05564                    sampling_typ = GET_ALL
05565 
05566 
05567     void         *func          Callback function
05568     INT          request_id     Request id (unique number assigned
05569                                 by bm_request_event)
05570 
05571   Output:
05572     none
05573 
05574   Function value:
05575     BM_SUCCESS              Successful completion
05576     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05577                             MIDAS.H should be increased.
05578     BM_INVALID_HANDLE       Buffer handle is invalid
05579     RPC_NET_ERROR           Network error
05580 
05581 \********************************************************************/
05582 {
05583   if (rpc_is_remote())
05584     return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle,
05585                     event_id, trigger_mask, sampling_type,
05586                     (INT) func, request_id);
05587 
05588 #ifdef LOCAL_ROUTINES
05589   {
05590     INT i;
05591     BUFFER_CLIENT *pclient;
05592 
05593     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05594       cm_msg(MERROR, "bm_add_event_request",
05595              "invalid buffer handle %d", buffer_handle);
05596       return BM_INVALID_HANDLE;
05597     }
05598     if (!_buffer[buffer_handle - 1].attached) {
05599       cm_msg(MERROR, "bm_add_event_request",
05600              "invalid buffer handle %d", buffer_handle);
05601       return BM_INVALID_HANDLE;
05602     }
05603 
05604     /* avoid callback/non callback requests */
05605     if (func == NULL && _buffer[buffer_handle - 1].callback) {
05606       cm_msg(MERROR, "bm_add_event_request",
05607              "mixing callback/non callback requests not possible");
05608       return BM_INVALID_MIXING;
05609     }
05610 
05611     /* get a pointer to the proper client structure */
05612     pclient =
05613         &(_buffer[buffer_handle - 1].buffer_header->
05614           client[_buffer[buffer_handle - 1].client_index]);
05615 
05616     /* lock buffer */
05617     bm_lock_buffer(buffer_handle);
05618 
05619     /* look for a empty request entry */
05620     for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05621       if (!pclient->event_request[i].valid)
05622         break;
05623     if (i == MAX_EVENT_REQUESTS) {
05624       bm_unlock_buffer(buffer_handle);
05625       return BM_NO_MEMORY;
05626     }
05627 
05628     /* setup event_request structure */
05629     pclient->event_request[i].id = request_id;
05630     pclient->event_request[i].valid = TRUE;
05631     pclient->event_request[i].event_id = event_id;
05632     pclient->event_request[i].trigger_mask = trigger_mask;
05633     pclient->event_request[i].sampling_type = sampling_type;
05634     pclient->event_request[i].dispatch = func;
05635     pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05636 
05637     /* set callback flag in buffer structure */
05638     if (func != NULL)
05639       _buffer[buffer_handle - 1].callback = TRUE;
05640 
05641     /*
05642        Save the index of the last request in the list so that later only the
05643        requests 0..max_request_index-1 have to be searched through.
05644      */
05645     if (i + 1 > pclient->max_request_index)
05646       pclient->max_request_index = i + 1;
05647     bm_unlock_buffer(buffer_handle);
05648   }
05649 
05650 #endif                          /* LOCAL_ROUTINES */
05651   return BM_SUCCESS;
05652 }
05653 
05654 
05655 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05656 
05657 /********************************************************************/
05658 /**
05659 Place an event request based on certain characteristics.
05660 Multiple event requests can be placed for each buffer, which
05661 are later identified by their request ID. They can contain different callback
05662 routines. Example see bm_open_buffer() and bm_receive_event()
05663 @param buffer_handle buffer handle obtained via bm_open_buffer()
05664 @param event_id event ID for requested events. Use EVENTID_ALL
05665 to receive events with any ID.
05666 @param trigger_mask trigger mask for requested events.
05667 The requested events must have at least one bit in its
05668 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05669 receive events with any trigger mask.
05670 @param sampling_type specifies how many events to receive.
05671 A value of GET_ALL receives all events which
05672 match the specified event ID and trigger mask. If the events are consumed slower
05673 than produced, the producer is automatically slowed down. A value of GET_SOME
05674 receives as much events as possible without slowing down the producer. GET_ALL is
05675 typically used by the logger, while GET_SOME is typically used by analyzers.
05676 @param request_id request ID returned by the function.
05677 This ID is passed to the callback routine and must
05678 be used in the bm_delete_request() routine.
05679 @param func allback routine which gets called when an event of the
05680 specified type is received.
05681 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05682 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05683 should be increased.
05684 */
05685 INT bm_request_event(HNDLE buffer_handle,
05686                      short int event_id,
05687                      short int trigger_mask,
05688                      INT sampling_type,
05689                      HNDLE * request_id,
05690                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05691 {
05692   INT index, status;
05693 
05694   /* allocate new space for the local request list */
05695   if (_request_list_entries == 0) {
05696     _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05697     memset(_request_list, 0, sizeof(REQUEST_LIST));
05698     if (_request_list == NULL) {
05699       cm_msg(MERROR, "bm_request_event",
05700              "not enough memory to allocate request list buffer");
05701       return BM_NO_MEMORY;
05702     }
05703     _request_list_entries = 1;
05704     index = 0;
05705   }
05706 
05707   else {
05708 
05709     /* check for a deleted entry */
05710     for (index = 0; index < _request_list_entries; index++)
05711       if (!_request_list[index].buffer_handle)
05712         break;
05713 
05714     /* if not found, create new one */
05715     if (index == _request_list_entries) {
05716       _request_list =
05717           (REQUEST_LIST *) realloc(_request_list,
05718                                    sizeof(REQUEST_LIST) *
05719                                    (_request_list_entries + 1));
05720       if (_request_list == NULL) {
05721         cm_msg(MERROR, "bm_request_event",
05722                "not enough memory to allocate request list buffer");
05723         return BM_NO_MEMORY;
05724       }
05725       memset(&_request_list[_request_list_entries], 0,
05726              sizeof(REQUEST_LIST));
05727       _request_list_entries++;
05728     }
05729   }
05730 
05731   /* initialize request list */
05732   _request_list[index].buffer_handle = buffer_handle;
05733   _request_list[index].event_id = event_id;
05734   _request_list[index].trigger_mask = trigger_mask;
05735   _request_list[index].dispatcher = func;
05736   *request_id = index;
05737 
05738   /* add request in buffer structure */
05739   status =
05740       bm_add_event_request(buffer_handle, event_id, trigger_mask,
05741                            sampling_type, func, index);
05742   if (status != BM_SUCCESS)
05743     return status;
05744   return BM_SUCCESS;
05745 }
05746 
05747 
05748 /********************************************************************/
05749 /**
05750 Delete a previously placed request for a specific event
05751 type in the client structure of the buffer refereced by buffer_handle.
05752 @param buffer_handle  Handle to the buffer where the re-
05753                                 quest should be placed in
05754 @param request_id     Request id returned by bm_request_event
05755 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
05756 */
05757 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05758 {
05759   if (rpc_is_remote())
05760     return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle,
05761                     request_id);
05762 
05763 #ifdef LOCAL_ROUTINES
05764   {
05765     INT i, deleted;
05766     BUFFER_CLIENT *pclient;
05767 
05768     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05769       cm_msg(MERROR, "bm_remove_event_request",
05770              "invalid buffer handle %d", buffer_handle);
05771       return BM_INVALID_HANDLE;
05772     }
05773     if (!_buffer[buffer_handle - 1].attached) {
05774       cm_msg(MERROR, "bm_remove_event_request",
05775              "invalid buffer handle %d", buffer_handle);
05776       return BM_INVALID_HANDLE;
05777     }
05778 
05779     /* get a pointer to the proper client structure */
05780     pclient =
05781         &(_buffer[buffer_handle - 1].buffer_header->
05782           client[_buffer[buffer_handle - 1].client_index]);
05783 
05784     /* lock buffer */
05785     bm_lock_buffer(buffer_handle);
05786 
05787     /* check all requests and set to zero if matching */
05788     for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05789       if (pclient->event_request[i].valid
05790           && pclient->event_request[i].id == request_id) {
05791         memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05792         deleted++;
05793       }
05794 
05795     /* calculate new max_request_index entry */
05796     for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05797       if (pclient->event_request[i].valid)
05798         break;
05799     pclient->max_request_index = i + 1;
05800 
05801     /* caluclate new all_flag */
05802     pclient->all_flag = FALSE;
05803     for (i = 0; i < pclient->max_request_index; i++)
05804       if (pclient->event_request[i].valid &&
05805           (pclient->event_request[i].sampling_type & GET_ALL)) {
05806         pclient->all_flag = TRUE;
05807         break;
05808       }
05809     bm_unlock_buffer(buffer_handle);
05810     if (!deleted)
05811       return BM_NOT_FOUND;
05812   }
05813 
05814 #endif                          /* LOCAL_ROUTINES */
05815   return BM_SUCCESS;
05816 }
05817 
05818 
05819 /********************************************************************/
05820 /** 
05821 Deletes an event request previously done with bm_request_event().
05822 When an event request gets deleted, events of that requested type are
05823 not received any more. When a buffer is closed via bm_close_buffer(), all
05824 event requests from that buffer are deleted automatically
05825 @param request_id request identifier given by bm_request_event()
05826 @return BM_SUCCESS, BM_INVALID_HANDLE
05827 */
05828 INT bm_delete_request(INT request_id)
05829 {
05830   if (request_id < 0 || request_id >= _request_list_entries)
05831     return BM_INVALID_HANDLE;
05832 
05833   /* remove request entry from buffer */
05834   bm_remove_event_request(_request_list[request_id].buffer_handle,
05835                           request_id);
05836   memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05837   return BM_SUCCESS;
05838 }
05839 
05840 
05841 /********************************************************************/
05842 /** 
05843 Sends an event to a buffer.
05844 This function check if the buffer has enough space for the
05845 event, then copies the event to the buffer in shared memory.
05846 If clients have requests for the event, they are notified via an UDP packet.
05847 \code
05848 char event[1000];
05849 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05850 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05851 
05852 // set first byte of event
05853 *(event+sizeof(EVENT_HEADER)) = <...>
05854 #include <stdio.h>
05855 #include "midas.h"
05856 main()
05857 {
05858  INT status, i;
05859  HNDLE hbuf;
05860  char event[1000];
05861  status = cm_connect_experiment("", "Sample", "Producer", NULL);
05862  if (status != CM_SUCCESS)
05863  return 1;
05864  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
05865 
05866  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05867  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05868 
05869  // set event data
05870  for (i=0 ; i<100 ; i++)
05871  *(event+sizeof(EVENT_HEADER)+i) = i;
05872  // send event
05873  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
05874  cm_disconnect_experiment();
05875  return 0;
05876 }
05877 \endcode
05878 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05879 @param source Address of event buffer
05880 @param buf_size Size of event including event header in bytes
05881 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
05882 blocks if the buffer has not enough free space to receive the event.
05883 If TRUE, the function returns immediately with a
05884 value of BM_ASYNC_RETURN without writing the event to the buffer
05885 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
05886 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
05887 buffer has not enough space to receive event<br>
05888 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
05889 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
05890 recompile.
05891 */
05892 INT bm_send_event(INT buffer_handle,
05893                   void *source, INT buf_size, INT async_flag)
05894 {
05895   EVENT_HEADER *pevent;
05896 
05897   /* check if event size defined in header matches buf_size */
05898   if (ALIGN(buf_size) !=
05899       (INT) ALIGN(((EVENT_HEADER *) source)->data_size +
05900                   sizeof(EVENT_HEADER))) {
05901     cm_msg(MERROR, "bm_send_event",
05902            "event size (%d) mismatch in header (%d)",
05903            ALIGN(buf_size),
05904            (INT) ALIGN(((EVENT_HEADER *) source)->data_size +
05905                        sizeof(EVENT_HEADER)));
05906     return BM_INVALID_PARAM;
05907   }
05908 
05909   /* check for maximal event size */
05910   if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
05911     cm_msg(MERROR, "bm_send_event",
05912            "event size (%d) larger than maximum event size (%d)",
05913            ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
05914     return BM_NO_MEMORY;
05915   }
05916   if (rpc_is_remote())
05917     return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source,
05918                     buf_size, async_flag);
05919 
05920 #ifdef LOCAL_ROUTINES
05921   {
05922     BUFFER *pbuf;
05923     BUFFER_HEADER *pheader;
05924     BUFFER_CLIENT *pclient, *pc;
05925     EVENT_REQUEST *prequest;
05926     EVENT_HEADER *pevent_test;
05927     INT i, j, min_wp, size, total_size, status;
05928     INT increment;
05929     INT my_client_index;
05930     INT old_write_pointer;
05931     INT old_read_pointer, new_read_pointer;
05932     INT num_requests_client;
05933     char *pdata;
05934     BOOL blocking;
05935     INT n_blocking;
05936     INT request_id;
05937     char str[80];
05938 
05939     pbuf = &_buffer[buffer_handle - 1];
05940     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05941       cm_msg(MERROR, "bm_send_event",
05942              "invalid buffer handle %d", buffer_handle);
05943       return BM_INVALID_HANDLE;
05944     }
05945     if (!pbuf->attached) {
05946       cm_msg(MERROR, "bm_send_event",
05947              "invalid buffer handle %d", buffer_handle);
05948       return BM_INVALID_HANDLE;
05949     }
05950     pevent = (EVENT_HEADER *) source;
05951     total_size = buf_size;
05952 
05953     /* round up total_size to next DWORD boundary */
05954     total_size = ALIGN(total_size);
05955 
05956     /* look if there is space in the cache */
05957     if (pbuf->write_cache_size) {
05958       status = BM_SUCCESS;
05959       if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
05960         status = bm_flush_cache(buffer_handle, async_flag);
05961       if (status != BM_SUCCESS)
05962         return status;
05963       if (total_size < pbuf->write_cache_size) {
05964         memcpy(pbuf->write_cache + pbuf->write_cache_wp,
05965                source, total_size);
05966         pbuf->write_cache_wp += total_size;
05967         return BM_SUCCESS;
05968       }
05969     }
05970 
05971     /* calculate some shorthands */
05972     pheader = _buffer[buffer_handle - 1].buffer_header;
05973     pdata = (char *) (pheader + 1);
05974     my_client_index = _buffer[buffer_handle - 1].client_index;
05975     pclient = pheader->client;
05976 
05977     /* check if buffer is large enough */
05978     if (total_size >= pheader->size) {
05979       cm_msg(MERROR, "bm_send_event",
05980              "total event size (%d) larger than buffer size (%d)",
05981              total_size, pheader->size);
05982       return BM_NO_MEMORY;
05983     }
05984 
05985     /* lock the buffer */
05986     bm_lock_buffer(buffer_handle);
05987 
05988     /* check if enough space in buffer left */
05989     do {
05990       size = pheader->read_pointer - pheader->write_pointer;
05991       if (size <= 0)
05992         size += pheader->size;
05993       if (size <= total_size) { /* note the '<=' to avoid 100% filling */
05994 
05995         /* if not enough space, find out who's blocking */
05996         n_blocking = 0;
05997         for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
05998           if (pc->pid) {
05999             if (pc->read_pointer == pheader->read_pointer) {
06000 
06001               /*
06002                  First assume that the client with the "minimum" read pointer
06003                  is not really blocking due to a GET_ALL request.
06004                */
06005               blocking = FALSE;
06006               request_id = -1;
06007 
06008               /* check if this request blocks */
06009               prequest = pc->event_request;
06010               pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06011               for (j = 0; j < pc->max_request_index; j++, prequest++)
06012                 if (prequest->valid
06013                     && bm_match_event(prequest->
06014                                       event_id,
06015                                       prequest->
06016                                       trigger_mask, pevent_test)) {
06017                   request_id = prequest->id;
06018                   if (prequest->sampling_type & GET_ALL) {
06019                     blocking = TRUE;
06020                     break;
06021                   }
06022                 }
06023               if (!blocking) {
06024 
06025                 /*
06026                    The blocking guy has no GET_ALL request for this event
06027                    -> shift its read pointer.
06028                  */
06029                 old_read_pointer = pc->read_pointer;
06030                 increment =
06031                     sizeof(EVENT_HEADER) +
06032                     ((EVENT_HEADER *) (pdata +
06033                                        pc->read_pointer))->data_size;
06034 
06035                 /* correct increment for DWORD boundary */
06036                 increment = ALIGN(increment);
06037                 new_read_pointer =
06038                     (pc->read_pointer + increment) % pheader->size;
06039                 if (new_read_pointer > pheader->size - (int)
06040                     sizeof(EVENT_HEADER))
06041                   new_read_pointer = 0;
06042                 pc->read_pointer = new_read_pointer;
06043               }
06044 
06045               else {
06046                 n_blocking++;
06047               }
06048 
06049               /* wake that client if it has a request */
06050               if (pc->read_wait && request_id != -1) {
06051 
06052 #ifdef DEBUG_MSG
06053                 cm_msg(MDEBUG,
06054                        "Send wake: rp=%d, wp=%d",
06055                        pheader->read_pointer, pheader->write_pointer);
06056 
06057 #endif                          /*  */
06058                 sprintf(str, "B %s %d", pheader->name, request_id);
06059                 ss_resume(pc->port, str);
06060               }
06061             }                   /* read_pointer blocks */
06062           }                     /* client loop */
06063         if (n_blocking > 0) {
06064 
06065           /* at least one client is blocking */
06066           bm_unlock_buffer(buffer_handle);
06067 
06068           /* return now in ASYNC mode */
06069           if (async_flag)
06070             return BM_ASYNC_RETURN;
06071 
06072 #ifdef DEBUG_MSG
06073           cm_msg(MDEBUG,
06074                  "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06075                  pheader->read_pointer,
06076                  pheader->write_pointer,
06077                  100 - 100.0 * size / pheader->size);
06078 
06079 #endif                          /*  */
06080 
06081           /* has the read pointer moved in between ? */
06082           size = pheader->read_pointer - pheader->write_pointer;
06083           if (size <= 0)
06084             size += pheader->size;
06085 
06086           /* suspend process */
06087           if (size <= total_size) {
06088 
06089             /* signal other clients wait mode */
06090             pclient[my_client_index].write_wait = total_size;
06091             status = ss_suspend(1000, MSG_BM);
06092             pclient[my_client_index].write_wait = 0;
06093 
06094             /* return if TCP connection broken */
06095             if (status == SS_ABORT)
06096               return SS_ABORT;
06097           }
06098 #ifdef DEBUG_MSG
06099           cm_msg(MDEBUG,
06100                  "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06101                  pheader->read_pointer,
06102                  pheader->write_pointer,
06103                  100 - 100.0 * size / pheader->size);
06104 
06105 #endif                          /*  */
06106           bm_lock_buffer(buffer_handle);
06107 
06108           /* has the write pointer moved in between ? */
06109           size = pheader->read_pointer - pheader->write_pointer;
06110           if (size <= 0)
06111             size += pheader->size;
06112         }
06113 
06114         else {
06115 
06116           /*
06117              calculate new global read pointer as "minimum" of
06118              client read pointers
06119            */
06120           min_wp = pheader->write_pointer;
06121           for (i = 0, pc = pclient;
06122                i < pheader->max_client_index; i++, pc++)
06123             if (pc->pid) {
06124               if (pc->read_pointer < min_wp)
06125                 min_wp = pc->read_pointer;
06126               if (pc->read_pointer >
06127                   pheader->write_pointer
06128                   && pc->read_pointer - pheader->size < min_wp)
06129                 min_wp = pc->read_pointer - pheader->size;
06130             }
06131           if (min_wp < 0)
06132             min_wp += pheader->size;
06133           pheader->read_pointer = min_wp;
06134         }
06135       }                         /* if (size <= total_size) */
06136     } while (size <= total_size);
06137 
06138     /* we have space, so let's copy the event */
06139     old_write_pointer = pheader->write_pointer;
06140     if (pheader->write_pointer + total_size <= pheader->size) {
06141       memcpy(pdata + pheader->write_pointer, pevent, total_size);
06142       pheader->write_pointer =
06143           (pheader->write_pointer + total_size) % pheader->size;
06144       if (pheader->write_pointer >
06145           pheader->size - (int) sizeof(EVENT_HEADER))
06146         pheader->write_pointer = 0;
06147     }
06148 
06149     else {
06150 
06151       /* split event */
06152       size = pheader->size - pheader->write_pointer;
06153       memcpy(pdata + pheader->write_pointer, pevent, size);
06154       memcpy(pdata, (char *) pevent + size, total_size - size);
06155       pheader->write_pointer = total_size - size;
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         for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06164           if (prequest->valid
06165               && bm_match_event(prequest->event_id,
06166                                 prequest->trigger_mask, pevent)) {
06167             if (prequest->sampling_type & GET_ALL)
06168               pclient[i].num_waiting_events++;
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 
06176 #ifdef DEBUG_MSG
06177           cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06178                  pheader->read_pointer, pheader->write_pointer);
06179 
06180 #endif                          /*  */
06181           sprintf(str, "B %s %d", pheader->name, request_id);
06182           ss_resume(pclient[i].port, str);
06183         }
06184 
06185         /* if that client has no request, shift its read pointer */
06186         if (num_requests_client == 0
06187             && pclient[i].read_pointer == old_write_pointer)
06188           pclient[i].read_pointer = pheader->write_pointer;
06189       }
06190 
06191     /* shift read pointer of own client */
06192 /* 16.4.99 SR, outcommented to receive own messages
06193 
06194   if (pclient[my_client_index].read_pointer == old_write_pointer)
06195     pclient[my_client_index].read_pointer = pheader->write_pointer;
06196 */
06197 
06198     /* calculate global read pointer as "minimum" of client read pointers */
06199     min_wp = pheader->write_pointer;
06200     for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06201       if (pc->pid) {
06202         if (pc->read_pointer < min_wp)
06203           min_wp = pc->read_pointer;
06204         if (pc->read_pointer > pheader->write_pointer
06205             && pc->read_pointer - pheader->size < min_wp)
06206           min_wp = pc->read_pointer - pheader->size;
06207       }
06208     if (min_wp < 0)
06209       min_wp += pheader->size;
06210 
06211 #ifdef DEBUG_MSG
06212     if (min_wp == pheader->read_pointer)
06213       cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
06214 
06215     else
06216       cm_msg(MDEBUG,
06217              "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
06218              pheader->write_pointer, pheader->read_pointer, min_wp, size);
06219 
06220 #endif                          /*  */
06221     pheader->read_pointer = min_wp;
06222 
06223     /* update statistics */
06224     pheader->num_in_events++;
06225 
06226     /* unlock the buffer */
06227     bm_unlock_buffer(buffer_handle);
06228   }
06229 
06230 #endif                          /* LOCAL_ROUTINES */
06231   return BM_SUCCESS;
06232 }
06233 
06234 
06235 /********************************************************************/
06236 /**
06237 Empty write cache.
06238 This function should be used if events in the write cache
06239 should be visible to the consumers immediately. It should be called at the
06240 end of each run, otherwise events could be kept in the write buffer and will
06241 flow to the data of the next run.
06242 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06243 @param async_flag Synchronous/asynchronous flag.
06244 If FALSE, the function blocks if the buffer has not
06245 enough free space to receive the full cache. If TRUE, the function returns
06246 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06247 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06248 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06249 and buffer has not enough space to receive cache<br>
06250 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06251 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
06252 and recompile.
06253 */
06254 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06255 {
06256   if (rpc_is_remote())
06257     return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06258 
06259 #ifdef LOCAL_ROUTINES
06260   {
06261     BUFFER *pbuf;
06262     BUFFER_HEADER *pheader;
06263     BUFFER_CLIENT *pclient, *pc;
06264     EVENT_REQUEST *prequest;
06265     EVENT_HEADER *pevent, *pevent_test;
06266     INT i, j, min_wp, size, total_size, status;
06267     INT increment;
06268     INT my_client_index;
06269     INT old_write_pointer;
06270     INT old_read_pointer, new_read_pointer;
06271     char *pdata;
06272     BOOL blocking;
06273     INT n_blocking;
06274     INT request_id;
06275     char str[80];
06276 
06277     pbuf = &_buffer[buffer_handle - 1];
06278     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06279       cm_msg(MERROR, "bm_flush_cache",
06280              "invalid buffer handle %d", buffer_handle);
06281       return BM_INVALID_HANDLE;
06282     }
06283     if (!pbuf->attached) {
06284       cm_msg(MERROR, "bm_flush_cache",
06285              "invalid buffer handle %d", buffer_handle);
06286       return BM_INVALID_HANDLE;
06287     }
06288     if (pbuf->write_cache_size == 0)
06289       return BM_SUCCESS;
06290 
06291     /* check if anything needs to be flushed */
06292     if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06293       return BM_SUCCESS;
06294 
06295     /* calculate some shorthands */
06296     pheader = _buffer[buffer_handle - 1].buffer_header;
06297     pdata = (char *) (pheader + 1);
06298     my_client_index = _buffer[buffer_handle - 1].client_index;
06299     pclient = pheader->client;
06300     pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06301 
06302     /* lock the buffer */
06303     bm_lock_buffer(buffer_handle);
06304 
06305 #ifdef DEBUG_MSG
06306     cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d",
06307            pheader->read_pointer, pheader->write_pointer);
06308 
06309 #endif                          /*  */
06310 
06311     /* check if enough space in buffer left */
06312     do {
06313       size = pheader->read_pointer - pheader->write_pointer;
06314       if (size <= 0)
06315         size += pheader->size;
06316       if (size <= pbuf->write_cache_wp) {
06317 
06318         /* if not enough space, find out who's blocking */
06319         n_blocking = 0;
06320         for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06321           if (pc->pid) {
06322             if (pc->read_pointer == pheader->read_pointer) {
06323 
06324               /*
06325                  First assume that the client with the "minimum" read pointer
06326                  is not really blocking due to a GET_ALL request.
06327                */
06328               blocking = FALSE;
06329               request_id = -1;
06330 
06331               /* check if this request blocks. */
06332               prequest = pc->event_request;
06333               pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06334               for (j = 0; j < pc->max_request_index; j++, prequest++)
06335                 if (prequest->valid
06336                     && bm_match_event(prequest->
06337                                       event_id,
06338                                       prequest->
06339                                       trigger_mask, pevent_test)) {
06340                   request_id = prequest->id;
06341                   if (prequest->sampling_type & GET_ALL) {
06342                     blocking = TRUE;
06343                     break;
06344                   }
06345                 }
06346               if (!blocking) {
06347 
06348                 /*
06349                    The blocking guy has no GET_ALL request for this event
06350                    -> shift its read pointer.
06351                  */
06352                 old_read_pointer = pc->read_pointer;
06353                 increment =
06354                     sizeof(EVENT_HEADER) +
06355                     ((EVENT_HEADER *) (pdata +
06356                                        pc->read_pointer))->data_size;
06357 
06358                 /* correct increment for DWORD boundary */
06359                 increment = ALIGN(increment);
06360                 new_read_pointer =
06361                     (pc->read_pointer + increment) % pheader->size;
06362                 if (new_read_pointer > pheader->size - (int)
06363                     sizeof(EVENT_HEADER))
06364                   new_read_pointer = 0;
06365 
06366 #ifdef DEBUG_MSG
06367                 cm_msg(MDEBUG,
06368                        "bm_flush_cache: shift client %d rp=%d -> =%d",
06369                        i, pc->read_pointer, new_read_pointer);
06370 
06371 #endif                          /*  */
06372                 pc->read_pointer = new_read_pointer;
06373               }
06374 
06375               else {
06376                 n_blocking++;
06377               }
06378 
06379               /* wake that client if it has a request */
06380               if (pc->read_wait && request_id != -1) {
06381 
06382 #ifdef DEBUG_MSG
06383                 cm_msg(MDEBUG,
06384                        "Send wake: rp=%d, wp=%d",
06385                        pheader->read_pointer, pheader->write_pointer);
06386 
06387 #endif                          /*  */
06388                 sprintf(str, "B %s %d", pheader->name, request_id);
06389                 ss_resume(pc->port, str);
06390               }
06391             }                   /* read_pointer blocks */
06392           }                     /* client loop */
06393         if (n_blocking > 0) {
06394 
06395           /* at least one client is blocking */
06396           bm_unlock_buffer(buffer_handle);
06397 
06398           /* return now in ASYNC mode */
06399           if (async_flag)
06400             return BM_ASYNC_RETURN;
06401 
06402 #ifdef DEBUG_MSG
06403           cm_msg(MDEBUG,
06404                  "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06405                  pheader->read_pointer,
06406                  pheader->write_pointer,
06407                  100 - 100.0 * size / pheader->size);
06408 
06409 #endif                          /*  */
06410 
06411           /* has the read pointer moved in between ? */
06412           size = pheader->read_pointer - pheader->write_pointer;
06413           if (size <= 0)
06414             size += pheader->size;
06415 
06416           /* suspend process */
06417           if (size <= pbuf->write_cache_wp) {
06418 
06419             /* signal other clients wait mode */
06420             pclient[my_client_index].write_wait = pbuf->write_cache_wp;
06421             status = ss_suspend(1000, MSG_BM);
06422             pclient[my_client_index].write_wait = 0;
06423 
06424             /* return if TCP connection broken */
06425             if (status == SS_ABORT)
06426               return SS_ABORT;
06427           }
06428 #ifdef DEBUG_MSG
06429           cm_msg(MDEBUG,
06430                  "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06431                  pheader->read_pointer,
06432                  pheader->write_pointer,
06433                  100 - 100.0 * size / pheader->size);
06434 
06435 #endif                          /*  */
06436           bm_lock_buffer(buffer_handle);
06437 
06438           /* has the write pointer moved in between ? */
06439           size = pheader->read_pointer - pheader->write_pointer;
06440           if (size <= 0)
06441             size += pheader->size;
06442         }
06443 
06444         else {
06445 
06446           /*
06447              calculate new global read pointer as "minimum" of
06448              client read pointers
06449            */
06450           min_wp = pheader->write_pointer;
06451           for (i = 0, pc = pclient;
06452                i < pheader->max_client_index; i++, pc++)
06453             if (pc->pid) {
06454               if (pc->read_pointer < min_wp)
06455                 min_wp = pc->read_pointer;
06456               if (pc->read_pointer >
06457                   pheader->write_pointer
06458                   && pc->read_pointer - pheader->size < min_wp)
06459                 min_wp = pc->read_pointer - pheader->size;
06460             }
06461           if (min_wp < 0)
06462             min_wp += pheader->size;
06463           pheader->read_pointer = min_wp;
06464         }
06465       }                         /* if (size <= total_size) */
06466     } while (size <= pbuf->write_cache_wp);
06467 
06468     /* we have space, so let's copy the event */
06469     old_write_pointer = pheader->write_pointer;
06470 
06471 #ifdef DEBUG_MSG
06472     cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d",
06473            pheader->read_pointer, pheader->write_pointer);
06474 
06475 #endif                          /*  */
06476     while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06477 
06478       /* loop over all events in cache */
06479       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06480       total_size = pevent->data_size + sizeof(EVENT_HEADER);
06481 
06482       /* correct size for DWORD boundary */
06483       total_size = ALIGN(total_size);
06484       if (pheader->write_pointer + total_size <= pheader->size) {
06485         memcpy(pdata + pheader->write_pointer, pevent, total_size);
06486         pheader->write_pointer =
06487             (pheader->write_pointer + total_size) % pheader->size;
06488         if (pheader->write_pointer >
06489             pheader->size - (int) sizeof(EVENT_HEADER))
06490           pheader->write_pointer = 0;
06491       }
06492 
06493       else {
06494 
06495         /* split event */
06496         size = pheader->size - pheader->write_pointer;
06497         memcpy(pdata + pheader->write_pointer, pevent, size);
06498         memcpy(pdata, (char *) pevent + size, total_size - size);
06499         pheader->write_pointer = total_size - size;
06500       } pbuf->write_cache_rp += total_size;
06501     } pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06502 
06503     /* check which clients are waiting */
06504     for (i = 0; i < pheader->max_client_index; i++)
06505       if (pclient[i].pid && pclient[i].read_wait) {
06506 
06507 #ifdef DEBUG_MSG
06508         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06509                pheader->read_pointer, pheader->write_pointer);
06510 
06511 #endif                          /*  */
06512         sprintf(str, "B %s %d", pheader->name, -1);
06513         ss_resume(pclient[i].port, str);
06514       }
06515 
06516     /* shift read pointer of own client */
06517 /* 16.4.99 SR, outcommented to receive own messages
06518 
06519   if (pclient[my_client_index].read_pointer == old_write_pointer)
06520     pclient[my_client_index].read_pointer = pheader->write_pointer;
06521 */
06522 
06523     /* calculate global read pointer as "minimum" of client read pointers */
06524     min_wp = pheader->write_pointer;
06525     for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06526       if (pc->pid) {
06527 
06528 #ifdef DEBUG_MSG
06529         cm_msg(MDEBUG,
06530                "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06531 
06532 #endif                          /*  */
06533         if (pc->read_pointer < min_wp)
06534           min_wp = pc->read_pointer;
06535         if (pc->read_pointer > pheader->write_pointer
06536             && pc->read_pointer - pheader->size < min_wp)
06537           min_wp = pc->read_pointer - pheader->size;
06538       }
06539     if (min_wp < 0)
06540       min_wp += pheader->size;
06541 
06542 #ifdef DEBUG_MSG
06543     if (min_wp == pheader->read_pointer)
06544       cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06545 
06546     else
06547       cm_msg(MDEBUG,
06548              "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06549              pheader->write_pointer, pheader->read_pointer, min_wp, size);
06550 
06551 #endif                          /*  */
06552     pheader->read_pointer = min_wp;
06553 
06554     /* update statistics */
06555     pheader->num_in_events++;
06556 
06557     /* unlock the buffer */
06558     bm_unlock_buffer(buffer_handle);
06559   }
06560 
06561 #endif                          /* LOCAL_ROUTINES */
06562   return BM_SUCCESS;
06563 }
06564 
06565 
06566 /********************************************************************/
06567 /**
06568 Receives events directly.
06569 This function is an alternative way to receive events without
06570 a main loop.
06571 
06572 It can be used in analysis systems which actively receive events,
06573 rather than using callbacks. A analysis package could for example contain its own
06574 command line interface. A command
06575 like "receive 1000 events" could make it necessary to call bm_receive_event()
06576 1000 times in a row to receive these events and then return back to the
06577 command line prompt.
06578 The according bm_request_event() call contains NULL as the
06579 callback routine to indicate that bm_receive_event() is called to receive
06580 events.
06581 \code
06582 #include <stdio.h>
06583 #include "midas.h"
06584 void process_event(EVENT_HEADER *pheader)
06585 {
06586  printf("Received event #%d\r",
06587  pheader->serial_number);
06588 }
06589 main()
06590 {
06591   INT status, request_id;
06592   HNDLE hbuf;
06593   char event_buffer[1000];
06594   status = cm_connect_experiment("", "Sample",
06595   "Simple Analyzer", NULL);
06596   if (status != CM_SUCCESS)
06597    return 1;
06598   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06599   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06600 
06601   do
06602   {
06603    size = sizeof(event_buffer);
06604    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06605   if (status == CM_SUCCESS)
06606    process_event((EVENT_HEADER *) event_buffer);
06607    <...do something else...>
06608    status = cm_yield(0);
06609   } while (status != RPC_SHUTDOWN &&
06610   status != SS_ABORT);
06611   cm_disconnect_experiment();
06612   return 0;
06613 }
06614 \endcode
06615 @param buffer_handle buffer handle
06616 @param destination destination address where event is written to
06617 @param buf_size size of destination buffer on input, size of event plus
06618 header on return.
06619 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06620 blocks if no event is available. If TRUE, the function returns immediately
06621 with a value of BM_ASYNC_RETURN without receiving any event.
06622 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06623 BM_TRUNCATED   The event is larger than the destination buffer and was
06624                therefore truncated <br>
06625 BM_ASYNC_RETURN No event available
06626 */
06627 INT bm_receive_event(INT buffer_handle,
06628                      void *destination, INT * buf_size, INT async_flag)
06629 {
06630   if (rpc_is_remote()) {
06631     if (*buf_size > NET_BUFFER_SIZE) {
06632       cm_msg(MERROR, "bm_receive_event",
06633              "max. event size larger than NET_BUFFER_SIZE");
06634       return RPC_NET_ERROR;
06635     }
06636     return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06637                     destination, buf_size, async_flag);
06638   }
06639 #ifdef LOCAL_ROUTINES
06640   {
06641     BUFFER *pbuf;
06642     BUFFER_HEADER *pheader;
06643     BUFFER_CLIENT *pclient, *pc, *pctmp;
06644     EVENT_REQUEST *prequest;
06645     EVENT_HEADER *pevent;
06646     char *pdata;
06647     INT convert_flags;
06648     INT i, min_wp, size, max_size, total_size, status = 0;
06649     INT my_client_index;
06650     BOOL found;
06651     INT old_read_pointer, new_read_pointer;
06652 
06653     pbuf = &_buffer[buffer_handle - 1];
06654     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06655       cm_msg(MERROR, "bm_receive_event",
06656              "invalid buffer handle %d", buffer_handle);
06657       return BM_INVALID_HANDLE;
06658     }
06659     if (!pbuf->attached) {
06660       cm_msg(MERROR, "bm_receive_event",
06661              "invalid buffer handle %d", buffer_handle);
06662       return BM_INVALID_HANDLE;
06663     }
06664     max_size = *buf_size;
06665     *buf_size = 0;
06666     if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06667       convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06668 
06669     else
06670       convert_flags = 0;
06671   CACHE_READ:
06672     /* look if there is anything in the cache */
06673     if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06674       pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06675       size = pevent->data_size + sizeof(EVENT_HEADER);
06676       if (size > max_size) {
06677         memcpy(destination,
06678                pbuf->read_cache + pbuf->read_cache_rp, max_size);
06679         cm_msg(MERROR, "bm_receive_event",
06680                "event size larger than buffer size");
06681         *buf_size = max_size;
06682         status = BM_TRUNCATED;
06683       }
06684 
06685       else {
06686         memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06687         *buf_size = size;
06688         status = BM_SUCCESS;
06689       }
06690 
06691       /* now convert event header */
06692       if (convert_flags) {
06693         pevent = (EVENT_HEADER *) destination;
06694         rpc_convert_single(&pevent->event_id, TID_SHORT,
06695                            RPC_OUTGOING, convert_flags);
06696         rpc_convert_single(&pevent->trigger_mask,
06697                            TID_SHORT, RPC_OUTGOING, convert_flags);
06698         rpc_convert_single(&pevent->serial_number, TID_DWORD,
06699                            RPC_OUTGOING, convert_flags);
06700         rpc_convert_single(&pevent->time_stamp, TID_DWORD,
06701                            RPC_OUTGOING, convert_flags);
06702         rpc_convert_single(&pevent->data_size, TID_DWORD,
06703                            RPC_OUTGOING, convert_flags);
06704       }
06705 
06706       /* correct size for DWORD boundary */
06707       size = ALIGN(size);
06708       pbuf->read_cache_rp += size;
06709       if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06710         pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06711       return status;
06712     }
06713 
06714     /* calculate some shorthands */
06715     pheader = pbuf->buffer_header;
06716     pdata = (char *) (pheader + 1);
06717     my_client_index = pbuf->client_index;
06718     pclient = pheader->client;
06719     pc = pheader->client + my_client_index;
06720 
06721     /* first do a quick check without locking the buffer */
06722     if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06723       return BM_ASYNC_RETURN;
06724 
06725     /* lock the buffer */
06726     bm_lock_buffer(buffer_handle);
06727   LOOP:while (pheader->write_pointer == pc->read_pointer) {
06728       bm_unlock_buffer(buffer_handle);
06729 
06730       /* return now in ASYNC mode */
06731       if (async_flag == ASYNC)
06732         return BM_ASYNC_RETURN;
06733       pc->read_wait = TRUE;
06734 
06735       /* check again pointers (may have moved in between) */
06736       if (pheader->write_pointer == pc->read_pointer) {
06737 
06738 #ifdef DEBUG_MSG
06739         cm_msg(MDEBUG,
06740                "Receive sleep: grp=%d, rp=%d wp=%d",
06741                pheader->read_pointer,
06742                pc->read_pointer, pheader->write_pointer);
06743 
06744 #endif                          /*  */
06745         status = ss_suspend(1000, MSG_BM);
06746 
06747 #ifdef DEBUG_MSG
06748         cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
06749                pheader->read_pointer, pheader->write_pointer);
06750 
06751 #endif                          /*  */
06752 
06753         /* return if TCP connection broken */
06754         if (status == SS_ABORT)
06755           return SS_ABORT;
06756       }
06757       pc->read_wait = FALSE;
06758       bm_lock_buffer(buffer_handle);
06759     }
06760 
06761     /* check if event at current read pointer matches a request */
06762     found = FALSE;
06763 
06764     do {
06765       pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06766       total_size = pevent->data_size + sizeof(EVENT_HEADER);
06767       total_size = ALIGN(total_size);
06768       prequest = pc->event_request;
06769       for (i = 0; i < pc->max_request_index; i++, prequest++)
06770         if (prequest->valid
06771             && bm_match_event(prequest->event_id,
06772                               prequest->trigger_mask, pevent)) {
06773 
06774           /* we found one, so copy it */
06775           if (pbuf->read_cache_size > 0
06776               && !(total_size >
06777                    pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06778             if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
06779               goto CACHE_FULL;
06780             if (pc->read_pointer + total_size <= pheader->size) {
06781 
06782               /* copy event to cache */
06783               memcpy(pbuf->read_cache +
06784                      pbuf->read_cache_wp, pevent, total_size);
06785             }
06786 
06787             else {
06788 
06789               /* event is splitted */
06790               size = pheader->size - pc->read_pointer;
06791               memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06792               memcpy((char *) pbuf->read_cache +
06793                      pbuf->read_cache_wp + size, pdata, total_size - size);
06794           }}
06795 
06796           else {
06797             if (pc->read_pointer + total_size <= pheader->size) {
06798 
06799               /* event is not splitted */
06800               if (total_size > max_size)
06801                 memcpy(destination, pevent, max_size);
06802 
06803               else
06804                 memcpy(destination, pevent, total_size);
06805             }
06806 
06807             else {
06808 
06809               /* event is splitted */
06810               size = pheader->size - pc->read_pointer;
06811               if (size > max_size)
06812                 memcpy(destination, pevent, max_size);
06813 
06814               else
06815                 memcpy(destination, pevent, size);
06816               if (total_size > max_size) {
06817                 if (size <= max_size)
06818                   memcpy((char *)
06819                          destination + size, pdata, max_size - size);
06820               }
06821 
06822               else
06823                 memcpy((char *)
06824                        destination + size, pdata, total_size - size);
06825             } if (total_size < max_size)
06826               *buf_size = total_size;
06827 
06828             else
06829               *buf_size = max_size;
06830 
06831             /* now convert event header */
06832             if (convert_flags) {
06833               pevent = (EVENT_HEADER *) destination;
06834               rpc_convert_single(&pevent->
06835                                  event_id,
06836                                  TID_SHORT, RPC_OUTGOING, convert_flags);
06837               rpc_convert_single(&pevent->
06838                                  trigger_mask,
06839                                  TID_SHORT, RPC_OUTGOING, convert_flags);
06840               rpc_convert_single(&pevent->
06841                                  serial_number,
06842                                  TID_DWORD, RPC_OUTGOING, convert_flags);
06843               rpc_convert_single(&pevent->time_stamp,
06844                                  TID_DWORD, RPC_OUTGOING, convert_flags);
06845               rpc_convert_single(&pevent->data_size,
06846                                  TID_DWORD, RPC_OUTGOING, convert_flags);
06847             }
06848           }
06849           if (pbuf->read_cache_size > 0
06850               && !(total_size >
06851                    pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06852             pbuf->read_cache_wp += total_size;
06853           }
06854 
06855           else {
06856             if (total_size > max_size) {
06857               cm_msg(MERROR,
06858                      "bm_receive_event",
06859                      "event size larger than buffer size");
06860               status = BM_TRUNCATED;
06861             }
06862 
06863             else
06864               status = BM_SUCCESS;
06865           }
06866 
06867           /* update statistics */
06868           found = TRUE;
06869           pheader->num_out_events++;
06870           break;
06871         }
06872       old_read_pointer = pc->read_pointer;
06873 
06874       /* shift read pointer */
06875       new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06876       if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06877         new_read_pointer = 0;
06878 
06879 #ifdef DEBUG_MSG
06880       cm_msg(MDEBUG,
06881              "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06882              pheader->write_pointer, pc->read_pointer,
06883              new_read_pointer, found, total_size);
06884 
06885 #endif                          /*  */
06886       pc->read_pointer = new_read_pointer;
06887 
06888       /*
06889          Repeat until a requested event is found or no more events
06890          are available.
06891        */
06892       if (pbuf->read_cache_size == 0 && found)
06893         break;
06894 
06895       /* break if event has bypassed read cache */
06896       if (pbuf->read_cache_size > 0
06897           && total_size > pbuf->read_cache_size
06898           && pbuf->read_cache_wp == 0)
06899         break;
06900     } while (pheader->write_pointer != pc->read_pointer);
06901   CACHE_FULL:
06902     /* calculate global read pointer as "minimum" of client read pointers */
06903     min_wp = pheader->write_pointer;
06904     for (i = 0, pctmp = pclient; i < pheader->max_client_index;
06905          i++, pctmp++)
06906       if (pctmp->pid) {
06907         if (pctmp->read_pointer < min_wp)
06908           min_wp = pctmp->read_pointer;
06909         if (pctmp->read_pointer > pheader->write_pointer
06910             && pctmp->read_pointer - pheader->size < min_wp)
06911           min_wp = pctmp->read_pointer - pheader->size;
06912       }
06913     if (min_wp < 0)
06914       min_wp += pheader->size;
06915     pheader->read_pointer = min_wp;
06916 
06917     /*
06918        If read pointer has been changed, it may have freed up some space
06919        for waiting producers. So check if free space is now more than 50%
06920        of the buffer size and wake waiting producers.
06921      */
06922     size = pc->read_pointer - pheader->write_pointer;
06923     if (size <= 0)
06924       size += pheader->size;
06925     if (size >= pheader->size * 0.5)
06926       for (i = 0, pctmp = pclient;
06927            i < pheader->max_client_index; i++, pctmp++)
06928         if (pctmp->pid && (pctmp->write_wait < size)
06929             && (pctmp->pid != ss_getpid()
06930                 ||              /* check if not own thread */
06931                 (pctmp->pid == ss_getpid()
06932                  && pctmp->tid != ss_gettid()))) {
06933 
06934 #ifdef DEBUG_MSG
06935           cm_msg(MDEBUG,
06936                  "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06937                  pheader->read_pointer,
06938                  pheader->write_pointer,
06939                  100 - 100.0 * size / pheader->size);
06940 
06941 #endif                          /*  */
06942           ss_resume(pctmp->port, "B  ");
06943         }
06944 
06945     /* if no matching event found, start again */
06946     if (!found)
06947       goto LOOP;
06948     bm_unlock_buffer(buffer_handle);
06949     if (pbuf->read_cache_size > 0
06950         && !(total_size > pbuf->read_cache_size
06951              && pbuf->read_cache_wp == 0))
06952       goto CACHE_READ;
06953     return status;
06954   }
06955 
06956 #else                           /* LOCAL_ROUTINES */
06957   return SS_SUCCESS;
06958 
06959 #endif                          /*  */
06960 }
06961 
06962 
06963 /********************************************************************/
06964 /**
06965 Skip all events in current buffer.
06966 
06967 Useful for single event displays to see the newest events
06968 @param buffer_handle      Handle of the buffer. Must be obtained
06969                           via bm_open_buffer.
06970 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
06971 */
06972 INT bm_skip_event(INT buffer_handle)
06973 {
06974   if (rpc_is_remote())
06975     return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
06976 
06977 #ifdef LOCAL_ROUTINES
06978   {
06979     BUFFER *pbuf;
06980     BUFFER_HEADER *pheader;
06981     BUFFER_CLIENT *pclient;
06982 
06983     if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06984       cm_msg(MERROR, "bm_skip_event",
06985              "invalid buffer handle %d", buffer_handle);
06986       return BM_INVALID_HANDLE;
06987     }
06988     pbuf = &_buffer[buffer_handle - 1];
06989     pheader = pbuf->buffer_header;
06990     if (!pbuf->attached) {
06991       cm_msg(MERROR, "bm_skip_event",
06992              "invalid buffer handle %d", buffer_handle);
06993       return BM_INVALID_HANDLE;
06994     }
06995 
06996     /* clear cache */
06997     if (pbuf->read_cache_wp > pbuf->read_cache_rp)
06998       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06999     bm_lock_buffer(buffer_handle);
07000 
07001     /* forward read pointer to global write pointer */
07002     pclient = pheader->client + pbuf->client_index;
07003     pclient->read_pointer = pheader->write_pointer;
07004     bm_unlock_buffer(buffer_handle);
07005   }
07006 
07007 #endif                          /*  */
07008   return BM_SUCCESS;
07009 }
07010 
07011 
07012 /********************************************************************/
07013 /**
07014 Check a buffer if an event is available and call the dispatch function if found.
07015 @param buffer_name       Name of buffer
07016 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
07017                     RPC_NET_ERROR
07018 */
07019 INT bm_push_event(char *buffer_name)
07020 {
07021 
07022 #ifdef LOCAL_ROUTINES
07023   {
07024     BUFFER *pbuf;
07025     BUFFER_HEADER *pheader;
07026     BUFFER_CLIENT *pclient, *pc, *pctmp;
07027     EVENT_REQUEST *prequest;
07028     EVENT_HEADER *pevent;
07029     char *pdata;
07030     INT i, min_wp, size, total_size, buffer_handle;
07031     INT my_client_index;
07032     BOOL found;
07033     INT old_read_pointer, new_read_pointer;
07034 
07035     for (i = 0; i < _buffer_entries; i++)
07036       if (strcmp(buffer_name, _buffer[i].buffer_header->name)
07037           == 0)
07038         break;
07039     if (i == _buffer_entries)
07040       return BM_INVALID_HANDLE;
07041     buffer_handle = i + 1;
07042     pbuf = &_buffer[buffer_handle - 1];
07043     if (!pbuf->attached)
07044       return BM_INVALID_HANDLE;
07045 
07046     /* return immediately if no callback routine is defined */
07047     if (!pbuf->callback)
07048       return BM_SUCCESS;
07049     if (_event_buffer_size == 0) {
07050       _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
07051       if (_event_buffer == NULL) {
07052         cm_msg(MERROR, "bm_push_event",
07053                "not enough memory to allocate cache buffer");
07054         return BM_NO_MEMORY;
07055       }
07056       _event_buffer_size = 1000;
07057     }
07058   CACHE_READ:
07059     /* look if there is anything in the cache */
07060     if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
07061       pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
07062       size = pevent->data_size + sizeof(EVENT_HEADER);
07063 
07064       /* correct size for DWORD boundary */
07065       size = ALIGN(size);
07066 
07067       /* increment read pointer */
07068       pbuf->read_cache_rp += size;
07069       if (pbuf->read_cache_rp == pbuf->read_cache_wp)
07070         pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07071 
07072       /* call dispatcher */
07073       for (i = 0; i < _request_list_entries; i++)
07074         if (_request_list[i].buffer_handle ==
07075             buffer_handle
07076             && bm_match_event(_request_list[i].event_id,
07077                               _request_list[i].trigger_mask, pevent)) {
07078 
07079           /* if event is fragmented, call defragmenter */
07080           if ((pevent->event_id & 0xF000) ==
07081               EVENTID_FRAG1 || (pevent->event_id & 0xF000) == EVENTID_FRAG)
07082             bm_defragment_event(buffer_handle, i, pevent,
07083                                 (void *) (pevent + 1),
07084                                 _request_list[i].dispatcher);
07085 
07086           else
07087             _request_list[i].
07088                 dispatcher(buffer_handle, i,
07089                            pevent, (void *) (pevent + 1));
07090         }
07091       return BM_MORE_EVENTS;
07092     }
07093 
07094     /* calculate some shorthands */
07095     pheader = pbuf->buffer_header;
07096     pdata = (char *) (pheader + 1);
07097     my_client_index = pbuf->client_index;
07098     pclient = pheader->client;
07099     pc = pheader->client + my_client_index;
07100 
07101     /* first do a quick check without locking the buffer */
07102     if (pheader->write_pointer == pc->read_pointer)
07103       return BM_SUCCESS;
07104 
07105     /* lock the buffer */
07106     bm_lock_buffer(buffer_handle);
07107   LOOP:if (pheader->write_pointer == pc->read_pointer) {
07108       bm_unlock_buffer(buffer_handle);
07109 
07110       /* return if no event available */
07111       return BM_SUCCESS;
07112     }
07113 
07114     /* check if event at current read pointer matches a request */
07115     found = FALSE;
07116 
07117     do {
07118       pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07119       total_size = pevent->data_size + sizeof(EVENT_HEADER);
07120       total_size = ALIGN(total_size);
07121       prequest = pc->event_request;
07122       for (i = 0; i < pc->max_request_index; i++, prequest++)
07123         if (prequest->valid
07124             && bm_match_event(prequest->event_id,
07125                               prequest->trigger_mask, pevent)) {
07126 
07127           /* we found one, so copy it */
07128           if (pbuf->read_cache_size > 0
07129               && !(total_size >
07130                    pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07131 
07132             /* copy dispatch function and event to cache */
07133             if (pbuf->read_cache_size -
07134                 pbuf->read_cache_wp <
07135                 total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
07136               goto CACHE_FULL;
07137             if (pc->read_pointer + total_size <= pheader->size) {
07138 
07139               /* copy event to cache */
07140               memcpy(pbuf->read_cache +
07141                      pbuf->read_cache_wp, pevent, total_size);
07142             }
07143 
07144             else {
07145 
07146               /* event is splitted */
07147               size = pheader->size - pc->read_pointer;
07148               memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07149               memcpy((char *) pbuf->read_cache +
07150                      pbuf->read_cache_wp + size, pdata, total_size - size);
07151             } pbuf->read_cache_wp += total_size;
07152           }
07153 
07154           else {
07155 
07156             /* copy event to copy buffer, save dispatcher */
07157             if (total_size > _event_buffer_size) {
07158               _event_buffer = (EVENT_HEADER *)
07159                   realloc(_event_buffer, total_size);
07160               _event_buffer_size = total_size;
07161             }
07162             if (pc->read_pointer + total_size <= pheader->size) {
07163               memcpy(_event_buffer, pevent, total_size);
07164             }
07165 
07166             else {
07167 
07168               /* event is splitted */
07169               size = pheader->size - pc->read_pointer;
07170               memcpy(_event_buffer, pevent, size);
07171               memcpy((char *) _event_buffer +
07172                      size, pdata, total_size - size);
07173           }}
07174           /* update statistics */
07175           found = TRUE;
07176           pheader->num_out_events++;
07177           break;
07178         }
07179       old_read_pointer = pc->read_pointer;
07180 
07181       /* shift read pointer */
07182       new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07183       if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07184         new_read_pointer = 0;
07185 
07186 #ifdef DEBUG_MSG
07187       cm_msg(MDEBUG,
07188              "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07189              pheader->write_pointer, pc->read_pointer,
07190              new_read_pointer, found, total_size);
07191 
07192 #endif                          /*  */
07193       pc->read_pointer = new_read_pointer;
07194 
07195       /*
07196          Repeat until a requested event is found or no more events
07197          are available or large event received.
07198        */
07199       if (pbuf->read_cache_size == 0 && found)
07200         break;
07201 
07202       /* break if event has bypassed read cache */
07203       if (pbuf->read_cache_size > 0
07204           && total_size > pbuf->read_cache_size
07205           && pbuf->read_cache_wp == 0)
07206         break;
07207     } while (pheader->write_pointer != pc->read_pointer);
07208   CACHE_FULL:
07209     /* calculate global read pointer as "minimum" of client read pointers */
07210     min_wp = pheader->write_pointer;
07211     for (i = 0, pctmp = pclient; i < pheader->max_client_index;
07212          i++, pctmp++)
07213       if (pctmp->pid) {
07214         if (pctmp->read_pointer < min_wp)
07215           min_wp = pctmp->read_pointer;
07216         if (pctmp->read_pointer > pheader->write_pointer
07217             && pctmp->read_pointer - pheader->size < min_wp)
07218           min_wp = pctmp->read_pointer - pheader->size;
07219       }
07220     if (min_wp < 0)
07221       min_wp += pheader->size;
07222     pheader->read_pointer = min_wp;
07223 
07224     /*
07225        If read pointer has been changed, it may have freed up some space
07226        for waiting producers. So check if free space is now more than 50%
07227        of the buffer size and wake waiting producers.
07228      */
07229     size = pc->read_pointer - pheader->write_pointer;
07230     if (size <= 0)
07231       size += pheader->size;
07232     if (size >= pheader->size * 0.5)
07233       for (i = 0, pctmp = pclient;
07234            i < pheader->max_client_index; i++, pctmp++)
07235         if (pctmp->pid && (pctmp->write_wait < size)
07236             && (pctmp->pid != ss_getpid()
07237                 ||              /* check if not own thread */
07238                 (pctmp->pid == ss_getpid()
07239                  && pctmp->tid != ss_gettid()))) {
07240 
07241 #ifdef DEBUG_MSG
07242           cm_msg(MDEBUG,
07243                  "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07244                  pheader->read_pointer,
07245                  pheader->write_pointer,
07246                  100 - 100.0 * size / pheader->size);
07247 
07248 #endif                          /*  */
07249           ss_resume(pctmp->port, "B  ");
07250         }
07251 
07252     /* if no matching event found, start again */
07253     if (!found)
07254       goto LOOP;
07255     bm_unlock_buffer(buffer_handle);
07256     if (pbuf->read_cache_size > 0
07257         && !(total_size > pbuf->read_cache_size
07258              && pbuf->read_cache_wp == 0))
07259       goto CACHE_READ;
07260 
07261     /* call dispatcher */
07262     for (i = 0; i < _request_list_entries; i++)
07263       if (_request_list[i].buffer_handle == buffer_handle
07264           && bm_match_event(_request_list[i].event_id,
07265                             _request_list[i].trigger_mask,
07266                             _event_buffer)) {
07267         if ((_event_buffer->event_id & 0xF000) ==
07268             EVENTID_FRAG1
07269             || (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07270           bm_defragment_event(buffer_handle, i,
07271                               _event_buffer, (void *) (((EVENT_HEADER *)
07272                                                         _event_buffer) +
07273                                                        1),
07274                               _request_list[i].dispatcher);
07275 
07276         else
07277           _request_list[i].dispatcher(buffer_handle,
07278                                       i,
07279                                       _event_buffer,
07280                                       (void *) (((EVENT_HEADER *)
07281                                                  _event_buffer) + 1));
07282       }
07283     return BM_MORE_EVENTS;
07284   }
07285 
07286 #else                           /* LOCAL_ROUTINES */
07287   return BM_SUCCESS;
07288 
07289 #endif                          /*  */
07290 }
07291 
07292 
07293 /********************************************************************/
07294 /**
07295 Check if any requested event is waiting in a buffer
07296 @return TRUE             More events are waiting<br>
07297         FALSE            No more events are waiting
07298 */
07299 INT bm_check_buffers()
07300 {
07301 
07302 #ifdef LOCAL_ROUTINES
07303   {
07304     INT index, status;
07305     INT server_type, server_conn, tid;
07306     BOOL bMore;
07307     DWORD start_time;
07308 
07309     server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07310     server_conn = rpc_get_server_acception();
07311     tid = ss_gettid();
07312 
07313     /* if running as a server, buffer checking is done by client
07314        via ASYNC bm_receive_event */
07315     if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
07316       return FALSE;
07317     bMore = FALSE;
07318     start_time = ss_millitime();
07319 
07320     /* go through all buffers */
07321     for (index = 0; index < _buffer_entries; index++) {
07322       if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07323         continue;
07324       if (server_type != ST_SINGLE && _buffer[index].index != tid)
07325         continue;
07326       if (!_buffer[index].attached)
07327         continue;
07328 
07329       do {
07330         status = bm_push_event(_buffer[index].buffer_header->name);
07331         if (status != BM_MORE_EVENTS)
07332           break;
07333 
07334         /* stop after one second */
07335         if (ss_millitime() - start_time > 1000) {
07336           bMore = TRUE;
07337           break;
07338         }
07339       } while (TRUE);
07340     }
07341     return bMore;
07342   }
07343 
07344 #else                           /* LOCAL_ROUTINES */
07345   return FALSE;
07346 
07347 #endif                          /*  */
07348 }
07349 
07350 
07351 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07352 /********************************************************************/
07353 INT bm_mark_read_waiting(BOOL flag)
07354 /********************************************************************\
07355 
07356   Routine: bm_mark_read_waiting
07357 
07358   Purpose: Mark all open buffers ready for receiving events.
07359            Called internally by ss_suspend
07360 
07361 
07362   Input:
07363     BOOL flag               TRUE for waiting, FALSE for not waiting
07364 
07365   Output:
07366     none
07367 
07368   Function value:
07369     BM_SUCCESS              Successful completion
07370 
07371 \********************************************************************/
07372 {
07373   if (rpc_is_remote())
07374     return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07375 
07376 #ifdef LOCAL_ROUTINES
07377   {
07378     INT i;
07379     BUFFER_HEADER *pheader;
07380     BUFFER_CLIENT *pclient;
07381 
07382     /* Mark all buffer for read waiting */
07383     for (i = 0; i < _buffer_entries; i++) {
07384       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE
07385           && _buffer[i].index != rpc_get_server_acception())
07386         continue;
07387       if (rpc_get_server_option(RPC_OSERVER_TYPE) !=
07388           ST_SINGLE && _buffer[i].index != ss_gettid())
07389         continue;
07390       if (!_buffer[i].attached)
07391         continue;
07392       pheader = _buffer[i].buffer_header;
07393       pclient = pheader->client + _buffer[i].client_index;
07394       pclient->read_wait = flag;
07395     }
07396   }
07397 
07398 #endif                          /* LOCAL_ROUTINES */
07399   return BM_SUCCESS;
07400 }
07401 
07402 
07403 /********************************************************************/
07404 INT bm_notify_client(char *buffer_name, int socket)
07405 /********************************************************************\
07406 
07407   Routine: bm_notify_client
07408 
07409   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07410            the connected client
07411 
07412   Input:
07413     char  *buffer_name      Name of buffer
07414     int   socket            Network socket to client
07415 
07416   Output:
07417     none
07418 
07419   Function value:
07420     BM_SUCCESS              Successful completion
07421 
07422 \********************************************************************/
07423 {
07424   char buffer[32];
07425   NET_COMMAND *nc;
07426   INT i, convert_flags;
07427   static DWORD last_time = 0;
07428 
07429   for (i = 0; i < _buffer_entries; i++)
07430     if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07431       break;
07432   if (i == _buffer_entries)
07433     return BM_INVALID_HANDLE;
07434 
07435   /* don't send notification if client has no callback defined
07436      to receive events -> client calls bm_receive_event manually */
07437   if (!_buffer[i].callback)
07438     return DB_SUCCESS;
07439   convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07440 
07441   /* only send notification once each 500ms */
07442   if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07443     return DB_SUCCESS;
07444   last_time = ss_millitime();
07445   if (convert_flags & CF_ASCII) {
07446     sprintf(buffer, "MSG_BM&%s", buffer_name);
07447     send_tcp(socket, buffer, strlen(buffer) + 1, 0);
07448   }
07449 
07450   else {
07451     nc = (NET_COMMAND *) buffer;
07452     nc->header.routine_id = MSG_BM;
07453     nc->header.param_size = 0;
07454     if (convert_flags) {
07455       rpc_convert_single(&nc->header.routine_id, TID_DWORD,
07456                          RPC_OUTGOING, convert_flags);
07457       rpc_convert_single(&nc->header.param_size, TID_DWORD,
07458                          RPC_OUTGOING, convert_flags);
07459     }
07460 
07461     /* send the update notification to the client */
07462     send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07463   }
07464   return BM_SUCCESS;
07465 }
07466 
07467 
07468 /********************************************************************/
07469 INT bm_poll_event(INT flag)
07470 /********************************************************************\
07471 
07472   Routine: bm_poll_event
07473 
07474   Purpose: Poll an event from a remote server. Gets called by
07475            rpc_client_dispatch
07476 
07477   Input:
07478     INT flag         TRUE if called from cm_yield
07479 
07480   Output:
07481     none
07482 
07483   Function value:
07484     TRUE             More events are waiting
07485     FALSE            No more events are waiting
07486     SS_ABORT         Network connection broken
07487 
07488 \********************************************************************/
07489 {
07490   INT status, size, i, request_id;
07491   DWORD start_time;
07492   BOOL bMore;
07493   static BOOL bMoreLast = FALSE;
07494 
07495   if (_event_buffer_size == 0) {
07496     _event_buffer =
07497         (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07498     if (!_event_buffer) {
07499       cm_msg(MERROR, "bm_poll_event",
07500              "not enough memory to allocate event buffer");
07501       return SS_ABORT;
07502     }
07503     _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07504   }
07505   start_time = ss_millitime();
07506 
07507   /* if we got event notification, turn off read_wait */
07508   if (!flag)
07509     bm_mark_read_waiting(FALSE);
07510 
07511   /* if called from yield, return if no more events */
07512   if (flag) {
07513     if (!bMoreLast)
07514       return FALSE;
07515   }
07516   bMore = FALSE;
07517 
07518   /* loop over all requests */
07519   for (request_id = 0; request_id < _request_list_entries; request_id++) {
07520 
07521     /* continue if no dispatcher set (manual bm_receive_event) */
07522     if (_request_list[request_id].dispatcher == NULL)
07523       continue;
07524 
07525     do {
07526 
07527       /* receive event */
07528       size = _event_buffer_size;
07529       status =
07530           bm_receive_event(_request_list[request_id].
07531                            buffer_handle, _event_buffer, &size, ASYNC);
07532 
07533       /* call user function if successful */
07534       if (status == BM_SUCCESS)
07535 
07536         /* search proper request for this event */
07537         for (i = 0; i < _request_list_entries; i++)
07538           if ((_request_list[i].buffer_handle ==
07539                _request_list[request_id].buffer_handle)
07540               && bm_match_event(_request_list[i].
07541                                 event_id,
07542                                 _request_list[i].
07543                                 trigger_mask, _event_buffer)) {
07544             if ((_event_buffer->event_id & 0xF000) ==
07545                 EVENTID_FRAG1
07546                 || (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07547               bm_defragment_event(_request_list[i].
07548                                   buffer_handle, i,
07549                                   _event_buffer,
07550                                   (void *) (((EVENT_HEADER *)
07551                                              _event_buffer)
07552                                             + 1),
07553                                   _request_list[i].dispatcher);
07554 
07555             else
07556               _request_list[i].
07557                   dispatcher(_request_list[i].
07558                              buffer_handle, i,
07559                              _event_buffer, (void *) (((EVENT_HEADER *)
07560                                                        _event_buffer)
07561                                                       + 1));
07562           }
07563 
07564       /* break if no more events */
07565       if (status == BM_ASYNC_RETURN)
07566         break;
07567 
07568       /* break if server died */
07569       if (status == RPC_NET_ERROR)
07570         return SS_ABORT;
07571 
07572       /* stop after one second */
07573       if (ss_millitime() - start_time > 1000) {
07574         bMore = TRUE;
07575         break;
07576       }
07577     } while (TRUE);
07578   }
07579   if (!bMore)
07580     bm_mark_read_waiting(TRUE);
07581   bMoreLast = bMore;
07582   return bMore;
07583 }
07584 
07585 
07586 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07587 
07588 /********************************************************************/
07589 /** 
07590 Clears event buffer and cache.
07591 If an event buffer is large and a consumer is slow in analyzing
07592 events, events are usually received some time after they are produced.
07593 This effect is even more experienced if a read cache is used
07594 (via bm_set_cache_size()).
07595 When changes to the hardware are made in the experience, the consumer will then
07596 still analyze old events before any new event which reflects the hardware change.
07597 Users can be fooled by looking at histograms which reflect the hardware change
07598 many seconds after they have been made.
07599 
07600 To overcome this potential problem, the analyzer can call
07601 bm_empty_buffers() just after the hardware change has been made which
07602 skips all old events contained in event buffers and read caches.
07603 Technically this is done by forwarding the read pointer of the client.
07604 No events are really deleted, they are still visible to other clients like
07605 the logger.
07606 
07607 Note that the front-end also contains write buffers which can delay the
07608 delivery of events.
07609 The standard front-end framework mfe.c reduces this effect by flushing
07610 all buffers once every second.
07611 @return BM_SUCCESS
07612 */
07613 INT bm_empty_buffers()
07614 {
07615   if (rpc_is_remote())
07616     return rpc_call(RPC_BM_EMPTY_BUFFERS);
07617 
07618 #ifdef LOCAL_ROUTINES
07619   {
07620     INT index, server_type, server_conn, tid;
07621     BUFFER *pbuf;
07622     BUFFER_CLIENT *pclient;
07623 
07624     server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07625     server_conn = rpc_get_server_acception();
07626     tid = ss_gettid();
07627 
07628     /* go through all buffers */
07629     for (index = 0; index < _buffer_entries; index++) {
07630       if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07631         continue;
07632       if (server_type != ST_SINGLE && _buffer[index].index != tid)
07633         continue;
07634       if (!_buffer[index].attached)
07635         continue;
07636       pbuf = &_buffer[index];
07637 
07638       /* empty cache */
07639       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07640 
07641       /* set read pointer to write pointer */
07642       pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07643       bm_lock_buffer(index + 1);
07644       pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07645       bm_unlock_buffer(index + 1);
07646     }
07647   }
07648 
07649 #endif                          /* LOCAL_ROUTINES */
07650   return BM_SUCCESS;
07651 }
07652 
07653 
07654 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07655 
07656 #define MAX_DEFRAG_EVENTS 10
07657 typedef struct {
07658   WORD event_id;
07659   DWORD data_size;
07660   DWORD received;
07661   EVENT_HEADER *pevent;
07662 } EVENT_DEFRAG_BUFFER;
07663 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07664 
07665 /********************************************************************/
07666 void bm_defragment_event(HNDLE buffer_handle,
07667                          HNDLE request_id,
07668                          EVENT_HEADER * pevent,
07669                          void *pdata,
07670                          void (*dispatcher) (HNDLE,
07671                                              HNDLE,
07672                                              EVENT_HEADER *, void *))
07673 /********************************************************************\
07674 
07675   Routine: bm_defragment_event
07676 
07677   Purpose: Called internally from the event receiving routines
07678            bm_push_event and bm_poll_event to recombine event
07679            fragments and call the user callback routine upon
07680            completion.
07681 
07682   Input:
07683     HNDLE buffer_handle  Handle for the buffer containing event
07684     HNDLE request_id     Handle for event request
07685     EVENT_HEADER *pevent Pointer to event header
07686     void *pata           Pointer to event data
07687     dispatcher()         User callback routine
07688 
07689   Output:
07690     <calls dispatcher() after successfull recombination of event>
07691 
07692   Function value:
07693     void
07694 
07695 \********************************************************************/
07696 {
07697   INT i;
07698 
07699   if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07700 
07701     /*---- start new event ----*/
07702 
07703     /* check if fragments already stored */
07704     for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07705       if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07706         break;
07707     if (i < MAX_DEFRAG_EVENTS) {
07708       free(defrag_buffer[i].pevent);
07709       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07710       cm_msg(MERROR, "bm_defragement_event",
07711              "Received new event with ID %d while old fragments were not completed",
07712              (pevent->event_id & 0x0FFF));
07713     }
07714 
07715     /* search new slot */
07716     for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07717       if (defrag_buffer[i].event_id == 0)
07718         break;
07719     if (i == MAX_DEFRAG_EVENTS) {
07720       cm_msg(MERROR, "bm_defragment_evnet",
07721              "Not eough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07722       return;
07723     }
07724 
07725     /* check event size */
07726     if (pevent->data_size != sizeof(DWORD)) {
07727       cm_msg(MERROR, "bm_defragment_evnet",
07728              "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07729              pevent->data_size, sizeof(DWORD));
07730       return;
07731     }
07732 
07733     /* setup defragment buffer */
07734     defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07735     defrag_buffer[i].data_size = *(DWORD *) pdata;
07736     defrag_buffer[i].received = 0;
07737     defrag_buffer[i].pevent =
07738         (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) +
07739                                 defrag_buffer[i].data_size);
07740     if (defrag_buffer[i].pevent == NULL) {
07741       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07742       cm_msg(MERROR, "bm_defragement_event",
07743              "Not enough memory to allocate event defragment buffer");
07744       return;
07745     }
07746     memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07747     defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07748     defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07749     return;
07750   }
07751 
07752   /* search buffer for that event */
07753   for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07754     if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07755       break;
07756   if (i == MAX_DEFRAG_EVENTS) {
07757 
07758     /* no buffer available -> no first fragment received */
07759     free(defrag_buffer[i].pevent);
07760     memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07761     cm_msg(MERROR, "bm_defragement_event",
07762            "Received fragment with no first fragment (ID %d)",
07763            pevent->event_id & 0x0FFF);
07764     return;
07765   }
07766 
07767   /* add fragment to buffer */
07768   if (pevent->data_size + defrag_buffer[i].received >
07769       defrag_buffer[i].data_size) {
07770     free(defrag_buffer[i].pevent);
07771     memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07772     cm_msg(MERROR, "bm_defragement_event",
07773            "Received fragments with more data (%d) than event size (%d)",
07774            pevent->data_size + defrag_buffer[i].received,
07775            defrag_buffer[i].data_size);
07776     return;
07777   }
07778   memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07779          defrag_buffer[i].received, pdata, pevent->data_size);
07780   defrag_buffer[i].received += pevent->data_size;
07781   if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07782 
07783     /* event complete */
07784     dispatcher(buffer_handle, request_id,
07785                defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
07786     free(defrag_buffer[i].pevent);
07787     memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07788   }
07789 }
07790 
07791 
07792 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07793 
07794 /** @} */// end of bmfunctionc
07795 
07796 /********************************************************************/
07797 /** @addtogroup rpcfunctionc
07798  *  
07799  *  @{  */
07800 
07801 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07802 
07803 /********************************************************************\
07804 *                                                                    *
07805 *                         RPC functions                              *
07806 *                                                                    *
07807 \********************************************************************/
07808 
07809 /* globals */
07810 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07811 RPC_SERVER_CONNECTION _server_connection;
07812 static int _lsock;
07813 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07814 static INT _server_acception_index = 0;
07815 static INT _server_type;
07816 static char _server_name[256];
07817 static RPC_LIST *rpc_list = NULL;
07818 int _opt_tcp_size = OPT_TCP_SIZE;
07819 
07820 /********************************************************************\
07821 *                       conversion functions                         *
07822 \********************************************************************/
07823 void rpc_calc_convert_flags(INT hw_type,
07824                             INT remote_hw_type, INT * convert_flags)
07825 {
07826   *convert_flags = 0;
07827 
07828   /* big/little endian conversion */
07829   if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07830        (hw_type & DRI_LITTLE_ENDIAN)) ||
07831       ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07832     *convert_flags |= CF_ENDIAN;
07833 
07834   /* float conversion between IEEE and VAX G */
07835   if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07836     *convert_flags |= CF_VAX2IEEE;
07837 
07838   /* float conversion between VAX G and IEEE */
07839   if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07840     *convert_flags |= CF_IEEE2VAX;
07841 
07842   /* ASCII format */
07843   if (remote_hw_type & DR_ASCII)
07844     *convert_flags |= CF_ASCII;
07845 }
07846 
07847 
07848 /********************************************************************/
07849 void rpc_get_convert_flags(INT * convert_flags)
07850 {
07851   rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
07852                          _server_connection.remote_hw_type, convert_flags);
07853 }
07854 
07855 /********************************************************************/
07856 void rpc_ieee2vax_float(float *var)
07857 {
07858   unsigned short int lo, hi;
07859 
07860   /* swap hi and lo word */
07861   lo = *((short int *) (var) + 1);
07862   hi = *((short int *) (var));
07863 
07864   /* correct exponent */
07865   if (lo != 0)
07866     lo += 0x100;
07867   *((short int *) (var) + 1) = hi;
07868   *((short int *) (var)) = lo;
07869 } void rpc_vax2ieee_float(float *var)
07870 {
07871   unsigned short int lo, hi;
07872 
07873   /* swap hi and lo word */
07874   lo = *((short int *) (var) + 1);
07875   hi = *((short int *) (var));
07876 
07877   /* correct exponent */
07878   if (hi != 0)
07879     hi -= 0x100;
07880   *((short int *) (var) + 1) = hi;
07881   *((short int *) (var)) = lo;
07882 } void rpc_vax2ieee_double(double *var)
07883 {
07884   unsigned short int i1, i2, i3, i4;
07885 
07886   /* swap words */
07887   i1 = *((short int *) (var) + 3);
07888   i2 = *((short int *) (var) + 2);
07889   i3 = *((short int *) (var) + 1);
07890   i4 = *((short int *) (var));
07891 
07892   /* correct exponent */
07893   if (i4 != 0)
07894     i4 -= 0x20;
07895   *((short int *) (var) + 3) = i4;
07896   *((short int *) (var) + 2) = i3;
07897   *((short int *) (var) + 1) = i2;
07898   *((short int *) (var)) = i1;
07899 } void rpc_ieee2vax_double(double *var)
07900 {
07901   unsigned short int i1, i2, i3, i4;
07902 
07903   /* swap words */
07904   i1 = *((short int *) (var) + 3);
07905   i2 = *((short int *) (var) + 2);
07906   i3 = *((short int *) (var) + 1);
07907   i4 = *((short int *) (var));
07908 
07909   /* correct exponent */
07910   if (i1 != 0)
07911     i1 += 0x20;
07912   *((short int *) (var) + 3) = i4;
07913   *((short int *) (var) + 2) = i3;
07914   *((short int *) (var) + 1) = i2;
07915   *((short int *) (var)) = i1;
07916 }
07917 
07918 /********************************************************************/
07919 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07920 {
07921   if (convert_flags & CF_ENDIAN) {
07922     if (tid == TID_WORD || tid == TID_SHORT)
07923       WORD_SWAP(data);
07924     if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL
07925         || tid == TID_FLOAT)
07926       DWORD_SWAP(data);
07927     if (tid == TID_DOUBLE)
07928       QWORD_SWAP(data);
07929   }
07930   if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07931       ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07932     if (tid == TID_FLOAT)
07933       rpc_ieee2vax_float((float *) data);
07934     if (tid == TID_DOUBLE)
07935       rpc_ieee2vax_double((double *) data);
07936   }
07937   if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07938       ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07939     if (tid == TID_FLOAT)
07940       rpc_vax2ieee_float((float *) data);
07941     if (tid == TID_DOUBLE)
07942       rpc_vax2ieee_double((double *) data);
07943   }
07944 } void rpc_convert_data(void *data,
07945                         INT tid,
07946                         INT flags, INT total_size, INT convert_flags)
07947 /********************************************************************\
07948 
07949   Routine: rpc_convert_data
07950 
07951   Purpose: Convert data format between differenct computers
07952 
07953   Input:
07954     void   *data            Pointer to data
07955     INT    tid              Type ID of data, one of TID_xxx
07956     INT    flags            Combination of following flags:
07957                               RPC_IN: data is input parameter
07958                               RPC_OUT: data is output variable
07959                               RPC_FIXARRAY, RPC_VARARRAY: data is array
07960                                 of "size" bytes (see next param.)
07961                               RPC_OUTGOING: data is outgoing
07962     INT    total_size       Size of bytes of data. Used for variable
07963                             length arrays.
07964     INT    convert_flags    Flags for data conversion
07965 
07966   Output:
07967     void   *data            Is converted according to _convert_flag
07968                             value
07969 
07970   Function value:
07971     RPC_SUCCESS             Successful completion
07972 
07973 \********************************************************************/
07974 {
07975   INT i, n, single_size;
07976   char *p;
07977 
07978   /* convert array */
07979   if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
07980     single_size = tid_size[tid];
07981 
07982     /* don't convert TID_ARRAY & TID_STRUCT */
07983     if (single_size == 0)
07984       return;
07985     n = total_size / single_size;
07986     for (i = 0; i < n; i++) {
07987       p = (char *) data + (i * single_size);
07988       rpc_convert_single(p, tid, flags, convert_flags);
07989   }}
07990 
07991   else {
07992     rpc_convert_single(data, tid, flags, convert_flags);
07993   }
07994 }
07995 
07996 
07997 /********************************************************************\
07998 *                       type ID functions                            *
07999 \********************************************************************/
08000 INT rpc_tid_size(INT id)
08001 {
08002   if (id < TID_LAST)
08003     return tid_size[id];
08004   return 0;
08005 }
08006 
08007 char *rpc_tid_name(INT id)
08008 {
08009   if (id < TID_LAST)
08010     return tid_name[id];
08011 
08012   else
08013     return "<unknown>";
08014 }
08015 
08016 
08017 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08018 
08019 /********************************************************************\
08020 *                        client functions                            *
08021 \********************************************************************/
08022 
08023 /********************************************************************/
08024 /**
08025 Register RPC client for standalone mode (without standard
08026            midas server)
08027 @param list           Array of RPC_LIST structures containing
08028                             function IDs and parameter definitions.
08029                             The end of the list must be indicated by
08030                             a function ID of zero.
08031 @param name          Name of this client
08032 @return RPC_SUCCESS
08033 */
08034 INT rpc_register_client(char *name, RPC_LIST * list)
08035 {
08036   rpc_set_name(name);
08037   rpc_register_functions(rpc_get_internal_list(0), NULL);
08038   rpc_register_functions(list, NULL);
08039   return RPC_SUCCESS;
08040 }
08041 
08042 
08043 /********************************************************************/
08044 /**
08045 Register a set of RPC functions (both as clients or servers)
08046 @param new_list       Array of RPC_LIST structures containing
08047                             function IDs and parameter definitions.
08048                             The end of the list must be indicated by
08049                             a function ID of zero.
08050 @param func          Default dispatch function
08051 
08052 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
08053 */
08054 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
08055 {
08056   INT i, j, iold, inew;
08057 
08058   /* count number of new functions */
08059   for (i = 0; new_list[i].id != 0; i++) {
08060 
08061     /* check double defined functions */
08062     for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
08063       if (rpc_list[j].id == new_list[i].id)
08064         return RPC_DOUBLE_DEFINED;
08065   }
08066   inew = i;
08067 
08068   /* count number of existing functions */
08069   for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
08070   iold = i;
08071 
08072   /* allocate new memory for rpc_list */
08073   if (rpc_list == NULL)
08074     rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
08075 
08076   else
08077     rpc_list =
08078         (RPC_LIST *) realloc(rpc_list,
08079                              sizeof(RPC_LIST) * (iold + inew + 1));
08080   if (rpc_list == NULL) {
08081     cm_msg(MERROR, "rpc_register_functions", "out of memory");
08082     return RPC_NO_MEMORY;
08083   }
08084 
08085   /* append new functions */
08086   for (i = iold; i < iold + inew; i++) {
08087     memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
08088 
08089     /* set default dispatcher */
08090     if (rpc_list[i].dispatch == NULL)
08091       rpc_list[i].dispatch = func;
08092 
08093     /* check valid ID for user functions */
08094     if (new_list != rpc_get_internal_list(0)
08095         && new_list != rpc_get_internal_list(1)
08096         && (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
08097       cm_msg(MERROR, "rpc_register_functions",
08098              "registered RPC function with invalid ID");
08099   }
08100 
08101   /* mark end of list */
08102   rpc_list[i].id = 0;
08103   return RPC_SUCCESS;
08104 }
08105 
08106 
08107 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08108 /********************************************************************/
08109 INT rpc_deregister_functions()
08110 /********************************************************************\
08111 
08112   Routine: rpc_deregister_functions
08113 
08114   Purpose: Free memory of previously registered functions
08115 
08116   Input:
08117     none
08118 
08119   Output:
08120     none
08121 
08122   Function value:
08123     RPC_SUCCESS              Successful completion
08124 
08125 \********************************************************************/
08126 {
08127   if (rpc_list)
08128     M_FREE(rpc_list);
08129   rpc_list = NULL;
08130   return RPC_SUCCESS;
08131 }
08132 
08133 
08134 /********************************************************************/
08135 INT rpc_register_function(INT id, INT(*func) (INT, void **))
08136 /********************************************************************\
08137 
08138   Routine: rpc_register_function
08139 
08140   Purpose: Replace a dispatch function for a specific rpc routine
08141 
08142   Input:
08143     INT      id             RPC ID
08144     INT      *func          New dispatch function
08145 
08146   Output:
08147    <implicit: func gets copied to rpc_list>
08148 
08149   Function value:
08150    RPC_SUCCESS              Successful completion
08151    RPC_INVALID_ID           RPC ID not found
08152 
08153 \********************************************************************/
08154 {
08155   INT i;
08156 
08157   for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
08158     if (rpc_list[i].id == id)
08159       break;
08160   if (rpc_list[i].id == id)
08161     rpc_list[i].dispatch = func;
08162 
08163   else
08164     return RPC_INVALID_ID;
08165   return RPC_SUCCESS;
08166 }
08167 
08168 
08169 /********************************************************************/
08170 INT rpc_client_dispatch(int sock)
08171 /********************************************************************\
08172 
08173   Routine: rpc_client_dispatch
08174 
08175   Purpose: Gets called whenever a client receives data from the
08176            server. Get set via rpc_connect. Internal use only.
08177 
08178 \********************************************************************/
08179 {
08180   INT hDB, hKey, n;
08181   NET_COMMAND *nc;
08182   INT status = 0;
08183   char net_buffer[256];
08184 
08185   nc = (NET_COMMAND *) net_buffer;
08186   n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08187   if (n <= 0)
08188     return SS_ABORT;
08189   if (nc->header.routine_id == MSG_ODB) {
08190 
08191     /* update a changed record */
08192     hDB = *((INT *) nc->param);
08193     hKey = *((INT *) nc->param + 1);
08194     status = db_update_record(hDB, hKey, 0);
08195   }
08196 
08197   else if (nc->header.routine_id == MSG_WATCHDOG) {
08198     nc->header.routine_id = 1;
08199     nc->header.param_size = 0;
08200     send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08201     status = RPC_SUCCESS;
08202   }
08203 
08204   else if (nc->header.routine_id == MSG_BM) {
08205     fd_set readfds;
08206     struct timeval timeout;
08207 
08208     /* receive further messages to empty TCP queue */
08209     do {
08210       FD_ZERO(&readfds);
08211       FD_SET(sock, &readfds);
08212       timeout.tv_sec = 0;
08213       timeout.tv_usec = 0;
08214       select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08215       if (FD_ISSET(sock, &readfds)) {
08216         n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08217         if (n <= 0)
08218           return SS_ABORT;
08219         if (nc->header.routine_id == MSG_ODB) {
08220 
08221           /* update a changed record */
08222           hDB = *((INT *) nc->param);
08223           hKey = *((INT *) nc->param + 1);
08224           status = db_update_record(hDB, hKey, 0);
08225         }
08226 
08227         else if (nc->header.routine_id == MSG_WATCHDOG) {
08228           nc->header.routine_id = 1;
08229           nc->header.param_size = 0;
08230           send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08231           status = RPC_SUCCESS;
08232         }
08233       }
08234     } while (FD_ISSET(sock, &readfds));
08235 
08236     /* poll event from server */
08237     status = bm_poll_event(FALSE);
08238   }
08239   return status;
08240 }
08241 
08242 
08243 /********************************************************************/
08244 INT rpc_client_connect(char *host_name,
08245                        INT port, char *client_name, HNDLE * hConnection)
08246 /********************************************************************\
08247 
08248   Routine: rpc_client_connect
08249 
08250   Purpose: Establish a network connection to a remote client
08251 
08252   Input:
08253     char *host_name          IP address of host to connect to.
08254     INT  port                TPC port to connect to.
08255     char *clinet_name        Client program name
08256 
08257   Output:
08258     HNDLE *hConnection       Handle for new connection which can be used
08259                              in future rpc_call(hConnection....) calls
08260 
08261   Function value:
08262     RPC_SUCCESS              Successful completion
08263     RPC_NET_ERROR            Error in socket call
08264     RPC_NO_CONNECTION        Maximum number of connections reached
08265     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08266 
08267 \********************************************************************/
08268 {
08269   INT i, status, index;
08270   struct sockaddr_in bind_addr;
08271   INT sock;
08272   INT remote_hw_type, hw_type;
08273   char str[200];
08274   char version[32], v1[32];
08275   char local_prog_name[NAME_LENGTH];
08276   char local_host_name[HOST_NAME_LENGTH];
08277   struct hostent *phe;
08278 
08279 #ifdef OS_WINNT
08280   {
08281     WSADATA WSAData;
08282 
08283     /* Start windows sockets */
08284     if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08285       return RPC_NET_ERROR;
08286   }
08287 
08288 #endif                          /*  */
08289 
08290   /* check if cm_connect_experiment was called */
08291   if (_client_name[0] == 0) {
08292     cm_msg(MERROR, "rpc_client_connect",
08293            "cm_connect_experiment/rpc_set_name not called");
08294     return RPC_NOT_REGISTERED;
08295   }
08296 
08297   /* check for broken connections */
08298   rpc_client_check();
08299 
08300   /* check if connection already exists */
08301   for (i = 0; i < MAX_RPC_CONNECTION; i++)
08302     if (_client_connection[i].send_sock != 0
08303         && strcmp(_client_connection[i].host_name,
08304                   host_name) == 0 && _client_connection[i].port == port) {
08305       *hConnection = i + 1;
08306       return RPC_SUCCESS;
08307     }
08308 
08309   /* search for free entry */
08310   for (i = 0; i < MAX_RPC_CONNECTION; i++)
08311     if (_client_connection[i].send_sock == 0)
08312       break;
08313 
08314   /* open new network connection */
08315   if (i == MAX_RPC_CONNECTION) {
08316     cm_msg(MERROR, "rpc_client_connect",
08317            "maximum number of connections exceeded");
08318     return RPC_NO_CONNECTION;
08319   }
08320 
08321   /* create a new socket for connecting to remote server */
08322   sock = socket(AF_INET, SOCK_STREAM, 0);
08323   if (sock == -1) {
08324     cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08325     return RPC_NET_ERROR;
08326   }
08327   index = i;
08328   strcpy(_client_connection[index].host_name, host_name);
08329   strcpy(_client_connection[index].client_name, client_name);
08330   _client_connection[index].port = port;
08331   _client_connection[index].exp_name[0] = 0;
08332   _client_connection[index].transport = RPC_TCP;
08333   _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08334   _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08335 
08336   /* connect to remote node */
08337   memset(&bind_addr, 0, sizeof(bind_addr));
08338   bind_addr.sin_family = AF_INET;
08339   bind_addr.sin_addr.s_addr = 0;
08340   bind_addr.sin_port = htons((short) port);
08341 
08342 #ifdef OS_VXWORKS
08343   {
08344     INT host_addr;
08345 
08346     host_addr = hostGetByName(host_name);
08347     memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08348   }
08349 #else                           /*  */
08350   phe = gethostbyname(host_name);
08351   if (phe == NULL) {
08352     cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08353     return RPC_NET_ERROR;
08354   }
08355   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08356 
08357 #endif                          /*  */
08358 
08359 #ifdef OS_UNIX
08360   do {
08361     status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08362 
08363     /* don't return if an alarm signal was cought */
08364   } while (status == -1 && errno == EINTR);
08365 
08366 #else                           /*  */
08367   status =
08368       connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08369 
08370 #endif                          /*  */
08371   if (status != 0) {
08372 
08373     /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08374        message should be displayed by application */
08375     return RPC_NET_ERROR;
08376   }
08377 
08378   /* set TCP_NODELAY option for better performance */
08379 #ifdef OS_VXWORKS
08380   i = 1;
08381   setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08382 
08383 #endif                          /*  */
08384 
08385   /* send local computer info */
08386   rpc_get_name(local_prog_name);
08387   gethostname(local_host_name, sizeof(local_host_name));
08388   hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08389   sprintf(str, "%d %s %s %s", hw_type, cm_get_version(),
08390           local_prog_name, local_host_name);
08391   send(sock, str, strlen(str) + 1, 0);
08392 
08393   /* receive remote computer info */
08394   i = recv_string(sock, str, sizeof(str), 10000);
08395   if (i <= 0) {
08396     cm_msg(MERROR, "rpc_client_connect",
08397            "timeout on receive remote computer info: %s", str);
08398     return RPC_NET_ERROR;
08399   }
08400   remote_hw_type = version[0] = 0;
08401   sscanf(str, "%d %s", &remote_hw_type, version);
08402   _client_connection[index].remote_hw_type = remote_hw_type;
08403   _client_connection[index].send_sock = sock;
08404 
08405   /* print warning if version patch level doesn't agree */
08406   strcpy(v1, version);
08407   if (strchr(v1, '.'))
08408     if (strchr(strchr(v1, '.') + 1, '.'))
08409       *strchr(strchr(v1, '.') + 1, '.') = 0;
08410   strcpy(str, cm_get_version());
08411   if (strchr(str, '.'))
08412     if (strchr(strchr(str, '.') + 1, '.'))
08413       *strchr(strchr(str, '.') + 1, '.') = 0;
08414   if (strcmp(v1, str) != 0) {
08415     sprintf(str,
08416             "remote MIDAS version %s differs from local version %s",
08417             version, cm_get_version());
08418     cm_msg(MERROR, "rpc_client_connect", str);
08419   }
08420   *hConnection = index + 1;
08421   return RPC_SUCCESS;
08422 }
08423 
08424 
08425 /********************************************************************/
08426 void rpc_client_check()
08427 /********************************************************************\
08428 
08429   Routine: rpc_client_check
08430 
08431   Purpose: Check all client connections if remote client closed link
08432 
08433   Function value:
08434     RPC_SUCCESS              Successful completion
08435     RPC_NET_ERROR            Error in socket call
08436     RPC_NO_CONNECTION        Maximum number of connections reached
08437     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08438 
08439 \********************************************************************/
08440 {
08441   INT i, status;
08442 
08443   /* check for broken connections */
08444   for (i = 0; i < MAX_RPC_CONNECTION; i++)
08445     if (_client_connection[i].send_sock != 0) {
08446       int sock;
08447       fd_set readfds;
08448       struct timeval timeout;
08449       char buffer[64];
08450 
08451       sock = _client_connection[i].send_sock;
08452       FD_ZERO(&readfds);
08453       FD_SET(sock, &readfds);
08454       timeout.tv_sec = 0;
08455       timeout.tv_usec = 0;
08456 
08457       do {
08458         status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08459       } while (status == -1);   /* dont return if an alarm signal was cought */
08460       if (FD_ISSET(sock, &readfds)) {
08461         status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08462         if (equal_ustring(buffer, "EXIT")) {
08463 
08464           /* normal exit */
08465           closesocket(sock);
08466           memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08467         }
08468         if (status <= 0) {
08469           cm_msg(MERROR, "rpc_client_check",
08470                  "Connection broken to \"%s\" on host %s",
08471                  _client_connection[i].client_name,
08472                  _client_connection[i].host_name);
08473 
08474           /* connection broken -> reset */
08475           closesocket(sock);
08476           memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08477         }
08478       }
08479     }
08480 }
08481 
08482 
08483 /********************************************************************/
08484 INT rpc_server_connect(char *host_name, char *exp_name)
08485 /********************************************************************\
08486 
08487   Routine: rpc_server_connect
08488 
08489   Purpose: Extablish a network connection to a remote MIDAS
08490            server using a callback scheme.
08491 
08492   Input:
08493     char *host_name         IP address of host to connect to.
08494 
08495     INT  port               TPC port to connect to.
08496 
08497     char *exp_name          Name of experiment to connect to. By using
08498                             this name, several experiments (e.g. online
08499                             DAQ and offline analysis) can run simultan-
08500                             eously on the same host.
08501 
08502   Output:
08503     none
08504 
08505   Function value:
08506     RPC_SUCCESS              Successful completion
08507     RPC_NET_ERROR            Error in socket call
08508     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08509     CM_UNDEF_EXP             Undefined experiment on server
08510 
08511 \********************************************************************/
08512 {
08513   INT i, status, flag;
08514   struct sockaddr_in bind_addr;
08515   INT sock, lsock1, lsock2, lsock3;
08516   INT listen_port1, listen_port2, listen_port3;
08517   INT remote_hw_type, hw_type;
08518   int size;
08519   char str[200], version[32], v1[32];
08520   char local_prog_name[NAME_LENGTH];
08521   struct hostent *phe;
08522 
08523 #ifdef OS_WINNT
08524   {
08525     WSADATA WSAData;
08526 
08527     /* Start windows sockets */
08528     if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08529       return RPC_NET_ERROR;
08530   }
08531 
08532 #endif                          /*  */
08533 
08534   /* check if local connection */
08535   if (host_name[0] == 0)
08536     return RPC_SUCCESS;
08537 
08538   /* register system functions */
08539   rpc_register_functions(rpc_get_internal_list(0), NULL);
08540 
08541   /* check if cm_connect_experiment was called */
08542   if (_client_name[0] == 0) {
08543     cm_msg(MERROR, "rpc_server_connect",
08544            "cm_connect_experiment/rpc_set_name not called");
08545     return RPC_NOT_REGISTERED;
08546   }
08547 
08548   /* check if connection already exists */
08549   if (_server_connection.send_sock != 0)
08550     return RPC_SUCCESS;
08551   strcpy(_server_connection.host_name, host_name);
08552   strcpy(_server_connection.exp_name, exp_name);
08553   _server_connection.transport = RPC_TCP;
08554   _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08555 
08556   /* create new TCP sockets for listening */
08557   lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08558   lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08559   lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08560   if (lsock3 == -1) {
08561     cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08562     return RPC_NET_ERROR;
08563   }
08564   flag = 1;
08565   setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR,
08566              (char *) &flag, sizeof(INT));
08567   setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR,
08568              (char *) &flag, sizeof(INT));
08569   setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR,
08570              (char *) &flag, sizeof(INT));
08571 
08572   /* let OS choose any port number */
08573   memset(&bind_addr, 0, sizeof(bind_addr));
08574   bind_addr.sin_family = AF_INET;
08575   bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08576   bind_addr.sin_port = 0;
08577   status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08578   bind_addr.sin_port = 0;
08579   status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08580   bind_addr.sin_port = 0;
08581   status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08582   if (status < 0) {
08583     cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08584     return RPC_NET_ERROR;
08585   }
08586 
08587   /* listen for connection */
08588   status = listen(lsock1, 1);
08589   status = listen(lsock2, 1);
08590   status = listen(lsock3, 1);
08591   if (status < 0) {
08592     cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08593     return RPC_NET_ERROR;
08594   }
08595 
08596   /* find out which port OS has chosen */
08597   size = sizeof(bind_addr);
08598   getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08599   listen_port1 = ntohs(bind_addr.sin_port);
08600   getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08601   listen_port2 = ntohs(bind_addr.sin_port);
08602   getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08603   listen_port3 = ntohs(bind_addr.sin_port);
08604 
08605   /* create a new socket for connecting to remote server */
08606   sock = socket(AF_INET, SOCK_STREAM, 0);
08607   if (sock == -1) {
08608     cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08609     return RPC_NET_ERROR;
08610   }
08611 
08612   /* connect to remote node */
08613   memset(&bind_addr, 0, sizeof(bind_addr));
08614   bind_addr.sin_family = AF_INET;
08615   bind_addr.sin_addr.s_addr = 0;
08616   bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08617 
08618 #ifdef OS_VXWORKS
08619   {
08620     INT host_addr;
08621 
08622     host_addr = hostGetByName(host_name);
08623     memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08624   }
08625 #else                           /*  */
08626   phe = gethostbyname(host_name);
08627   if (phe == NULL) {
08628     cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08629     return RPC_NET_ERROR;
08630   }
08631   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08632 
08633 #endif                          /*  */
08634 
08635 #ifdef OS_UNIX
08636   do {
08637     status =
08638         connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08639 
08640     /* don't return if an alarm signal was cought */
08641   } while (status == -1 && errno == EINTR);
08642 
08643 #else                           /*  */
08644   status =
08645       connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08646 
08647 #endif                          /*  */
08648   if (status != 0) {
08649 
08650 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08651     return RPC_NET_ERROR;
08652   }
08653 
08654   /* connect to experiment */
08655   if (exp_name[0] == 0)
08656     sprintf(str, "C %d %d %d %s Default", listen_port1,
08657             listen_port2, listen_port3, cm_get_version());
08658 
08659   else
08660     sprintf(str, "C %d %d %d %s %s", listen_port1, listen_port2,
08661             listen_port3, cm_get_version(), exp_name);
08662   send(sock, str, strlen(str) + 1, 0);
08663   i = recv_string(sock, str, sizeof(str), 10000);
08664   closesocket(sock);
08665   if (i <= 0) {
08666     cm_msg(MERROR, "rpc_server_connect",
08667            "timeout on receive status from server");
08668     return RPC_NET_ERROR;
08669   }
08670   status = version[0] = 0;
08671   sscanf(str, "%d %s", &status, version);
08672   if (status == 2) {
08673 
08674 /*  message "undefined experiment" should be displayed by application */
08675     return CM_UNDEF_EXP;
08676   }
08677 
08678   /* print warning if version patch level doesn't agree */
08679   strcpy(v1, version);
08680   if (strchr(v1, '.'))
08681     if (strchr(strchr(v1, '.') + 1, '.'))
08682       *strchr(strchr(v1, '.') + 1, '.') = 0;
08683   strcpy(str, cm_get_version());
08684   if (strchr(str, '.'))
08685     if (strchr(strchr(str, '.') + 1, '.'))
08686       *strchr(strchr(str, '.') + 1, '.') = 0;
08687   if (strcmp(v1, str) != 0) {
08688     sprintf(str,
08689             "remote MIDAS version %s differs from local version %s",
08690             version, cm_get_version());
08691     cm_msg(MERROR, "rpc_server_connect", str);
08692   }
08693 
08694   /* wait for callback on send and recv socket */
08695   size = sizeof(bind_addr);
08696   _server_connection.send_sock =
08697       accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08698   _server_connection.recv_sock =
08699       accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08700   _server_connection.event_sock =
08701       accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08702   if (_server_connection.send_sock == -1
08703       || _server_connection.recv_sock == -1
08704       || _server_connection.event_sock == -1) {
08705     cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08706     return RPC_NET_ERROR;
08707   }
08708   closesocket(lsock1);
08709   closesocket(lsock2);
08710   closesocket(lsock3);
08711 
08712   /* set TCP_NODELAY option for better performance */
08713 #ifdef OS_VXWORKS
08714   flag = 1;
08715   setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY,
08716              (char *) &flag, sizeof(flag));
08717   setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY,
08718              (char *) &flag, sizeof(flag));
08719 
08720 #endif                          /*  */
08721 
08722   /* increase send buffer size to 64kB */
08723   flag = 0x10000;
08724   setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
08725              (char *) &flag, sizeof(flag));
08726 
08727   /* send local computer info */
08728   rpc_get_name(local_prog_name);
08729   hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08730   sprintf(str, "%d %s", hw_type, local_prog_name);
08731   send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08732 
08733   /* receive remote computer info */
08734   i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08735   if (i <= 0) {
08736     cm_msg(MERROR, "rpc_server_connect",
08737            "timeout on receive remote computer info");
08738     return RPC_NET_ERROR;
08739   }
08740   sscanf(str, "%d", &remote_hw_type);
08741   _server_connection.remote_hw_type = remote_hw_type;
08742 
08743   /* set dispatcher which receives database updates */
08744   ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
08745                           (int (*)(void)) rpc_client_dispatch);
08746   return RPC_SUCCESS;
08747 }
08748 
08749 
08750 /********************************************************************/
08751 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08752 /********************************************************************\
08753 
08754   Routine: rpc_client_disconnect
08755 
08756   Purpose: Close a rpc connection to a MIDAS client
08757 
08758   Input:
08759     HNDLE  hConn           Handle of connection
08760     BOOL   bShutdown       Shut down remote server if TRUE
08761 
08762   Output:
08763     none
08764 
08765   Function value:
08766    RPC_SUCCESS             Successful completion
08767 
08768 \********************************************************************/
08769 {
08770   INT i;
08771 
08772   if (hConn == -1) {
08773 
08774     /* close all open connections */
08775     for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08776       if (_client_connection[i].send_sock != 0)
08777         rpc_client_disconnect(i + 1, FALSE);
08778 
08779     /* close server connection from other clients */
08780     for (i = 0; i < MAX_RPC_CONNECTION; i++)
08781       if (_server_acception[i].recv_sock) {
08782         send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08783         closesocket(_server_acception[i].recv_sock);
08784       }
08785   }
08786 
08787   else {
08788 
08789     /* notify server about exit */
08790 
08791     /* set FTCP mode (helps for rebooted VxWorks nodes) */
08792     rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08793     rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08794 
08795     /* close socket */
08796     if (_client_connection[hConn - 1].send_sock)
08797       closesocket(_client_connection[hConn - 1].send_sock);
08798     memset(&_client_connection[hConn - 1], 0,
08799            sizeof(RPC_CLIENT_CONNECTION));
08800   }
08801   return RPC_SUCCESS;
08802 }
08803 
08804 
08805 /********************************************************************/
08806 INT rpc_server_disconnect()
08807 /********************************************************************\
08808 
08809   Routine: rpc_server_disconnect
08810 
08811   Purpose: Close a rpc connection to a MIDAS server and close all
08812            server connections from other clients
08813 
08814   Input:
08815     none
08816 
08817   Output:
08818     none
08819 
08820   Function value:
08821    RPC_SUCCESS             Successful completion
08822    RPC_NET_ERROR           Error in socket call
08823    RPC_NO_CONNECTION       Maximum number of connections reached
08824 
08825 \********************************************************************/
08826 {
08827   static int rpc_server_disconnect_recursion_level = 0;
08828 
08829   if (rpc_server_disconnect_recursion_level)
08830     return RPC_SUCCESS;
08831   rpc_server_disconnect_recursion_level = 1;
08832 
08833   /* flush remaining events */
08834   rpc_flush_event();
08835 
08836   /* notify server about exit */
08837   rpc_call(RPC_ID_EXIT);
08838 
08839   /* close sockets */
08840   closesocket(_server_connection.send_sock);
08841   closesocket(_server_connection.recv_sock);
08842   closesocket(_server_connection.event_sock);
08843   memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08844   rpc_server_disconnect_recursion_level = 0;
08845   return RPC_SUCCESS;
08846 }
08847 
08848 
08849 /********************************************************************/
08850 INT rpc_is_remote(void)
08851 /********************************************************************\
08852 
08853   Routine: rpc_is_remote
08854 
08855   Purpose: Return true if program is connected to a remote server
08856 
08857   Input:
08858    none
08859 
08860   Output:
08861     none
08862 
08863   Function value:
08864     INT    RPC connection index
08865 
08866 \********************************************************************/
08867 {
08868   return _server_connection.send_sock != 0;
08869 }
08870 
08871 
08872 /********************************************************************/
08873 INT rpc_get_server_acception(void)
08874 /********************************************************************\
08875 
08876   Routine: rpc_get_server_acception
08877 
08878   Purpose: Return actual RPC server connection index
08879 
08880   Input:
08881    none
08882 
08883   Output:
08884     none
08885 
08886   Function value:
08887     INT    RPC server connection index
08888 
08889 \********************************************************************/
08890 {
08891   return _server_acception_index;
08892 }
08893 
08894 
08895 /********************************************************************/
08896 INT rpc_set_server_acception(INT index)
08897 /********************************************************************\
08898 
08899   Routine: rpc_set_server_acception
08900 
08901   Purpose: Set actual RPC server connection index
08902 
08903   Input:
08904     INT  index              Server index
08905 
08906   Output:
08907     none
08908 
08909   Function value:
08910     RPC_SUCCESS             Successful completion
08911 
08912 \********************************************************************/
08913 {
08914   _server_acception_index = index;
08915   return RPC_SUCCESS;
08916 }
08917 
08918 
08919 /********************************************************************/
08920 INT rpc_get_option(HNDLE hConn, INT item)
08921 /********************************************************************\
08922 
08923   Routine: rpc_get_option
08924 
08925   Purpose: Get actual RPC option
08926 
08927   Input:
08928     HNDLE hConn             RPC connection handle
08929     INT   item              One of RPC_Oxxx
08930 
08931   Output:
08932     none
08933 
08934   Function value:
08935     INT                     Actual option
08936 
08937 \********************************************************************/
08938 {
08939   switch (item) {
08940   case RPC_OTIMEOUT:
08941     if (hConn == -1)
08942       return _server_connection.rpc_timeout;
08943     return _client_connection[hConn - 1].rpc_timeout;
08944   case RPC_OTRANSPORT:
08945     if (hConn == -1)
08946       return _server_connection.transport;
08947     return _client_connection[hConn - 1].transport;
08948   case RPC_OHW_TYPE:
08949     {
08950       INT tmp_type, size;
08951       DWORD dummy;
08952       unsigned char *p;
08953       float f;
08954       double d;
08955 
08956       tmp_type = 0;
08957 
08958       /* test pointer size */
08959       size = sizeof(p);
08960       if (size == 2)
08961         tmp_type |= DRI_16;
08962       if (size == 4)
08963         tmp_type |= DRI_32;
08964       if (size == 8)
08965         tmp_type |= DRI_64;
08966 
08967       /* test if little or big endian machine */
08968       dummy = 0x12345678;
08969       p = (unsigned char *) &dummy;
08970       if (*p == 0x78)
08971         tmp_type |= DRI_LITTLE_ENDIAN;
08972 
08973       else if (*p == 0x12)
08974         tmp_type |= DRI_BIG_ENDIAN;
08975 
08976       else
08977         cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
08978 
08979       /* floating point format */
08980       f = (float) 1.2345;
08981       dummy = 0;
08982       memcpy(&dummy, &f, sizeof(f));
08983       if ((dummy & 0xFF) == 0x19 &&
08984           ((dummy >> 8) & 0xFF) == 0x04 &&
08985           ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
08986         tmp_type |= DRF_IEEE;
08987 
08988       else if ((dummy & 0xFF) == 0x9E &&
08989                ((dummy >> 8) & 0xFF) == 0x40 &&
08990                ((dummy >> 16) & 0xFF) == 0x19 &&
08991                ((dummy >> 24) & 0xFF) == 0x04)
08992         tmp_type |= DRF_G_FLOAT;
08993 
08994       else
08995         cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
08996       d = (double) 1.2345;
08997       dummy = 0;
08998       memcpy(&dummy, &d, sizeof(f));
08999       if ((dummy & 0xFF) == 0x8D &&     /* little endian */
09000           ((dummy >> 8) & 0xFF) == 0x97 &&
09001           ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
09002         tmp_type |= DRF_IEEE;
09003 
09004       else if ((dummy & 0xFF) == 0x83 &&        /* big endian */
09005                ((dummy >> 8) & 0xFF) == 0xC0 &&
09006                ((dummy >> 16) & 0xFF) == 0xF3 &&
09007                ((dummy >> 24) & 0xFF) == 0x3F)
09008         tmp_type |= DRF_IEEE;
09009 
09010       else if ((dummy & 0xFF) == 0x13 &&
09011                ((dummy >> 8) & 0xFF) == 0x40 &&
09012                ((dummy >> 16) & 0xFF) == 0x83 &&
09013                ((dummy >> 24) & 0xFF) == 0xC0)
09014         tmp_type |= DRF_G_FLOAT;
09015 
09016       else if ((dummy & 0xFF) == 0x9E &&
09017                ((dummy >> 8) & 0xFF) == 0x40 &&
09018                ((dummy >> 16) & 0xFF) == 0x18 &&
09019                ((dummy >> 24) & 0xFF) == 0x04)
09020         cm_msg(MERROR, "rpc_get_option",
09021                "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
09022 
09023       else
09024         cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09025       return tmp_type;
09026     }
09027   default:
09028     cm_msg(MERROR, "rpc_get_option", "invalid argument");
09029     break;
09030   }
09031   return 0;
09032 }
09033 
09034 
09035 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09036 
09037 /********************************************************************/
09038 /**
09039 Set RPC option
09040 @param hConn              RPC connection handle
09041 @param item               One of RPC_Oxxx
09042 @param value              Value to set
09043 @return RPC_SUCCESS
09044 */
09045 INT rpc_set_option(HNDLE hConn, INT item, INT value)
09046 {
09047   switch (item) {
09048   case RPC_OTIMEOUT:
09049     if (hConn == -1)
09050       _server_connection.rpc_timeout = value;
09051 
09052     else
09053       _client_connection[hConn - 1].rpc_timeout = value;
09054     break;
09055   case RPC_OTRANSPORT:
09056     if (hConn == -1)
09057       _server_connection.transport = value;
09058 
09059     else
09060       _client_connection[hConn - 1].transport = value;
09061     break;
09062   case RPC_NODELAY:
09063     if (hConn == -1)
09064       setsockopt(_server_connection.send_sock, IPPROTO_TCP,
09065                  TCP_NODELAY, (char *) &value, sizeof(value));
09066 
09067     else
09068       setsockopt(_client_connection[hConn - 1].send_sock,
09069                  IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
09070     break;
09071   default:
09072     cm_msg(MERROR, "rpc_set_option", "invalid argument");
09073     break;
09074   }
09075   return 0;
09076 }
09077 
09078 
09079 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09080 
09081 /********************************************************************/
09082 PTYPE rpc_get_server_option(INT item)
09083 /********************************************************************\
09084 
09085   Routine: rpc_get_server_option
09086 
09087   Purpose: Get actual RPC option for server connection
09088 
09089   Input:
09090     INT  item               One of RPC_Oxxx
09091 
09092   Output:
09093     none
09094 
09095   Function value:
09096     INT                     Actual option
09097 
09098 \********************************************************************/
09099 {
09100   INT i;
09101 
09102   if (item == RPC_OSERVER_TYPE)
09103     return _server_type;
09104   if (item == RPC_OSERVER_NAME)
09105     return (PTYPE) _server_name;
09106 
09107   /* return 0 for local calls */
09108   if (_server_type == ST_NONE)
09109     return 0;
09110 
09111   /* check which connections belongs to caller */
09112   if (_server_type == ST_MTHREAD) {
09113     for (i = 0; i < MAX_RPC_CONNECTION; i++)
09114       if (_server_acception[i].tid == ss_gettid())
09115         break;
09116   }
09117 
09118   else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09119     i = max(0, _server_acception_index - 1);
09120 
09121   else
09122     i = 0;
09123   switch (item) {
09124   case RPC_CONVERT_FLAGS:
09125     return _server_acception[i].convert_flags;
09126   case RPC_ODB_HANDLE:
09127     return _server_acception[i].odb_handle;
09128   case RPC_CLIENT_HANDLE:
09129     return _server_acception[i].client_handle;
09130   case RPC_SEND_SOCK:
09131     return _server_acception[i].send_sock;
09132   case RPC_WATCHDOG_TIMEOUT:
09133     return _server_acception[i].watchdog_timeout;
09134   }
09135   return 0;
09136 }
09137 
09138 
09139 /********************************************************************/
09140 INT rpc_set_server_option(INT item, PTYPE value)
09141 /********************************************************************\
09142 
09143   Routine: rpc_set_server_option
09144 
09145   Purpose: Set RPC option for server connection
09146 
09147   Input:
09148    INT  item               One of RPC_Oxxx
09149    INT  value              Value to set
09150 
09151   Output:
09152     none
09153 
09154   Function value:
09155     RPC_SUCCESS             Successful completion
09156 
09157 \********************************************************************/
09158 {
09159   INT i;
09160 
09161   if (item == RPC_OSERVER_TYPE) {
09162     _server_type = value;
09163     return RPC_SUCCESS;
09164   }
09165   if (item == RPC_OSERVER_NAME) {
09166     strcpy(_server_name, (char *) value);
09167     return RPC_SUCCESS;
09168   }
09169 
09170   /* check which connections belongs to caller */
09171   if (_server_type == ST_MTHREAD) {
09172     for (i = 0; i < MAX_RPC_CONNECTION; i++)
09173       if (_server_acception[i].tid == ss_gettid())
09174         break;
09175   }
09176 
09177   else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09178     i = max(0, _server_acception_index - 1);
09179 
09180   else
09181     i = 0;
09182   switch (item) {
09183   case RPC_CONVERT_FLAGS:
09184     _server_acception[i].convert_flags = value;
09185     break;
09186   case RPC_ODB_HANDLE:
09187     _server_acception[i].odb_handle = value;
09188     break;
09189   case RPC_CLIENT_HANDLE:
09190     _server_acception[i].client_handle = value;
09191     break;
09192   case RPC_WATCHDOG_TIMEOUT:
09193     _server_acception[i].watchdog_timeout = value;
09194     break;
09195   }
09196   return RPC_SUCCESS;
09197 }
09198 
09199 
09200 /********************************************************************/
09201 INT rpc_get_name(char *name)
09202 /********************************************************************\
09203 
09204   Routine: rpc_get_name
09205 
09206   Purpose: Get name set by rpc_set_name
09207 
09208   Input:
09209     none
09210 
09211   Output:
09212     char*  name             The location pointed by *name receives a
09213                             copy of the _prog_name
09214 
09215   Function value:
09216     RPC_SUCCESS             Successful completion
09217 
09218 \********************************************************************/
09219 {
09220   strcpy(name, _client_name);
09221   return RPC_SUCCESS;
09222 }
09223 
09224 
09225 /********************************************************************/
09226 INT rpc_set_name(char *name)
09227 /********************************************************************\
09228 
09229   Routine: rpc_set_name
09230 
09231   Purpose: Set name of actual program for further rpc connections
09232 
09233   Input:
09234    char *name               Program name, up to NAME_LENGTH chars,
09235                             no blanks
09236 
09237   Output:
09238     none
09239 
09240   Function value:
09241     RPC_SUCCESS             Successful completion
09242 
09243 \********************************************************************/
09244 {
09245   strcpy(_client_name, name);
09246   return RPC_SUCCESS;
09247 }
09248 
09249 
09250 /********************************************************************/
09251 INT rpc_set_debug(void (*func) (char *), INT mode)
09252 /********************************************************************\
09253 
09254   Routine: rpc_set_debug
09255 
09256   Purpose: Set a function which is called on every RPC call to
09257            display the function name and parameters of the RPC
09258            call.
09259 
09260   Input:
09261    void *func(char*)        Pointer to function.
09262    INT  mode                Debug mode
09263 
09264   Output:
09265     none
09266 
09267   Function value:
09268     RPC_SUCCESS             Successful completion
09269 
09270 \********************************************************************/
09271 {
09272   _debug_print = func;
09273   _debug_mode = mode;
09274   return RPC_SUCCESS;
09275 }
09276 
09277 
09278 /********************************************************************/
09279 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09280 {
09281   switch (arg_type) {
09282 
09283     /* On the stack, the minimum parameter size is sizeof(int).
09284        To avoid problems on little endian systems, treat all
09285        smaller parameters as int's */
09286   case TID_BYTE:
09287   case TID_SBYTE:
09288   case TID_CHAR:
09289   case TID_WORD:
09290   case TID_SHORT:
09291     *((int *) arg) = va_arg(*arg_ptr, int);
09292 
09293     break;
09294   case TID_INT:
09295   case TID_BOOL:
09296     *((INT *) arg) = va_arg(*arg_ptr, INT);
09297     break;
09298   case TID_DWORD:
09299     *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09300     break;
09301 
09302     /* float variables are passed as double by the compiler */
09303   case TID_FLOAT:
09304     *((float *) arg) = (float) va_arg(*arg_ptr, double);
09305 
09306     break;
09307   case TID_DOUBLE:
09308     *((double *) arg) = va_arg(*arg_ptr, double);
09309 
09310     break;
09311   case TID_ARRAY:
09312     *((char **) arg) = va_arg(*arg_ptr, char *);
09313 
09314     break;
09315   }
09316 }
09317 
09318 
09319 /********************************************************************/
09320 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09321 /********************************************************************\
09322 
09323   Routine: rpc_client_call
09324 
09325   Purpose: Call a function on a MIDAS client
09326 
09327   Input:
09328     INT  hConn              Client connection
09329     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09330 
09331     ...                     variable argument list
09332 
09333   Output:
09334     (depends on argument list)
09335 
09336   Function value:
09337     RPC_SUCCESS             Successful completion
09338     RPC_NET_ERROR           Error in socket call
09339     RPC_NO_CONNECTION       No active connection
09340     RPC_TIMEOUT             Timeout in RPC call
09341     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09342     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09343 
09344 \********************************************************************/
09345 {
09346   va_list ap, aptmp;
09347   char arg[8], arg_tmp[8];
09348   INT arg_type, transport, rpc_timeout;
09349   INT i, index, status, rpc_index;
09350   INT param_size, arg_size, send_size;
09351   INT tid, flags;
09352   fd_set readfds;
09353   struct timeval timeout;
09354   char *param_ptr, str[80];
09355   BOOL bpointer, bbig;
09356   NET_COMMAND *nc;
09357   int send_sock;
09358 
09359   index = hConn - 1;
09360   if (_client_connection[index].send_sock == 0) {
09361     cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09362     return RPC_NO_CONNECTION;
09363   }
09364   send_sock = _client_connection[index].send_sock;
09365   rpc_timeout = _client_connection[index].rpc_timeout;
09366   transport = _client_connection[index].transport;
09367 
09368   /* init network buffer */
09369   if (_net_send_buffer_size == 0) {
09370     _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09371     if (_net_send_buffer == NULL) {
09372       cm_msg(MERROR, "rpc_client_call",
09373              "not enough memory to allocate network buffer");
09374       return RPC_EXCEED_BUFFER;
09375     }
09376     _net_send_buffer_size = NET_BUFFER_SIZE;
09377   }
09378   nc = (NET_COMMAND *) _net_send_buffer;
09379   nc->header.routine_id = routine_id;
09380   if (transport == RPC_FTCP)
09381     nc->header.routine_id |= TCP_FAST;
09382   for (i = 0;; i++)
09383     if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09384       break;
09385   rpc_index = i;
09386   if (rpc_list[i].id == 0) {
09387     sprintf(str, "invalid rpc ID (%d)", routine_id);
09388     cm_msg(MERROR, "rpc_client_call", str);
09389     return RPC_INVALID_ID;
09390   }
09391 
09392   /* examine variable argument list and convert it to parameter array */
09393   va_start(ap, routine_id);
09394 
09395   /* find out if we are on a big endian system */
09396   bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09397   for (i = 0, param_ptr = nc->param;
09398        rpc_list[rpc_index].param[i].tid != 0; i++) {
09399     tid = rpc_list[rpc_index].param[i].tid;
09400     flags = rpc_list[rpc_index].param[i].flags;
09401     bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09402         (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY)
09403         || tid == TID_STRING || tid == TID_ARRAY
09404         || tid == TID_STRUCT || tid == TID_LINK;
09405     if (bpointer)
09406       arg_type = TID_ARRAY;
09407 
09408     else
09409       arg_type = tid;
09410 
09411     /* floats are passed as doubles, at least under NT */
09412     if (tid == TID_FLOAT && !bpointer)
09413       arg_type = TID_DOUBLE;
09414 
09415     /* get pointer to argument */
09416     rpc_va_arg(&ap, arg_type, arg);
09417 
09418     /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09419     if (bbig) {
09420       if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09421         arg[0] = arg[3];
09422       }
09423       if (tid == TID_WORD || tid == TID_SHORT) {
09424         arg[0] = arg[2];
09425         arg[1] = arg[3];
09426       }
09427     }
09428     if (flags & RPC_IN) {
09429       if (bpointer)
09430         arg_size = tid_size[tid];
09431 
09432       else
09433         arg_size = tid_size[arg_type];
09434 
09435       /* for strings, the argument size depends on the string length */
09436       if (tid == TID_STRING || tid == TID_LINK)
09437         arg_size = 1 + strlen((char *) *((char **) arg));
09438 
09439       /* for varibale length arrays, the size is given by
09440          the next parameter on the stack */
09441       if (flags & RPC_VARARRAY) {
09442         memcpy(&aptmp, &ap, sizeof(ap));
09443         rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09444         if (flags & RPC_OUT)
09445           arg_size = *((INT *) * ((void **) arg_tmp));
09446 
09447         else
09448           arg_size = *((INT *) arg_tmp);
09449         *((INT *) param_ptr) = ALIGN(arg_size);
09450         param_ptr += ALIGN(sizeof(INT));
09451       }
09452       if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09453         arg_size = rpc_list[rpc_index].param[i].n;
09454 
09455       /* always align parameter size */
09456       param_size = ALIGN(arg_size);
09457       if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09458         cm_msg(MERROR, "rpc_client_call",
09459                "parameters (%d) too large for network buffer (%d)",
09460                (PTYPE) param_ptr - (PTYPE) nc + param_size,
09461                NET_BUFFER_SIZE);
09462         return RPC_EXCEED_BUFFER;
09463       }
09464       if (bpointer)
09465         memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09466 
09467       else {
09468 
09469         /* floats are passed as doubles on most systems */
09470         if (tid != TID_FLOAT)
09471           memcpy(param_ptr, arg, arg_size);
09472 
09473         else
09474           *((float *) param_ptr) = (float) *((double *) arg);
09475       } param_ptr += param_size;
09476     }
09477   } va_end(ap);
09478   nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09479   send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09480 
09481   /* in FAST TCP mode, only send call and return immediately */
09482   if (transport == RPC_FTCP) {
09483     i = send_tcp(send_sock, (char *) nc, send_size, 0);
09484     if (i != send_size) {
09485       cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09486       return RPC_NET_ERROR;
09487     }
09488     return RPC_SUCCESS;
09489   }
09490 
09491   /* in TCP mode, send and wait for reply on send socket */
09492   i = send_tcp(send_sock, (char *) nc, send_size, 0);
09493   if (i != send_size) {
09494     cm_msg(MERROR, "rpc_client_call",
09495            "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09496            rpc_list[rpc_index].name, _client_connection[index].host_name);
09497     return RPC_NET_ERROR;
09498   }
09499 
09500   /* make some timeout checking */
09501   if (rpc_timeout > 0) {
09502     FD_ZERO(&readfds);
09503     FD_SET(send_sock, &readfds);
09504     timeout.tv_sec = rpc_timeout / 1000;
09505     timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09506 
09507     do {
09508       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09509 
09510       /* if an alarm signal was cought, restart select with reduced timeout */
09511       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09512         timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09513     } while (status == -1);     /* dont return if an alarm signal was cought */
09514     if (!FD_ISSET(send_sock, &readfds)) {
09515       cm_msg(MERROR, "rpc_client_call",
09516              "rpc timeout, routine = \"%s\", host = \"%s\"",
09517              rpc_list[rpc_index].name,
09518              _client_connection[index].host_name);
09519 
09520       /* disconnect to avoid that the reply to this rpc_call comes at
09521          the next rpc_call */
09522       rpc_client_disconnect(hConn, FALSE);
09523       return RPC_TIMEOUT;
09524     }
09525   }
09526 
09527   /* receive result on send socket */
09528   i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09529   if (i <= 0) {
09530     cm_msg(MERROR, "rpc_client_call",
09531            "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09532            rpc_list[rpc_index].name, _client_connection[index].host_name);
09533     return RPC_NET_ERROR;
09534   }
09535 
09536   /* extract result variables and place it to argument list */
09537   status = nc->header.routine_id;
09538   va_start(ap, routine_id);
09539   for (i = 0, param_ptr = nc->param;
09540        rpc_list[rpc_index].param[i].tid != 0; i++) {
09541     tid = rpc_list[rpc_index].param[i].tid;
09542     flags = rpc_list[rpc_index].param[i].flags;
09543     bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09544         (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY)
09545         || tid == TID_STRING || tid == TID_ARRAY
09546         || tid == TID_STRUCT || tid == TID_LINK;
09547     if (bpointer)
09548       arg_type = TID_ARRAY;
09549 
09550     else
09551       arg_type = rpc_list[rpc_index].param[i].tid;
09552     if (tid == TID_FLOAT && !bpointer)
09553       arg_type = TID_DOUBLE;
09554     rpc_va_arg(&ap, arg_type, arg);
09555     if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09556       tid = rpc_list[rpc_index].param[i].tid;
09557       flags = rpc_list[rpc_index].param[i].flags;
09558       arg_size = tid_size[tid];
09559       if (tid == TID_STRING || tid == TID_LINK)
09560         arg_size = strlen((char *) (param_ptr)) + 1;
09561       if (flags & RPC_VARARRAY) {
09562         arg_size = *((INT *) param_ptr);
09563         param_ptr += ALIGN(sizeof(INT));
09564       }
09565       if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09566         arg_size = rpc_list[rpc_index].param[i].n;
09567 
09568       /* return parameters are always pointers */
09569       if (*((char **) arg))
09570         memcpy((void *) *((char **) arg), param_ptr, arg_size);
09571 
09572       /* parameter size is always aligned */
09573       param_size = ALIGN(arg_size);
09574       param_ptr += param_size;
09575     }
09576   } va_end(ap);
09577   return status;
09578 }
09579 
09580 
09581 /********************************************************************/
09582 INT rpc_call(const INT routine_id, ...)
09583 /********************************************************************\
09584 
09585   Routine: rpc_call
09586 
09587   Purpose: Call a function on a MIDAS server
09588 
09589   Input:
09590     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09591 
09592     ...                     variable argument list
09593 
09594   Output:
09595     (depends on argument list)
09596 
09597   Function value:
09598     RPC_SUCCESS             Successful completion
09599     RPC_NET_ERROR           Error in socket call
09600     RPC_NO_CONNECTION       No active connection
09601     RPC_TIMEOUT             Timeout in RPC call
09602     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09603     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09604 
09605 \********************************************************************/
09606 {
09607   va_list ap, aptmp;
09608   char arg[8], arg_tmp[8];
09609   INT arg_type, transport, rpc_timeout;
09610   INT i, index, status;
09611   INT param_size, arg_size, send_size;
09612   INT tid, flags;
09613   fd_set readfds;
09614   struct timeval timeout;
09615   char *param_ptr, str[80];
09616   BOOL bpointer, bbig;
09617   NET_COMMAND *nc;
09618   int send_sock;
09619 
09620   send_sock = _server_connection.send_sock;
09621   transport = _server_connection.transport;
09622   rpc_timeout = _server_connection.rpc_timeout;
09623 
09624   /* init network buffer */
09625   if (_net_send_buffer_size == 0) {
09626     _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09627     if (_net_send_buffer == NULL) {
09628       cm_msg(MERROR, "rpc_call",
09629              "not enough memory to allocate network buffer");
09630       return RPC_EXCEED_BUFFER;
09631     }
09632     _net_send_buffer_size = NET_BUFFER_SIZE;
09633   }
09634   nc = (NET_COMMAND *) _net_send_buffer;
09635   nc->header.routine_id = routine_id;
09636   if (transport == RPC_FTCP)
09637     nc->header.routine_id |= TCP_FAST;
09638   for (i = 0;; i++)
09639     if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09640       break;
09641   index = i;
09642   if (rpc_list[i].id == 0) {
09643     sprintf(str, "invalid rpc ID (%d)", routine_id);
09644     cm_msg(MERROR, "rpc_call", str);
09645     return RPC_INVALID_ID;
09646   }
09647 
09648   /* examine variable argument list and convert it to parameter array */
09649   va_start(ap, routine_id);
09650 
09651   /* find out if we are on a big endian system */
09652   bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09653   for (i = 0, param_ptr = nc->param;
09654        rpc_list[index].param[i].tid != 0; i++) {
09655     tid = rpc_list[index].param[i].tid;
09656     flags = rpc_list[index].param[i].flags;
09657     bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09658         (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY)
09659         || tid == TID_STRING || tid == TID_ARRAY
09660         || tid == TID_STRUCT || tid == TID_LINK;
09661     if (bpointer)
09662       arg_type = TID_ARRAY;
09663 
09664     else
09665       arg_type = tid;
09666 
09667     /* floats are passed as doubles, at least under NT */
09668     if (tid == TID_FLOAT && !bpointer)
09669       arg_type = TID_DOUBLE;
09670 
09671     /* get pointer to argument */
09672     rpc_va_arg(&ap, arg_type, arg);
09673 
09674     /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09675     if (bbig) {
09676       if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09677         arg[0] = arg[3];
09678       }
09679       if (tid == TID_WORD || tid == TID_SHORT) {
09680         arg[0] = arg[2];
09681         arg[1] = arg[3];
09682       }
09683     }
09684     if (flags & RPC_IN) {
09685       if (bpointer)
09686         arg_size = tid_size[tid];
09687 
09688       else
09689         arg_size = tid_size[arg_type];
09690 
09691       /* for strings, the argument size depends on the string length */
09692       if (tid == TID_STRING || tid == TID_LINK)
09693         arg_size = 1 + strlen((char *) *((char **) arg));
09694 
09695       /* for varibale length arrays, the size is given by
09696          the next parameter on the stack */
09697       if (flags & RPC_VARARRAY) {
09698         memcpy(&aptmp, &ap, sizeof(ap));
09699         rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09700         if (flags & RPC_OUT)
09701           arg_size = *((INT *) * ((void **) arg_tmp));
09702 
09703         else
09704           arg_size = *((INT *) arg_tmp);
09705         *((INT *) param_ptr) = ALIGN(arg_size);
09706         param_ptr += ALIGN(sizeof(INT));
09707       }
09708       if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09709         arg_size = rpc_list[index].param[i].n;
09710 
09711       /* always align parameter size */
09712       param_size = ALIGN(arg_size);
09713       if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09714         cm_msg(MERROR, "rpc_call",
09715                "parameters (%d) too large for network buffer (%d)",
09716                (PTYPE) param_ptr - (PTYPE) nc + param_size,
09717                NET_BUFFER_SIZE);
09718         return RPC_EXCEED_BUFFER;
09719       }
09720       if (bpointer)
09721         memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09722 
09723       else {
09724 
09725         /* floats are passed as doubles on most systems */
09726         if (tid != TID_FLOAT)
09727           memcpy(param_ptr, arg, arg_size);
09728 
09729         else
09730           *((float *) param_ptr) = (float) *((double *) arg);
09731       } param_ptr += param_size;
09732     }
09733   } va_end(ap);
09734   nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09735   send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09736 
09737   /* in FAST TCP mode, only send call and return immediately */
09738   if (transport == RPC_FTCP) {
09739     i = send_tcp(send_sock, (char *) nc, send_size, 0);
09740     if (i != send_size) {
09741       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09742       return RPC_NET_ERROR;
09743     }
09744     return RPC_SUCCESS;
09745   }
09746 
09747   /* in TCP mode, send and wait for reply on send socket */
09748   i = send_tcp(send_sock, (char *) nc, send_size, 0);
09749   if (i != send_size) {
09750     cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09751     return RPC_NET_ERROR;
09752   }
09753 
09754   /* make some timeout checking */
09755   if (rpc_timeout > 0) {
09756     FD_ZERO(&readfds);
09757     FD_SET(send_sock, &readfds);
09758     timeout.tv_sec = rpc_timeout / 1000;
09759     timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09760 
09761     do {
09762       status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09763 
09764       /* if an alarm signal was cought, restart select with reduced timeout */
09765       if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09766         timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09767     } while (status == -1);     /* dont return if an alarm signal was cought */
09768     if (!FD_ISSET(send_sock, &readfds)) {
09769       cm_msg(MERROR, "rpc_call",
09770              "rpc timeout, routine = \"%s\"", rpc_list[index].name);
09771 
09772       /* disconnect to avoid that the reply to this rpc_call comes at
09773          the next rpc_call */
09774       rpc_server_disconnect();
09775       return RPC_TIMEOUT;
09776     }
09777   }
09778 
09779   /* receive result on send socket */
09780   i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09781   if (i <= 0) {
09782     cm_msg(MERROR, "rpc_call",
09783            "recv_tcp() failed, routine = \"%s\"", rpc_list[index].name);
09784     return RPC_NET_ERROR;
09785   }
09786 
09787   /* extract result variables and place it to argument list */
09788   status = nc->header.routine_id;
09789   va_start(ap, routine_id);
09790   for (i = 0, param_ptr = nc->param;
09791        rpc_list[index].param[i].tid != 0; i++) {
09792     tid = rpc_list[index].param[i].tid;
09793     flags = rpc_list[index].param[i].flags;
09794     bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09795         (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY)
09796         || tid == TID_STRING || tid == TID_ARRAY
09797         || tid == TID_STRUCT || tid == TID_LINK;
09798     if (bpointer)
09799       arg_type = TID_ARRAY;
09800 
09801     else
09802       arg_type = rpc_list[index].param[i].tid;
09803     if (tid == TID_FLOAT && !bpointer)
09804       arg_type = TID_DOUBLE;
09805     rpc_va_arg(&ap, arg_type, arg);
09806     if (rpc_list[index].param[i].flags & RPC_OUT) {
09807       tid = rpc_list[index].param[i].tid;
09808       arg_size = tid_size[tid];
09809       if (tid == TID_STRING || tid == TID_LINK)
09810         arg_size = strlen((char *) (param_ptr)) + 1;
09811       if (flags & RPC_VARARRAY) {
09812         arg_size = *((INT *) param_ptr);
09813         param_ptr += ALIGN(sizeof(INT));
09814       }
09815       if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09816         arg_size = rpc_list[index].param[i].n;
09817 
09818       /* return parameters are always pointers */
09819       if (*((char **) arg))
09820         memcpy((void *) *((char **) arg), param_ptr, arg_size);
09821 
09822       /* parameter size is always aligned */
09823       param_size = ALIGN(arg_size);
09824       param_ptr += param_size;
09825     }
09826   } va_end(ap);
09827   return status;
09828 }
09829 
09830 
09831 /********************************************************************/
09832 INT rpc_set_opt_tcp_size(INT tcp_size)
09833 {
09834   INT old;
09835 
09836   old = _opt_tcp_size;
09837   _opt_tcp_size = tcp_size;
09838   return old;
09839 }
09840 
09841 INT rpc_get_opt_tcp_size()
09842 {
09843   return _opt_tcp_size;
09844 }
09845 
09846 
09847 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09848 
09849 /********************************************************************/
09850 /**
09851 Fast send_event routine which bypasses the RPC layer and
09852            sends the event directly at the TCP level.
09853 @param buffer_handle      Handle of the buffer to send the event to.
09854                             Must be obtained via bm_open_buffer.
09855 @param source            Address of the event to send. It must have
09856                             a proper event header.
09857 @param buf_size           Size of event in bytes with header.
09858 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09859                             function returns immediately if it cannot
09860                             send the event over the network. In SYNC
09861                             mode, it waits until the packet is sent
09862                             (blocking).
09863 
09864 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
09865         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
09866 */
09867 INT rpc_send_event(INT buffer_handle,
09868                    void *source, INT buf_size, INT async_flag)
09869 {
09870   INT i;
09871   NET_COMMAND *nc;
09872   unsigned long flag;
09873   BOOL would_block = 0;
09874   DWORD aligned_buf_size;
09875 
09876   aligned_buf_size = ALIGN(buf_size);
09877   if (aligned_buf_size !=
09878       (INT) ALIGN(((EVENT_HEADER *) source)->data_size +
09879                   sizeof(EVENT_HEADER))) {
09880     cm_msg(MERROR, "rpc_send_event", "event size mismatch");
09881     return BM_INVALID_PARAM;
09882   }
09883   if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
09884     cm_msg(MERROR, "rpc_send_event",
09885            "event size (%d) larger than maximum event size (%d)",
09886            ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
09887     return RPC_EXCEED_BUFFER;
09888   }
09889   if (!rpc_is_remote())
09890     return bm_send_event(buffer_handle, source, buf_size, async_flag);
09891 
09892   /* init network buffer */
09893   if (!_tcp_buffer)
09894     _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
09895   if (!_tcp_buffer) {
09896     cm_msg(MERROR, "rpc_send_event",
09897            "not enough memory to allocate network buffer");
09898     return RPC_EXCEED_BUFFER;
09899   }
09900 
09901   /* check if not enough space in TCP buffer */
09902   if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09903       (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
09904 
09905     /* set socket to nonblocking IO */
09906     if (async_flag == ASYNC) {
09907       flag = 1;
09908 
09909 #ifdef OS_VXWORKS
09910       ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09911 
09912 #else                           /*  */
09913       ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09914 
09915 #endif                          /*  */
09916     }
09917     i = send_tcp(_server_connection.send_sock,
09918                  _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
09919     if (i < 0)
09920 #ifdef OS_WINNT
09921       would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
09922 
09923 #else                           /*  */
09924       would_block = (errno == EWOULDBLOCK);
09925 
09926 #endif                          /*  */
09927 
09928     /* set socket back to blocking IO */
09929     if (async_flag == ASYNC) {
09930       flag = 0;
09931 
09932 #ifdef OS_VXWORKS
09933       ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
09934 
09935 #else                           /*  */
09936       ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
09937 
09938 #endif                          /*  */
09939     }
09940 
09941     /* increment read pointer */
09942     if (i > 0)
09943       _tcp_rp += i;
09944 
09945     /* check if whole buffer is sent */
09946     if (_tcp_rp == _tcp_wp)
09947       _tcp_rp = _tcp_wp = 0;
09948     if (i < 0 && !would_block) {
09949       printf("send_tcp() returned %d\n", i);
09950       cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
09951       return RPC_NET_ERROR;
09952     }
09953 
09954     /* return if buffer is not emptied */
09955     if (_tcp_wp > 0)
09956       return BM_ASYNC_RETURN;
09957   }
09958   nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
09959   nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
09960   nc->header.param_size = 4 * 8 + aligned_buf_size;
09961 
09962   /* assemble parameters manually */
09963   *((INT *) (&nc->param[0])) = buffer_handle;
09964   *((INT *) (&nc->param[8])) = buf_size;
09965 
09966   /* send events larger than optimal buffer size directly */
09967   if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
09968       (DWORD) _opt_tcp_size) {
09969 
09970     /* send header */
09971     send_tcp(_server_connection.send_sock,
09972              _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
09973 
09974     /* send data */
09975     send_tcp(_server_connection.send_sock, (char *) source,
09976              aligned_buf_size, 0);
09977 
09978     /* send last two parameters */
09979     *((INT *) (&nc->param[0])) = buf_size;
09980     *((INT *) (&nc->param[8])) = 0;
09981     send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
09982   }
09983 
09984   else {
09985 
09986     /* copy event */
09987     memcpy(&nc->param[16], source, buf_size);
09988 
09989     /* last two parameters (buf_size and async_flag */
09990     *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
09991     *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
09992     _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09993   }
09994   return RPC_SUCCESS;
09995 }
09996 
09997 
09998 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09999 /********************************************************************/
10000 int rpc_get_send_sock()
10001 /********************************************************************\
10002 
10003   Routine: rpc_get_send_sock
10004 
10005   Purpose: Return send socket to MIDAS server. Used by MFE.C for
10006            optimized event sending.
10007 
10008   Input:
10009     none
10010 
10011   Output:
10012     none
10013 
10014   Function value:
10015     int    socket
10016 
10017 \********************************************************************/
10018 {
10019   return _server_connection.send_sock;
10020 }
10021 
10022 
10023 /********************************************************************/
10024 int rpc_get_event_sock()
10025 /********************************************************************\
10026 
10027   Routine: rpc_get_event_sock
10028 
10029   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
10030            optimized event sending.
10031 
10032   Input:
10033     none
10034 
10035   Output:
10036     none
10037 
10038   Function value:
10039     int    socket
10040 
10041 \********************************************************************/
10042 {
10043   return _server_connection.event_sock;
10044 }
10045 
10046 
10047 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10048 
10049 /********************************************************************/
10050 /**
10051 Send event residing in the TCP cache buffer filled by
10052            rpc_send_event. This routine should be called when a
10053            run is stopped.
10054 
10055 @return RPC_SUCCESS, RPC_NET_ERROR
10056 */
10057 INT rpc_flush_event()
10058 {
10059   INT i;
10060 
10061   if (!rpc_is_remote())
10062     return RPC_SUCCESS;
10063 
10064   /* return if rpc_send_event was not called */
10065   if (!_tcp_buffer || _tcp_wp == 0)
10066     return RPC_SUCCESS;
10067 
10068   /* empty TCP buffer */
10069   if (_tcp_wp > 0) {
10070     i = send_tcp(_server_connection.send_sock,
10071                  _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10072     if (i != _tcp_wp - _tcp_rp) {
10073       cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
10074       return RPC_NET_ERROR;
10075     }
10076   }
10077   _tcp_rp = _tcp_wp = 0;
10078   return RPC_SUCCESS;
10079 }
10080 
10081 
10082 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10083 
10084 /********************************************************************/
10085 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
10086 /********************************************************************\
10087 
10088   Routine: rpc_transition_dispatch
10089 
10090   Purpose: Gets called when a transition function was registered and
10091            a transition occured. Internal use only.
10092 
10093   Input:
10094     INT    index            RPC function ID
10095     void   *prpc_param      RPC parameters
10096 
10097   Output:
10098     none
10099 
10100   Function value:
10101     INT    return value from called user routine
10102 
10103 \********************************************************************/
10104 {
10105   INT status, i;
10106 
10107   if (index == RPC_RC_TRANSITION) {
10108     for (i = 0; _trans_table[i].transition; i++)
10109       if (_trans_table[i].transition == CINT(0))
10110         break;
10111 
10112     /* erase error string */
10113     *(CSTRING(2)) = 0;
10114 
10115     /* call registerd function */
10116     if (_trans_table[i].transition == CINT(0)
10117         && _trans_table[i].func)
10118       status = _trans_table[i].func(CINT(1), CSTRING(2));
10119 
10120     else
10121       status = RPC_SUCCESS;
10122   }
10123 
10124   else {
10125     cm_msg(MERROR, "rpc_transition_dispatch",
10126            "received unrecognized command");
10127     status = RPC_INVALID_ID;
10128   }
10129   return status;
10130 }
10131 
10132 
10133 /********************************************************************\
10134 *                        server functions                            *
10135 \********************************************************************/
10136 
10137 /********************************************************************/
10138 INT recv_tcp_server(INT index,
10139                     char *buffer,
10140                     DWORD buffer_size, INT flags, INT * remaining)
10141 /********************************************************************\
10142 
10143   Routine: recv_tcp_server
10144 
10145   Purpose: TCP receive routine with local cache. To speed up network
10146            performance, a 64k buffer is read in at once and split into
10147            several RPC command on successive calls to recv_tcp_server.
10148            Therefore, the number of recv() calls is minimized.
10149 
10150            This routine is ment to be called by the server process.
10151            Clients should call recv_tcp instead.
10152 
10153   Input:
10154     INT   index              Index of server connection
10155     DWORD buffer_size        Size of the buffer in bytes.
10156     INT   flags              Flags passed to recv()
10157     INT   convert_flags      Convert flags needed for big/little
10158                              endian conversion
10159 
10160   Output:
10161     char  *buffer            Network receive buffer.
10162     INT   *remaining         Remaining data in cache
10163 
10164   Function value:
10165     INT                      Same as recv()
10166 
10167 \********************************************************************/
10168 {
10169   INT size, param_size;
10170   NET_COMMAND *nc;
10171   INT write_ptr, read_ptr, misalign;
10172   char *net_buffer;
10173   INT copied, status;
10174   INT sock;
10175 
10176   sock = _server_acception[index].recv_sock;
10177   if (flags & MSG_PEEK) {
10178     status = recv(sock, buffer, buffer_size, flags);
10179     if (status == -1)
10180       cm_msg(MERROR, "recv_tcp_server",
10181              "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)",
10182              buffer_size, status, errno, strerror(errno));
10183     return status;
10184   }
10185   if (!_server_acception[index].net_buffer) {
10186     if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10187       _server_acception[index].net_buffer_size = NET_TCP_SIZE;
10188 
10189     else
10190       _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
10191     _server_acception[index].net_buffer =
10192         (char *) M_MALLOC(_server_acception[index].net_buffer_size);
10193     _server_acception[index].write_ptr = 0;
10194     _server_acception[index].read_ptr = 0;
10195     _server_acception[index].misalign = 0;
10196   }
10197   if (!_server_acception[index].net_buffer) {
10198     cm_msg(MERROR, "recv_tcp_server",
10199            "not enough memory to allocate network buffer");
10200     return -1;
10201   }
10202   if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10203     cm_msg(MERROR, "recv_tcp_server",
10204            "parameters too large for network buffer");
10205     return -1;
10206   }
10207   copied = 0;
10208   param_size = -1;
10209   write_ptr = _server_acception[index].write_ptr;
10210   read_ptr = _server_acception[index].read_ptr;
10211   misalign = _server_acception[index].misalign;
10212   net_buffer = _server_acception[index].net_buffer;
10213 
10214   do {
10215     if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10216       if (param_size == -1) {
10217         if (copied > 0) {
10218 
10219           /* assemble split header */
10220           memcpy(buffer + copied,
10221                  net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER)
10222                  - copied);
10223           nc = (NET_COMMAND *) (buffer);
10224         }
10225 
10226         else
10227           nc = (NET_COMMAND *) (net_buffer + read_ptr);
10228         param_size = (INT) nc->header.param_size;
10229         if (_server_acception[index].convert_flags)
10230           rpc_convert_single(&param_size, TID_DWORD,
10231                              0, _server_acception[index].convert_flags);
10232       }
10233 
10234       /* check if parameters fit in buffer */
10235       if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10236         cm_msg(MERROR, "recv_tcp_server",
10237                "parameters too large for network buffer");
10238         _server_acception[index].read_ptr =
10239             _server_acception[index].write_ptr = 0;
10240         return -1;
10241       }
10242 
10243       /* check if we have all parameters in buffer */
10244       if (write_ptr - read_ptr >=
10245           param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10246         break;
10247     }
10248 
10249     /* not enough data, so copy partially and get new */
10250     size = write_ptr - read_ptr;
10251     if (size > 0) {
10252       memcpy(buffer + copied, net_buffer + read_ptr, size);
10253       copied += size;
10254       read_ptr = write_ptr;
10255     }
10256 #ifdef OS_UNIX
10257     do {
10258       write_ptr =
10259           recv(sock, net_buffer + misalign,
10260                _server_acception[index].net_buffer_size - 8, flags);
10261 
10262       /* don't return if an alarm signal was cought */
10263     } while (write_ptr == -1 && errno == EINTR);
10264 
10265 #else                           /*  */
10266     write_ptr =
10267         recv(sock, net_buffer + misalign,
10268              _server_acception[index].net_buffer_size - 8, flags);
10269 
10270 #endif                          /*  */
10271 
10272     /* abort if connection broken */
10273     if (write_ptr <= 0) {
10274       cm_msg(MERROR, "recv_tcp_server",
10275              "recv() returned %d, errno: %d (%s)", write_ptr,
10276              errno, strerror(errno));
10277       if (remaining)
10278         *remaining = 0;
10279       return write_ptr;
10280     }
10281     read_ptr = misalign;
10282     write_ptr += misalign;
10283     misalign = write_ptr % 8;
10284   } while (TRUE);
10285 
10286   /* copy rest of parameters */
10287   size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10288   memcpy(buffer + copied, net_buffer + read_ptr, size);
10289   read_ptr += size;
10290   if (remaining) {
10291 
10292     /* don't keep rpc_server_receive in an infinite loop */
10293     if (write_ptr - read_ptr < param_size)
10294       *remaining = 0;
10295 
10296     else
10297       *remaining = write_ptr - read_ptr;
10298   }
10299   _server_acception[index].write_ptr = write_ptr;
10300   _server_acception[index].read_ptr = read_ptr;
10301   _server_acception[index].misalign = misalign;
10302   return size + copied;
10303 }
10304 
10305 
10306 /********************************************************************/
10307 INT recv_tcp_check(int sock)
10308 /********************************************************************\
10309 
10310   Routine: recv_tcp_check
10311 
10312   Purpose: Check if in TCP receive buffer associated with sock is
10313            some data. Called by ss_suspend.
10314 
10315   Input:
10316     INT   sock               TCP receive socket
10317 
10318   Output:
10319     none
10320 
10321   Function value:
10322     INT   count              Number of bytes remaining in TCP buffer
10323 
10324 \********************************************************************/
10325 {
10326   INT index;
10327 
10328   /* figure out to which connection socket belongs */
10329   for (index = 0; index < MAX_RPC_CONNECTION; index++)
10330     if (_server_acception[index].recv_sock == sock)
10331       break;
10332   return _server_acception[index].write_ptr -
10333       _server_acception[index].read_ptr;
10334 }
10335 
10336 
10337 /********************************************************************/
10338 INT recv_event_server(INT index,
10339                       char *buffer,
10340                       DWORD buffer_size, INT flags, INT * remaining)
10341 /********************************************************************\
10342 
10343   Routine: recv_event_server
10344 
10345   Purpose: TCP event receive routine with local cache. To speed up
10346            network performance, a 64k buffer is read in at once and
10347            split into several RPC command on successive calls to
10348            recv_event_server. Therefore, the number of recv() calls
10349            is minimized.
10350 
10351            This routine is ment to be called by the server process.
10352            Clients should call recv_tcp instead.
10353 
10354   Input:
10355     INT   index              Index of server connection
10356     DWORD buffer_size        Size of the buffer in bytes.
10357     INT   flags              Flags passed to recv()
10358     INT   convert_flags      Convert flags needed for big/little
10359                              endian conversion
10360 
10361   Output:
10362     char  *buffer            Network receive buffer.
10363     INT   *remaining         Remaining data in cache
10364 
10365   Function value:
10366     INT                      Same as recv()
10367 
10368 \********************************************************************/
10369 {
10370   INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10371   EVENT_HEADER *pevent;
10372   INT write_ptr, read_ptr, misalign;
10373   char *net_buffer;
10374   INT copied, status;
10375   INT sock;
10376   RPC_SERVER_ACCEPTION *psa;
10377 
10378   psa = &_server_acception[index];
10379   sock = psa->event_sock;
10380   if (flags & MSG_PEEK) {
10381     status = recv(sock, buffer, buffer_size, flags);
10382     if (status == -1)
10383       cm_msg(MERROR, "recv_event_server",
10384              "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)",
10385              buffer_size, status, errno, strerror(errno));
10386     return status;
10387   }
10388   if (!psa->ev_net_buffer) {
10389     if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10390       psa->net_buffer_size = NET_TCP_SIZE;
10391 
10392     else
10393       psa->net_buffer_size = NET_BUFFER_SIZE;
10394     psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10395     psa->ev_write_ptr = 0;
10396     psa->ev_read_ptr = 0;
10397     psa->ev_misalign = 0;
10398   }
10399   if (!psa->ev_net_buffer) {
10400     cm_msg(MERROR, "recv_event_server",
10401            "not enough memory to allocate network buffer");
10402     return -1;
10403   }
10404   header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10405   if ((INT) buffer_size < header_size) {
10406     cm_msg(MERROR, "recv_event_server",
10407            "parameters too large for network buffer");
10408     return -1;
10409   }
10410   copied = 0;
10411   event_size = -1;
10412   write_ptr = psa->ev_write_ptr;
10413   read_ptr = psa->ev_read_ptr;
10414   misalign = psa->ev_misalign;
10415   net_buffer = psa->ev_net_buffer;
10416 
10417   do {
10418     if (write_ptr - read_ptr >= header_size - copied) {
10419       if (event_size == -1) {
10420         if (copied > 0) {
10421 
10422           /* assemble split header */
10423           memcpy(buffer + copied,
10424                  net_buffer + read_ptr, header_size - copied);
10425           pbh = (INT *) buffer;
10426         }
10427 
10428         else
10429           pbh = (INT *) (net_buffer + read_ptr);
10430         pevent = (EVENT_HEADER *) (pbh + 1);
10431         event_size = pevent->data_size;
10432         if (psa->convert_flags)
10433           rpc_convert_single(&event_size, TID_DWORD,
10434                              0, psa->convert_flags);
10435         aligned_event_size = ALIGN(event_size);
10436       }
10437 
10438       /* check if data part fits in buffer */
10439       if ((INT) buffer_size < aligned_event_size + header_size) {
10440         cm_msg(MERROR, "recv_event_server",
10441                "parameters too large for network buffer");
10442         psa->ev_read_ptr = psa->ev_write_ptr = 0;
10443         return -1;
10444       }
10445 
10446       /* check if we have whole event in buffer */
10447       if (write_ptr - read_ptr >=
10448           aligned_event_size + header_size - copied)
10449         break;
10450     }
10451 
10452     /* not enough data, so copy partially and get new */
10453     size = write_ptr - read_ptr;
10454     if (size > 0) {
10455       memcpy(buffer + copied, net_buffer + read_ptr, size);
10456       copied += size;
10457       read_ptr = write_ptr;
10458     }
10459 #ifdef OS_UNIX
10460     do {
10461       write_ptr =
10462           recv(sock, net_buffer + misalign,
10463                psa->net_buffer_size - 8, flags);
10464 
10465       /* don't return if an alarm signal was cought */
10466     } while (write_ptr == -1 && errno == EINTR);
10467 
10468 #else                           /*  */
10469     write_ptr =
10470         recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10471 
10472 #endif                          /*  */
10473 
10474     /* abort if connection broken */
10475     if (write_ptr <= 0) {
10476       cm_msg(MERROR, "recv_event_server",
10477              "recv() returned %d, errno: %d (%s)", write_ptr,
10478              errno, strerror(errno));
10479       if (remaining)
10480         *remaining = 0;
10481       return write_ptr;
10482     }
10483     read_ptr = misalign;
10484     write_ptr += misalign;
10485     misalign = write_ptr % 8;
10486   } while (TRUE);
10487 
10488   /* copy rest of event */
10489   size = aligned_event_size + header_size - copied;
10490   if (size > 0) {
10491     memcpy(buffer + copied, net_buffer + read_ptr, size);
10492     read_ptr += size;
10493   }
10494   if (remaining)
10495     *remaining = write_ptr - read_ptr;
10496   psa->ev_write_ptr = write_ptr;
10497   psa->ev_read_ptr = read_ptr;
10498   psa->ev_misalign = misalign;
10499 
10500   /* convert header little endian/big endian */
10501   if (psa->convert_flags) {
10502     pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10503     rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10504     rpc_convert_single(&pevent->event_id, TID_SHORT, 0,
10505                        psa->convert_flags);
10506     rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0,
10507                        psa->convert_flags);
10508     rpc_convert_single(&pevent->serial_number, TID_DWORD, 0,
10509                        psa->convert_flags);
10510     rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0,
10511                        psa->convert_flags);
10512     rpc_convert_single(&pevent->data_size, TID_DWORD, 0,
10513                        psa->convert_flags);
10514   }
10515   return header_size + event_size;
10516 }
10517 
10518 
10519 /********************************************************************/
10520 INT recv_event_check(int sock)
10521 /********************************************************************\
10522 
10523   Routine: recv_event_check
10524 
10525   Purpose: Check if in TCP event receive buffer associated with sock
10526            is some data. Called by ss_suspend.
10527 
10528   Input:
10529     INT   sock               TCP receive socket
10530 
10531   Output:
10532     none
10533 
10534   Function value:
10535     INT   count              Number of bytes remaining in TCP buffer
10536 
10537 \********************************************************************/
10538 {
10539   INT index;
10540 
10541   /* figure out to which connection socket belongs */
10542   for (index = 0; index < MAX_RPC_CONNECTION; index++)
10543     if (_server_acception[index].event_sock == sock)
10544       break;
10545   return _server_acception[index].ev_write_ptr -
10546       _server_acception[index].ev_read_ptr;
10547 }
10548 
10549 
10550 /********************************************************************/
10551 INT rpc_register_server(INT server_type,
10552                         char *name, INT * port, INT(*func) (INT, void **))
10553 /********************************************************************\
10554 
10555   Routine: rpc_register_server
10556 
10557   Purpose: Register the calling process as a MIDAS RPC server. Note
10558            that cm_connnect_experiment must be called prior to any call of
10559            rpc_register_server.
10560 
10561   Input:
10562     INT   server_type       One of the following constants:
10563                             ST_SINGLE: register a single process server
10564                             ST_MTHREAD: for each connection, start
10565                                         a new thread to serve it
10566                             ST_MPROCESS: for each connection, start
10567                                          a new process to server it
10568                             ST_SUBPROCESS: the routine was called from
10569                                            a multi process server
10570                             ST_REMOTE: register a client program server
10571                                        connected to the ODB
10572     char  *name             Name of .EXE file to start in MPROCESS mode
10573     INT   *port             TCP port for listen. NULL if listen as main
10574                             server (MIDAS_TCP_PORT is then used). If *port=0,
10575                             the OS chooses a free port and returns it. If
10576                             *port != 0, this port is used.
10577     INT   *func             Default dispatch function
10578 
10579   Output:
10580     INT   *port             Port under which server is listening.
10581 
10582   Function value:
10583     RPC_SUCCESS             Successful completion
10584     RPC_NET_ERROR           Error in socket call
10585     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10586 
10587 \********************************************************************/
10588 {
10589   struct sockaddr_in bind_addr;
10590   INT status, flag;
10591   int size;
10592 
10593 #ifdef OS_WINNT
10594   {
10595     WSADATA WSAData;
10596 
10597     /* Start windows sockets */
10598     if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10599       return RPC_NET_ERROR;
10600   }
10601 
10602 #endif                          /*  */
10603   rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10604 
10605   /* register system functions */
10606   rpc_register_functions(rpc_get_internal_list(0), func);
10607   if (name != NULL)
10608     rpc_set_server_option(RPC_OSERVER_NAME, (PTYPE) name);
10609 
10610   /* in subprocess mode, don't start listener */
10611   if (server_type == ST_SUBPROCESS)
10612     return RPC_SUCCESS;
10613 
10614   /* create a socket for listening */
10615   _lsock = socket(AF_INET, SOCK_STREAM, 0);
10616   if (_lsock == -1) {
10617     cm_msg(MERROR, "rpc_register_server", "socket() failed");
10618     return RPC_NET_ERROR;
10619   }
10620 
10621   /* reuse address, needed if previous server stopped (30s timeout!) */
10622   flag = 1;
10623   status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR,
10624                       (char *) &flag, sizeof(INT));
10625   if (status < 0) {
10626     cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
10627     return RPC_NET_ERROR;
10628   }
10629 
10630   /* bind local node name and port to socket */
10631   memset(&bind_addr, 0, sizeof(bind_addr));
10632   bind_addr.sin_family = AF_INET;
10633   bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10634   if (!port)
10635     bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10636 
10637   else
10638     bind_addr.sin_port = htons((short) (*port));
10639   status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10640   if (status < 0) {
10641     cm_msg(MERROR, "rpc_register_server", "bind() failed: %s",
10642            strerror(errno));
10643     return RPC_NET_ERROR;
10644   }
10645 
10646   /* listen for connection */
10647 #ifdef OS_MSDOS
10648   status = listen(_lsock, 1);
10649 
10650 #else                           /*  */
10651   status = listen(_lsock, SOMAXCONN);
10652 
10653 #endif                          /*  */
10654   if (status < 0) {
10655     cm_msg(MERROR, "rpc_register_server", "listen() failed");
10656     return RPC_NET_ERROR;
10657   }
10658 
10659   /* return port wich OS has choosen */
10660   if (port && *port == 0) {
10661     size = sizeof(bind_addr);
10662     getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10663     *port = ntohs(bind_addr.sin_port);
10664   }
10665 
10666   /* define callbacks for ss_suspend */
10667   if (server_type == ST_REMOTE)
10668     ss_suspend_set_dispatch(CH_LISTEN, &_lsock,
10669                             (int (*)(void)) rpc_client_accept);
10670 
10671   else
10672     ss_suspend_set_dispatch(CH_LISTEN, &_lsock,
10673                             (int (*)(void)) rpc_server_accept);
10674   return RPC_SUCCESS;
10675 }
10676 
10677 
10678 /********************************************************************/
10679 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10680 /********************************************************************\
10681 
10682   Routine: rpc_execute
10683 
10684   Purpose: Execute a RPC command received over the network
10685 
10686   Input:
10687     INT  sock               TCP socket to which the result should be
10688                             send back
10689 
10690     char *buffer            Command buffer
10691     INT  convert_flags      Flags for data conversion
10692 
10693   Output:
10694     none
10695 
10696   Function value:
10697     RPC_SUCCESS             Successful completion
10698     RPC_INVALID_ID          Invalid routine_id received
10699     RPC_NET_ERROR           Error in socket call
10700     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10701     RPC_SHUTDOWN            Shutdown requested
10702     SS_ABORT                TCP connection broken
10703     SS_EXIT                 TCP connection closed
10704 
10705 \********************************************************************/
10706 {
10707   INT i, index, routine_id, status;
10708   char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10709   INT tid, flags;
10710   NET_COMMAND *nc_in, *nc_out;
10711   INT param_size, max_size;
10712   void *prpc_param[20];
10713   char str[1024], debug_line[1024];
10714 
10715 /* return buffer must be auto for multi-thread servers */
10716   char return_buffer[NET_BUFFER_SIZE];
10717 
10718   /* extract pointer array to parameters */
10719   nc_in = (NET_COMMAND *) buffer;
10720   nc_out = (NET_COMMAND *) return_buffer;
10721 
10722   /* convert header format (byte swapping) */
10723   if (convert_flags) {
10724     rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0,
10725                        convert_flags);
10726     rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0,
10727                        convert_flags);
10728   }
10729 
10730   /* no result return in FAST TCP mode */
10731   if (nc_in->header.routine_id & TCP_FAST)
10732     sock = 0;
10733 
10734   /* find entry in rpc_list */
10735   routine_id = nc_in->header.routine_id & ~TCP_FAST;
10736   for (i = 0;; i++)
10737     if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10738       break;
10739   index = i;
10740   if (rpc_list[i].id == 0) {
10741     cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10742     return RPC_INVALID_ID;
10743   }
10744   in_param_ptr = nc_in->param;
10745   out_param_ptr = nc_out->param;
10746   if (_debug_print)
10747     sprintf(debug_line, "%s(", rpc_list[index].name);
10748   for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10749     tid = rpc_list[index].param[i].tid;
10750     flags = rpc_list[index].param[i].flags;
10751     if (flags & RPC_IN) {
10752       param_size = ALIGN(tid_size[tid]);
10753       if (tid == TID_STRING || tid == TID_LINK)
10754         param_size = ALIGN(1 + strlen((char *) (in_param_ptr)));
10755       if (flags & RPC_VARARRAY) {
10756 
10757         /* for arrays, the size is stored as a INT in front of the array */
10758         param_size = *((INT *) in_param_ptr);
10759         if (convert_flags)
10760           rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10761         param_size = ALIGN(param_size);
10762         in_param_ptr += ALIGN(sizeof(INT));
10763       }
10764       if (tid == TID_STRUCT)
10765         param_size = ALIGN(rpc_list[index].param[i].n);
10766       prpc_param[i] = in_param_ptr;
10767 
10768       /* convert data format */
10769       if (convert_flags) {
10770         if (flags & RPC_VARARRAY)
10771           rpc_convert_data(in_param_ptr, tid, flags,
10772                            param_size, convert_flags);
10773 
10774         else
10775           rpc_convert_data(in_param_ptr, tid, flags,
10776                            rpc_list[index].param[i].
10777                            n * tid_size[tid], convert_flags);
10778       }
10779       if (_debug_print) {
10780         db_sprintf(str, in_param_ptr, param_size, 0,
10781                    rpc_list[index].param[i].tid);
10782         if (rpc_list[index].param[i].tid == TID_STRING) {
10783 
10784           /* check for long strings (db_create_record...) */
10785           if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10786             strcat(debug_line, "\"");
10787             strcat(debug_line, str);
10788             strcat(debug_line, "\"");
10789           }
10790 
10791           else
10792             strcat(debug_line, "...");
10793         }
10794 
10795         else
10796           strcat(debug_line, str);
10797       }
10798       in_param_ptr += param_size;
10799     }
10800     if (flags & RPC_OUT) {
10801       param_size = ALIGN(tid_size[tid]);
10802       if (flags & RPC_VARARRAY || tid == TID_STRING) {
10803 
10804         /* save maximum array length */
10805         max_size = *((INT *) in_param_ptr);
10806         if (convert_flags)
10807           rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10808         max_size = ALIGN(max_size);
10809         *((INT *) out_param_ptr) = max_size;
10810 
10811         /* save space for return array length */
10812         out_param_ptr += ALIGN(sizeof(INT));
10813 
10814         /* use maximum array length from input */
10815         param_size += max_size;
10816       }
10817       if (rpc_list[index].param[i].tid == TID_STRUCT)
10818         param_size = ALIGN(rpc_list[index].param[i].n);
10819       if ((PTYPE) out_param_ptr - (PTYPE) nc_out +
10820           param_size > NET_BUFFER_SIZE) {
10821         cm_msg(MERROR, "rpc_execute",
10822                "return parameters (%d) too large for network buffer (%d)",
10823                (PTYPE) out_param_ptr - (PTYPE) nc_out +
10824                param_size, NET_BUFFER_SIZE);
10825         return RPC_EXCEED_BUFFER;
10826       }
10827 
10828       /* if parameter goes both directions, copy input to output */
10829       if (rpc_list[index].param[i].flags & RPC_IN)
10830         memcpy(out_param_ptr, prpc_param[i], param_size);
10831       if (_debug_print && !(flags & RPC_IN))
10832         strcat(debug_line, "-");
10833       prpc_param[i] = out_param_ptr;
10834       out_param_ptr += param_size;
10835     }
10836     if (_debug_print)
10837       if (rpc_list[index].param[i + 1].tid)
10838         strcat(debug_line, ", ");
10839   }
10840   if (_debug_print) {
10841     strcat(debug_line, ")");
10842     _debug_print(debug_line);
10843   }
10844   last_param_ptr = out_param_ptr;
10845 
10846   /*********************************\
10847   *   call dispatch function        *
10848   \*********************************/
10849   if (rpc_list[index].dispatch)
10850     status = rpc_list[index].dispatch(routine_id, prpc_param);
10851 
10852   else
10853     status = RPC_INVALID_ID;
10854   if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN
10855       || routine_id == RPC_ID_WATCHDOG)
10856     status = RPC_SUCCESS;
10857 
10858   /* return immediately for closed down client connections */
10859   if (!sock && routine_id == RPC_ID_EXIT)
10860     return SS_EXIT;
10861   if (!sock && routine_id == RPC_ID_SHUTDOWN)
10862     return RPC_SHUTDOWN;
10863 
10864   /* Return if TCP connection broken */
10865   if (status == SS_ABORT)
10866     return SS_ABORT;
10867 
10868   /* if sock == 0, we are in FTCP mode and may not sent results */
10869   if (!sock)
10870     return RPC_SUCCESS;
10871 
10872   /* compress variable length arrays */
10873   out_param_ptr = nc_out->param;
10874   for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10875     if (rpc_list[index].param[i].flags & RPC_OUT) {
10876       tid = rpc_list[index].param[i].tid;
10877       flags = rpc_list[index].param[i].flags;
10878       param_size = ALIGN(tid_size[tid]);
10879       if (tid == TID_STRING) {
10880         max_size = *((INT *) out_param_ptr);
10881         param_size = strlen((char *) prpc_param[i]) + 1;
10882         param_size = ALIGN(param_size);
10883 
10884         /* move string ALIGN(sizeof(INT)) left */
10885         memcpy(out_param_ptr,
10886                out_param_ptr + ALIGN(sizeof(INT)), param_size);
10887 
10888         /* move remaining parameters to end of string */
10889         memcpy(out_param_ptr + param_size,
10890                out_param_ptr + max_size +
10891                ALIGN(sizeof(INT)),
10892                (PTYPE) last_param_ptr -
10893                ((PTYPE) out_param_ptr + max_size + ALIGN(sizeof(INT))));
10894       }
10895       if (flags & RPC_VARARRAY) {
10896 
10897         /* store array length at current out_param_ptr */
10898         max_size = *((INT *) out_param_ptr);
10899         param_size = *((INT *) prpc_param[i + 1]);
10900         *((INT *) out_param_ptr) = param_size;
10901         if (convert_flags)
10902           rpc_convert_single(out_param_ptr, TID_INT,
10903                              RPC_OUTGOING, convert_flags);
10904         out_param_ptr += ALIGN(sizeof(INT));
10905         param_size = ALIGN(param_size);
10906 
10907         /* move remaining parameters to end of array */
10908         memcpy(out_param_ptr + param_size,
10909                out_param_ptr + max_size +
10910                ALIGN(sizeof(INT)),
10911                (PTYPE) last_param_ptr -
10912                ((PTYPE) out_param_ptr + max_size + ALIGN(sizeof(INT))));
10913       }
10914       if (tid == TID_STRUCT)
10915         param_size = ALIGN(rpc_list[index].param[i].n);
10916 
10917       /* convert data format */
10918       if (convert_flags) {
10919         if (flags & RPC_VARARRAY)
10920           rpc_convert_data(out_param_ptr, tid,
10921                            rpc_list[index].param[i].
10922                            flags | RPC_OUTGOING,
10923                            param_size, convert_flags);
10924 
10925         else
10926           rpc_convert_data(out_param_ptr, tid,
10927                            rpc_list[index].param[i].
10928                            flags | RPC_OUTGOING,
10929                            rpc_list[index].param[i].
10930                            n * tid_size[tid], convert_flags);
10931       }
10932       out_param_ptr += param_size;
10933     }
10934 
10935   /* send return parameters */
10936   param_size = (PTYPE) out_param_ptr - (PTYPE) nc_out->param;
10937   nc_out->header.routine_id = status;
10938   nc_out->header.param_size = param_size;
10939 
10940   /* convert header format (byte swapping) if necessary */
10941   if (convert_flags) {
10942     rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
10943                        RPC_OUTGOING, convert_flags);
10944     rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
10945                        RPC_OUTGOING, convert_flags);
10946   }
10947   status =
10948       send_tcp(sock, return_buffer,
10949                sizeof(NET_COMMAND_HEADER) + param_size, 0);
10950   if (status < 0) {
10951     cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
10952     return RPC_NET_ERROR;
10953   }
10954 
10955   /* print return buffer */
10956 /*
10957   printf("Return buffer, ID %d:\n", routine_id);
10958   for (i=0; i<param_size ; i++)
10959     {
10960     status = (char) nc_out->param[i];
10961     printf("%02X ", status);
10962     if (i%8 == 7)
10963       printf("\n");
10964     }
10965 */
10966   /* return SS_EXIT if RPC_EXIT is called */
10967   if (routine_id == RPC_ID_EXIT)
10968     return SS_EXIT;
10969 
10970   /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
10971   if (routine_id == RPC_ID_SHUTDOWN)
10972     return RPC_SHUTDOWN;
10973   return RPC_SUCCESS;
10974 }
10975 
10976 
10977 /********************************************************************/
10978 INT rpc_execute_ascii(INT sock, char *buffer)
10979 /********************************************************************\
10980 
10981   Routine: rpc_execute_ascii
10982 
10983   Purpose: Execute a RPC command received over the network in ASCII
10984            mode
10985 
10986   Input:
10987     INT  sock               TCP socket to which the result should be
10988                             send back
10989 
10990     char *buffer            Command buffer
10991 
10992   Output:
10993     none
10994 
10995   Function value:
10996     RPC_SUCCESS             Successful completion
10997     RPC_INVALID_ID          Invalid routine_id received
10998     RPC_NET_ERROR           Error in socket call
10999     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11000     RPC_SHUTDOWN            Shutdown requested
11001     SS_ABORT                TCP connection broken
11002     SS_EXIT                 TCP connection closed
11003 
11004 \********************************************************************/
11005 {
11006 
11007 #define ASCII_BUFFER_SIZE 64500
11008 #define N_APARAM           1024
11009   INT i, j, index, status, index_in;
11010   char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11011   INT routine_id, tid, flags, array_tid, n_param;
11012   INT param_size, item_size, num_values;
11013   void *prpc_param[20];
11014   char *arpc_param[N_APARAM], *pc;
11015   char str[1024], debug_line[1024];
11016   char buffer1[ASCII_BUFFER_SIZE];      /* binary in */
11017   char buffer2[ASCII_BUFFER_SIZE];      /* binary out */
11018   char return_buffer[ASCII_BUFFER_SIZE];        /* ASCII out */
11019 
11020   /* parse arguments */
11021   arpc_param[0] = buffer;
11022   for (i = 1; i < N_APARAM; i++) {
11023     arpc_param[i] = strchr(arpc_param[i - 1], '&');
11024     if (arpc_param[i] == NULL)
11025       break;
11026     *arpc_param[i] = 0;
11027     arpc_param[i]++;
11028   }
11029 
11030   /* decode '%' */
11031   for (i = 0; i < N_APARAM && arpc_param[i]; i++)
11032     while ((pc = strchr(arpc_param[i], '%')) != NULL) {
11033       if (isxdigit(pc[1]) && isxdigit(pc[2])) {
11034         str[0] = pc[1];
11035         str[1] = pc[2];
11036         str[2] = 0;
11037         sscanf(str, "%02X", &i);
11038         *pc++ = i;
11039         while (pc[2]) {
11040           pc[0] = pc[2];
11041           pc++;
11042         }
11043       }
11044     }
11045 
11046   /* find entry in rpc_list */
11047   for (i = 0;; i++)
11048     if (rpc_list[i].id == 0
11049         || strcmp(arpc_param[0], rpc_list[i].name) == 0)
11050       break;
11051   index = i;
11052   routine_id = rpc_list[i].id;
11053   if (rpc_list[i].id == 0) {
11054     cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
11055     return RPC_INVALID_ID;
11056   }
11057   in_param_ptr = buffer1;
11058   out_param_ptr = buffer2;
11059   index_in = 1;
11060   if (_debug_print)
11061     sprintf(debug_line, "%s(", rpc_list[index].name);
11062   for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11063     tid = rpc_list[index].param[i].tid;
11064     flags = rpc_list[index].param[i].flags;
11065     if (flags & RPC_IN) {
11066       if (flags & RPC_VARARRAY) {
11067         sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
11068         prpc_param[i] = in_param_ptr;
11069         for (j = 0; j < n_param; j++) {
11070           db_sscanf(arpc_param[index_in++],
11071                     in_param_ptr, &param_size, 0, array_tid);
11072           in_param_ptr += param_size;
11073         }
11074         in_param_ptr = (char *) ALIGN(((PTYPE) in_param_ptr));
11075         if (_debug_print)
11076           strcat(debug_line, "<array>");
11077       }
11078 
11079       else {
11080         db_sscanf(arpc_param[index_in++], in_param_ptr,
11081                   &param_size, 0, tid);
11082         param_size = ALIGN(param_size);
11083         if (tid == TID_STRING || tid == TID_LINK)
11084           param_size = ALIGN(1 + strlen((char *) (in_param_ptr)));
11085 
11086         /*
11087            if (tid == TID_STRUCT)
11088            param_size = ALIGN( rpc_list[index].param[i].n );
11089          */
11090         prpc_param[i] = in_param_ptr;
11091         if (_debug_print) {
11092           db_sprintf(str, in_param_ptr, param_size, 0,
11093                      rpc_list[index].param[i].tid);
11094           if (rpc_list[index].param[i].tid == TID_STRING) {
11095 
11096             /* check for long strings (db_create_record...) */
11097             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11098               strcat(debug_line, "\"");
11099               strcat(debug_line, str);
11100               strcat(debug_line, "\"");
11101             }
11102 
11103             else
11104               strcat(debug_line, "...");
11105           }
11106 
11107           else
11108             strcat(debug_line, str);
11109         }
11110         in_param_ptr += param_size;
11111       }
11112       if ((PTYPE) in_param_ptr - (PTYPE) buffer1 > ASCII_BUFFER_SIZE) {
11113         cm_msg(MERROR, "rpc_ascii_execute",
11114                "parameters (%d) too large for network buffer (%d)",
11115                param_size, ASCII_BUFFER_SIZE);
11116         return RPC_EXCEED_BUFFER;
11117       }
11118     }
11119     if (flags & RPC_OUT) {
11120       param_size = ALIGN(tid_size[tid]);
11121       if (flags & RPC_VARARRAY || tid == TID_STRING) {
11122 
11123         /* reserve maximum array length */
11124         param_size = atoi(arpc_param[index_in]);
11125         param_size = ALIGN(param_size);
11126       }
11127 
11128 /*
11129       if (rpc_list[index].param[i].tid == TID_STRUCT)
11130         param_size = ALIGN( rpc_list[index].param[i].n );
11131 */
11132       if ((PTYPE) out_param_ptr - (PTYPE) buffer2 +
11133           param_size > ASCII_BUFFER_SIZE) {
11134         cm_msg(MERROR, "rpc_execute",
11135                "return parameters (%d) too large for network buffer (%d)",
11136                (PTYPE) out_param_ptr - (PTYPE) buffer2 +
11137                param_size, ASCII_BUFFER_SIZE);
11138         return RPC_EXCEED_BUFFER;
11139       }
11140 
11141       /* if parameter goes both directions, copy input to output */
11142       if (rpc_list[index].param[i].flags & RPC_IN)
11143         memcpy(out_param_ptr, prpc_param[i], param_size);
11144       if (_debug_print && !(flags & RPC_IN))
11145         strcat(debug_line, "-");
11146       prpc_param[i] = out_param_ptr;
11147       out_param_ptr += param_size;
11148     }
11149     if (_debug_print)
11150       if (rpc_list[index].param[i + 1].tid)
11151         strcat(debug_line, ", ");
11152   }
11153   if (_debug_print) {
11154     strcat(debug_line, ")");
11155     _debug_print(debug_line);
11156   }
11157   last_param_ptr = out_param_ptr;
11158 
11159   /*********************************\
11160   *   call dispatch function        *
11161   \*********************************/
11162   if (rpc_list[index].dispatch)
11163     status = rpc_list[index].dispatch(routine_id, prpc_param);
11164 
11165   else
11166     status = RPC_INVALID_ID;
11167   if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN
11168       || routine_id == RPC_ID_WATCHDOG)
11169     status = RPC_SUCCESS;
11170 
11171   /* Return if TCP connection broken */
11172   if (status == SS_ABORT)
11173     return SS_ABORT;
11174 
11175   /* if sock == 0, we are in FTCP mode and may not sent results */
11176   if (!sock)
11177     return RPC_SUCCESS;
11178 
11179   /* send return status */
11180   out_param_ptr = return_buffer;
11181   sprintf(out_param_ptr, "%d", status);
11182   out_param_ptr += strlen(out_param_ptr);
11183 
11184   /* convert return parameters */
11185   for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11186     if (rpc_list[index].param[i].flags & RPC_OUT) {
11187       *out_param_ptr++ = '&';
11188       tid = rpc_list[index].param[i].tid;
11189       flags = rpc_list[index].param[i].flags;
11190       param_size = ALIGN(tid_size[tid]);
11191       if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11192         strcpy(out_param_ptr, (char *) prpc_param[i]);
11193         param_size = strlen((char *) prpc_param[i]);
11194       }
11195 
11196       else if (flags & RPC_VARARRAY) {
11197         if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
11198           param_size = *((INT *) prpc_param[i + 1]);
11199 
11200           /* write number of bytes to output */
11201           sprintf(out_param_ptr, "%d", param_size);
11202           out_param_ptr += strlen(out_param_ptr) + 1;   /* '0' finishes param */
11203           memcpy(out_param_ptr, prpc_param[i], param_size);
11204           out_param_ptr += param_size;
11205           *out_param_ptr = 0;
11206         }
11207 
11208         else {
11209           if (rpc_list[index].id == RPC_DB_GET_DATA1) {
11210             param_size = *((INT *) prpc_param[i + 1]);
11211             array_tid = *((INT *) prpc_param[i + 2]);
11212             num_values = *((INT *) prpc_param[i + 3]);
11213           }
11214 
11215           else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
11216             param_size = *((INT *) prpc_param[i + 1]);
11217             array_tid = *((INT *) prpc_param[i + 3]);
11218             num_values = 1;
11219           }
11220 
11221           else if (rpc_list[index].id == RPC_HS_READ) {
11222             param_size = *((INT *) prpc_param[i + 1]);
11223             if (i == 6) {
11224               array_tid = TID_DWORD;
11225               num_values = param_size / sizeof(DWORD);
11226             }
11227 
11228             else {
11229               array_tid = *((INT *) prpc_param[10]);
11230               num_values = *((INT *) prpc_param[11]);
11231             }
11232           }
11233 
11234           else {                /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11235 
11236             param_size = *((INT *) prpc_param[i + 1]);
11237             array_tid = tid;
11238             if (tid == TID_STRING)
11239               num_values = param_size / NAME_LENGTH;
11240 
11241             else
11242               num_values = param_size / tid_size[tid];
11243           }
11244 
11245           /* derive size of individual item */
11246           if (array_tid == TID_STRING)
11247             item_size = param_size / num_values;
11248 
11249           else
11250             item_size = tid_size[array_tid];
11251 
11252           /* write number of elements to output */
11253           sprintf(out_param_ptr, "%d", num_values);
11254           out_param_ptr += strlen(out_param_ptr);
11255 
11256           /* write array of values to output */
11257           for (j = 0; j < num_values; j++) {
11258             *out_param_ptr++ = '&';
11259             db_sprintf(out_param_ptr,
11260                        prpc_param[i], item_size, j, array_tid);
11261             out_param_ptr += strlen(out_param_ptr);
11262           }
11263         }
11264       }
11265 
11266 /*
11267       else if (tid == TID_STRUCT)
11268         param_size = ALIGN( rpc_list[index].param[i].n );
11269 */
11270       else
11271         db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11272       out_param_ptr += strlen(out_param_ptr);
11273       if ((PTYPE) out_param_ptr - (PTYPE) return_buffer >
11274           ASCII_BUFFER_SIZE) {
11275         cm_msg(MERROR, "rpc_execute",
11276                "return parameter (%d) too large for network buffer (%d)",
11277                param_size, ASCII_BUFFER_SIZE);
11278         return RPC_EXCEED_BUFFER;
11279       }
11280     }
11281 
11282   /* send return parameters */
11283   param_size = (PTYPE) out_param_ptr - (PTYPE) return_buffer + 1;
11284   status = send_tcp(sock, return_buffer, param_size, 0);
11285   if (status < 0) {
11286     cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11287     return RPC_NET_ERROR;
11288   }
11289 
11290   /* print return buffer */
11291   if (_debug_print) {
11292     if (strlen(return_buffer) > sizeof(debug_line)) {
11293       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11294       strcat(debug_line, "...");
11295     }
11296 
11297     else
11298       sprintf(debug_line, "-> %s", return_buffer);
11299     _debug_print(debug_line);
11300   }
11301 
11302   /* return SS_EXIT if RPC_EXIT is called */
11303   if (routine_id == RPC_ID_EXIT)
11304     return SS_EXIT;
11305 
11306   /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11307   if (routine_id == RPC_ID_SHUTDOWN)
11308     return RPC_SHUTDOWN;
11309   return RPC_SUCCESS;
11310 }
11311 
11312 
11313 /********************************************************************/
11314 INT rpc_server_accept(int lsock)
11315 /********************************************************************\
11316 
11317   Routine: rpc_server_accept
11318 
11319   Purpose: Accept new incoming connections
11320 
11321   Input:
11322     INT    lscok            Listen socket
11323 
11324   Output:
11325     none
11326 
11327   Function value:
11328     RPC_SUCCESS             Successful completion
11329     RPC_NET_ERROR           Error in socket call
11330     RPC_CONNCLOSED          Connection was closed
11331     RPC_SHUTDOWN            Listener shutdown
11332     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11333 
11334 \********************************************************************/
11335 {
11336   INT index, i;
11337   int size, status;
11338   char command, version[32], v1[32];
11339   INT sock, port1, port2, port3;
11340   struct sockaddr_in acc_addr;
11341   struct hostent *phe;
11342   char str[100];
11343   char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11344   char debug_str[30];
11345   char *argv[10];
11346   char net_buffer[256];
11347   struct linger ling;
11348   static struct callback_addr callback;
11349 
11350   if (lsock > 0) {
11351     size = sizeof(acc_addr);
11352     sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11353     if (sock == -1)
11354       return RPC_NET_ERROR;
11355   }
11356 
11357   else {
11358 
11359     /* lsock is stdin -> already connected from inetd */
11360     size = sizeof(acc_addr);
11361     sock = lsock;
11362     getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11363   }
11364   /* receive string with timeout */
11365   i = recv_string(sock, net_buffer, 256, 10000);
11366   if (i > 0) {
11367     command = (char) toupper(net_buffer[0]);
11368     switch (command) {
11369     case 'S':
11370                 /*----------- shutdown listener ----------------------*/
11371       closesocket(sock);
11372       return RPC_SHUTDOWN;
11373     case 7:
11374       ss_shell(sock);
11375       closesocket(sock);
11376       break;
11377     case 'I':
11378                 /*----------- return available experiments -----------*/
11379       cm_scan_experiments();
11380       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11381         sprintf(str, "%s", exptab[i].name);
11382         send(sock, str, strlen(str) + 1, 0);
11383       }
11384       send(sock, "", 1, 0);
11385       closesocket(sock);
11386       break;
11387     case 'C':
11388                 /*----------- connect to experiment -----------*/
11389 
11390       /* get callback information */
11391       callback.experiment[0] = 0;
11392       port1 = port2 = version[0] = 0;
11393       sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2,
11394              &port3, version);
11395       strcpy(callback.experiment,
11396              strchr(strchr
11397                     (strchr
11398                      (strchr(net_buffer + 2, ' ') + 1,
11399                       ' ') + 1, ' ') + 1, ' ') + 1);
11400 
11401       /* print warning if version patch level doesn't agree */
11402       strcpy(v1, version);
11403       if (strchr(v1, '.'))
11404         if (strchr(strchr(v1, '.') + 1, '.'))
11405           *strchr(strchr(v1, '.') + 1, '.') = 0;
11406       strcpy(str, cm_get_version());
11407       if (strchr(str, '.'))
11408         if (strchr(strchr(str, '.') + 1, '.'))
11409           *strchr(strchr(str, '.') + 1, '.') = 0;
11410       if (strcmp(v1, str) != 0) {
11411         sprintf(str,
11412                 "client MIDAS version %s differs from local version %s",
11413                 version, cm_get_version());
11414         cm_msg(MERROR, "rpc_server_accept", str);
11415         sprintf(str, "received string: %s", net_buffer + 2);
11416         cm_msg(MERROR, "rpc_server_accept", str);
11417       }
11418       callback.host_port1 = (short) port1;
11419       callback.host_port2 = (short) port2;
11420       callback.host_port3 = (short) port3;
11421       callback.debug = _debug_mode;
11422 
11423       /* get the name of the remote host */
11424 #ifdef OS_VXWORKS
11425       {
11426         INT status;
11427 
11428         status =
11429             hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11430         if (status != 0) {
11431           cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11432           break;
11433         }
11434       }
11435 
11436 #else                           /*  */
11437       phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11438       if (phe == NULL) {
11439 
11440         /* use IP number instead */
11441         strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11442       }
11443 
11444       else
11445         strcpy(callback.host_name, phe->h_name);
11446 
11447 #endif                          /*  */
11448       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11449 
11450         /* update experiment definition */
11451         cm_scan_experiments();
11452 
11453         /* lookup experiment */
11454         if (equal_ustring(callback.experiment, "Default"))
11455           index = 0;
11456 
11457         else
11458           for (index = 0;
11459                index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11460             if (equal_ustring(callback.experiment, exptab[index].name))
11461               break;
11462         if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11463           sprintf(str,
11464                   "experiment %s not defined in exptab\r",
11465                   callback.experiment);
11466           cm_msg(MERROR, "rpc_server_accept", str);
11467           send(sock, "2", 2, 0);        /* 2 means exp. not found */
11468           closesocket(sock);
11469           break;
11470         }
11471         strcpy(callback.directory, exptab[index].directory);
11472         strcpy(callback.user, exptab[index].user);
11473 
11474         /* create a new process */
11475         sprintf(host_port1_str, "%d", callback.host_port1);
11476         sprintf(host_port2_str, "%d", callback.host_port2);
11477         sprintf(host_port3_str, "%d", callback.host_port3);
11478         sprintf(debug_str, "%d", callback.debug);
11479         argv[0] = (char *)
11480             rpc_get_server_option(RPC_OSERVER_NAME);
11481         argv[1] = callback.host_name;
11482         argv[2] = host_port1_str;
11483         argv[3] = host_port2_str;
11484         argv[4] = host_port3_str;
11485         argv[5] = debug_str;
11486         argv[6] = callback.experiment;
11487         argv[7] = callback.directory;
11488         argv[8] = callback.user;
11489         argv[9] = NULL;
11490 
11491 /*
11492           cm_msg(MINFO, "", "%s %s %s %s %s %s %s %s %s %s",
11493             argv[0], argv[1], argv[2], argv[3], argv[4],
11494             argv[5], argv[6], argv[7], argv[8], argv[9]);
11495 */
11496         status = ss_spawnv(P_NOWAIT, (char *)
11497                            rpc_get_server_option(RPC_OSERVER_NAME), argv);
11498         if (status != SS_SUCCESS) {
11499           cm_msg(MERROR, "rpc_server_accept", "cannot spawn subprocess");
11500           sprintf(str, "3");    /* 3 means cannot spawn subprocess */
11501           send(sock, str, strlen(str) + 1, 0);
11502           closesocket(sock);
11503           break;
11504         }
11505         sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
11506         send(sock, str, strlen(str) + 1, 0);
11507         closesocket(sock);
11508       }
11509 
11510       else {
11511         sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
11512         send(sock, str, strlen(str) + 1, 0);
11513         closesocket(sock);
11514       }
11515 
11516       /* look for next free entry */
11517       for (index = 0; index < MAX_RPC_CONNECTION; index++)
11518         if (_server_acception[index].recv_sock == 0)
11519           break;
11520       if (index == MAX_RPC_CONNECTION)
11521         return RPC_NET_ERROR;
11522       callback.index = index;
11523 
11524         /*----- multi thread server ------------------------*/
11525       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11526         ss_thread_create(rpc_server_thread, (void *) (&callback));
11527 
11528         /*----- single thread server -----------------------*/
11529       if (rpc_get_server_option(RPC_OSERVER_TYPE) ==
11530           ST_SINGLE
11531           || rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11532         rpc_server_callback(&callback);
11533       break;
11534     default:
11535       cm_msg(MERROR, "rpc_server_accept",
11536              "received unknown command '%c'", command);
11537       closesocket(sock);
11538       break;
11539     }
11540   }
11541 
11542   else {                        /* if i>0 */
11543 
11544 
11545     /* lingering needed for PCTCP */
11546     ling.l_onoff = 1;
11547     ling.l_linger = 0;
11548     setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11549     closesocket(sock);
11550   }
11551   return RPC_SUCCESS;
11552 }
11553 
11554 
11555 /********************************************************************/
11556 INT rpc_client_accept(int lsock)
11557 /********************************************************************\
11558 
11559   Routine: rpc_client_accept
11560 
11561   Purpose: Accept new incoming connections as a client
11562 
11563   Input:
11564     INT    lsock            Listen socket
11565 
11566   Output:
11567     none
11568 
11569   Function value:
11570     RPC_SUCCESS             Successful completion
11571     RPC_NET_ERROR           Error in socket call
11572     RPC_CONNCLOSED          Connection was closed
11573     RPC_SHUTDOWN            Listener shutdown
11574     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11575 
11576 \********************************************************************/
11577 {
11578   INT index, i, version, status;
11579   int size, sock;
11580   struct sockaddr_in acc_addr;
11581   INT client_hw_type = 0, hw_type;
11582   char str[100], client_program[NAME_LENGTH];
11583   char host_name[HOST_NAME_LENGTH];
11584   INT convert_flags;
11585   char net_buffer[256], *p;
11586 
11587   size = sizeof(acc_addr);
11588   sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11589   if (sock == -1)
11590     return RPC_NET_ERROR;
11591 
11592   /* get the name of the calling host */
11593 /* outcommented for speed reasons SR 7.10.98
11594 #ifdef OS_VXWORKS
11595   {
11596   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11597   if (status != 0)
11598     strcpy(host_name, "unknown");
11599   }
11600 #else
11601   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11602   if (phe == NULL)
11603     strcpy(host_name, "unknown");
11604   strcpy(host_name, phe->h_name);
11605 #endif
11606 */
11607   strcpy(host_name, "");
11608 
11609   /* look for next free entry */
11610   for (index = 0; index < MAX_RPC_CONNECTION; index++)
11611     if (_server_acception[index].recv_sock == 0)
11612       break;
11613   if (index == MAX_RPC_CONNECTION) {
11614     closesocket(sock);
11615     return RPC_NET_ERROR;
11616   }
11617 
11618   /* receive string with timeout */
11619   i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11620   if (i <= 0) {
11621     closesocket(sock);
11622     return RPC_NET_ERROR;
11623   }
11624 
11625   /* get remote computer info */
11626   p = strtok(net_buffer, " ");
11627   if (p != NULL) {
11628     client_hw_type = atoi(p);
11629     p = strtok(NULL, " ");
11630   }
11631   if (p != NULL) {
11632     version = atoi(p);
11633     p = strtok(NULL, " ");
11634   }
11635   if (p != NULL) {
11636     strcpy(client_program, p);
11637     p = strtok(NULL, " ");
11638   }
11639   if (p != NULL) {
11640     strcpy(host_name, p);
11641     p = strtok(NULL, " ");
11642   }
11643 
11644   /* save information in _server_acception structure */
11645   _server_acception[index].recv_sock = sock;
11646   _server_acception[index].send_sock = 0;
11647   _server_acception[index].event_sock = 0;
11648   _server_acception[index].remote_hw_type = client_hw_type;
11649   strcpy(_server_acception[index].host_name, host_name);
11650   strcpy(_server_acception[index].prog_name, client_program);
11651   _server_acception[index].tid = ss_gettid();
11652   _server_acception[index].last_activity = ss_millitime();
11653   _server_acception[index].watchdog_timeout = 0;
11654 
11655   /* send my own computer id */
11656   hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11657   sprintf(str, "%d %s", hw_type, cm_get_version());
11658   status = send(sock, str, strlen(str) + 1, 0);
11659   if (status != (INT) strlen(str) + 1)
11660     return RPC_NET_ERROR;
11661   rpc_set_server_acception(index + 1);
11662   rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11663   rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11664 
11665   /* set callback function for ss_suspend */
11666   ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11667                           (int (*)(void)) rpc_server_receive);
11668   return RPC_SUCCESS;
11669 }
11670 
11671 
11672 /********************************************************************/
11673 INT rpc_server_callback(struct callback_addr * pcallback)
11674 /********************************************************************\
11675 
11676   Routine: rpc_server_callback
11677 
11678   Purpose: Callback a remote client. Setup _server_acception entry
11679            with optional conversion flags and establish two-way
11680            TCP connection.
11681 
11682   Input:
11683     callback_addr pcallback Pointer to a callback structure
11684 
11685   Output:
11686     none
11687 
11688   Function value:
11689     RPC_SUCCESS             Successful completion
11690 
11691 \********************************************************************/
11692 {
11693   INT index, status;
11694   int recv_sock, send_sock, event_sock;
11695   struct sockaddr_in bind_addr;
11696   struct hostent *phe;
11697   char str[100], client_program[NAME_LENGTH];
11698   char host_name[HOST_NAME_LENGTH];
11699   INT client_hw_type, hw_type;
11700   INT convert_flags;
11701   char net_buffer[256];
11702   struct callback_addr callback;
11703   int flag;
11704 
11705   /* copy callback information */
11706   memcpy(&callback, pcallback, sizeof(callback));
11707   index = callback.index;
11708 
11709   /* create new sockets for TCP */
11710   recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11711   send_sock = socket(AF_INET, SOCK_STREAM, 0);
11712   event_sock = socket(AF_INET, SOCK_STREAM, 0);
11713   if (event_sock == -1)
11714     return RPC_NET_ERROR;
11715 
11716   /* callback to remote node */
11717   memset(&bind_addr, 0, sizeof(bind_addr));
11718   bind_addr.sin_family = AF_INET;
11719   bind_addr.sin_port = htons(callback.host_port1);
11720 
11721 #ifdef OS_VXWORKS
11722   {
11723     INT host_addr;
11724 
11725     host_addr = hostGetByName(callback.host_name);
11726     memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11727   }
11728 #else                           /*  */
11729   phe = gethostbyname(callback.host_name);
11730   if (phe == NULL) {
11731     cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11732     return RPC_NET_ERROR;
11733   }
11734   memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11735 
11736 #endif                          /*  */
11737 
11738   /* connect receive socket */
11739 #ifdef OS_UNIX
11740   do {
11741     status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11742 
11743     /* don't return if an alarm signal was cought */
11744   } while (status == -1 && errno == EINTR);
11745 
11746 #else                           /*  */
11747   status =
11748       connect(recv_sock, (struct sockaddr *) &bind_addr,
11749               sizeof(bind_addr));
11750 
11751 #endif                          /*  */
11752   if (status != 0) {
11753     cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11754     goto error;
11755   }
11756   bind_addr.sin_port = htons(callback.host_port2);
11757 
11758   /* connect send socket */
11759 #ifdef OS_UNIX
11760   do {
11761     status =
11762         connect(send_sock, (struct sockaddr *) &bind_addr,
11763                 sizeof(bind_addr));
11764 
11765     /* don't return if an alarm signal was cought */
11766   } while (status == -1 && errno == EINTR);
11767 
11768 #else                           /*  */
11769   status =
11770       connect(send_sock, (struct sockaddr *) &bind_addr,
11771               sizeof(bind_addr));
11772 
11773 #endif                          /*  */
11774   if (status != 0) {
11775     cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11776     goto error;
11777   }
11778   bind_addr.sin_port = htons(callback.host_port3);
11779 
11780   /* connect event socket */
11781 #ifdef OS_UNIX
11782   do {
11783     status =
11784         connect(event_sock, (struct sockaddr *) &bind_addr,
11785                 sizeof(bind_addr));
11786 
11787     /* don't return if an alarm signal was cought */
11788   } while (status == -1 && errno == EINTR);
11789 
11790 #else                           /*  */
11791   status =
11792       connect(event_sock, (struct sockaddr *) &bind_addr,
11793               sizeof(bind_addr));
11794 
11795 #endif                          /*  */
11796   if (status != 0) {
11797     cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11798     goto error;
11799   }
11800 
11801   /* increase receive buffer size to 64k */
11802 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11803   flag = 0x10000;
11804   setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag,
11805              sizeof(INT));
11806 
11807 #endif                          /*  */
11808   if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11809     cm_msg(MERROR, "rpc_server_callback",
11810            "timeout on receive remote computer info");
11811     goto error;
11812   }
11813 
11814   /* get remote computer info */
11815   sscanf(net_buffer, "%d", &client_hw_type);
11816   strcpy(client_program, strchr(net_buffer, ' ') + 1);
11817 
11818   /* get the name of the remote host */
11819 #ifdef OS_VXWORKS
11820   status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11821   if (status != 0)
11822     strcpy(host_name, "unknown");
11823 
11824 #else                           /*  */
11825   phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11826   if (phe == NULL)
11827     strcpy(host_name, "unknown");
11828 
11829   else
11830     strcpy(host_name, phe->h_name);
11831 
11832 #endif                          /*  */
11833 
11834   /* save information in _server_acception structure */
11835   _server_acception[index].recv_sock = recv_sock;
11836   _server_acception[index].send_sock = send_sock;
11837   _server_acception[index].event_sock = event_sock;
11838   _server_acception[index].remote_hw_type = client_hw_type;
11839   strcpy(_server_acception[index].host_name, host_name);
11840   strcpy(_server_acception[index].prog_name, client_program);
11841   _server_acception[index].tid = ss_gettid();
11842   _server_acception[index].last_activity = ss_millitime();
11843   _server_acception[index].watchdog_timeout = 0;
11844 
11845   /* send my own computer id */
11846   hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11847   sprintf(str, "%d", hw_type);
11848   send(recv_sock, str, strlen(str) + 1, 0);
11849   rpc_set_server_acception(index + 1);
11850   rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11851   rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11852 
11853   /* set callback function for ss_suspend */
11854   ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11855                           (int (*)(void)) rpc_server_receive);
11856   if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11857     printf("Connection to %s:%s established\n",
11858            _server_acception[index].host_name,
11859            _server_acception[index].prog_name);
11860   return RPC_SUCCESS;
11861 error:closesocket(recv_sock);
11862   closesocket(send_sock);
11863   closesocket(event_sock);
11864   return RPC_NET_ERROR;
11865 }
11866 
11867 
11868 /********************************************************************/
11869 INT rpc_server_thread(void *pointer)
11870 /********************************************************************\
11871 
11872   Routine: rpc_server_thread
11873 
11874   Purpose: New thread for a multi-threaded server. Callback to the
11875            client and process RPC requests.
11876 
11877   Input:
11878     vcoid  pointer          pointer to callback_addr structure.
11879 
11880   Output:
11881     none
11882 
11883   Function value:
11884     RPC_SUCCESS             Successful completion
11885 
11886 \********************************************************************/
11887 {
11888   struct callback_addr callback;
11889   int status, mutex_alarm, mutex_elog;
11890   static DWORD last_checked = 0;
11891 
11892   memcpy(&callback, pointer, sizeof(callback));
11893   status = rpc_server_callback(&callback);
11894   if (status != RPC_SUCCESS)
11895     return status;
11896 
11897   /* create alarm and elog mutexes */
11898   ss_mutex_create("ALARM", &mutex_alarm);
11899   ss_mutex_create("ELOG", &mutex_elog);
11900   cm_set_experiment_mutex(mutex_alarm, mutex_elog);
11901 
11902   do {
11903     status = ss_suspend(5000, 0);
11904     if (rpc_check_channels() == RPC_NET_ERROR)
11905       break;
11906 
11907     /* check alarms every 10 seconds */
11908     if (!rpc_is_remote() && ss_time() - last_checked > 10) {
11909       al_check();
11910       last_checked = ss_time();
11911     }
11912   } while (status != SS_ABORT && status != SS_EXIT);
11913 
11914   /* delete entry in suspend table for this thread */
11915   ss_suspend_exit();
11916   return RPC_SUCCESS;
11917 }
11918 
11919 
11920 /********************************************************************/
11921 INT rpc_server_receive(INT index, int sock, BOOL check)
11922 /********************************************************************\
11923 
11924   Routine: rpc_server_receive
11925 
11926   Purpose: Receive rpc commands and execute them. Close the connection
11927            if client has broken TCP pipe.
11928 
11929   Input:
11930     INT    index            Index to _server_acception structure in-
11931                             dicating which connection got data.
11932     int    sock             Socket which got data
11933     BOOL   check            If TRUE, only check if connection is
11934                             broken. This may be called via
11935                             bm_receive_event/ss_suspend(..,MSG_BM)
11936 
11937   Output:
11938     none
11939 
11940   Function value:
11941     RPC_SUCCESS             Successful completion
11942     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
11943     SS_EXIT                 Server connection was closed
11944     SS_ABORT                Server connection was broken
11945 
11946 \********************************************************************/
11947 {
11948   INT status, n_received;
11949   INT remaining, *pbh, start_time;
11950   char test_buffer[256], str[80];
11951   EVENT_HEADER *pevent;
11952 
11953   /* init network buffer */
11954   if (_net_recv_buffer_size == 0) {
11955     _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
11956     if (_net_recv_buffer == NULL) {
11957       cm_msg(MERROR, "rpc_server_receive",
11958              "not enough memory to allocate network buffer");
11959       return RPC_EXCEED_BUFFER;
11960     }
11961     _net_recv_buffer_size = NET_BUFFER_SIZE;
11962   }
11963 
11964   /* only check if TCP connection is broken */
11965   if (check) {
11966     n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
11967     if (n_received == -1)
11968       cm_msg(MERROR, "rpc_server_receive",
11969              "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)",
11970              sizeof(test_buffer), n_received, errno, strerror(errno));
11971     if (n_received <= 0)
11972       return SS_ABORT;
11973     return SS_SUCCESS;
11974   }
11975   remaining = 0;
11976 
11977   /* receive command */
11978   if (sock == _server_acception[index].recv_sock) {
11979 
11980     do {
11981       if (_server_acception[index].remote_hw_type == DR_ASCII)
11982         n_received =
11983             recv_string(_server_acception[index].recv_sock,
11984                         _net_recv_buffer, NET_BUFFER_SIZE, 10000);
11985 
11986       else
11987         n_received =
11988             recv_tcp_server(index, _net_recv_buffer,
11989                             NET_BUFFER_SIZE, 0, &remaining);
11990       if (n_received <= 0) {
11991         status = SS_ABORT;
11992         cm_msg(MERROR, "rpc_server_receive",
11993                "recv_tcp_server() returned %d, abort", n_received);
11994         goto error;
11995       }
11996       rpc_set_server_acception(index + 1);
11997       if (_server_acception[index].remote_hw_type == DR_ASCII)
11998         status =
11999             rpc_execute_ascii(_server_acception[index].
12000                               recv_sock, _net_recv_buffer);
12001 
12002       else
12003         status =
12004             rpc_execute(_server_acception[index].recv_sock,
12005                         _net_recv_buffer,
12006                         _server_acception[index].convert_flags);
12007       if (status == SS_ABORT) {
12008         cm_msg(MERROR, "rpc_server_receive",
12009                "rpc_execute() returned %d, abort", status);
12010         goto error;
12011       }
12012       if (status == SS_EXIT || status == RPC_SHUTDOWN) {
12013         if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12014           printf("Connection to %s:%s closed\n",
12015                  _server_acception[index].host_name,
12016                  _server_acception[index].prog_name);
12017         goto exit;
12018       }
12019     } while (remaining);
12020   }
12021 
12022   else {
12023 
12024     /* receive event */
12025     if (sock == _server_acception[index].event_sock) {
12026       start_time = ss_millitime();
12027 
12028       do {
12029         n_received =
12030             recv_event_server(index, _net_recv_buffer,
12031                               NET_BUFFER_SIZE, 0, &remaining);
12032         if (n_received <= 0) {
12033           status = SS_ABORT;
12034           cm_msg(MERROR, "rpc_server_receive",
12035                  "recv_event_server() returned %d, abort", n_received);
12036           goto error;
12037         }
12038 
12039         /* send event to buffer */
12040         pbh = (INT *) _net_recv_buffer;
12041         pevent = (EVENT_HEADER *) (pbh + 1);
12042         status =
12043             bm_send_event(*pbh, pevent,
12044                           pevent->data_size + sizeof(EVENT_HEADER), SYNC);
12045         if (status != BM_SUCCESS)
12046           cm_msg(MERROR, "rpc_server_receive",
12047                  "bm_send_event() returned %d", status);
12048 
12049         /* repeat for maximum 0.5 sec */
12050       } while (ss_millitime() - start_time < 500 && remaining);
12051     }
12052   }
12053   return RPC_SUCCESS;
12054 error:strcpy(str, _server_acception[index].host_name);
12055   if (strchr(str, '.'))
12056     *strchr(str, '.') = 0;
12057   cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
12058          _server_acception[index].prog_name, str);
12059 exit:
12060   /* disconnect from experiment as MIDAS server */
12061   if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12062     HNDLE hDB, hKey;
12063 
12064     cm_get_experiment_database(&hDB, &hKey);
12065 
12066     /* only disconnect from experiment if previously connected.
12067        Necessary for pure RPC servers (RPC_SRVR) */
12068     if (hDB) {
12069 
12070 #ifdef LOCAL_ROUTINES
12071       ss_alarm(0, cm_watchdog);
12072 
12073 #endif                          /*  */
12074       cm_delete_client_info(hDB, 0);
12075       bm_close_all_buffers();
12076       db_close_all_databases();
12077       rpc_deregister_functions();
12078       cm_set_experiment_database(0, 0);
12079       _msg_buffer = 0;
12080     }
12081   }
12082 
12083   /* close server connection */
12084   if (_server_acception[index].recv_sock)
12085     closesocket(_server_acception[index].recv_sock);
12086   if (_server_acception[index].send_sock)
12087     closesocket(_server_acception[index].send_sock);
12088   if (_server_acception[index].event_sock)
12089     closesocket(_server_acception[index].event_sock);
12090 
12091   /* free TCP cache */
12092   M_FREE(_server_acception[index].net_buffer);
12093   _server_acception[index].net_buffer = NULL;
12094 
12095   /* mark this entry as invalid */
12096   memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12097 
12098   /* signal caller a shutdonw */
12099   if (status == RPC_SHUTDOWN)
12100     return status;
12101 
12102   /* don't abort if other than main connection is broken */
12103   if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12104     return SS_SUCCESS;
12105   return status;
12106 }
12107 
12108 
12109 /********************************************************************/
12110 INT rpc_server_shutdown(void)
12111 /********************************************************************\
12112 
12113   Routine: rpc_server_shutdown
12114 
12115   Purpose: Shutdown RPC server, abort all connections
12116 
12117   Input:
12118     none
12119 
12120   Output:
12121     none
12122 
12123   Function value:
12124     RPC_SUCCESS             Successful completion
12125 
12126 \********************************************************************/
12127 {
12128   INT i;
12129   struct linger ling;
12130 
12131   /* close all open connections */
12132   for (i = 0; i < MAX_RPC_CONNECTION; i++)
12133     if (_server_acception[i].recv_sock != 0) {
12134 
12135       /* lingering needed for PCTCP */
12136       ling.l_onoff = 1;
12137       ling.l_linger = 0;
12138       setsockopt(_server_acception[i].recv_sock, SOL_SOCKET,
12139                  SO_LINGER, (char *) &ling, sizeof(ling));
12140       closesocket(_server_acception[i].recv_sock);
12141       if (_server_acception[i].send_sock) {
12142         setsockopt(_server_acception[i].send_sock,
12143                    SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12144         closesocket(_server_acception[i].send_sock);
12145       }
12146       if (_server_acception[i].event_sock) {
12147         setsockopt(_server_acception[i].event_sock,
12148                    SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12149         closesocket(_server_acception[i].event_sock);
12150       }
12151       _server_acception[i].recv_sock = 0;
12152       _server_acception[i].send_sock = 0;
12153       _server_acception[i].event_sock = 0;
12154     }
12155   if (_lsock) {
12156     closesocket(_lsock);
12157     _lsock = 0;
12158     _server_registered = FALSE;
12159   }
12160 
12161   /* free suspend structures */
12162   ss_suspend_exit();
12163   return RPC_SUCCESS;
12164 }
12165 
12166 
12167 /********************************************************************/
12168 INT rpc_check_channels(void)
12169 /********************************************************************\
12170 
12171   Routine: rpc_check_channels
12172 
12173   Purpose: Check open rpc channels by sending watchdog messages
12174 
12175   Input:
12176     none
12177 
12178   Output:
12179     none
12180 
12181   Function value:
12182     RPC_SUCCESS             Channel is still alive
12183     RPC_NET_ERROR           Connection is broken
12184 
12185 \********************************************************************/
12186 {
12187   INT status, index, i, convert_flags;
12188   NET_COMMAND nc;
12189   fd_set readfds;
12190   struct timeval timeout;
12191 
12192   for (index = 0; index < MAX_RPC_CONNECTION; index++) {
12193     if (_server_acception[index].recv_sock
12194         && _server_acception[index].tid == ss_gettid()
12195         && _server_acception[index].watchdog_timeout
12196         && (ss_millitime() -
12197             _server_acception[index].last_activity >
12198             (DWORD) _server_acception[index].watchdog_timeout)) {
12199 
12200 /* printf("Send watchdog message to %s on %s\n",
12201                 _server_acception[index].prog_name,
12202                 _server_acception[index].host_name); */
12203 
12204       /* send a watchdog message */
12205       nc.header.routine_id = MSG_WATCHDOG;
12206       nc.header.param_size = 0;
12207       convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12208       if (convert_flags) {
12209         rpc_convert_single(&nc.header.routine_id,
12210                            TID_DWORD, RPC_OUTGOING, convert_flags);
12211         rpc_convert_single(&nc.header.param_size, TID_DWORD,
12212                            RPC_OUTGOING, convert_flags);
12213       }
12214 
12215       /* send the header to the client */
12216       i = send_tcp(_server_acception[index].send_sock,
12217                    (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12218       if (i < 0)
12219         goto exit;
12220 
12221       /* make some timeout checking */
12222       FD_ZERO(&readfds);
12223       FD_SET(_server_acception[index].send_sock, &readfds);
12224       FD_SET(_server_acception[index].recv_sock, &readfds);
12225       timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
12226       timeout.tv_usec =
12227           (_server_acception[index].watchdog_timeout % 1000) * 1000;
12228 
12229       do {
12230         status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12231 
12232         /* if an alarm signal was cought, restart select with reduced timeout */
12233         if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12234           timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12235       } while (status == -1);   /* dont return if an alarm signal was cought */
12236       if (!FD_ISSET(_server_acception[index].send_sock, &readfds)
12237           && !FD_ISSET(_server_acception[index].recv_sock, &readfds))
12238         goto exit;
12239 
12240       /* receive result on send socket */
12241       if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
12242         i = recv_tcp(_server_acception[index].send_sock,
12243                      (char *) &nc, sizeof(nc), 0);
12244         if (i <= 0)
12245           goto exit;
12246       }
12247     }
12248   }
12249   return RPC_SUCCESS;
12250 exit:cm_msg(MINFO, "rpc_check_channels",
12251          "client [%s]%s failed watchdog test after %d sec",
12252          _server_acception[index].host_name,
12253          _server_acception[index].prog_name,
12254          _server_acception[index].watchdog_timeout / 1000);
12255 
12256   /* disconnect from experiment */
12257   if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12258     cm_disconnect_experiment();
12259 
12260   /* close server connection */
12261   if (_server_acception[index].recv_sock)
12262     closesocket(_server_acception[index].recv_sock);
12263   if (_server_acception[index].send_sock)
12264     closesocket(_server_acception[index].send_sock);
12265   if (_server_acception[index].event_sock)
12266     closesocket(_server_acception[index].event_sock);
12267 
12268   /* free TCP cache */
12269   M_FREE(_server_acception[index].net_buffer);
12270   _server_acception[index].net_buffer = NULL;
12271 
12272   /* mark this entry as invalid */
12273   memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12274   return RPC_NET_ERROR;
12275 }
12276 
12277 
12278 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12279 
12280 /** @} */// end of rpcfunctionc
12281 
12282 /********************************************************************/
12283 /** @addtogroup bkfunctionc
12284  *  
12285  *  @{  */
12286 
12287 /********************************************************************\
12288 *                                                                    *
12289 *                 Bank functions                                     *
12290 *                                                                    *
12291 \********************************************************************/
12292 
12293 /********************************************************************/
12294 /**
12295 Initializes an event for Midas banks structure.
12296 Before banks can be created in an event, bk_init() has to be called first.
12297 @param event pointer to the area of event
12298 */
12299 void bk_init(void *event)
12300 {
12301   ((BANK_HEADER *) event)->data_size = 0;
12302   ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12303 }
12304 
12305 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12306 
12307 /********************************************************************/
12308 BOOL bk_is32(void *event)
12309 /********************************************************************\
12310 
12311   Routine: bk_is32
12312 
12313   Purpose: Return true if banks inside event are 32-bit banks
12314 
12315   Input:
12316     void   *event           pointer to the event
12317 
12318   Output:
12319     none
12320 
12321   Function value:
12322     none
12323 
12324 \********************************************************************/
12325 {
12326   return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12327 }
12328 
12329 
12330 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12331 
12332 /********************************************************************/
12333 /**
12334 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12335 Before banks can be created in an event, bk_init32() has to be called first.
12336 @param event pointer to the area of event
12337 @return void
12338 */
12339 void bk_init32(void *event)
12340 {
12341   ((BANK_HEADER *) event)->data_size = 0;
12342   ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12343 }
12344 
12345 /********************************************************************/
12346 /**
12347 Returns the size of an event containing banks.
12348 The total size of an event is the value returned by bk_size() plus the size
12349 of the event header (sizeof(EVENT_HEADER)).
12350 @param event pointer to the area of event
12351 @return number of bytes contained in data area of event
12352 */
12353 INT bk_size(void *event)
12354 {
12355   return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12356 }
12357 
12358 
12359 /********************************************************************/
12360 /**
12361 Create a Midas bank.
12362 The data pointer pdata must be used as an address to fill a
12363 bank. It is incremented with every value written to the bank and finally points
12364 to a location just after the last byte of the bank. It is then passed to
12365 the function bk_close() to finish the bank creation.
12366 \code
12367 INT *pdata;
12368 bk_init(pevent);
12369 bk_create(pevent, "ADC0", TID_INT, &pdata);
12370 *pdata++ = 123
12371 *pdata++ = 456
12372 bk_close(pevent, pdata);
12373 \endcode
12374 @param event pointer to the data area
12375 @param name of the bank, must be exactly 4 charaters
12376 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12377 midas.h
12378 @param pdata pointer to the data area of the newly created bank
12379 @return void
12380 */
12381 void bk_create(void *event, const char *name, WORD type, void *pdata)
12382 {
12383   if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12384     BANK32 *pbk32;
12385 
12386     pbk32 =
12387         (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12388                     ((BANK_HEADER *) event)->data_size);
12389     strncpy(pbk32->name, name, 4);
12390     pbk32->type = type;
12391     pbk32->data_size = 0;
12392     *((void **) pdata) = pbk32 + 1;
12393   }
12394 
12395   else {
12396     BANK *pbk;
12397 
12398     pbk =
12399         (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12400                   ((BANK_HEADER *) event)->data_size);
12401     strncpy(pbk->name, name, 4);
12402     pbk->type = type;
12403     pbk->data_size = 0;
12404     *((void **) pdata) = pbk + 1;
12405 }}
12406 
12407 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12408 
12409 /********************************************************************/
12410 int bk_delete(void *event, const char *name)
12411 /********************************************************************\
12412 
12413   Routine: bk_delete
12414 
12415   Purpose: Delete a MIDAS bank inside an event
12416 
12417   Input:
12418     void   *event           pointer to the event
12419     char   *name            Name of bank (exactly four letters)
12420 
12421   Function value:
12422     CM_SUCCESS              Bank has been deleted
12423     0                       Bank has not been found
12424 
12425 \********************************************************************/
12426 {
12427   BANK *pbk;
12428   BANK32 *pbk32;
12429   DWORD dname;
12430   int remaining;
12431 
12432   if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12433 
12434     /* locate bank */
12435     pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12436     strncpy((char *) &dname, name, 4);
12437 
12438     do {
12439       if (*((DWORD *) pbk32->name) == dname) {
12440 
12441         /* bank found, delete it */
12442         remaining =
12443             (int) ((char *) event +
12444                    ((BANK_HEADER *) event)->data_size +
12445                    sizeof(BANK_HEADER)) -
12446             (int) ((char *) (pbk32 + 1) + ALIGN(pbk32->data_size));
12447 
12448         /* reduce total event size */
12449         ((BANK_HEADER *) event)->data_size -=
12450             sizeof(BANK32) + ALIGN(pbk32->data_size);
12451 
12452         /* copy remaining bytes */
12453         if (remaining > 0)
12454           memcpy(pbk32,
12455                  (char *) (pbk32 + 1) +
12456                  ALIGN(pbk32->data_size), remaining);
12457         return CM_SUCCESS;
12458       }
12459       pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN(pbk32->data_size));
12460     } while ((DWORD) pbk32 - (DWORD) event <
12461              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12462   }
12463 
12464   else {
12465 
12466     /* locate bank */
12467     pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12468     strncpy((char *) &dname, name, 4);
12469 
12470     do {
12471       if (*((DWORD *) pbk->name) == dname) {
12472 
12473         /* bank found, delete it */
12474         remaining =
12475             (int) ((char *) event +
12476                    ((BANK_HEADER *) event)->data_size +
12477                    sizeof(BANK_HEADER)) -
12478             (int) ((char *) (pbk + 1) + ALIGN(pbk->data_size));
12479 
12480         /* reduce total event size */
12481         ((BANK_HEADER *) event)->data_size -=
12482             sizeof(BANK) + ALIGN(pbk->data_size);
12483 
12484         /* copy remaining bytes */
12485         if (remaining > 0)
12486           memcpy(pbk,
12487                  (char *) (pbk + 1) + ALIGN(pbk->data_size), remaining);
12488         return CM_SUCCESS;
12489       }
12490       pbk = (BANK *) ((char *) (pbk + 1) + ALIGN(pbk->data_size));
12491     } while ((DWORD) pbk - (DWORD) event <
12492              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12493   }
12494   return 0;
12495 }
12496 
12497 
12498 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12499 
12500 /********************************************************************/
12501 /**
12502 Close the Midas bank priviously created by bk_create().
12503 The data pointer pdata must be obtained by bk_create() and
12504 used as an address to fill a bank. It is incremented with every value written
12505 to the bank and finally points to a location just after the last byte of the
12506 bank. It is then passed to bk_close() to finish the bank creation
12507 @param event pointer to current composed event
12508 @param pdata  pointer to the data
12509 @return number of bytes contained in bank
12510 */
12511 INT bk_close(void *event, void *pdata)
12512 {
12513   if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12514     BANK32 *pbk32;
12515 
12516     pbk32 =
12517         (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12518                     ((BANK_HEADER *) event)->data_size);
12519     pbk32->data_size = (DWORD) (INT) pdata - (INT) (pbk32 + 1);
12520     if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12521       printf("Warning: bank %c%c%c%c has zero size\n",
12522              pbk32->name[0], pbk32->name[1], pbk32->name[2],
12523              pbk32->name[3]);
12524     ((BANK_HEADER *) event)->data_size +=
12525         sizeof(BANK32) + ALIGN(pbk32->data_size);
12526     return pbk32->data_size;
12527   }
12528 
12529   else {
12530     BANK *pbk;
12531 
12532     pbk =
12533         (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12534                   ((BANK_HEADER *) event)->data_size);
12535     pbk->data_size = (WORD) (INT) pdata - (INT) (pbk + 1);
12536     if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12537       printf("Warning: bank %c%c%c%c has zero size\n",
12538              pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12539     ((BANK_HEADER *) event)->data_size +=
12540         sizeof(BANK) + ALIGN(pbk->data_size);
12541     return pbk->data_size;
12542   }
12543 }
12544 
12545 
12546 /********************************************************************/
12547 /**
12548 Extract the MIDAS bank name listing of an event.
12549 The bklist should be dimensioned with STRING_BANKLIST_MAX
12550 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12551 \code
12552 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12553 {
12554   INT    n_adc, nbanks;
12555   WORD   *pdata;
12556   char   banklist[STRING_BANKLIST_MAX];
12557 
12558   // Display # of banks and list of banks in the event
12559   nbanks = bk_list(pevent, banklist);
12560   printf("#banks:%d List:%s\n", nbanks, banklist);
12561 
12562   // look for ADC0 bank, return if not present
12563   n_adc = bk_locate(pevent, "ADC0", &pdata);
12564   ...
12565 }
12566 \endcode
12567 @param event pointer to current composed event
12568 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12569 @return number of bank found in this event.
12570 */
12571 INT bk_list(void *event, char *bklist)
12572 {                               /* Full event */
12573   INT nbk, size;
12574   BANK *pmbk = NULL;
12575   BANK32 *pmbk32 = NULL;
12576   char *pdata;
12577 
12578   /* compose bank list */
12579   bklist[0] = 0;
12580   nbk = 0;
12581 
12582   do {
12583 
12584     /* scan all banks for bank name only */
12585     if (bk_is32(event)) {
12586       size = bk_iterate32(event, &pmbk32, &pdata);
12587       if (pmbk32 == NULL)
12588         break;
12589     }
12590 
12591     else {
12592       size = bk_iterate(event, &pmbk, &pdata);
12593       if (pmbk == NULL)
12594         break;
12595     }
12596     nbk++;
12597     if (nbk > BANKLIST_MAX) {
12598       cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12599       return (nbk - 1);
12600     }
12601     if (bk_is32(event))
12602       strncat(bklist, (char *) pmbk32->name, 4);
12603 
12604     else
12605       strncat(bklist, (char *) pmbk->name, 4);
12606   } while (1);
12607   return (nbk);
12608 }
12609 
12610 
12611 /********************************************************************/
12612 /**
12613 Locates a MIDAS bank of given name inside an event.
12614 @param event pointer to current composed event
12615 @param name bank name to look for
12616 @param pdata pointer to data area of bank, NULL if bank not found
12617 @return number of values inside the bank
12618 */
12619 INT bk_locate(void *event, const char *name, void *pdata)
12620 {
12621   BANK *pbk;
12622   BANK32 *pbk32;
12623   DWORD dname;
12624 
12625   if (bk_is32(event)) {
12626     pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12627     strncpy((char *) &dname, name, 4);
12628 
12629     do {
12630       if (*((DWORD *) pbk32->name) == dname) {
12631         *((void **) pdata) = pbk32 + 1;
12632         if (tid_size[pbk32->type & 0xFF] == 0)
12633           return pbk32->data_size;
12634         return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12635       }
12636       pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN(pbk32->data_size));
12637     } while ((DWORD) pbk32 - (DWORD) event <
12638              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12639   }
12640 
12641   else {
12642     pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12643     strncpy((char *) &dname, name, 4);
12644 
12645     do {
12646       if (*((DWORD *) pbk->name) == dname) {
12647         *((void **) pdata) = pbk + 1;
12648         if (tid_size[pbk->type & 0xFF] == 0)
12649           return pbk->data_size;
12650         return pbk->data_size / tid_size[pbk->type & 0xFF];
12651       }
12652       pbk = (BANK *) ((char *) (pbk + 1) + ALIGN(pbk->data_size));
12653     } while ((DWORD) pbk - (DWORD) event <
12654              ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12655   }
12656 
12657   /* bank not found */
12658   *((void **) pdata) = NULL;
12659   return 0;
12660 }
12661 
12662 
12663 /********************************************************************/
12664 /**
12665 Finds a MIDAS bank of given name inside an event.
12666 @param pbkh pointer to current composed event
12667 @param name bank name to look for
12668 @param bklen number of elemtents in bank
12669 @param bktype bank type, one of TID_xxx
12670 @param pdata pointer to data area of bank, NULL if bank not found
12671 @return 1 if bank found, 0 otherwise
12672 */
12673 INT bk_find(BANK_HEADER * pbkh,
12674             const char *name, DWORD * bklen, DWORD * bktype, void **pdata)
12675 {
12676   BANK *pbk;
12677   BANK32 *pbk32;
12678   DWORD dname;
12679 
12680   if (bk_is32(pbkh)) {
12681     pbk32 = (BANK32 *) (pbkh + 1);
12682     strncpy((char *) &dname, name, 4);
12683 
12684     do {
12685       if (*((DWORD *) pbk32->name) == dname) {
12686         *((void **) pdata) = pbk32 + 1;
12687         if (tid_size[pbk32->type & 0xFF] == 0)
12688           *bklen = pbk32->data_size;
12689 
12690         else
12691           *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12692         *bktype = pbk32->type;
12693         return 1;
12694       }
12695       pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN(pbk32->data_size));
12696     } while ((DWORD) pbk32 - (DWORD) pbkh <
12697              pbkh->data_size + sizeof(BANK_HEADER));
12698   }
12699 
12700   else {
12701     pbk = (BANK *) (pbkh + 1);
12702     strncpy((char *) &dname, name, 4);
12703 
12704     do {
12705       if (*((DWORD *) pbk->name) == dname) {
12706         *((void **) pdata) = pbk + 1;
12707         if (tid_size[pbk->type & 0xFF] == 0)
12708           *bklen = pbk->data_size;
12709 
12710         else
12711           *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12712         *bktype = pbk->type;
12713         return 1;
12714       }
12715       pbk = (BANK *) ((char *) (pbk + 1) + ALIGN(pbk->data_size));
12716     } while ((DWORD) pbk - (DWORD) pbkh <
12717              pbkh->data_size + sizeof(BANK_HEADER));
12718   }
12719 
12720   /* bank not found */
12721   *((void **) pdata) = NULL;
12722   return 0;
12723 }
12724 
12725 
12726 /********************************************************************/
12727 /**
12728 Iterates through banks inside an event.
12729 The function can be used to enumerate all banks of an event.
12730 The returned pointer to the bank header has following structure:
12731 \code
12732 typedef struct {
12733 char   name[4];
12734 WORD   type;
12735 WORD   data_size;
12736 } BANK;
12737 \endcode
12738 where type is a TID_xxx value and data_size the size of the bank in bytes.
12739 \code
12740 BANK *pbk;
12741 INT  size;
12742 void *pdata;
12743 char name[5];
12744 pbk = NULL;
12745 do
12746 {
12747  size = bk_iterate(event, &pbk, &pdata);
12748  if (pbk == NULL)
12749   break;
12750  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12751  name[4] = 0;
12752  printf("bank %s found\n", name);
12753 } while(TRUE);
12754 \endcode
12755 @param event Pointer to data area of event.
12756 @param pbk pointer to the bank header, must be NULL for the first call to
12757 this function.
12758 @param pdata Pointer to the bank header, must be NULL for the first
12759 call to this function
12760 @return Size of bank in bytes
12761 */
12762 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12763 {
12764   if (*pbk == NULL)
12765     *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12766 
12767   else
12768     *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN((*pbk)->data_size));
12769   *((void **) pdata) = (*pbk) + 1;
12770   if ((DWORD) * pbk - (DWORD) event >=
12771       ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12772     *pbk = *((BANK **) pdata) = NULL;
12773     return 0;
12774   }
12775   return (*pbk)->data_size;
12776 }
12777 
12778 
12779 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12780 
12781 /********************************************************************/
12782 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12783 /********************************************************************\
12784 
12785   Routine: bk_iterate
12786 
12787   Purpose: Iterate through 32 bit MIDAS banks inside an event
12788 
12789   Input:
12790     void   *event           pointer to the event
12791     BANK   **pbk32          must be NULL for the first call to bk_iterate
12792 
12793   Output:
12794     BANK   **pbk            pointer to the bank header
12795     void   *pdata           pointer to data area of the bank
12796 
12797   Function value:
12798     INT    size of the bank in bytes
12799 
12800 \********************************************************************/
12801 {
12802   if (*pbk == NULL)
12803     *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12804 
12805   else
12806     *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN((*pbk)->data_size));
12807   *((void **) pdata) = (*pbk) + 1;
12808   if ((DWORD) * pbk - (DWORD) event >=
12809       ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12810     *pbk = *((BANK32 **) pdata) = NULL;
12811     return 0;
12812   }
12813   return (*pbk)->data_size;
12814 }
12815 
12816 
12817 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12818 
12819 /********************************************************************/
12820 /**
12821 Swaps bytes from little endian to big endian or vice versa for a whole event.
12822 
12823 An event contains a flag which is set by bk_init() to identify
12824 the endian format of an event. If force is FALSE, this flag is evaluated and
12825 the event is only swapped if it is in the "wrong" format for this system.
12826 An event can be swapped to the "wrong" format on purpose for example by a
12827 front-end which wants to produce events in a "right" format for a back-end
12828 analyzer which has different byte ordering.
12829 @param event pointer to data area of event
12830 @param force If TRUE, the event is always swapped, if FALSE, the event
12831 is only swapped if it is in the wrong format.
12832 @return 1==event has been swap, 0==event has not been swapped.
12833 */
12834 INT bk_swap(void *event, BOOL force)
12835 {
12836   BANK_HEADER *pbh;
12837   BANK *pbk;
12838   BANK32 *pbk32;
12839   void *pdata;
12840   WORD type;
12841   BOOL b32;
12842 
12843   pbh = (BANK_HEADER *) event;
12844 
12845   /* only swap if flags in high 16-bit */
12846   if (pbh->flags < 0x10000 && !force)
12847     return 0;
12848 
12849   /* swap bank header */
12850   DWORD_SWAP(&pbh->data_size);
12851   DWORD_SWAP(&pbh->flags);
12852 
12853   /* check for 32bit banks */
12854   b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12855   pbk = (BANK *) (pbh + 1);
12856   pbk32 = (BANK32 *) pbk;
12857 
12858   /* scan event */
12859   while ((PTYPE) pbk - (PTYPE) pbh <
12860          (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12861 
12862     /* swap bank header */
12863     if (b32) {
12864       DWORD_SWAP(&pbk32->type);
12865       DWORD_SWAP(&pbk32->data_size);
12866       pdata = pbk32 + 1;
12867       type = (WORD) pbk32->type;
12868     }
12869 
12870     else {
12871       WORD_SWAP(&pbk->type);
12872       WORD_SWAP(&pbk->data_size);
12873       pdata = pbk + 1;
12874       type = pbk->type;
12875     }
12876 
12877     /* pbk points to next bank */
12878     if (b32) {
12879       pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN(pbk32->data_size));
12880       pbk = (BANK *) pbk32;
12881     }
12882 
12883     else {
12884       pbk = (BANK *) ((char *) (pbk + 1) + ALIGN(pbk->data_size));
12885       pbk32 = (BANK32 *) pbk;
12886     } switch (type) {
12887     case TID_WORD:
12888     case TID_SHORT:
12889       while ((PTYPE) pdata < (PTYPE) pbk) {
12890         WORD_SWAP(pdata);
12891         pdata = (void *) (((WORD *) pdata) + 1);
12892       } break;
12893     case TID_DWORD:
12894     case TID_INT:
12895     case TID_BOOL:
12896     case TID_FLOAT:
12897       while ((PTYPE) pdata < (PTYPE) pbk) {
12898         DWORD_SWAP(pdata);
12899         pdata = (void *) (((DWORD *) pdata) + 1);
12900       } break;
12901     case TID_DOUBLE:
12902       while ((PTYPE) pdata < (PTYPE) pbk) {
12903         QWORD_SWAP(pdata);
12904         pdata = (void *) (((double *) pdata) + 1);
12905       } break;
12906     }
12907   }
12908   return CM_SUCCESS;
12909 }
12910 
12911 
12912 /** @} */// end of bkfunctionc
12913 
12914 /********************************************************************/
12915 /** @addtogroup hsfunctionc
12916  *  
12917  *  @{  */
12918 
12919 #if !defined(OS_VXWORKS)
12920 /********************************************************************\
12921 *                                                                    *
12922 *                 History functions                                  *
12923 *                                                                    *
12924 \********************************************************************/
12925 
12926 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12927 static HISTORY *_history;
12928 static INT _history_entries = 0;
12929 static char _hs_path_name[MAX_STRING_LENGTH];
12930 
12931 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12932 
12933 /********************************************************************/
12934 /**
12935 Sets the path for future history file accesses. Should
12936 be called before any other history function is called.
12937 @param path             Directory where history files reside
12938 @return HS_SUCCESS
12939 */
12940 INT hs_set_path(char *path)
12941 {
12942 
12943   /* set path locally and remotely */
12944   if (rpc_is_remote())
12945     rpc_call(RPC_HS_SET_PATH, path);
12946   strcpy(_hs_path_name, path);
12947 
12948   /* check for trailing directory seperator */
12949   if (strlen(_hs_path_name) > 0
12950       && _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
12951     strcat(_hs_path_name, DIR_SEPARATOR_STR);
12952   return HS_SUCCESS;
12953 }
12954 
12955 
12956 /********************************************************************/
12957 /**
12958 Open history file belonging to certain date. Internal use
12959            only.
12960 @param ltime          Date for which a history file should be opened.
12961 @param suffix         File name suffix like "hst", "idx", "idf"
12962 @param mode           R/W access mode
12963 @param fh             File handle
12964 @return HS_SUCCESS
12965 */
12966 INT hs_open_file(DWORD ltime, char *suffix, INT mode, int *fh)
12967 {
12968   struct tm *tms;
12969   char file_name[256];
12970 
12971   /* generate new file name YYMMDD.xxx */
12972 #if !defined(OS_VXWORKS)
12973 #if !defined(OS_VMS)
12974   tzset();
12975 
12976 #endif                          /*  */
12977 #endif                          /*  */
12978   tms = localtime((const time_t *) &ltime);
12979   sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
12980           tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
12981 
12982   /* open file, add O_BINARY flag for Windows NT */
12983   *fh = open(file_name, mode | O_BINARY, 0644);
12984   return HS_SUCCESS;
12985 }
12986 
12987 
12988 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12989 
12990 /********************************************************************/
12991 INT hs_gen_index(DWORD ltime)
12992 /********************************************************************\
12993 
12994   Routine: hs_gen_index
12995 
12996   Purpose: Regenerate index files ("idx" and "idf" files) for a given
12997            history file ("hst"). Interal use only.
12998 
12999   Input:
13000     DWORD  ltime            Date for which a history file should
13001                             be analyzed.
13002 
13003   Output:
13004     none
13005 
13006   Function value:
13007     HS_SUCCESS              Successful completion
13008     HS_FILE_ERROR           Index files cannot be created
13009 
13010 \********************************************************************/
13011 {
13012   char event_name[NAME_LENGTH];
13013   int fh, fhd, fhi;
13014   INT n;
13015   HIST_RECORD rec;
13016   INDEX_RECORD irec;
13017   DEF_RECORD def_rec;
13018 
13019   printf("Recovering index files...\n");
13020   if (ltime == 0)
13021     ltime = time(NULL);
13022 
13023   /* open new index file */
13024   hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
13025   hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
13026   if (fhd < 0 || fhi < 0) {
13027     cm_msg(MERROR, "hs_gen_index", "cannot create index file");
13028     return HS_FILE_ERROR;
13029   }
13030 
13031   /* open history file */
13032   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13033   if (fh < 0)
13034     return HS_FILE_ERROR;
13035   lseek(fh, 0, SEEK_SET);
13036 
13037   /* loop over file records in .hst file */
13038   do {
13039     n = read(fh, (char *) &rec, sizeof(rec));
13040     if (n < sizeof(rec))
13041       break;
13042 
13043     /* check if record type is definition */
13044     if (rec.record_type == RT_DEF) {
13045 
13046       /* read name */
13047       read(fh, event_name, sizeof(event_name));
13048       printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
13049 
13050       /* write definition index record */
13051       def_rec.event_id = rec.event_id;
13052       memcpy(def_rec.event_name, event_name, sizeof(event_name));
13053       def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
13054       write(fhd, (char *) &def_rec, sizeof(def_rec));
13055 
13056       /* skip tags */
13057       lseek(fh, rec.data_size, SEEK_CUR);
13058     }
13059 
13060     else {
13061 
13062       /* write index record */
13063       irec.event_id = rec.event_id;
13064       irec.time = rec.time;
13065       irec.offset = TELL(fh) - sizeof(rec);
13066       write(fhi, (char *) &irec, sizeof(irec));
13067 
13068       /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
13069 
13070       /* skip data */
13071       lseek(fh, rec.data_size, SEEK_CUR);
13072     }
13073   } while (TRUE);
13074   close(fh);
13075   close(fhi);
13076   close(fhd);
13077   printf("...done.\n");
13078   return HS_SUCCESS;
13079 }
13080 
13081 
13082 /********************************************************************/
13083 INT hs_search_file(DWORD * ltime, INT direction)
13084 /********************************************************************\
13085 
13086   Routine: hs_search_file
13087 
13088   Purpose: Search an history file for a given date. If not found,
13089            look for files after date (direction==1) or before date
13090            (direction==-1) up to one year.
13091 
13092   Input:
13093     DWORD  *ltime           Date of history file
13094     INT    direction        Search direction
13095 
13096   Output:
13097     DWORD  *ltime           Date of history file found
13098 
13099   Function value:
13100     HS_SUCCESS              Successful completion
13101     HS_FILE_ERROR           No file found
13102 
13103 \********************************************************************/
13104 {
13105   DWORD lt;
13106   int fh, fhd, fhi;
13107   struct tm *tms;
13108 
13109   if (*ltime == 0)
13110     *ltime = time(NULL);
13111   lt = *ltime;
13112 
13113   do {
13114 
13115     /* try to open history file for date "lt" */
13116     hs_open_file(lt, "hst", O_RDONLY, &fh);
13117 
13118     /* if not found, look for next day */
13119     if (fh < 0)
13120       lt += direction * 3600 * 24;
13121 
13122     /* stop if more than a year before starting point or in the future */
13123   } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365
13124            && lt < (DWORD) time(NULL));
13125   if (fh < 0)
13126     return HS_FILE_ERROR;
13127   if (lt != *ltime) {
13128 
13129     /* if switched to new day, set start_time to 0:00 */
13130     tms = localtime((const time_t *) &lt);
13131     tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13132     *ltime = mktime(tms);
13133   }
13134 
13135   /* check if index files are there */
13136   hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
13137   hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
13138   close(fh);
13139   close(fhd);
13140   close(fhi);
13141 
13142   /* generate them if not */
13143   if (fhd < 0 || fhi < 0)
13144     hs_gen_index(*ltime);
13145   return HS_SUCCESS;
13146 }
13147 
13148 
13149 /********************************************************************/
13150 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
13151 /********************************************************************\
13152 
13153   Routine: hs_define_evnet
13154 
13155   Purpose: Define a new event for which a history should be recorded.
13156            This routine must be called before any call to
13157            hs_write_event. It also should be called if the definition
13158            of the event has changed.
13159 
13160            The event definition is written directly to the history
13161            file. If the definition is identical to a previous
13162            definition, it is not written to the file.
13163 
13164 
13165   Input:
13166     DWORD  event_id         ID for this event. Must be unique.
13167     char   name             Name of this event
13168     TAG    tag              Tag list containing names and types of
13169                             variables in this event.
13170     DWORD  size             Size of tag array
13171 
13172   Output:
13173     <none>
13174 
13175   Function value:
13176     HS_SUCCESS              Successful completion
13177     HS_NO_MEMEORY           Out of memory
13178     HS_FILE_ERROR           Cannot open history file
13179 
13180 \********************************************************************/
13181 {
13182 
13183 /* History events are only written locally (?)
13184 
13185   if (rpc_is_remote())
13186     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
13187 */
13188   {
13189     HIST_RECORD rec, prev_rec;
13190     DEF_RECORD def_rec;
13191     char str[256], event_name[NAME_LENGTH], *buffer;
13192     int fh, fhi, fhd;
13193     INT i, n, len, index;
13194     struct tm *tmb;
13195 
13196     /* allocate new space for the new history descriptor */
13197     if (_history_entries == 0) {
13198       _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
13199       memset(_history, 0, sizeof(HISTORY));
13200       if (_history == NULL)
13201         return HS_NO_MEMORY;
13202       _history_entries = 1;
13203       index = 0;
13204     }
13205 
13206     else {
13207 
13208       /* check if history already open */
13209       for (i = 0; i < _history_entries; i++)
13210         if (_history[i].event_id == event_id)
13211           break;
13212 
13213       /* if not found, create new one */
13214       if (i == _history_entries) {
13215         _history =
13216             (HISTORY *) realloc(_history,
13217                                 sizeof(HISTORY) * (_history_entries + 1));
13218         memset(&_history[_history_entries], 0, sizeof(HISTORY));
13219         _history_entries++;
13220         if (_history == NULL) {
13221           _history_entries--;
13222           return HS_NO_MEMORY;
13223         }
13224       }
13225       index = i;
13226     }
13227 
13228     /* assemble definition record header */
13229     rec.record_type = RT_DEF;
13230     rec.event_id = event_id;
13231     rec.time = time(NULL);
13232     rec.data_size = size;
13233     strncpy(event_name, name, NAME_LENGTH);
13234 
13235     /* pad tag names with zeos */
13236     for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
13237       len = strlen(tag[i].name);
13238       memset(tag[i].name + len, 0, NAME_LENGTH - len);
13239     }
13240 
13241     /* if history structure not set up, do so now */
13242     if (!_history[index].hist_fh) {
13243 
13244       /* open history file */
13245       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13246       if (fh < 0)
13247         return HS_FILE_ERROR;
13248 
13249       /* open index files */
13250       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13251       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13252       lseek(fh, 0, SEEK_END);
13253       lseek(fhi, 0, SEEK_END);
13254       lseek(fhd, 0, SEEK_END);
13255 
13256       /* regenerate index if missing */
13257       if (TELL(fh) > 0 && TELL(fhd) == 0) {
13258         close(fh);
13259         close(fhi);
13260         close(fhd);
13261         hs_gen_index(rec.time);
13262         hs_open_file(rec.time, "hst", O_RDWR, &fh);
13263         hs_open_file(rec.time, "idx", O_RDWR, &fhi);
13264         hs_open_file(rec.time, "idf", O_RDWR, &fhd);
13265         lseek(fh, 0, SEEK_END);
13266         lseek(fhi, 0, SEEK_END);
13267         lseek(fhd, 0, SEEK_END);
13268       }
13269       tmb = localtime((const time_t *) &rec.time);
13270       tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
13271 
13272       /* setup history structure */
13273       _history[index].hist_fh = fh;
13274       _history[index].index_fh = fhi;
13275       _history[index].def_fh = fhd;
13276       _history[index].def_offset = TELL(fh);
13277       _history[index].event_id = event_id;
13278       strcpy(_history[index].event_name, event_name);
13279       _history[index].base_time = mktime(tmb);
13280       _history[index].n_tag = size / sizeof(TAG);
13281       _history[index].tag = (TAG *) M_MALLOC(size);
13282       memcpy(_history[index].tag, tag, size);
13283 
13284       /* search previous definition */
13285       n = TELL(fhd) / sizeof(def_rec);
13286       def_rec.event_id = 0;
13287       for (i = n - 1; i >= 0; i--) {
13288         lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13289         read(fhd, (char *) &def_rec, sizeof(def_rec));
13290         if (def_rec.event_id == event_id)
13291           break;
13292       }
13293       lseek(fhd, 0, SEEK_END);
13294 
13295       /* if definition found, compare it with new one */
13296       if (def_rec.event_id == event_id) {
13297         buffer = (char *) M_MALLOC(size);
13298         memset(buffer, 0, size);
13299         lseek(fh, def_rec.def_offset, SEEK_SET);
13300         read(fh, (char *) &prev_rec, sizeof(prev_rec));
13301         read(fh, str, NAME_LENGTH);
13302         read(fh, buffer, size);
13303         lseek(fh, 0, SEEK_END);
13304         if (prev_rec.data_size != size
13305             || strcmp(str, event_name) != 0
13306             || memcmp(buffer, tag, size) != 0) {
13307 
13308           /* write definition to history file */
13309           write(fh, (char *) &rec, sizeof(rec));
13310           write(fh, event_name, NAME_LENGTH);
13311           write(fh, (char *) tag, size);
13312 
13313           /* write index record */
13314           def_rec.event_id = event_id;
13315           memcpy(def_rec.event_name, event_name, sizeof(event_name));
13316           def_rec.def_offset = _history[index].def_offset;
13317           write(fhd, (char *) &def_rec, sizeof(def_rec));
13318         }
13319 
13320         else
13321           /* definition identical, just remember old offset */
13322           _history[index].def_offset = def_rec.def_offset;
13323         M_FREE(buffer);
13324       }
13325 
13326       else {
13327 
13328         /* write definition to history file */
13329         write(fh, (char *) &rec, sizeof(rec));
13330         write(fh, event_name, NAME_LENGTH);
13331         write(fh, (char *) tag, size);
13332 
13333         /* write definition index record */
13334         def_rec.event_id = event_id;
13335         memcpy(def_rec.event_name, event_name, sizeof(event_name));
13336         def_rec.def_offset = _history[index].def_offset;
13337         write(fhd, (char *) &def_rec, sizeof(def_rec));
13338       }
13339     }
13340 
13341     else {
13342       fh = _history[index].hist_fh;
13343       fhd = _history[index].def_fh;
13344 
13345       /* compare definition with previous definition */
13346       buffer = (char *) M_MALLOC(size);
13347       memset(buffer, 0, size);
13348       lseek(fh, _history[index].def_offset, SEEK_SET);
13349       read(fh, (char *) &prev_rec, sizeof(prev_rec));
13350       read(fh, str, NAME_LENGTH);
13351       read(fh, buffer, size);
13352       lseek(fh, 0, SEEK_END);
13353       lseek(fhd, 0, SEEK_END);
13354       if (prev_rec.data_size != size
13355           || strcmp(str, event_name) != 0
13356           || memcmp(buffer, tag, size) != 0) {
13357 
13358         /* save new definition offset */
13359         _history[index].def_offset = TELL(fh);
13360 
13361         /* write definition to history file */
13362         write(fh, (char *) &rec, sizeof(rec));
13363         write(fh, event_name, NAME_LENGTH);
13364         write(fh, (char *) tag, size);
13365 
13366         /* write index record */
13367         def_rec.event_id = event_id;
13368         memcpy(def_rec.event_name, event_name, sizeof(event_name));
13369         def_rec.def_offset = _history[index].def_offset;
13370         write(fhd, (char *) &def_rec, sizeof(def_rec));
13371       }
13372       M_FREE(buffer);
13373     }
13374   }
13375   return HS_SUCCESS;
13376 }
13377 
13378 
13379 /********************************************************************/
13380 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13381 /********************************************************************\
13382 
13383   Routine: hs_write_event
13384 
13385   Purpose: Write an event to a history file.
13386 
13387   Input:
13388     DWORD  event_id         Event ID
13389     void   *data            Data buffer containing event
13390     DWORD  size             Data buffer size in bytes
13391 
13392   Output:
13393     none
13394                             future hs_write_event
13395 
13396   Function value:
13397     HS_SUCCESS              Successful completion
13398     HS_NO_MEMEORY           Out of memory
13399     HS_FILE_ERROR           Cannot write to history file
13400     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13401 
13402 \********************************************************************/
13403 {
13404 
13405 /* history events are only written locally (?)
13406 
13407   if (rpc_is_remote())
13408     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13409 */
13410   HIST_RECORD rec, drec;
13411   DEF_RECORD def_rec;
13412   INDEX_RECORD irec;
13413   int fh, fhi, fhd;
13414   INT index;
13415   struct tm tmb, tmr;
13416 
13417   /* find index to history structure */
13418   for (index = 0; index < _history_entries; index++)
13419     if (_history[index].event_id == event_id)
13420       break;
13421   if (index == _history_entries)
13422     return HS_UNDEFINED_EVENT;
13423 
13424   /* assemble record header */
13425   rec.record_type = RT_DATA;
13426   rec.event_id = _history[index].event_id;
13427   rec.time = time(NULL);
13428   rec.def_offset = _history[index].def_offset;
13429   rec.data_size = size;
13430   irec.event_id = _history[index].event_id;
13431   irec.time = rec.time;
13432 
13433   /* check if new day */
13434   memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13435   memcpy(&tmb, localtime((const time_t *) &_history[index].base_time),
13436          sizeof(tmb));
13437   if (tmr.tm_yday != tmb.tm_yday) {
13438 
13439     /* close current history file */
13440     close(_history[index].hist_fh);
13441     close(_history[index].def_fh);
13442     close(_history[index].index_fh);
13443 
13444     /* open new history file */
13445     hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13446     if (fh < 0)
13447       return HS_FILE_ERROR;
13448 
13449     /* open new index file */
13450     hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13451     if (fhi < 0)
13452       return HS_FILE_ERROR;
13453 
13454     /* open new definition index file */
13455     hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13456     if (fhd < 0)
13457       return HS_FILE_ERROR;
13458     lseek(fh, 0, SEEK_END);
13459     lseek(fhi, 0, SEEK_END);
13460     lseek(fhd, 0, SEEK_END);
13461 
13462     /* remember new file handles */
13463     _history[index].hist_fh = fh;
13464     _history[index].index_fh = fhi;
13465     _history[index].def_fh = fhd;
13466     _history[index].def_offset = TELL(fh);
13467     rec.def_offset = _history[index].def_offset;
13468     tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13469     _history[index].base_time = mktime(&tmr);
13470 
13471     /* write definition from _history structure */
13472     drec.record_type = RT_DEF;
13473     drec.event_id = _history[index].event_id;
13474     drec.time = rec.time;
13475     drec.data_size = _history[index].n_tag * sizeof(TAG);
13476     write(fh, (char *) &drec, sizeof(drec));
13477     write(fh, _history[index].event_name, NAME_LENGTH);
13478     write(fh, (char *) _history[index].tag, drec.data_size);
13479 
13480     /* write definition index record */
13481     def_rec.event_id = _history[index].event_id;
13482     memcpy(def_rec.event_name, _history[index].event_name,
13483            sizeof(def_rec.event_name));
13484     def_rec.def_offset = _history[index].def_offset;
13485     write(fhd, (char *) &def_rec, sizeof(def_rec));
13486   }
13487 
13488   /* got to end of file */
13489   lseek(_history[index].hist_fh, 0, SEEK_END);
13490   irec.offset = TELL(_history[index].hist_fh);
13491 
13492   /* write record header */
13493   write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13494 
13495   /* write data */
13496   write(_history[index].hist_fh, (char *) data, size);
13497 
13498   /* write index record */
13499   lseek(_history[index].index_fh, 0, SEEK_END);
13500   if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) <
13501       sizeof(irec))
13502     return HS_FILE_ERROR;
13503   return HS_SUCCESS;
13504 }
13505 
13506 
13507 /********************************************************************/
13508 INT hs_enum_events(DWORD ltime,
13509                    char *event_name,
13510                    DWORD * name_size, INT event_id[], DWORD * id_size)
13511 /********************************************************************\
13512 
13513   Routine: hs_enum_events
13514 
13515   Purpose: Enumerate events for a given date
13516 
13517   Input:
13518     DWORD  ltime            Date at which events should be enumerated
13519 
13520   Output:
13521     char   *event_name      Array containing event names
13522     DWORD  *name_size       Size of name array
13523     char   *event_id        Array containing event IDs
13524     DWORD  *id_size         Size of ID array
13525 
13526   Function value:
13527     HS_SUCCESS              Successful completion
13528     HS_NO_MEMEORY           Out of memory
13529     HS_FILE_ERROR           Cannot open history file
13530 
13531 \********************************************************************/
13532 {
13533   int fh, fhd;
13534   INT status, i, j, n;
13535   DEF_RECORD def_rec;
13536 
13537   if (rpc_is_remote())
13538     return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name,
13539                     name_size, event_id, id_size);
13540 
13541   /* search latest history file */
13542   status = hs_search_file(&ltime, -1);
13543   if (status != HS_SUCCESS) {
13544     cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13545     return HS_FILE_ERROR;
13546   }
13547 
13548   /* open history and definition files */
13549   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13550   hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13551   if (fh < 0 || fhd < 0) {
13552     cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13553     return HS_FILE_ERROR;
13554   }
13555   lseek(fhd, 0, SEEK_SET);
13556 
13557   /* loop over definition index file */
13558   n = 0;
13559 
13560   do {
13561 
13562     /* read event definition */
13563     j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13564     if (j < (int) sizeof(def_rec))
13565       break;
13566 
13567     /* look for existing entry for this event id */
13568     for (i = 0; i < n; i++)
13569       if (event_id[i] == (INT) def_rec.event_id) {
13570         strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13571         break;
13572       }
13573 
13574     /* new entry found */
13575     if (i == n) {
13576       if (i * NAME_LENGTH > (INT) * name_size
13577           || i * sizeof(INT) > (INT) * id_size) {
13578         cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13579         close(fh);
13580         close(fhd);
13581         return HS_NO_MEMORY;
13582       }
13583 
13584       /* copy definition record */
13585       strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13586       event_id[i] = def_rec.event_id;
13587       n++;
13588     }
13589   } while (TRUE);
13590   close(fh);
13591   close(fhd);
13592   *name_size = n * NAME_LENGTH;
13593   *id_size = n * sizeof(INT);
13594   return HS_SUCCESS;
13595 }
13596 
13597 
13598 /********************************************************************/
13599 INT hs_count_events(DWORD ltime, DWORD * count)
13600 /********************************************************************\
13601 
13602   Routine: hs_count_events
13603 
13604   Purpose: Count number of different events for a given date
13605 
13606   Input:
13607     DWORD  ltime            Date at which events should be counted
13608 
13609   Output:
13610     DWORD  *count           Number of different events found
13611 
13612   Function value:
13613     HS_SUCCESS              Successful completion
13614     HS_FILE_ERROR           Cannot open history file
13615 
13616 \********************************************************************/
13617 {
13618   int fh, fhd;
13619   INT status, i, j, n;
13620   DWORD *id;
13621   DEF_RECORD def_rec;
13622 
13623   if (rpc_is_remote())
13624     return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
13625 
13626   /* search latest history file */
13627   status = hs_search_file(&ltime, -1);
13628   if (status != HS_SUCCESS) {
13629     cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13630     return HS_FILE_ERROR;
13631   }
13632 
13633   /* open history and definition files */
13634   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13635   hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13636   if (fh < 0 || fhd < 0) {
13637     cm_msg(MERROR, "hs_count_events", "cannot open index files");
13638     return HS_FILE_ERROR;
13639   }
13640 
13641   /* allocate event id array */
13642   lseek(fhd, 0, SEEK_END);
13643   id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
13644   lseek(fhd, 0, SEEK_SET);
13645 
13646   /* loop over index file */
13647   n = 0;
13648 
13649   do {
13650 
13651     /* read definition index record */
13652     j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13653     if (j < (int) sizeof(def_rec))
13654       break;
13655 
13656     /* look for existing entries */
13657     for (i = 0; i < n; i++)
13658       if (id[i] == def_rec.event_id)
13659         break;
13660 
13661     /* new entry found */
13662     if (i == n) {
13663       id[i] = def_rec.event_id;
13664       n++;
13665     }
13666   } while (TRUE);
13667   M_FREE(id);
13668   close(fh);
13669   close(fhd);
13670   *count = n;
13671   return HS_SUCCESS;
13672 }
13673 
13674 
13675 /********************************************************************/
13676 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
13677 /********************************************************************\
13678 
13679   Routine: hs_get_event_id
13680 
13681   Purpose: Return event ID for a given name. If event cannot be found
13682            in current definition file, go back in time until found
13683 
13684   Input:
13685     DWORD  ltime            Date at which event ID should be looked for
13686 
13687   Output:
13688     DWORD  *id              Event ID
13689 
13690   Function value:
13691     HS_SUCCESS              Successful completion
13692     HS_FILE_ERROR           Cannot open history file
13693     HS_UNDEFINED_EVENT      Event "name" not found
13694 
13695 \********************************************************************/
13696 {
13697   int fh, fhd;
13698   INT status, i;
13699   DWORD lt;
13700   DEF_RECORD def_rec;
13701 
13702   if (rpc_is_remote())
13703     return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
13704 
13705   /* search latest history file */
13706   if (ltime == 0)
13707     ltime = time(NULL);
13708   lt = ltime;
13709 
13710   do {
13711     status = hs_search_file(&lt, -1);
13712     if (status != HS_SUCCESS) {
13713       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13714       return HS_FILE_ERROR;
13715     }
13716 
13717     /* open history and definition files */
13718     hs_open_file(lt, "hst", O_RDONLY, &fh);
13719     hs_open_file(lt, "idf", O_RDONLY, &fhd);
13720     if (fh < 0 || fhd < 0) {
13721       cm_msg(MERROR, "hs_count_events", "cannot open index files");
13722       return HS_FILE_ERROR;
13723     }
13724 
13725     /* loop over index file */
13726     *id = 0;
13727 
13728     do {
13729 
13730       /* read definition index record */
13731       i = read(fhd, (char *) &def_rec, sizeof(def_rec));
13732       if (i < (int) sizeof(def_rec))
13733         break;
13734       if (strcmp(name, def_rec.event_name) == 0) {
13735         *id = def_rec.event_id;
13736         close(fh);
13737         close(fhd);
13738         return HS_SUCCESS;
13739       }
13740     } while (TRUE);
13741     close(fh);
13742     close(fhd);
13743 
13744     /* not found -> go back one day */
13745     lt -= 3600 * 24;
13746   } while (lt > ltime - 3600 * 24 * 365 * 10);  /* maximum 10 years */
13747   return HS_UNDEFINED_EVENT;
13748 }
13749 
13750 
13751 /********************************************************************/
13752 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
13753 /********************************************************************\
13754 
13755   Routine: hs_count_vars
13756 
13757   Purpose: Count number of variables for a given date and event id
13758 
13759   Input:
13760     DWORD  ltime            Date at which tags should be counted
13761 
13762   Output:
13763     DWORD  *count           Number of tags
13764 
13765   Function value:
13766     HS_SUCCESS              Successful completion
13767     HS_FILE_ERROR           Cannot open history file
13768 
13769 \********************************************************************/
13770 {
13771   int fh, fhd;
13772   INT i, n, status;
13773   DEF_RECORD def_rec;
13774   HIST_RECORD rec;
13775 
13776   if (rpc_is_remote())
13777     return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
13778 
13779   /* search latest history file */
13780   status = hs_search_file(&ltime, -1);
13781   if (status != HS_SUCCESS) {
13782     cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
13783     return HS_FILE_ERROR;
13784   }
13785 
13786   /* open history and definition files */
13787   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13788   hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13789   if (fh < 0 || fhd < 0) {
13790     cm_msg(MERROR, "hs_count_tags", "cannot open index files");
13791     return HS_FILE_ERROR;
13792   }
13793 
13794   /* search last definition */
13795   lseek(fhd, 0, SEEK_END);
13796   n = TELL(fhd) / sizeof(def_rec);
13797   def_rec.event_id = 0;
13798   for (i = n - 1; i >= 0; i--) {
13799     lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13800     read(fhd, (char *) &def_rec, sizeof(def_rec));
13801     if (def_rec.event_id == event_id)
13802       break;
13803   }
13804   if (def_rec.event_id != event_id) {
13805     cm_msg(MERROR, "hs_count_tags",
13806            "event %d not found in index file", event_id);
13807     return HS_FILE_ERROR;
13808   }
13809 
13810   /* read definition */
13811   lseek(fh, def_rec.def_offset, SEEK_SET);
13812   read(fh, (char *) &rec, sizeof(rec));
13813   *count = rec.data_size / sizeof(TAG);
13814   close(fh);
13815   close(fhd);
13816   return HS_SUCCESS;
13817 }
13818 
13819 
13820 /********************************************************************/
13821 INT hs_enum_vars(DWORD ltime,
13822                  DWORD event_id,
13823                  char *var_name,
13824                  DWORD * size, DWORD * var_n, DWORD * n_size)
13825 /********************************************************************\
13826 
13827   Routine: hs_enum_vars
13828 
13829   Purpose: Enumerate variable tags for a given date and event id
13830 
13831   Input:
13832     DWORD  ltime            Date at which tags should be enumerated
13833     DWORD  event_id         Event ID
13834 
13835   Output:
13836     char   *var_name        Array containing variable names
13837     DWORD  *size            Size of name array
13838     DWORD  *var_n           Array size of variable
13839     DWORD  *n_size          Size of n array
13840 
13841   Function value:
13842     HS_SUCCESS              Successful completion
13843     HS_NO_MEMEORY           Out of memory
13844     HS_FILE_ERROR           Cannot open history file
13845 
13846 \********************************************************************/
13847 {
13848   char str[256];
13849   int fh, fhd;
13850   INT i, n, status;
13851   DEF_RECORD def_rec;
13852   HIST_RECORD rec;
13853   TAG *tag;
13854 
13855   if (rpc_is_remote())
13856     return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
13857 
13858   /* search latest history file */
13859   status = hs_search_file(&ltime, -1);
13860   if (status != HS_SUCCESS) {
13861     cm_msg(MERROR, "hs_enum_tags", "cannot find recent history file");
13862     return HS_FILE_ERROR;
13863   }
13864 
13865   /* open history and definition files */
13866   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13867   hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13868   if (fh < 0 || fhd < 0) {
13869     cm_msg(MERROR, "hs_enum_tags", "cannot open index files");
13870     return HS_FILE_ERROR;
13871   }
13872 
13873   /* search last definition */
13874   lseek(fhd, 0, SEEK_END);
13875   n = TELL(fhd) / sizeof(def_rec);
13876   def_rec.event_id = 0;
13877   for (i = n - 1; i >= 0; i--) {
13878     lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13879     read(fhd, (char *) &def_rec, sizeof(def_rec));
13880     if (def_rec.event_id == event_id)
13881       break;
13882   }
13883   if (def_rec.event_id != event_id) {
13884     cm_msg(MERROR, "hs_enum_tags",
13885            "event %d not found in index file", event_id);
13886     return HS_FILE_ERROR;
13887   }
13888 
13889   /* read definition header */
13890   lseek(fh, def_rec.def_offset, SEEK_SET);
13891   read(fh, (char *) &rec, sizeof(rec));
13892   read(fh, str, NAME_LENGTH);
13893 
13894   /* read event definition */
13895   n = rec.data_size / sizeof(TAG);
13896   tag = (TAG *) M_MALLOC(rec.data_size);
13897   read(fh, (char *) tag, rec.data_size);
13898   if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
13899 
13900     /* store partial definition */
13901     for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
13902       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13903       var_n[i] = tag[i].n_data;
13904     }
13905     cm_msg(MERROR, "hs_enum_tags", "tag buffer too small");
13906     M_FREE(tag);
13907     close(fh);
13908     close(fhd);
13909     return HS_NO_MEMORY;
13910   }
13911 
13912   /* store full definition */
13913   for (i = 0; i < n; i++) {
13914     strcpy(var_name + i * NAME_LENGTH, tag[i].name);
13915     var_n[i] = tag[i].n_data;
13916   }
13917   *size = n * NAME_LENGTH;
13918   *n_size = n;
13919   M_FREE(tag);
13920   close(fh);
13921   close(fhd);
13922   return HS_SUCCESS;
13923 }
13924 
13925 
13926 /********************************************************************/
13927 INT hs_get_var(DWORD ltime,
13928                DWORD event_id, char *var_name, DWORD * type, INT * n_data)
13929 /********************************************************************\
13930 
13931   Routine: hs_get_var
13932 
13933   Purpose: Get definition for certain variable
13934 
13935   Input:
13936     DWORD  ltime            Date at which variable definition should
13937                             be returned
13938     DWORD  event_id         Event ID
13939     char   *var_name        Name of variable
13940 
13941   Output:
13942     INT    *type            Type of variable
13943     INT    *n_data          Number of items in variable
13944 
13945   Function value:
13946     HS_SUCCESS              Successful completion
13947     HS_NO_MEMEORY           Out of memory
13948     HS_FILE_ERROR           Cannot open history file
13949 
13950 \********************************************************************/
13951 {
13952   char str[256];
13953   int fh, fhd;
13954   INT i, n, status;
13955   DEF_RECORD def_rec;
13956   HIST_RECORD rec;
13957   TAG *tag;
13958 
13959   if (rpc_is_remote())
13960     return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name,
13961                     type, n_data);
13962 
13963   /* search latest history file */
13964   status = hs_search_file(&ltime, -1);
13965   if (status != HS_SUCCESS) {
13966     cm_msg(MERROR, "hs_enum_tags", "cannot find recent history file");
13967     return HS_FILE_ERROR;
13968   }
13969 
13970   /* open history and definition files */
13971   hs_open_file(ltime, "hst", O_RDONLY, &fh);
13972   hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13973   if (fh < 0 || fhd < 0) {
13974     cm_msg(MERROR, "hs_enum_tags", "cannot open index files");
13975     return HS_FILE_ERROR;
13976   }
13977 
13978   /* search last definition */
13979   lseek(fhd, 0, SEEK_END);
13980   n = TELL(fhd) / sizeof(def_rec);
13981   def_rec.event_id = 0;
13982   for (i = n - 1; i >= 0; i--) {
13983     lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13984     read(fhd, (char *) &def_rec, sizeof(def_rec));
13985     if (def_rec.event_id == event_id)
13986       break;
13987   }
13988   if (def_rec.event_id != event_id) {
13989     cm_msg(MERROR, "hs_enum_tags",
13990            "event %d not found in index file", event_id);
13991     return HS_FILE_ERROR;
13992   }
13993 
13994   /* read definition header */
13995   lseek(fh, def_rec.def_offset, SEEK_SET);
13996   read(fh, (char *) &rec, sizeof(rec));
13997   read(fh, str, NAME_LENGTH);
13998 
13999   /* read event definition */
14000   n = rec.data_size / sizeof(TAG);
14001   tag = (TAG *) M_MALLOC(rec.data_size);
14002   read(fh, (char *) tag, rec.data_size);
14003 
14004   /* search variable */
14005   for (i = 0; i < n; i++)
14006     if (strcmp(tag[i].name, var_name) == 0)
14007       break;
14008   close(fh);
14009   close(fhd);
14010   if (i < n) {
14011     *type = tag[i].type;
14012     *n_data = tag[i].n_data;
14013   }
14014 
14015   else {
14016     *type = *n_data = 0;
14017     cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
14018     M_FREE(tag);
14019     return HS_UNDEFINED_VAR;
14020   }
14021   M_FREE(tag);
14022   return HS_SUCCESS;
14023 }
14024 
14025 
14026 /********************************************************************/
14027 INT hs_read(DWORD event_id,
14028             DWORD start_time,
14029             DWORD end_time,
14030             DWORD interval,
14031             char *tag_name,
14032             DWORD var_index,
14033             DWORD * time_buffer,
14034             DWORD * tbsize,
14035             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
14036 /********************************************************************\
14037 
14038   Routine: hs_read
14039 
14040   Purpose: Read history for a variable at a certain time interval
14041 
14042   Input:
14043     DWORD  event_id         Event ID
14044     DWORD  start_time       Starting Date/Time
14045     DWORD  end_time         End Date/Time
14046     DWORD  interval         Minimum time in seconds between reported
14047                             events. Can be used to skip events
14048     char   *tag_name        Variable name inside event
14049     DWORD  var_index        Index if variable is array
14050 
14051   Output:
14052     DWORD  *time_buffer     Buffer containing times for each value
14053     DWORD  *tbsize          Size of time buffer
14054     void   *data_buffer     Buffer containing variable values
14055     DWORD  *dbsize          Data buffer size
14056     DWORD  *type            Type of variable (one of TID_xxx)
14057     DWORD  *n               Number of time/value pairs found
14058                             in specified interval and placed into
14059                             time_buffer and data_buffer
14060 
14061 
14062   Function value:
14063     HS_SUCCESS              Successful completion
14064     HS_NO_MEMEORY           Out of memory
14065     HS_FILE_ERROR           Cannot open history file
14066     HS_WRONG_INDEX          var_index exceeds array size of variable
14067     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
14068     HS_TRUNCATED            Buffer too small, data has been truncated
14069 
14070 \********************************************************************/
14071 {
14072   DWORD prev_time, last_irec_time;
14073   int fh, fhd, fhi, cp = 0;
14074   INT i, delta, index = 0, status, cache_size;
14075   INDEX_RECORD irec, *pirec;
14076   HIST_RECORD rec, drec;
14077   INT old_def_offset, var_size = 0, var_offset = 0;
14078   TAG *tag;
14079   char str[NAME_LENGTH];
14080   struct tm *tms;
14081   char *cache;
14082 
14083   if (rpc_is_remote())
14084     return rpc_call(RPC_HS_READ, event_id, start_time, end_time,
14085                     interval, tag_name, var_index, time_buffer,
14086                     tbsize, data_buffer, dbsize, type, n);
14087 
14088   /* if not time given, use present to one hour in past */
14089   if (start_time == 0)
14090     start_time = time(NULL) - 3600;
14091   if (end_time == 0)
14092     end_time = time(NULL);
14093 
14094   /* search history file for start_time */
14095   status = hs_search_file(&start_time, 1);
14096   if (status != HS_SUCCESS) {
14097     cm_msg(MERROR, "hs_read", "cannot find recent history file");
14098     *tbsize = *dbsize = *n = 0;
14099     return HS_FILE_ERROR;
14100   }
14101 
14102   /* open history and definition files */
14103   hs_open_file(start_time, "hst", O_RDONLY, &fh);
14104   hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14105   hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14106   if (fh < 0 || fhd < 0 || fhi < 0) {
14107     cm_msg(MERROR, "hs_read", "cannot open index files");
14108     *tbsize = *dbsize = *n = 0;
14109     return HS_FILE_ERROR;
14110   }
14111 
14112   /* try to read index file into cache */
14113   lseek(fhi, 0, SEEK_END);
14114   cache_size = TELL(fhi);
14115   if (cache_size > 0) {
14116     cache = (char *) M_MALLOC(cache_size);
14117     if (cache) {
14118       lseek(fhi, 0, SEEK_SET);
14119       i = read(fhi, cache, cache_size);
14120       if (i < cache_size) {
14121         M_FREE(cache);
14122         close(fh);
14123         close(fhd);
14124         close(fhi);
14125         return HS_FILE_ERROR;
14126       }
14127     }
14128 
14129     /* search record closest to start time */
14130     if (cache == NULL) {
14131       lseek(fhi, 0, SEEK_END);
14132       delta = (TELL(fhi) / sizeof(irec)) / 2;
14133       lseek(fhi, delta * sizeof(irec), SEEK_SET);
14134 
14135       do {
14136         delta = (int) (abs(delta) / 2.0 + 0.5);
14137         read(fhi, (char *) &irec, sizeof(irec));
14138         if (irec.time > start_time)
14139           delta = -delta;
14140         lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14141       } while (abs(delta) > 1 && irec.time != start_time);
14142       read(fhi, (char *) &irec, sizeof(irec));
14143       if (irec.time > start_time)
14144         delta = -abs(delta);
14145       i = TELL(fhi) + (delta - 1) * sizeof(irec);
14146       if (i <= 0)
14147         lseek(fhi, 0, SEEK_SET);
14148 
14149       else
14150         lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14151       read(fhi, (char *) &irec, sizeof(irec));
14152     }
14153 
14154     else {
14155       delta = (cache_size / sizeof(irec)) / 2;
14156       cp = delta * sizeof(irec);
14157 
14158       do {
14159         delta = (int) (abs(delta) / 2.0 + 0.5);
14160         pirec = (INDEX_RECORD *) (cache + cp);
14161         if (pirec->time > start_time)
14162           delta = -delta;
14163         cp = cp + delta * sizeof(irec);
14164       } while (abs(delta) > 1 && pirec->time != start_time);
14165       pirec = (INDEX_RECORD *) (cache + cp);
14166       if (pirec->time > start_time)
14167         delta = -abs(delta);
14168       if (cp <= delta * (int) sizeof(irec))
14169         cp = 0;
14170 
14171       else
14172         cp = cp + delta * sizeof(irec);
14173       if (cp >= cache_size)
14174         cp = cache_size - sizeof(irec);
14175       if (cp < 0)
14176         cp = 0;
14177       memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
14178       cp += sizeof(irec);
14179     }
14180   }
14181 
14182   else {                        /* file size > 0 */
14183 
14184     cache = NULL;
14185     irec.time = start_time;
14186   }
14187 
14188   /* read records, skip wrong IDs */
14189   old_def_offset = -1;
14190   *n = 0;
14191   prev_time = 0;
14192   last_irec_time = 0;
14193 
14194   do {
14195     if (irec.time < last_irec_time) {
14196       cm_msg(MERROR, "hs_read",
14197              "corrupted history data: time does not increase: %d -> %d",
14198              last_irec_time, irec.time);
14199       *tbsize = *dbsize = *n = 0;
14200       return HS_FILE_ERROR;
14201     }
14202     last_irec_time = irec.time;
14203     if (irec.event_id == event_id && irec.time <= end_time
14204         && irec.time >= start_time) {
14205 
14206       /* check if record time more than "interval" seconds after previous time */
14207       if (irec.time >= prev_time + interval) {
14208         prev_time = irec.time;
14209         lseek(fh, irec.offset, SEEK_SET);
14210         read(fh, (char *) &rec, sizeof(rec));
14211 
14212         /* if definition changed, read new definition */
14213         if ((INT) rec.def_offset != old_def_offset) {
14214           lseek(fh, rec.def_offset, SEEK_SET);
14215           read(fh, (char *) &drec, sizeof(drec));
14216           read(fh, str, NAME_LENGTH);
14217           tag = (TAG *) M_MALLOC(drec.data_size);
14218           if (tag == NULL) {
14219             *n = *tbsize = *dbsize = 0;
14220             if (cache)
14221               M_FREE(cache);
14222             close(fh);
14223             close(fhd);
14224             close(fhi);
14225             return HS_NO_MEMORY;
14226           }
14227           read(fh, (char *) tag, drec.data_size);
14228 
14229           /* find index of tag_name in new definition */
14230           index = -1;
14231           for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
14232             if (equal_ustring(tag[i].name, tag_name)) {
14233               index = i;
14234               break;
14235             }
14236 
14237           /*
14238              if ((DWORD) i == drec.data_size/sizeof(TAG))
14239              {
14240              *n = *tbsize = *dbsize = 0;
14241              if (cache)
14242              M_FREE(cache);
14243 
14244              return HS_UNDEFINED_VAR;
14245              }
14246            */
14247           if (index >= 0 && var_index >= tag[i].n_data) {
14248             *n = *tbsize = *dbsize = 0;
14249             if (cache)
14250               M_FREE(cache);
14251             M_FREE(tag);
14252             close(fh);
14253             close(fhd);
14254             close(fhi);
14255             return HS_WRONG_INDEX;
14256           }
14257 
14258           /* calculate offset for variable */
14259           if (index >= 0) {
14260             *type = tag[i].type;
14261 
14262             /* loop over all previous variables */
14263             for (i = 0, var_offset = 0; i < index; i++)
14264               var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
14265 
14266             /* strings have size n_data */
14267             if (tag[index].type == TID_STRING)
14268               var_size = tag[i].n_data;
14269 
14270             else
14271               var_size = rpc_tid_size(tag[index].type);
14272             var_offset += var_size * var_index;
14273           }
14274           M_FREE(tag);
14275           old_def_offset = rec.def_offset;
14276           lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14277         }
14278         if (index >= 0) {
14279 
14280           /* check if data fits in buffers */
14281           if ((*n) * sizeof(DWORD) >= *tbsize ||
14282               (*n) * var_size >= *dbsize) {
14283             *dbsize = (*n) * var_size;
14284             *tbsize = (*n) * sizeof(DWORD);
14285             if (cache)
14286               M_FREE(cache);
14287             close(fh);
14288             close(fhd);
14289             close(fhi);
14290             return HS_TRUNCATED;
14291           }
14292 
14293           /* copy time from header */
14294           time_buffer[*n] = irec.time;
14295 
14296           /* copy data from record */
14297           lseek(fh, var_offset, SEEK_CUR);
14298           read(fh, (char *) data_buffer + (*n) * var_size, var_size);
14299 
14300           /* increment counter */
14301           (*n)++;
14302         }
14303       }
14304     }
14305 
14306     /* read next index record */
14307     if (cache) {
14308       if (cp >= cache_size) {
14309         i = -1;
14310         M_FREE(cache);
14311         cache = NULL;
14312       }
14313 
14314       else
14315         i = sizeof(irec);
14316       if (cp < cache_size) {
14317         memcpy(&irec, cache + cp, sizeof(irec));
14318         cp += sizeof(irec);
14319       }
14320     }
14321 
14322     else
14323       i = read(fhi, (char *) &irec, sizeof(irec));
14324 
14325     /* end of file: search next history file */
14326     if (i <= 0) {
14327       close(fh);
14328       close(fhd);
14329       close(fhi);
14330 
14331       /* advance one day */
14332       tms = localtime((const time_t *) &last_irec_time);
14333       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14334       last_irec_time = mktime(tms);
14335       last_irec_time += 3600 * 24;
14336       if (last_irec_time > end_time)
14337         break;
14338 
14339       /* search next file */
14340       status = hs_search_file(&last_irec_time, 1);
14341       if (status != HS_SUCCESS)
14342         break;
14343 
14344       /* open history and definition files */
14345       hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14346       hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14347       hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14348       if (fh < 0 || fhd < 0 || fhi < 0) {
14349         cm_msg(MERROR, "hs_read", "cannot open index files");
14350         break;
14351       }
14352 
14353       /* try to read index file into cache */
14354       lseek(fhi, 0, SEEK_END);
14355       cache_size = TELL(fhi);
14356       lseek(fhi, 0, SEEK_SET);
14357       cache = (char *) M_MALLOC(cache_size);
14358       if (cache) {
14359         i = read(fhi, cache, cache_size);
14360         if (i < cache_size)
14361           break;
14362 
14363         /* read first record */
14364         cp = 0;
14365         memcpy(&irec, cache, sizeof(irec));
14366       }
14367 
14368       else {
14369 
14370         /* read first record */
14371         i = read(fhi, (char *) &irec, sizeof(irec));
14372         if (i <= 0)
14373           break;
14374       }
14375 
14376       /* old definition becomes invalid */
14377       old_def_offset = -1;
14378     }
14379   } while (irec.time < end_time);
14380   if (cache)
14381     M_FREE(cache);
14382   close(fh);
14383   close(fhd);
14384   close(fhi);
14385   *dbsize = *n * var_size;
14386   *tbsize = *n * sizeof(DWORD);
14387   return HS_SUCCESS;
14388 }
14389 
14390 
14391 /********************************************************************/
14392 INT hs_dump(DWORD event_id,
14393             DWORD start_time,
14394             DWORD end_time, DWORD interval, BOOL binary_time)
14395 /********************************************************************\
14396 
14397   Routine: hs_dump
14398 
14399   Purpose: Display history for a given event at stdout. The output
14400            can be redirected to be read by Excel for example.
14401 
14402   Input:
14403     DWORD  event_id         Event ID
14404     DWORD  start_time       Starting Date/Time
14405     DWORD  end_time         End Date/Time
14406     DWORD  interval         Minimum time in seconds between reported
14407                             events. Can be used to skip events
14408     BOOL   binary_time      Display DWORD time stamp
14409   Output:
14410     <screen output>
14411 
14412   Function value:
14413     HS_SUCCESS              Successful completion
14414     HS_FILE_ERROR           Cannot open history file
14415 
14416 \********************************************************************/
14417 {
14418   DWORD prev_time, last_irec_time;
14419   int fh, fhd, fhi;
14420   INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14421   INDEX_RECORD irec;
14422   HIST_RECORD rec, drec;
14423   INT old_def_offset, offset;
14424   TAG *tag = NULL, *old_tag = NULL;
14425   char str[NAME_LENGTH], data_buffer[10000];
14426   struct tm *tms;
14427 
14428   /* if not time given, use present to one hour in past */
14429   if (start_time == 0)
14430     start_time = time(NULL) - 3600;
14431   if (end_time == 0)
14432     end_time = time(NULL);
14433 
14434   /* search history file for start_time */
14435   status = hs_search_file(&start_time, 1);
14436   if (status != HS_SUCCESS) {
14437     cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14438     return HS_FILE_ERROR;
14439   }
14440 
14441   /* open history and definition files */
14442   hs_open_file(start_time, "hst", O_RDONLY, &fh);
14443   hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14444   hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14445   if (fh < 0 || fhd < 0 || fhi < 0) {
14446     cm_msg(MERROR, "hs_dump", "cannot open index files");
14447     return HS_FILE_ERROR;
14448   }
14449 
14450   /* search record closest to start time */
14451   lseek(fhi, 0, SEEK_END);
14452   delta = (TELL(fhi) / sizeof(irec)) / 2;
14453   lseek(fhi, delta * sizeof(irec), SEEK_SET);
14454 
14455   do {
14456     delta = (int) (abs(delta) / 2.0 + 0.5);
14457     read(fhi, (char *) &irec, sizeof(irec));
14458     if (irec.time > start_time)
14459       delta = -delta;
14460     i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14461   } while (abs(delta) > 1 && irec.time != start_time);
14462   read(fhi, (char *) &irec, sizeof(irec));
14463   if (irec.time > start_time)
14464     delta = -abs(delta);
14465   i = TELL(fhi) + (delta - 1) * sizeof(irec);
14466   if (i <= 0)
14467     lseek(fhi, 0, SEEK_SET);
14468 
14469   else
14470     lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14471   read(fhi, (char *) &irec, sizeof(irec));
14472 
14473   /* read records, skip wrong IDs */
14474   old_def_offset = -1;
14475   prev_time = 0;
14476   last_irec_time = 0;
14477 
14478   do {
14479     if (irec.time < last_irec_time) {
14480       cm_msg(MERROR, "hs_dump",
14481              "corrupted history data: time does not increase: %d -> %d",
14482              last_irec_time, irec.time);
14483       return HS_FILE_ERROR;
14484     }
14485     last_irec_time = irec.time;
14486     if (irec.event_id == event_id && irec.time <= end_time
14487         && irec.time >= start_time) {
14488       if (irec.time >= prev_time + interval) {
14489         prev_time = irec.time;
14490         lseek(fh, irec.offset, SEEK_SET);
14491         read(fh, (char *) &rec, sizeof(rec));
14492 
14493         /* if definition changed, read new definition */
14494         if ((INT) rec.def_offset != old_def_offset) {
14495           lseek(fh, rec.def_offset, SEEK_SET);
14496           read(fh, (char *) &drec, sizeof(drec));
14497           read(fh, str, NAME_LENGTH);
14498           if (tag == NULL)
14499             tag = (TAG *) M_MALLOC(drec.data_size);
14500 
14501           else
14502             tag = (TAG *) realloc(tag, drec.data_size);
14503           if (tag == NULL)
14504             return HS_NO_MEMORY;
14505           read(fh, (char *) tag, drec.data_size);
14506           n_tag = drec.data_size / sizeof(TAG);
14507 
14508           /* print tag names if definition has changed */
14509           if (old_tag == NULL || old_n_tag != n_tag
14510               || memcmp(old_tag, tag, drec.data_size) != 0) {
14511             printf("Date\t");
14512             for (i = 0; i < n_tag; i++) {
14513               if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14514                 printf("%s\t", tag[i].name);
14515 
14516               else
14517                 for (j = 0; j < (INT) tag[i].n_data; j++)
14518                   printf("%s%d\t", tag[i].name, j);
14519             }
14520             printf("\n");
14521             if (old_tag == NULL)
14522               old_tag = (TAG *) M_MALLOC(drec.data_size);
14523 
14524             else
14525               old_tag = (TAG *) realloc(old_tag, drec.data_size);
14526             memcpy(old_tag, tag, drec.data_size);
14527             old_n_tag = n_tag;
14528           }
14529           old_def_offset = rec.def_offset;
14530           lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14531         }
14532 
14533         /* print time from header */
14534         if (binary_time)
14535           printf("%li ", irec.time);
14536 
14537         else {
14538           sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14539           str[20] = '\t';
14540           printf(str);
14541         }
14542         /* read data */
14543         read(fh, data_buffer, rec.data_size);
14544 
14545         /* interprete data from tag definition */
14546         offset = 0;
14547         for (i = 0; i < n_tag; i++) {
14548 
14549           /* strings have a length of n_data */
14550           if (tag[i].type == TID_STRING) {
14551             printf("%s\t", data_buffer + offset);
14552             offset += tag[i].n_data;
14553           }
14554 
14555           else if (tag[i].n_data == 1) {
14556 
14557             /* non-array data */
14558             db_sprintf(str,
14559                        data_buffer + offset,
14560                        rpc_tid_size(tag[i].type), 0, tag[i].type);
14561             printf("%s\t", str);
14562             offset += rpc_tid_size(tag[i].type);
14563           }
14564 
14565           else
14566             /* loop over array data */
14567             for (j = 0; j < (INT) tag[i].n_data; j++) {
14568               db_sprintf(str,
14569                          data_buffer + offset,
14570                          rpc_tid_size(tag[i].type), 0, tag[i].type);
14571               printf("%s\t", str);
14572               offset += rpc_tid_size(tag[i].type);
14573             }
14574         }
14575         printf("\n");
14576       }
14577     }
14578 
14579     /* read next index record */
14580     i = read(fhi, (char *) &irec, sizeof(irec));
14581 
14582     /* end of file: search next history file */
14583     if (i <= 0) {
14584       close(fh);
14585       close(fhd);
14586       close(fhi);
14587 
14588       /* advance one day */
14589       tms = localtime((const time_t *) &last_irec_time);
14590       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14591       last_irec_time = mktime(tms);
14592       last_irec_time += 3600 * 24;
14593       if (last_irec_time > end_time)
14594         break;
14595 
14596       /* search next file */
14597       status = hs_search_file(&last_irec_time, 1);
14598       if (status != HS_SUCCESS)
14599         break;
14600 
14601       /* open history and definition files */
14602       hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14603       hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14604       hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14605       if (fh < 0 || fhd < 0 || fhi < 0) {
14606         cm_msg(MERROR, "hs_dump", "cannot open index files");
14607         break;
14608       }
14609 
14610       /* read first record */
14611       i = read(fhi, (char *) &irec, sizeof(irec));
14612       if (i <= 0)
14613         break;
14614 
14615       /* old definition becomes invalid */
14616       old_def_offset = -1;
14617     }
14618   } while (irec.time < end_time);
14619   M_FREE(tag);
14620   M_FREE(old_tag);
14621   close(fh);
14622   close(fhd);
14623   close(fhi);
14624   return HS_SUCCESS;
14625 }
14626 
14627 
14628 /********************************************************************/
14629 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14630 /********************************************************************\
14631 
14632   Routine: hs_fdump
14633 
14634   Purpose: Display history for a given history file
14635 
14636   Input:
14637     char   *file_name       Name of file to dump
14638     DWORD  event_id         Event ID
14639     BOOL   binary_time      Display DWORD time stamp
14640 
14641   Output:
14642     <screen output>
14643 
14644   Function value:
14645     HS_SUCCESS              Successful completion
14646     HS_FILE_ERROR           Cannot open history file
14647 
14648 \********************************************************************/
14649 {
14650   int fh;
14651   INT n;
14652   HIST_RECORD rec;
14653   char event_name[NAME_LENGTH];
14654   char str[80];
14655 
14656   /* open file, add O_BINARY flag for Windows NT */
14657   fh = open(file_name, O_RDONLY | O_BINARY, 0644);
14658   if (fh < 0) {
14659     cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
14660     return HS_FILE_ERROR;
14661   }
14662 
14663   /* loop over file records in .hst file */
14664   do {
14665     n = read(fh, (char *) &rec, sizeof(rec));
14666     if (n < sizeof(rec))
14667       break;
14668 
14669     /* check if record type is definition */
14670     if (rec.record_type == RT_DEF) {
14671 
14672       /* read name */
14673       read(fh, event_name, sizeof(event_name));
14674       if (rec.event_id == id || id == 0)
14675         printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
14676 
14677       /* skip tags */
14678       lseek(fh, rec.data_size, SEEK_CUR);
14679     }
14680 
14681     else {
14682 
14683       /* print data record */
14684       if (binary_time)
14685         sprintf(str, "%li ", rec.time);
14686 
14687       else {
14688         strcpy(str, ctime((const time_t *) &rec.time) + 4);
14689         str[15] = 0;
14690       } if (rec.event_id == id || id == 0)
14691         printf("ID %ld, %s, size %ld\n", rec.event_id, str, rec.data_size);
14692 
14693       /* skip data */
14694       lseek(fh, rec.data_size, SEEK_CUR);
14695     }
14696   } while (TRUE);
14697   close(fh);
14698   return HS_SUCCESS;
14699 }
14700 
14701 
14702 #endif                          /* OS_VXWORKS hs section */
14703 
14704 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14705 
14706 /** @} */// end of hsfunctionc
14707 
14708 /********************************************************************/
14709 /** @addtogroup elfunctionc
14710  *  
14711  *  @{  */
14712 
14713 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14714 
14715 /********************************************************************\
14716 *                                                                    *
14717 *               Electronic logbook functions                         *
14718 *                                                                    *
14719 \********************************************************************/
14720 
14721 /********************************************************************/
14722 void el_decode(char *message, char *key, char *result)
14723 {
14724   char *pc;
14725 
14726   if (result == NULL)
14727     return;
14728   *result = 0;
14729   if (strstr(message, key)) {
14730     for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
14731       *result++ = *pc++;
14732     *result = 0;
14733   }
14734 }
14735 
14736 
14737 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14738 
14739 /********************************************************************/
14740 /**
14741 Submit an ELog entry.
14742 @param run  Run Number.
14743 @param author Message author.
14744 @param type Message type.
14745 @param system Message system.
14746 @param subject Subject.
14747 @param text Message text.
14748 @param reply_to In reply to this message.
14749 @param encoding Text encoding, either HTML or plain.
14750 @param afilename1   File name of attachment.
14751 @param buffer1      File contents.
14752 @param buffer_size1 Size of buffer in bytes.
14753 @param afilename2   File name of attachment.
14754 @param buffer2      File contents.
14755 @param buffer_size2 Size of buffer in bytes.
14756 @param afilename3   File name of attachment.
14757 @param buffer3      File contents.
14758 @param buffer_size3 Size of buffer in bytes.
14759 @param tag          If given, edit existing message.
14760 @param tag_size     Maximum size of tag.
14761 @return EL_SUCCESS
14762 */
14763 INT el_submit(int run,
14764               char *author,
14765               char *type,
14766               char *system,
14767               char *subject,
14768               char *text,
14769               char *reply_to,
14770               char *encoding,
14771               char *afilename1,
14772               char *buffer1,
14773               INT buffer_size1,
14774               char *afilename2,
14775               char *buffer2,
14776               INT buffer_size2,
14777               char *afilename3,
14778               char *buffer3, INT buffer_size3, char *tag, INT tag_size)
14779 {
14780   if (rpc_is_remote())
14781     return rpc_call(RPC_EL_SUBMIT, run, author, type, system,
14782                     subject, text, reply_to, encoding,
14783                     afilename1, buffer1, buffer_size1,
14784                     afilename2, buffer2, buffer_size2,
14785                     afilename3, buffer3, buffer_size3, tag, tag_size);
14786 
14787 #ifdef LOCAL_ROUTINES
14788   {
14789     INT n, size, fh, status, run_number, mutex, buffer_size =
14790         0, index, offset = 0, tail_size = 0;
14791     struct tm *tms = NULL;
14792     char afilename[256], file_name[256], afile_name[3][256],
14793         dir[256], str[256], start_str[80], end_str[80], last[80],
14794         date[80], thread[80], attachment[256];
14795     HNDLE hDB;
14796     time_t now;
14797     char message[10000], *p, *buffer = NULL;
14798     BOOL bedit;
14799 
14800     cm_get_experiment_database(&hDB, NULL);
14801     bedit = (tag[0] != 0);
14802 
14803     /* request semaphore */
14804     cm_get_experiment_mutex(NULL, &mutex);
14805     ss_mutex_wait_for(mutex, 0);
14806 
14807     /* get run number from ODB if not given */
14808     if (run > 0)
14809       run_number = run;
14810 
14811     else {
14812 
14813       /* get run number */
14814       size = sizeof(run_number);
14815       status =
14816           db_get_value(hDB, 0, "/Runinfo/Run number",
14817                        &run_number, &size, TID_INT, TRUE);
14818       assert(status == SUCCESS);
14819     }
14820     if (run_number < 0) {
14821       cm_msg(MERROR, "el_submit",
14822              "aborting on attempt to use invalid run number %d",
14823              run_number);
14824       abort();
14825     }
14826     for (index = 0; index < 3; index++) {
14827 
14828       /* generate filename for attachment */
14829       afile_name[index][0] = file_name[0] = 0;
14830       if (index == 0) {
14831         strcpy(afilename, afilename1);
14832         buffer = buffer1;
14833         buffer_size = buffer_size1;
14834       }
14835 
14836       else if (index == 1) {
14837         strcpy(afilename, afilename2);
14838         buffer = buffer2;
14839         buffer_size = buffer_size2;
14840       }
14841 
14842       else if (index == 2) {
14843         strcpy(afilename, afilename3);
14844         buffer = buffer3;
14845         buffer_size = buffer_size3;
14846       }
14847       if (afilename[0]) {
14848         strcpy(file_name, afilename);
14849         p = file_name;
14850         while (strchr(p, ':'))
14851           p = strchr(p, ':') + 1;
14852         while (strchr(p, '\\'))
14853           p = strchr(p, '\\') + 1;      /* NT */
14854         while (strchr(p, '/'))
14855           p = strchr(p, '/') + 1;       /* Unix */
14856         while (strchr(p, ']'))
14857           p = strchr(p, ']') + 1;       /* VMS */
14858 
14859         /* assemble ELog filename */
14860         if (p[0]) {
14861           dir[0] = 0;
14862           if (hDB > 0) {
14863             size = sizeof(dir);
14864             memset(dir, 0, size);
14865             status =
14866                 db_get_value(hDB, 0,
14867                              "/Logger/Elog dir",
14868                              dir, &size, TID_STRING, FALSE);
14869             if (status != DB_SUCCESS)
14870               db_get_value(hDB, 0,
14871                            "/Logger/Data dir",
14872                            dir, &size, TID_STRING, TRUE);
14873             if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14874               strcat(dir, DIR_SEPARATOR_STR);
14875           }
14876 #if !defined(OS_VXWORKS)
14877 #if !defined(OS_VMS)
14878           tzset();
14879 
14880 #endif                          /*  */
14881 #endif                          /*  */
14882           time((time_t *) & now);
14883           tms = localtime((const time_t *) &now);
14884           strcpy(str, p);
14885           sprintf(afile_name[index],
14886                   "%02d%02d%02d_%02d%02d%02d_%s",
14887                   tms->tm_year % 100,
14888                   tms->tm_mon + 1, tms->tm_mday,
14889                   tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14890           sprintf(file_name,
14891                   "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
14892                   tms->tm_year % 100, tms->tm_mon + 1,
14893                   tms->tm_mday, tms->tm_hour, tms->tm_min,
14894                   tms->tm_sec, str);
14895 
14896           /* save attachment */
14897           fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14898           if (fh < 0) {
14899             cm_msg(MERROR, "el_submit",
14900                    "Cannot write attachment file \"%s\"", file_name);
14901           }
14902 
14903           else {
14904             write(fh, buffer, buffer_size);
14905             close(fh);
14906           }
14907         }
14908       }
14909     }
14910 
14911     /* generate new file name YYMMDD.log in data directory */
14912     cm_get_experiment_database(&hDB, NULL);
14913     size = sizeof(dir);
14914     memset(dir, 0, size);
14915     status =
14916         db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size,
14917                      TID_STRING, FALSE);
14918     if (status != DB_SUCCESS)
14919       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size,
14920                    TID_STRING, TRUE);
14921     if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14922       strcat(dir, DIR_SEPARATOR_STR);
14923 
14924 #if !defined(OS_VXWORKS)
14925 #if !defined(OS_VMS)
14926     tzset();
14927 
14928 #endif                          /*  */
14929 #endif                          /*  */
14930     if (bedit) {
14931 
14932       /* edit existing message */
14933       strcpy(str, tag);
14934       if (strchr(str, '.')) {
14935         offset = atoi(strchr(str, '.') + 1);
14936         *strchr(str, '.') = 0;
14937       }
14938       sprintf(file_name, "%s%s.log", dir, str);
14939       fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14940       if (fh < 0) {
14941         ss_mutex_release(mutex);
14942         return EL_FILE_ERROR;
14943       }
14944       lseek(fh, offset, SEEK_SET);
14945       read(fh, str, 16);
14946       size = atoi(str + 9);
14947       read(fh, message, size);
14948       el_decode(message, "Date: ", date);
14949       el_decode(message, "Thread: ", thread);
14950       el_decode(message, "Attachment: ", attachment);
14951 
14952       /* buffer tail of logfile */
14953       lseek(fh, 0, SEEK_END);
14954       tail_size = TELL(fh) - (offset + size);
14955       if (tail_size > 0) {
14956         buffer = (char *) M_MALLOC(tail_size);
14957         if (buffer == NULL) {
14958           close(fh);
14959           ss_mutex_release(mutex);
14960           return EL_FILE_ERROR;
14961         }
14962         lseek(fh, offset + size, SEEK_SET);
14963         n = read(fh, buffer, tail_size);
14964       }
14965       lseek(fh, offset, SEEK_SET);
14966     }
14967 
14968     else {
14969 
14970       /* create new message */
14971       time((time_t *) & now);
14972       tms = localtime((const time_t *) &now);
14973       sprintf(file_name, "%s%02d%02d%02d.log", dir,
14974               tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
14975       fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14976       if (fh < 0) {
14977         ss_mutex_release(mutex);
14978         return EL_FILE_ERROR;
14979       }
14980       strcpy(date, ctime(&now));
14981       date[24] = 0;
14982       if (reply_to[0])
14983         sprintf(thread, "%16s %16s", reply_to, "0");
14984 
14985       else
14986         sprintf(thread, "%16s %16s", "0", "0");
14987       lseek(fh, 0, SEEK_END);
14988     }
14989 
14990     /* compose message */
14991     sprintf(message, "Date: %s\n", date);
14992     sprintf(message + strlen(message), "Thread: %s\n", thread);
14993     sprintf(message + strlen(message), "Run: %d\n", run_number);
14994     sprintf(message + strlen(message), "Author: %s\n", author);
14995     sprintf(message + strlen(message), "Type: %s\n", type);
14996     sprintf(message + strlen(message), "System: %s\n", system);
14997     sprintf(message + strlen(message), "Subject: %s\n", subject);
14998 
14999     /* keep original attachment if edit and no new attachment */
15000     if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0
15001         && afile_name[2][0] == 0)
15002       sprintf(message + strlen(message), "Attachment: %s", attachment);
15003 
15004     else {
15005       sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
15006       if (afile_name[1][0])
15007         sprintf(message + strlen(message), ",%s", afile_name[1]);
15008       if (afile_name[2][0])
15009         sprintf(message + strlen(message), ",%s", afile_name[2]);
15010     }
15011     sprintf(message + strlen(message), "\n");
15012     sprintf(message + strlen(message), "Encoding: %s\n", encoding);
15013     sprintf(message + strlen(message),
15014             "========================================\n");
15015     strcat(message, text);
15016     assert(strlen(message) < sizeof(message));  // bomb out on array overrun.
15017     size = 0;
15018     sprintf(start_str, "$Start$: %6d\n", size);
15019     sprintf(end_str, "$End$:   %6d\n\f", size);
15020     size = strlen(message) + strlen(start_str) + strlen(end_str);
15021     if (tag != NULL && !bedit)
15022       sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100,
15023               tms->tm_mon + 1, tms->tm_mday, (int) TELL(fh));
15024     sprintf(start_str, "$Start$: %6d\n", size);
15025     sprintf(end_str, "$End$:   %6d\n\f", size);
15026     write(fh, start_str, strlen(start_str));
15027     write(fh, message, strlen(message));
15028     write(fh, end_str, strlen(end_str));
15029     if (bedit) {
15030       if (tail_size > 0) {
15031         n = write(fh, buffer, tail_size);
15032         M_FREE(buffer);
15033       }
15034 
15035       /* truncate file here */
15036 #ifdef OS_WINNT
15037       chsize(fh, TELL(fh));
15038 
15039 #else                           /*  */
15040       ftruncate(fh, TELL(fh));
15041 
15042 #endif                          /*  */
15043     }
15044     close(fh);
15045 
15046     /* if reply, mark original message */
15047     if (reply_to[0] && !bedit) {
15048       strcpy(last, reply_to);
15049 
15050       do {
15051         status = el_search_message(last, &fh, FALSE);
15052         if (status == EL_SUCCESS) {
15053 
15054           /* position to next thread location */
15055           lseek(fh, 72, SEEK_CUR);
15056           memset(str, 0, sizeof(str));
15057           read(fh, str, 16);
15058           lseek(fh, -16, SEEK_CUR);
15059 
15060           /* if no reply yet, set it */
15061           if (atoi(str) == 0) {
15062             sprintf(str, "%16s", tag);
15063             write(fh, str, 16);
15064             close(fh);
15065             break;
15066           }
15067 
15068           else {
15069 
15070             /* if reply set, find last one in chain */
15071             strcpy(last, strtok(str, " "));
15072             close(fh);
15073           }
15074         }
15075 
15076         else
15077           /* stop on error */
15078           break;
15079       } while (TRUE);
15080     }
15081 
15082     /* release elog mutex */
15083     ss_mutex_release(mutex);
15084   }
15085 
15086 #endif                          /* LOCAL_ROUTINES */
15087   return EL_SUCCESS;
15088 }
15089 
15090 
15091 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15092 
15093 /********************************************************************/
15094 INT el_search_message(char *tag, int *fh, BOOL walk)
15095 {
15096   int i, size, offset, direction, last, status;
15097   struct tm *tms, ltms;
15098   DWORD lt, ltime, lact;
15099   char str[256], file_name[256], dir[256];
15100   HNDLE hDB;
15101 
15102 #if !defined(OS_VXWORKS)
15103 #if !defined(OS_VMS)
15104   tzset();
15105 
15106 #endif                          /*  */
15107 #endif                          /*  */
15108 
15109   /* open file */
15110   cm_get_experiment_database(&hDB, NULL);
15111   size = sizeof(dir);
15112   memset(dir, 0, size);
15113   status =
15114       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
15115                    FALSE);
15116   if (status != DB_SUCCESS)
15117     db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15118   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15119     strcat(dir, DIR_SEPARATOR_STR);
15120 
15121   /* check tag for direction */
15122   direction = 0;
15123   if (strpbrk(tag, "+-")) {
15124     direction = atoi(strpbrk(tag, "+-"));
15125     *strpbrk(tag, "+-") = 0;
15126   }
15127 
15128   /* if tag is given, open file directly */
15129   if (tag[0]) {
15130 
15131     /* extract time structure from tag */
15132     tms = &ltms;
15133     memset(tms, 0, sizeof(struct tm));
15134     tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
15135     tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
15136     tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
15137     tms->tm_hour = 12;
15138     if (tms->tm_year < 90)
15139       tms->tm_year += 100;
15140     ltime = lt = mktime(tms);
15141     strcpy(str, tag);
15142     if (strchr(str, '.')) {
15143       offset = atoi(strchr(str, '.') + 1);
15144       *strchr(str, '.') = 0;
15145     }
15146 
15147     else
15148       return EL_FILE_ERROR;
15149 
15150     do {
15151       tms = localtime((const time_t *) &ltime);
15152       sprintf(file_name, "%s%02d%02d%02d.log", dir,
15153               tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15154       *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15155       if (*fh < 0) {
15156         if (!walk)
15157           return EL_FILE_ERROR;
15158         if (direction == -1)
15159           ltime -= 3600 * 24;   /* one day back */
15160 
15161         else
15162           ltime += 3600 * 24;   /* go forward one day */
15163 
15164         /* set new tag */
15165         tms = localtime((const time_t *) &ltime);
15166         sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100,
15167                 tms->tm_mon + 1, tms->tm_mday);
15168       }
15169 
15170       /* in forward direction, stop today */
15171       if (direction != -1 && ltime > (DWORD) time(NULL) + 3600 * 24)
15172         break;
15173 
15174       /* in backward direction, go back 10 years */
15175       if (direction == -1
15176           && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
15177         break;
15178     } while (*fh < 0);
15179     if (*fh < 0)
15180       return EL_FILE_ERROR;
15181     lseek(*fh, offset, SEEK_SET);
15182 
15183     /* check if start of message */
15184     i = read(*fh, str, 15);
15185     if (i <= 0) {
15186       close(*fh);
15187       return EL_FILE_ERROR;
15188     }
15189     if (strncmp(str, "$Start$: ", 9) != 0) {
15190       close(*fh);
15191       return EL_FILE_ERROR;
15192     }
15193     lseek(*fh, offset, SEEK_SET);
15194   }
15195 
15196   /* open most recent file if no tag given */
15197   if (tag[0] == 0) {
15198     time((long *) &lt);
15199     ltime = lt;
15200 
15201     do {
15202       tms = localtime((const time_t *) &ltime);
15203       sprintf(file_name, "%s%02d%02d%02d.log", dir,
15204               tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15205       *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15206       if (*fh < 0)
15207         ltime -= 3600 * 24;     /* one day back */
15208     } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
15209     if (*fh < 0)
15210       return EL_FILE_ERROR;
15211 
15212     /* remember tag */
15213     sprintf(tag, "%02d%02d%02d", tms->tm_year % 100,
15214             tms->tm_mon + 1, tms->tm_mday);
15215     lseek(*fh, 0, SEEK_END);
15216     sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
15217   }
15218   if (direction == -1) {
15219 
15220     /* seek previous message */
15221     if (TELL(*fh) == 0) {
15222 
15223       /* go back one day */
15224       close(*fh);
15225       lt = ltime;
15226 
15227       do {
15228         lt -= 3600 * 24;
15229         tms = localtime((const time_t *) &lt);
15230         sprintf(str, "%02d%02d%02d.0",
15231                 tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15232         status = el_search_message(str, fh, FALSE);
15233       } while (status != EL_SUCCESS &&
15234                (INT) ltime - (INT) lt < 3600 * 24 * 365);
15235       if (status != EL_SUCCESS)
15236         return EL_FIRST_MSG;
15237 
15238       /* adjust tag */
15239       strcpy(tag, str);
15240 
15241       /* go to end of current file */
15242       lseek(*fh, 0, SEEK_END);
15243     }
15244 
15245     /* read previous message size */
15246     lseek(*fh, -17, SEEK_CUR);
15247     i = read(*fh, str, 17);
15248     if (i <= 0) {
15249       close(*fh);
15250       return EL_FILE_ERROR;
15251     }
15252     if (strncmp(str, "$End$: ", 7) == 0) {
15253       size = atoi(str + 7);
15254       lseek(*fh, -size, SEEK_CUR);
15255     }
15256 
15257     else {
15258       close(*fh);
15259       return EL_FILE_ERROR;
15260     }
15261 
15262     /* adjust tag */
15263     sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15264   }
15265   if (direction == 1) {
15266 
15267     /* seek next message */
15268 
15269     /* read current message size */
15270     last = TELL(*fh);
15271     i = read(*fh, str, 15);
15272     if (i <= 0) {
15273       close(*fh);
15274       return EL_FILE_ERROR;
15275     }
15276     lseek(*fh, -15, SEEK_CUR);
15277     if (strncmp(str, "$Start$: ", 9) == 0) {
15278       size = atoi(str + 9);
15279       lseek(*fh, size, SEEK_CUR);
15280     }
15281 
15282     else {
15283       close(*fh);
15284       return EL_FILE_ERROR;
15285     }
15286 
15287     /* if EOF, goto next day */
15288     i = read(*fh, str, 15);
15289     if (i < 15) {
15290       close(*fh);
15291       time((long *) &lact);
15292       lt = ltime;
15293 
15294       do {
15295         lt += 3600 * 24;
15296         tms = localtime((const time_t *) &lt);
15297         sprintf(str, "%02d%02d%02d.0",
15298                 tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15299         status = el_search_message(str, fh, FALSE);
15300       } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
15301       if (status != EL_SUCCESS)
15302         return EL_LAST_MSG;
15303 
15304       /* adjust tag */
15305       strcpy(tag, str);
15306 
15307       /* go to beginning of current file */
15308       lseek(*fh, 0, SEEK_SET);
15309     }
15310 
15311     else
15312       lseek(*fh, -15, SEEK_CUR);
15313 
15314     /* adjust tag */
15315     sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15316   }
15317   return EL_SUCCESS;
15318 }
15319 
15320 
15321 /********************************************************************/
15322 INT el_retrieve(char *tag,
15323                 char *date,
15324                 int *run,
15325                 char *author,
15326                 char *type,
15327                 char *system,
15328                 char *subject,
15329                 char *text,
15330                 int *textsize,
15331                 char *orig_tag,
15332                 char *reply_tag,
15333                 char *attachment1,
15334                 char *attachment2, char *attachment3, char *encoding)
15335 /********************************************************************\
15336 
15337   Routine: el_retrieve
15338 
15339   Purpose: Retrieve an ELog entry by its message tab
15340 
15341   Input:
15342     char   *tag             tag in the form YYMMDD.offset
15343     int    *size            Size of text buffer
15344 
15345   Output:
15346     char   *tag             tag of retrieved message
15347     char   *date            Date/time of message recording
15348     int    *run             Run number
15349     char   *author          Message author
15350     char   *type            Message type
15351     char   *system          Message system
15352     char   *subject         Subject
15353     char   *text            Message text
15354     char   *orig_tag        Original message if this one is a reply
15355     char   *reply_tag       Reply for current message
15356     char   *attachment1/2/3 File attachment
15357     char   *encoding        Encoding of message
15358     int    *size            Actual message text size
15359 
15360   Function value:
15361     EL_SUCCESS              Successful completion
15362     EL_LAST_MSG             Last message in log
15363 
15364 \********************************************************************/
15365 {
15366   int size, fh, offset, search_status;
15367   char str[256], *p;
15368   char message[10000], thread[256], attachment_all[256];
15369 
15370   if (tag[0]) {
15371     search_status = el_search_message(tag, &fh, TRUE);
15372     if (search_status != EL_SUCCESS)
15373       return search_status;
15374   }
15375 
15376   else {
15377 
15378     /* open most recent message */
15379     strcpy(tag, "-1");
15380     search_status = el_search_message(tag, &fh, TRUE);
15381     if (search_status != EL_SUCCESS)
15382       return search_status;
15383   }
15384 
15385   /* extract message size */
15386   offset = TELL(fh);
15387   read(fh, str, 16);
15388   size = atoi(str + 9);
15389   memset(message, 0, sizeof(message));
15390   read(fh, message, size);
15391   close(fh);
15392 
15393   /* decode message */
15394   if (strstr(message, "Run: ") && run)
15395     *run = atoi(strstr(message, "Run: ") + 5);
15396   el_decode(message, "Date: ", date);
15397   el_decode(message, "Thread: ", thread);
15398   el_decode(message, "Author: ", author);
15399   el_decode(message, "Type: ", type);
15400   el_decode(message, "System: ", system);
15401   el_decode(message, "Subject: ", subject);
15402   el_decode(message, "Attachment: ", attachment_all);
15403   el_decode(message, "Encoding: ", encoding);
15404 
15405   /* break apart attachements */
15406   if (attachment1 && attachment2 && attachment3) {
15407     attachment1[0] = attachment2[0] = attachment3[0] = 0;
15408     p = strtok(attachment_all, ",");
15409     if (p != NULL) {
15410       strcpy(attachment1, p);
15411       p = strtok(NULL, ",");
15412       if (p != NULL) {
15413         strcpy(attachment2, p);
15414         p = strtok(NULL, ",");
15415         if (p != NULL)
15416           strcpy(attachment3, p);
15417       }
15418     }
15419   }
15420 
15421   /* conver thread in reply-to and reply-from */
15422   if (orig_tag != NULL && reply_tag != NULL) {
15423     p = strtok(thread, " \r");
15424     if (p != NULL)
15425       strcpy(orig_tag, p);
15426 
15427     else
15428       strcpy(orig_tag, "");
15429     p = strtok(NULL, " \r");
15430     if (p != NULL)
15431       strcpy(reply_tag, p);
15432 
15433     else
15434       strcpy(reply_tag, "");
15435     if (atoi(orig_tag) == 0)
15436       orig_tag[0] = 0;
15437     if (atoi(reply_tag) == 0)
15438       reply_tag[0] = 0;
15439   }
15440   p = strstr(message, "========================================\n");
15441   if (text != NULL) {
15442     if (p != NULL) {
15443       p += 41;
15444       if ((int) strlen(p) >= *textsize) {
15445         strncpy(text, p, *textsize - 1);
15446         text[*textsize - 1] = 0;
15447         return EL_TRUNCATED;
15448       }
15449 
15450       else {
15451         strcpy(text, p);
15452 
15453         /* strip end tag */
15454         if (strstr(text, "$End$"))
15455           *strstr(text, "$End$") = 0;
15456         *textsize = strlen(text);
15457       }
15458     }
15459 
15460     else {
15461       text[0] = 0;
15462       *textsize = 0;
15463     }
15464   }
15465   if (search_status == EL_LAST_MSG)
15466     return EL_LAST_MSG;
15467   return EL_SUCCESS;
15468 }
15469 
15470 
15471 /********************************************************************/
15472 INT el_search_run(int run, char *return_tag)
15473 /********************************************************************\
15474 
15475   Routine: el_search_run
15476 
15477   Purpose: Find first message belonging to a specific run
15478 
15479   Input:
15480     int    run              Run number
15481 
15482   Output:
15483     char   *tag             tag of retrieved message
15484 
15485   Function value:
15486     EL_SUCCESS              Successful completion
15487     EL_LAST_MSG             Last message in log
15488 
15489 \********************************************************************/
15490 {
15491   int actual_run, fh, status;
15492   char tag[256];
15493 
15494   tag[0] = return_tag[0] = 0;
15495 
15496   do {
15497 
15498     /* open first message in file */
15499     strcat(tag, "-1");
15500     status = el_search_message(tag, &fh, TRUE);
15501     if (status == EL_FIRST_MSG)
15502       break;
15503     if (status != EL_SUCCESS)
15504       return status;
15505     close(fh);
15506     if (strchr(tag, '.') != NULL)
15507       strcpy(strchr(tag, '.'), ".0");
15508     el_retrieve(tag, NULL, &actual_run, NULL, NULL, NULL, NULL,
15509                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15510   } while (actual_run >= run);
15511   while (actual_run < run) {
15512     strcat(tag, "+1");
15513     status = el_search_message(tag, &fh, TRUE);
15514     if (status == EL_LAST_MSG)
15515       break;
15516     if (status != EL_SUCCESS)
15517       return status;
15518     close(fh);
15519     el_retrieve(tag, NULL, &actual_run, NULL, NULL, NULL, NULL,
15520                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15521   }
15522   strcpy(return_tag, tag);
15523   if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15524     return status;
15525   return EL_SUCCESS;
15526 }
15527 
15528 
15529 /********************************************************************/
15530 INT el_delete_message(char *tag)
15531 /********************************************************************\
15532 
15533   Routine: el_submit
15534 
15535   Purpose: Submit an ELog entry
15536 
15537   Input:
15538     char   *tag             Message tage
15539 
15540   Output:
15541     <none>
15542 
15543   Function value:
15544     EL_SUCCESS              Successful completion
15545 
15546 \********************************************************************/
15547 {
15548 
15549 #ifdef LOCAL_ROUTINES
15550   INT n, size, fh, mutex, offset = 0, tail_size, status;
15551   char dir[256], str[256], file_name[256];
15552   HNDLE hDB;
15553   char *buffer = NULL;
15554 
15555   cm_get_experiment_database(&hDB, NULL);
15556 
15557   /* request semaphore */
15558   cm_get_experiment_mutex(NULL, &mutex);
15559   ss_mutex_wait_for(mutex, 0);
15560 
15561   /* generate file name YYMMDD.log in data directory */
15562   cm_get_experiment_database(&hDB, NULL);
15563   size = sizeof(dir);
15564   memset(dir, 0, size);
15565   status =
15566       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
15567                    FALSE);
15568   if (status != DB_SUCCESS)
15569     db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15570   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15571     strcat(dir, DIR_SEPARATOR_STR);
15572   strcpy(str, tag);
15573   if (strchr(str, '.')) {
15574     offset = atoi(strchr(str, '.') + 1);
15575     *strchr(str, '.') = 0;
15576   }
15577   sprintf(file_name, "%s%s.log", dir, str);
15578   fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15579   if (fh < 0) {
15580     ss_mutex_release(mutex);
15581     return EL_FILE_ERROR;
15582   }
15583   lseek(fh, offset, SEEK_SET);
15584   read(fh, str, 16);
15585   size = atoi(str + 9);
15586 
15587   /* buffer tail of logfile */
15588   lseek(fh, 0, SEEK_END);
15589   tail_size = TELL(fh) - (offset + size);
15590   if (tail_size > 0) {
15591     buffer = (char *) M_MALLOC(tail_size);
15592     if (buffer == NULL) {
15593       close(fh);
15594       ss_mutex_release(mutex);
15595       return EL_FILE_ERROR;
15596     }
15597     lseek(fh, offset + size, SEEK_SET);
15598     n = read(fh, buffer, tail_size);
15599   }
15600   lseek(fh, offset, SEEK_SET);
15601   if (tail_size > 0) {
15602     n = write(fh, buffer, tail_size);
15603     M_FREE(buffer);
15604   }
15605 
15606   /* truncate file here */
15607 #ifdef OS_WINNT
15608   chsize(fh, TELL(fh));
15609 
15610 #else                           /*  */
15611   ftruncate(fh, TELL(fh));
15612 
15613 #endif                          /*  */
15614 
15615   /* if file length gets zero, delete file */
15616   tail_size = lseek(fh, 0, SEEK_END);
15617   close(fh);
15618   if (tail_size == 0)
15619     remove(file_name);
15620 
15621   /* release elog mutex */
15622   ss_mutex_release(mutex);
15623 
15624 #endif                          /* LOCAL_ROUTINES */
15625   return EL_SUCCESS;
15626 }
15627 
15628 
15629 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15630 
15631 /** @} */// end of elfunctionc
15632 
15633 /********************************************************************/
15634 /** @addtogroup alfunctionc
15635  *  
15636  *  @{  */
15637 
15638 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15639 
15640 /********************************************************************\
15641 *                                                                    *
15642 *                     Alarm functions                                *
15643 *                                                                    *
15644 \********************************************************************/
15645 
15646 /********************************************************************/
15647 BOOL al_evaluate_condition(char *condition, char *value)
15648 {
15649   HNDLE hDB, hkey;
15650   int i, j, index, size;
15651   KEY key;
15652   double value1, value2;
15653   char str[256], op[3], function[80];
15654   char data[10000];
15655   DWORD time;
15656 
15657   strcpy(str, condition);
15658   op[1] = op[2] = 0;
15659   value1 = value2 = 0;
15660   index = 0;
15661 
15662   /* find value and operator */
15663   for (i = strlen(str) - 1; i > 0; i--)
15664     if (strchr("<>=!", str[i]) != NULL)
15665       break;
15666   op[0] = str[i];
15667   value2 = atof(str + i + 1);
15668   str[i] = 0;
15669   if (i > 0 && strchr("<>=!", str[i - 1])) {
15670     op[1] = op[0];
15671     op[0] = str[--i];
15672     str[i] = 0;
15673   }
15674   i--;
15675   while (i > 0 && str[i] == ' ')
15676     i--;
15677   str[i + 1] = 0;
15678 
15679   /* check if function */
15680   function[0] = 0;
15681   if (str[i] == ')') {
15682     str[i--] = 0;
15683     if (strchr(str, '(')) {
15684       *strchr(str, '(') = 0;
15685       strcpy(function, str);
15686       for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
15687         str[j] = str[i];
15688       str[j] = 0;
15689       i = j - 1;
15690     }
15691   }
15692 
15693   /* find key */
15694   if (str[i] == ']') {
15695     str[i--] = 0;
15696     while (i > 0 && isdigit(str[i]))
15697       i--;
15698     index = atoi(str + i + 1);
15699     str[i] = 0;
15700   }
15701   cm_get_experiment_database(&hDB, NULL);
15702   db_find_key(hDB, 0, str, &hkey);
15703   if (!hkey) {
15704     cm_msg(MERROR, "al_evaluate_condition",
15705            "Cannot find key %s to evaluate alarm condition", str);
15706     if (value)
15707       strcpy(value, "unknown");
15708     return FALSE;
15709   }
15710   if (equal_ustring(function, "access")) {
15711 
15712     /* check key access time */
15713     db_get_key_time(hDB, hkey, &time);
15714     sprintf(str, "%ld", time);
15715     value1 = atof(str);
15716   }
15717 
15718   else {
15719 
15720     /* get key data and convert to double */
15721     db_get_key(hDB, hkey, &key);
15722     size = sizeof(data);
15723     db_get_data(hDB, hkey, data, &size, key.type);
15724     db_sprintf(str, data, size, index, key.type);
15725     value1 = atof(str);
15726   }
15727 
15728   /* return value */
15729   if (value)
15730     strcpy(value, str);
15731 
15732   /* now do logical operation */
15733   if (strcmp(op, "=") == 0)
15734     return value1 == value2;
15735   if (strcmp(op, "==") == 0)
15736     return value1 == value2;
15737   if (strcmp(op, "!=") == 0)
15738     return value1 != value2;
15739   if (strcmp(op, "<") == 0)
15740     return value1 < value2;
15741   if (strcmp(op, ">") == 0)
15742     return value1 > value2;
15743   if (strcmp(op, "<=") == 0)
15744     return value1 <= value2;
15745   if (strcmp(op, ">=") == 0)
15746     return value1 >= value2;
15747   return FALSE;
15748 }
15749 
15750 
15751 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15752 
15753 /********************************************************************/
15754 /**
15755 Trigger a certain alarm.
15756 \code  ...
15757   lazy.alarm[0] = 0;
15758   size = sizeof(lazy.alarm);
15759   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
15760 
15761   // trigger alarm if defined
15762   if (lazy.alarm[0])
15763     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
15764   ...
15765 \endcode
15766 @param alarm_name Alarm name, defined in /alarms/alarms
15767 @param alarm_message Optional message which goes with alarm
15768 @param default_class If alarm is not yet defined under
15769                     /alarms/alarms/<alarm_name>, a new one
15770                     is created and this default class is used.
15771 @param cond_str String displayed in alarm condition
15772 @param type Alarm type, one of AT_xxx
15773 @return AL_SUCCESS, AL_INVALID_NAME
15774 */
15775 INT al_trigger_alarm(char *alarm_name,
15776                      char *alarm_message,
15777                      char *default_class, char *cond_str, INT type)
15778 {
15779   if (rpc_is_remote())
15780     return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name,
15781                     alarm_message, default_class, cond_str, type);
15782 
15783 #ifdef LOCAL_ROUTINES
15784   {
15785     int status, size;
15786     HNDLE hDB, hkeyalarm;
15787     char str[256];
15788     ALARM alarm;
15789     BOOL flag;
15790 
15791     ALARM_ODB_STR(alarm_odb_str);
15792     cm_get_experiment_database(&hDB, NULL);
15793 
15794     /* check online mode */
15795     flag = TRUE;
15796     size = sizeof(flag);
15797     db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size,
15798                  TID_INT, TRUE);
15799     if (!flag)
15800       return AL_SUCCESS;
15801 
15802     /* find alarm */
15803     sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15804     db_find_key(hDB, 0, str, &hkeyalarm);
15805     if (!hkeyalarm) {
15806 
15807       /* alarm must be an internal analyzer alarm, so create a default alarm */
15808       status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
15809       db_find_key(hDB, 0, str, &hkeyalarm);
15810       if (!hkeyalarm) {
15811         cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
15812         return AL_ERROR_ODB;
15813       }
15814       if (default_class && default_class[0])
15815         db_set_value(hDB, hkeyalarm, "Alarm Class",
15816                      default_class, 32, 1, TID_STRING);
15817       status = TRUE;
15818       db_set_value(hDB, hkeyalarm, "Active", &status,
15819                    sizeof(status), 1, TID_BOOL);
15820     }
15821 
15822     /* set parameters for internal alarms */
15823     if (type != AT_EVALUATED && type != AT_PERIODIC) {
15824       db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
15825       strcpy(str, cond_str);
15826       db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
15827     }
15828     size = sizeof(alarm);
15829     status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15830     if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15831 
15832       /* make sure alarm record has right structure */
15833       db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
15834       size = sizeof(alarm);
15835       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15836       if (status != DB_SUCCESS) {
15837         cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
15838         return AL_ERROR_ODB;
15839       }
15840     }
15841 
15842     /* if internal alarm, check if active and check interval */
15843     if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15844 
15845       /* check global alarm flag */
15846       flag = TRUE;
15847       size = sizeof(flag);
15848       db_get_value(hDB, 0, "/Alarms/Alarm system active",
15849                    &flag, &size, TID_BOOL, TRUE);
15850       if (!flag)
15851         return AL_SUCCESS;
15852       if (!alarm.active)
15853         return AL_SUCCESS;
15854       if ((INT) ss_time() - (INT) alarm.checked_last <
15855           alarm.check_interval)
15856         return AL_SUCCESS;
15857 
15858       /* now the alarm will be triggered, so save time */
15859       alarm.checked_last = ss_time();
15860     }
15861 
15862     /* write back alarm message for internal alarms */
15863     if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15864       strncpy(alarm.alarm_message, alarm_message, 79);
15865       alarm.alarm_message[79] = 0;
15866     }
15867 
15868     /* now trigger alarm class defined in this alarm */
15869     if (alarm.alarm_class[0])
15870       al_trigger_class(alarm.alarm_class, alarm_message,
15871                        alarm.triggered > 0);
15872 
15873     /* signal alarm being triggered */
15874     cm_asctime(str, sizeof(str));
15875     if (!alarm.triggered)
15876       strcpy(alarm.time_triggered_first, str);
15877     alarm.triggered++;
15878     strcpy(alarm.time_triggered_last, str);
15879     alarm.checked_last = ss_time();
15880     status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
15881     if (status != DB_SUCCESS) {
15882       cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
15883       return AL_ERROR_ODB;
15884     }
15885   }
15886 
15887 #endif                          /* LOCAL_ROUTINES */
15888   return AL_SUCCESS;
15889 }
15890 
15891 
15892 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15893 
15894 /********************************************************************/
15895 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
15896 /********************************************************************\
15897 
15898   Routine: al_trigger_class
15899 
15900   Purpose: Trigger a certain alarm class
15901 
15902   Input:
15903     char   *alarm_class     Alarm class, must be defined in
15904                             /alarms/classes
15905     char   *alarm_message   Optional message which goes with alarm
15906     BOOL   first            TRUE if alarm is triggered first time
15907                             (used for elog)
15908 
15909   Output:
15910 
15911   Function value:
15912     AL_INVALID_NAME         Alarm class not defined
15913     AL_SUCCESS              Successful completion
15914 
15915 \********************************************************************/
15916 {
15917   int status, size, state;
15918   HNDLE hDB, hkeyclass;
15919   char str[256], command[256], tag[32];
15920   ALARM_CLASS ac;
15921 
15922   cm_get_experiment_database(&hDB, NULL);
15923 
15924   /* get alarm class */
15925   sprintf(str, "/Alarms/Classes/%s", alarm_class);
15926   db_find_key(hDB, 0, str, &hkeyclass);
15927   if (!hkeyclass) {
15928     cm_msg(MERROR, "al_trigger_class",
15929            "Alarm class %s not found in ODB", alarm_class);
15930     return AL_INVALID_NAME;
15931   }
15932   size = sizeof(ac);
15933   status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
15934   if (status != DB_SUCCESS) {
15935     cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
15936     return AL_ERROR_ODB;
15937   }
15938 
15939   /* write system message */
15940   if (ac.write_system_message &&
15941       (INT) ss_time() - (INT) ac.system_message_last >
15942       ac.system_message_interval) {
15943     sprintf(str, "%s: %s", alarm_class, alarm_message);
15944     cm_msg(MTALK, "al_trigger_class", str);
15945     ac.system_message_last = ss_time();
15946   }
15947 
15948   /* write elog message on first trigger */
15949   if (ac.write_elog_message && first)
15950     el_submit(0, "Alarm system", "Alarm", "General", alarm_class,
15951               str, "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
15952 
15953   /* execute command */
15954   if (ac.execute_command[0] && ac.execute_interval > 0 &&
15955       (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
15956     sprintf(str, "%s: %s", alarm_class, alarm_message);
15957     sprintf(command, ac.execute_command, str);
15958     cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
15959     ss_system(command);
15960     ac.execute_last = ss_time();
15961   }
15962 
15963   /* stop run */
15964   if (ac.stop_run) {
15965     state = STATE_STOPPED;
15966     size = sizeof(state);
15967     db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
15968     if (state != STATE_STOPPED)
15969       cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
15970   }
15971   status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
15972   if (status != DB_SUCCESS) {
15973     cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
15974     return AL_ERROR_ODB;
15975   }
15976   return AL_SUCCESS;
15977 }
15978 
15979 
15980 /********************************************************************/
15981 INT al_reset_alarm(char *alarm_name)
15982 /********************************************************************\
15983 
15984   Routine: al_reset_alarm
15985 
15986   Purpose: Reset (acknowledge) alarm
15987 
15988   Input:
15989     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
15990                             If NULL reset all alarms
15991 
15992   Output:
15993     <none>
15994 
15995   Function value:
15996     AL_INVALID_NAME         Alarm name not defined
15997     AL_RESET                Alarm was triggered and reset
15998     AL_SUCCESS              Successful completion
15999 
16000 \********************************************************************/
16001 {
16002   int status, size, i;
16003   HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
16004   KEY key;
16005   char str[256];
16006   ALARM alarm;
16007   ALARM_CLASS ac;
16008 
16009   cm_get_experiment_database(&hDB, NULL);
16010   if (alarm_name == NULL) {
16011 
16012     /* reset all alarms */
16013     db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
16014     if (hkeyalarm) {
16015       for (i = 0;; i++) {
16016         db_enum_link(hDB, hkeyalarm, i, &hsubkey);
16017         if (!hsubkey)
16018           break;
16019         db_get_key(hDB, hsubkey, &key);
16020         al_reset_alarm(key.name);
16021       }
16022     }
16023     return AL_SUCCESS;
16024   }
16025 
16026   /* find alarm and alarm class */
16027   sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16028   db_find_key(hDB, 0, str, &hkeyalarm);
16029   if (!hkeyalarm) {
16030     cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB",
16031            alarm_name);
16032     return AL_INVALID_NAME;
16033   }
16034   size = sizeof(alarm);
16035   status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16036   if (status != DB_SUCCESS) {
16037     cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
16038     return AL_ERROR_ODB;
16039   }
16040   sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
16041   db_find_key(hDB, 0, str, &hkeyclass);
16042   if (!hkeyclass) {
16043     cm_msg(MERROR, "al_reset_alarm",
16044            "Alarm class %s not found in ODB", alarm.alarm_class);
16045     return AL_INVALID_NAME;
16046   }
16047   size = sizeof(ac);
16048   status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16049   if (status != DB_SUCCESS) {
16050     cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
16051     return AL_ERROR_ODB;
16052   }
16053   if (alarm.triggered) {
16054     alarm.triggered = 0;
16055     alarm.time_triggered_first[0] = 0;
16056     alarm.time_triggered_last[0] = 0;
16057     alarm.checked_last = 0;
16058     ac.system_message_last = 0;
16059     ac.execute_last = 0;
16060     status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16061     if (status != DB_SUCCESS) {
16062       cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
16063       return AL_ERROR_ODB;
16064     }
16065     status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16066     if (status != DB_SUCCESS) {
16067       cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
16068       return AL_ERROR_ODB;
16069     }
16070     return AL_RESET;
16071   }
16072   return AL_SUCCESS;
16073 }
16074 
16075 
16076 /********************************************************************/
16077 INT al_check()
16078 /********************************************************************\
16079 
16080   Routine: al_scan
16081 
16082   Purpose: Scan ODB alarams and programs
16083 
16084   Input:
16085 
16086   Output:
16087 
16088   Function value:
16089     AL_SUCCESS              Successful completion
16090 
16091 \********************************************************************/
16092 {
16093   if (rpc_is_remote())
16094     return rpc_call(RPC_AL_CHECK);
16095 
16096 #ifdef LOCAL_ROUTINES
16097   {
16098     INT i, status, size, mutex;
16099     HNDLE hDB, hkeyroot, hkey;
16100     KEY key;
16101     ALARM alarm;
16102     char str[256], value[256];
16103     DWORD now;
16104     PROGRAM_INFO program_info;
16105     BOOL flag;
16106 
16107     ALARM_CLASS_STR(alarm_class_str);
16108     ALARM_ODB_STR(alarm_odb_str);
16109     ALARM_PERIODIC_STR(alarm_periodic_str);
16110     cm_get_experiment_database(&hDB, NULL);
16111     if (hDB == 0)
16112       return AL_SUCCESS;        /* called from server not yet connected */
16113 
16114     /* check online mode */
16115     flag = TRUE;
16116     size = sizeof(flag);
16117     db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size,
16118                  TID_INT, TRUE);
16119     if (!flag)
16120       return AL_SUCCESS;
16121 
16122     /* check global alarm flag */
16123     flag = TRUE;
16124     size = sizeof(flag);
16125     db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag,
16126                  &size, TID_BOOL, TRUE);
16127     if (!flag)
16128       return AL_SUCCESS;
16129 
16130     /* request semaphore */
16131     cm_get_experiment_mutex(&mutex, NULL);
16132     status = ss_mutex_wait_for(mutex, 100);
16133     if (status != SS_SUCCESS)
16134       return SUCCESS;           /* someone else is doing alarm business */
16135 
16136     /* check ODB alarms */
16137     db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16138     if (!hkeyroot) {
16139 
16140       /* create default ODB alarm */
16141       status =
16142           db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB",
16143                            strcomb(alarm_odb_str));
16144       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16145       if (!hkeyroot) {
16146         ss_mutex_release(mutex);
16147         return SUCCESS;
16148       }
16149       status =
16150           db_create_record(hDB, 0,
16151                            "/Alarms/Alarms/Demo periodic",
16152                            strcomb(alarm_periodic_str));
16153       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16154       if (!hkeyroot) {
16155         ss_mutex_release(mutex);
16156         return SUCCESS;
16157       }
16158 
16159       /* create default alarm classes */
16160       status =
16161           db_create_record(hDB, 0, "/Alarms/Classes/Alarm",
16162                            strcomb(alarm_class_str));
16163       status =
16164           db_create_record(hDB, 0, "/Alarms/Classes/Warning",
16165                            strcomb(alarm_class_str));
16166       if (status != DB_SUCCESS) {
16167         ss_mutex_release(mutex);
16168         return SUCCESS;
16169       }
16170     }
16171     for (i = 0;; i++) {
16172       status = db_enum_key(hDB, hkeyroot, i, &hkey);
16173       if (status == DB_NO_MORE_SUBKEYS)
16174         break;
16175       db_get_key(hDB, hkey, &key);
16176       size = sizeof(alarm);
16177       status = db_get_record(hDB, hkey, &alarm, &size, 0);
16178       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16179 
16180         /* make sure alarm record has right structure */
16181         db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
16182         size = sizeof(alarm);
16183         status = db_get_record(hDB, hkey, &alarm, &size, 0);
16184         if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16185           cm_msg(MERROR, "al_check", "Cannot get alarm record");
16186           continue;
16187         }
16188       }
16189 
16190       /* check periodic alarm only when active */
16191       if (alarm.active && alarm.type == AT_PERIODIC
16192           && alarm.check_interval > 0
16193           && (INT) ss_time() - (INT) alarm.checked_last >
16194           alarm.check_interval) {
16195 
16196         /* if checked_last has not been set, set it to current time */
16197         if (alarm.checked_last == 0) {
16198           alarm.checked_last = ss_time();
16199           db_set_record(hDB, hkey, &alarm, size, 0);
16200         }
16201 
16202         else
16203           al_trigger_alarm(key.name,
16204                            alarm.alarm_message,
16205                            alarm.alarm_class, "", AT_PERIODIC);
16206       }
16207 
16208       /* check alarm only when active and not internal */
16209       if (alarm.active && alarm.type == AT_EVALUATED
16210           && alarm.check_interval > 0
16211           && (INT) ss_time() - (INT) alarm.checked_last >
16212           alarm.check_interval) {
16213 
16214         /* if condition is true, trigger alarm */
16215         if (al_evaluate_condition(alarm.condition, value)) {
16216           sprintf(str, alarm.alarm_message, value);
16217           al_trigger_alarm(key.name, str,
16218                            alarm.alarm_class, "", AT_EVALUATED);
16219         }
16220 
16221         else {
16222           alarm.checked_last = ss_time();
16223           status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
16224           if (status != DB_SUCCESS) {
16225             cm_msg(MERROR, "al_check", "Cannot write back alarm record");
16226             continue;
16227           }
16228         }
16229       }
16230     }
16231 
16232     /* check /programs alarms */
16233     db_find_key(hDB, 0, "/Programs", &hkeyroot);
16234     if (hkeyroot) {
16235       for (i = 0;; i++) {
16236         status = db_enum_key(hDB, hkeyroot, i, &hkey);
16237         if (status == DB_NO_MORE_SUBKEYS)
16238           break;
16239         db_get_key(hDB, hkey, &key);
16240 
16241         /* don't check "execute on xxx" */
16242         if (key.type != TID_KEY)
16243           continue;
16244         size = sizeof(program_info);
16245         status = db_get_record(hDB, hkey, &program_info, &size, 0);
16246         if (status != DB_SUCCESS) {
16247           cm_msg(MERROR, "al_check", "Cannot get program info record");
16248           continue;
16249         }
16250         now = ss_time();
16251         rpc_get_name(str);
16252         str[strlen(key.name)] = 0;
16253         if (!equal_ustring(str, key.name)
16254             && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
16255           if (program_info.first_failed == 0)
16256             program_info.first_failed = now;
16257 
16258           /* fire alarm when not running for more than what specified in check interval */
16259           if (now - program_info.first_failed >=
16260               program_info.check_interval / 1000) {
16261 
16262             /* if not running and alarm calss defined, trigger alarm */
16263             if (program_info.alarm_class[0]) {
16264               sprintf(str, "Program %s is not running", key.name);
16265               al_trigger_alarm(key.name, str,
16266                                program_info.
16267                                alarm_class,
16268                                "Program not running", AT_PROGRAM);
16269             }
16270 
16271             /* auto restart program */
16272             if (program_info.auto_restart && program_info.start_command[0]) {
16273               ss_system(program_info.start_command);
16274               program_info.first_failed = 0;
16275               cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
16276             }
16277           }
16278         }
16279 
16280         else
16281           program_info.first_failed = 0;
16282         db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
16283       }
16284     }
16285     ss_mutex_release(mutex);
16286   }
16287 
16288 #endif                          /* LOCAL_COUTINES */
16289   return SUCCESS;
16290 }
16291 
16292 
16293 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16294 
16295 /** @} */// end of alfunctionc
16296 
16297 /***** sKIP eb_xxx **************************************************/
16298 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16299 /***** sKIP eb_xxx **************************************************/
16300 
16301 #if !defined(OS_VXWORKS)
16302 /********************************************************************\
16303 *                                                                    *
16304 *                 Event buffer functions                             *
16305 *                                                                    *
16306 \********************************************************************/
16307 
16308 /* PAA several modification in the eb_xxx()
16309    also new function eb_buffer_full()
16310 */
16311 static char *_event_ring_buffer = NULL;
16312 static INT _eb_size;
16313 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16314 
16315 /********************************************************************/
16316 INT eb_create_buffer(INT size)
16317 /********************************************************************\
16318 
16319   Routine: eb_create_buffer
16320 
16321   Purpose: Create an event buffer. Has to be called initially before
16322            any other eb_xxx function
16323 
16324   Input:
16325     INT    size             Size in bytes
16326 
16327   Output:
16328     none
16329 
16330   Function value:
16331     CM_SUCCESS              Successful completion
16332     BM_NO_MEMEORY           Out of memory
16333 
16334 \********************************************************************/
16335 {
16336   _event_ring_buffer = (char *) M_MALLOC(size);
16337   if (_event_ring_buffer == NULL)
16338     return BM_NO_MEMORY;
16339   memset(_event_ring_buffer, 0, size);
16340   _eb_size = size;
16341   _eb_write_pointer = _eb_read_pointer = _eb_end_pointer =
16342       _event_ring_buffer;
16343   _send_sock = rpc_get_event_sock();
16344   return CM_SUCCESS;
16345 }
16346 
16347 
16348 /********************************************************************/
16349 INT eb_free_buffer()
16350 /********************************************************************\
16351 
16352   Routine: eb_free_buffer
16353 
16354   Purpose: Free memory allocated voa eb_create_buffer
16355 
16356   Input:
16357     none
16358 
16359   Output:
16360     none
16361 
16362   Function value:
16363     CM_SUCCESS              Successful completion
16364 
16365 \********************************************************************/
16366 {
16367   if (_event_ring_buffer)
16368     M_FREE(_event_ring_buffer);
16369   _eb_size = 0;
16370   return CM_SUCCESS;
16371 }
16372 
16373 
16374 /********************************************************************/
16375 INT eb_free_space(void)
16376 /********************************************************************\
16377 
16378   Routine: eb_free_space
16379 
16380   Purpose: Compute and return usable free space in the event buffer
16381 
16382   Input:
16383     none
16384 
16385   Output:
16386     none
16387 
16388   Function value:
16389     INT    Number of usable free bytes in the event buffer
16390 
16391 \********************************************************************/
16392 {
16393   INT free;
16394 
16395   if (_event_ring_buffer == NULL) {
16396     cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16397     return -1;
16398   }
16399   if (_eb_write_pointer >= _eb_read_pointer) {
16400     free =
16401         _eb_size - ((PTYPE) _eb_write_pointer -
16402                     (PTYPE) _event_ring_buffer);
16403   } else if (_eb_write_pointer >= _event_ring_buffer) {
16404     free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16405   } else if (_eb_end_pointer == _event_ring_buffer) {
16406     _eb_write_pointer = _event_ring_buffer;
16407     free = _eb_size;
16408   } else if (_eb_read_pointer == _event_ring_buffer) {
16409     free = 0;
16410   } else {
16411     _eb_write_pointer = _event_ring_buffer;
16412     free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16413   }
16414   return free;
16415 }
16416 
16417 
16418 /********************************************************************/
16419 DWORD eb_get_level()
16420 /********************************************************************\
16421 
16422   Routine: eb_get_level
16423 
16424   Purpose: Return filling level of event buffer in percent
16425 
16426   Input:
16427     none
16428 
16429   Output:
16430     none
16431 
16432   Function value:
16433     DWORD level              0..99
16434 
16435 \********************************************************************/
16436 {
16437   INT size;
16438 
16439   size = _eb_size - eb_free_space();
16440   return (100 * size) / _eb_size;
16441 }
16442 
16443 
16444 /********************************************************************/
16445 BOOL eb_buffer_full(void)
16446 /********************************************************************\
16447 
16448   Routine: eb_buffer_full
16449 
16450   Purpose: Test if there is sufficient space in the event buffer
16451     for another event
16452 
16453   Input:
16454     none
16455 
16456   Output:
16457     none
16458 
16459   Function value:
16460     BOOL  Is there enough space for another event in the event buffer
16461 
16462 \********************************************************************/
16463 {
16464   INT free;
16465 
16466   free = eb_free_space();
16467 
16468   /* if max. event won't fit, return zero */
16469   return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16470 }
16471 
16472 
16473 /********************************************************************/
16474 EVENT_HEADER *eb_get_pointer()
16475 /********************************************************************\
16476 
16477   Routine: eb_get_pointer
16478 
16479   Purpose: Get pointer to next free location in event buffer
16480 
16481   Input:
16482     none
16483 
16484   Output:
16485     none
16486 
16487   Function value:
16488     EVENT_HEADER *            Pointer to free location
16489 
16490 \********************************************************************/
16491 {
16492 
16493   /* if max. event won't fit, return zero */
16494   if (eb_buffer_full()) {
16495 
16496 #ifdef OS_VXWORKS
16497     logMsg
16498         ("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16499          _eb_read_pointer - _event_ring_buffer,
16500          _eb_write_pointer - _event_ring_buffer,
16501          _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16502 
16503 #endif                          /*  */
16504     return NULL;
16505   }
16506 
16507   /* leave space for buffer handle */
16508   return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16509 }
16510 
16511 
16512 /********************************************************************/
16513 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16514 /********************************************************************\
16515 
16516   Routine: eb_increment_pointer
16517 
16518   Purpose: Increment write pointer of event buffer after an event
16519            has been copied into the buffer (at an address previously
16520            obtained via eb_get_pointer)
16521 
16522   Input:
16523     INT buffer_handle         Buffer handle event should be sent to
16524     INT event_size            Event size in bytes including header
16525 
16526   Output:
16527     none
16528 
16529   Function value:
16530     CM_SUCCESS                Successful completion
16531 
16532 \********************************************************************/
16533 {
16534   INT aligned_event_size;
16535 
16536   /* if not connected remotely, use bm_send_event */
16537   if (_send_sock == 0)
16538     return bm_send_event(buffer_handle,
16539                          _eb_write_pointer + sizeof(INT),
16540                          event_size, SYNC);
16541   aligned_event_size = ALIGN(event_size);
16542 
16543   /* copy buffer handle */
16544   *((INT *) _eb_write_pointer) = buffer_handle;
16545   _eb_write_pointer += sizeof(INT) + aligned_event_size;
16546   if (_eb_write_pointer > _eb_end_pointer)
16547     _eb_end_pointer = _eb_write_pointer;
16548   if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16549     cm_msg(MERROR, "eb_increment_pointer",
16550            "event size (%d) exeeds maximum event size (%d)",
16551            event_size, MAX_EVENT_SIZE);
16552   if (_eb_size -
16553       ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer) <
16554       MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16555     _eb_write_pointer = _event_ring_buffer;
16556 
16557     /* avoid rp==wp */
16558     if (_eb_read_pointer == _event_ring_buffer)
16559       _eb_write_pointer--;
16560   }
16561   return CM_SUCCESS;
16562 }
16563 
16564 
16565 /********************************************************************/
16566 INT eb_send_events(BOOL send_all)
16567 /********************************************************************\
16568 
16569   Routine: eb_send_events
16570 
16571   Purpose: Send events from the event buffer to the server
16572 
16573   Input:
16574     BOOL send_all             If FALSE, only send events if buffer
16575                               contains more than _opt_tcp_size bytes
16576 
16577   Output:
16578     none
16579 
16580   Function value:
16581     CM_SUCCESS                Successful completion
16582 
16583 \********************************************************************/
16584 {
16585   char *eb_wp, *eb_ep;
16586   INT size, i;
16587 
16588   /* write pointers are volatile, so make copy */
16589   eb_ep = _eb_end_pointer;
16590   eb_wp = _eb_write_pointer;
16591   if (eb_wp == _eb_read_pointer)
16592     return CM_SUCCESS;
16593   if (eb_wp > _eb_read_pointer) {
16594     size = (PTYPE) eb_wp - (PTYPE) _eb_read_pointer;
16595 
16596     /* don't send if less than optimal TCP buffer size available */
16597     if (size < (INT) _opt_tcp_size && !send_all)
16598       return CM_SUCCESS;
16599   }
16600 
16601   else {
16602 
16603     /* send last piece of event buffer */
16604     size = (PTYPE) eb_ep - (PTYPE) _eb_read_pointer;
16605   }
16606   while (size > _opt_tcp_size) {
16607 
16608     /* send buffer */
16609     i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
16610     if (i < 0) {
16611       printf("send_tcp() returned %d\n", i);
16612       cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16613       return RPC_NET_ERROR;
16614     }
16615     _eb_read_pointer += _opt_tcp_size;
16616     if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16617       _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16618     size -= _opt_tcp_size;
16619   }
16620   if (send_all || eb_wp < _eb_read_pointer) {
16621 
16622     /* send buffer */
16623     i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
16624     if (i < 0) {
16625       printf("send_tcp() returned %d\n", i);
16626       cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16627       return RPC_NET_ERROR;
16628     }
16629     _eb_read_pointer += size;
16630     if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16631       _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16632   }
16633 
16634   /* Check for case where eb_wp = eb_ring_buffer - 1 */
16635   if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
16636     return CM_SUCCESS;
16637   }
16638   if (eb_wp != _eb_read_pointer)
16639     return BM_MORE_EVENTS;
16640   return CM_SUCCESS;
16641 }
16642 
16643 
16644 #endif                          /* OS_VXWORKS  eb section */
16645 
16646 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16647 
16648 /********************************************************************/
16649 /** @addtogroup dmfunctionc
16650  *  
16651  *  @{  */
16652 
16653 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16654 
16655 /********************************************************************\
16656 *                                                                    *
16657 *                 Dual memory buffer functions                       *
16658 *                                                                    *
16659 * Provide a dual memory buffer scheme for handling front-end         *
16660 * event. This code as been requested for allowing contemporary       *
16661 * task handling a)acquisition, b)network transfer if possible.       *
16662 * The pre-compiler switch will determine the mode of operation.      *
16663 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
16664 * a seperate task taking care of the dm_area_send                    *
16665 *                                                                    *
16666 * "*" : visible functions                                            *
16667 * dm_buffer_create():     *Setup the dual memory buffer              *
16668 *                          Setup semaphore                           *
16669 *                          Spawn second thread                       *
16670 * dm_buffer_release():    *Release memory allocation for dm          *
16671 *                          Force a kill of 2nd thread                *
16672 *                          Remove semaphore                          *
16673 * dm_area_full():         *Check for both area being full            *
16674 *                          None blocking, may be used for interrupt  *
16675 *                          disable.                                  *
16676 * dm_pointer_get()     :  *Check memory space and return pointer     *
16677 *                          Blocking function with timeout if no more *
16678 *                          space for next event. If error will abort.*
16679 * dm_pointer_increment(): *Move pointer to next free location        *
16680 *                          None blocking. performs bm_send_event if  *
16681 *                          local connection.                         *
16682 * dm_area_send():         *Transfer FULL buffer(s)                   *
16683 *                          None blocking function.                   *
16684 *                          if DUAL_THREAD: Give sem_send semaphore   *
16685 *                          else transfer FULL buffer                 *
16686 * dm_area_flush():        *Transfer all remaining events from dm     *
16687 *                          Blocking function with timeout            *
16688 *                          if DUAL_THREAD: Give sem_flush semaphore. *
16689 * dm_task():               Secondary thread handling DUAL_THREAD     *
16690 *                          mechanism. Serves 2 requests:             *
16691 *                          dm_send:  Transfer FULL buffer only.      *
16692 *                          dm_flush: Transfer ALL buffers.           *
16693 * dm_area_switch():        internal, used by dm_pointer_get()        *
16694 * dm_active_full():        internal: check space in current buffer   *
16695 * dm_buffer_send():        internal: send data for given area        *
16696 * dm_buffer_time_get():    interal: return the time stamp of the     *
16697 *                          last switch                               *
16698 \********************************************************************/
16699 
16700 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
16701 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
16702 #define DM_KILL        12       /* Kill request for 2nd thread */
16703 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
16704 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
16705 typedef struct {
16706   char *pt;                     /* top pointer    memory buffer          */
16707   char *pw;                     /* write pointer  memory buffer          */
16708   char *pe;                     /* end   pointer  memory buffer          */
16709   char *pb;                     /* bottom pointer memory buffer          */
16710   BOOL full;                    /* TRUE if memory buffer is full         */
16711   DWORD serial;                 /* full buffer serial# for evt order     */
16712 } DMEM_AREA;
16713 typedef struct {
16714   DMEM_AREA *pa;                /* active memory buffer */
16715   DMEM_AREA area1;              /* mem buffer area 1 */
16716   DMEM_AREA area2;              /* mem buffer area 2 */
16717   DWORD serial;                 /* overall buffer serial# for evt order     */
16718   INT action;                   /* for multi thread configuration */
16719   DWORD last_active;            /* switch time stamp */
16720   HNDLE sem_send;               /* semaphore for dm_task */
16721   HNDLE sem_flush;              /* semaphore for dm_task */
16722 } DMEM_BUFFER;
16723 DMEM_BUFFER dm;
16724 INT dm_user_max_event_size;
16725 
16726 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16727 
16728 /********************************************************************/
16729 /**
16730 Setup a dual memory buffer. Has to be called initially before
16731            any other dm_xxx function
16732 @param size             Size in bytes
16733 @param user_max_event_size max event size
16734 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
16735 */
16736 INT dm_buffer_create(INT size, INT user_max_event_size)
16737 {
16738   dm.area1.pt = (char *) M_MALLOC(size);
16739   if (dm.area1.pt == NULL)
16740     return (BM_NO_MEMORY);
16741   dm.area2.pt = (char *) M_MALLOC(size);
16742   if (dm.area2.pt == NULL)
16743     return (BM_NO_MEMORY);
16744 
16745   /* check user event size against the system MAX_EVENT_SIZE */
16746   if (user_max_event_size > MAX_EVENT_SIZE) {
16747     cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
16748     return BM_MEMSIZE_MISMATCH;
16749   }
16750   dm_user_max_event_size = user_max_event_size;
16751   memset(dm.area1.pt, 0, size);
16752   memset(dm.area2.pt, 0, size);
16753 
16754   /* initialize pointers */
16755   dm.area1.pb = dm.area1.pt + size - 1024;
16756   dm.area1.pw = dm.area1.pe = dm.area1.pt;
16757   dm.area2.pb = dm.area2.pt + size - 1024;
16758   dm.area2.pw = dm.area2.pe = dm.area2.pt;
16759 
16760   /*-PAA-*/
16761 #ifdef DM_DEBUG
16762   printf(" in dm_buffer_create ---------------------------------\n");
16763   printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw,
16764          dm.area1.pe, dm.area1.pb);
16765   printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw,
16766          dm.area2.pe, dm.area2.pb);
16767 
16768 #endif                          /*  */
16769 
16770   /* activate first area */
16771   dm.pa = &dm.area1;
16772 
16773   /* Default not full */
16774   dm.area1.full = dm.area2.full = FALSE;
16775 
16776   /* Reset serial buffer number with proper starting sequence */
16777   dm.area1.serial = dm.area2.serial = 0;
16778 
16779   /* ensure proper serial on next increment */
16780   dm.serial = 1;
16781 
16782   /* set active buffer time stamp */
16783   dm.last_active = ss_millitime();
16784 
16785   /* get socket for event sending */
16786   _send_sock = rpc_get_event_sock();
16787 
16788 #ifdef DM_DUAL_THREAD
16789   {
16790     INT status;
16791     VX_TASK_SPAWN starg;
16792 
16793     /* create semaphore */
16794     status = ss_mutex_create("send", &dm.sem_send);
16795     if (status != SS_CREATED && status != SS_SUCCESS) {
16796       cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
16797       return status;
16798     }
16799     status = ss_mutex_create("flush", &dm.sem_flush);
16800     if (status != SS_CREATED && status != SS_SUCCESS) {
16801       cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
16802       return status;
16803     }
16804 
16805     /* spawn dm_task */
16806     memset(&starg, 0, sizeof(VX_TASK_SPAWN));
16807 
16808 #ifdef OS_VXWORKS
16809     /* Fill up the necessary arguments */
16810     strcpy(starg.name, "areaSend");
16811     starg.priority = 120;
16812     starg.stackSize = 20000;
16813 
16814 #endif                          /*  */
16815     if ((status =
16816          ss_thread_create(dm_task, (void *) &starg)) != SS_SUCCESS) {
16817       cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
16818       return status;
16819     }
16820 #ifdef OS_WINNT
16821     /* necessary for true MUTEX (NT) */
16822     ss_mutex_wait_for(dm.sem_send, 0);
16823 
16824 #endif                          /*  */
16825   }
16826 
16827 #endif                          /* DM_DUAL_THREAD */
16828   return CM_SUCCESS;
16829 }
16830 
16831 
16832 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16833 
16834 /********************************************************************/
16835 INT dm_buffer_release(void)
16836 /********************************************************************\
16837   Routine: dm_buffer_release
16838 
16839   Purpose: Release dual memory buffers
16840   Input:
16841     none
16842   Output:
16843     none
16844   Function value:
16845     CM_SUCCESS              Successful completion
16846 \********************************************************************/
16847 {
16848   if (dm.area1.pt) {
16849     free(dm.area1.pt);
16850     dm.area1.pt = NULL;
16851   }
16852   if (dm.area2.pt) {
16853     free(dm.area2.pt);
16854     dm.area2.pt = NULL;
16855   }
16856   dm.serial = 0;
16857   dm.area1.full = dm.area2.full = TRUE;
16858   dm.area1.serial = dm.area2.serial = 0;
16859 
16860 #ifdef DM_DUAL_THREAD
16861   /* kill spawned dm_task */
16862   dm.action = DM_KILL;
16863   ss_mutex_release(dm.sem_send);
16864   ss_mutex_release(dm.sem_flush);
16865 
16866   /* release semaphore */
16867   ss_mutex_delete(dm.sem_send, 0);
16868   ss_mutex_delete(dm.sem_flush, 0);
16869 
16870 #endif                          /*  */
16871   return CM_SUCCESS;
16872 }
16873 
16874 
16875 /********************************************************************/
16876 INLINE DMEM_AREA *dm_area_switch(void)
16877 /********************************************************************\
16878   Routine: dm_area_switch
16879 
16880   Purpose: set active area to the other empty area or NULL if both
16881            area are full. May have to check the serial consistancy...
16882   Input:
16883     none
16884   Output:
16885     none
16886   Function value:
16887     DMEM_AREA *            Pointer to active area or both full
16888 \********************************************************************/
16889 {
16890   volatile BOOL full1, full2;
16891 
16892   full1 = dm.area1.full;
16893   full2 = dm.area2.full;
16894   if (!full1 && !full2) {
16895     if (dm.area1.serial <= dm.area2.serial)
16896       return (&(dm.area1));
16897 
16898     else
16899       return (&(dm.area2));
16900   }
16901   if (!full1) {
16902     return (&(dm.area1));
16903   }
16904 
16905   else if (!full2) {
16906     return (&(dm.area2));
16907   }
16908   return (NULL);
16909 }
16910 
16911 
16912 /********************************************************************/
16913 INLINE BOOL dm_area_full(void)
16914 /********************************************************************\
16915   Routine: dm_area_full
16916 
16917   Purpose: Test if both area are full in order to block interrupt
16918   Input:
16919     none
16920   Output:
16921     none
16922   Function value:
16923     BOOL         TRUE if not enough space for another event
16924 \********************************************************************/
16925 {
16926   if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
16927     return TRUE;
16928   return FALSE;
16929 }
16930 
16931 
16932 /********************************************************************/
16933 INLINE BOOL dm_active_full(void)
16934 /********************************************************************\
16935   Routine: dm_active_full
16936 
16937   Purpose: Test if there is sufficient space in either event buffer
16938            for another event.
16939   Input:
16940     none
16941   Output:
16942     none
16943   Function value:
16944     BOOL         TRUE if not enough space for another event
16945 \********************************************************************/
16946 {
16947 
16948   /* catch both full areas, waiting for transfer */
16949   if (dm.pa == NULL)
16950     return TRUE;
16951 
16952   /* Check the space in the active buffer only
16953      as I don't switch buffer here */
16954   if (dm.pa->full)
16955     return TRUE;
16956   return (((PTYPE) dm.pa->pb - (PTYPE) dm.pa->pw) < (INT)
16957           (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
16958 }
16959 
16960 
16961 /********************************************************************/
16962 DWORD dm_buffer_time_get(void)
16963 /********************************************************************\
16964   Routine: dm_buffer_time_get
16965 
16966   Purpose: return the time from the last buffer switch.
16967 
16968   Input:
16969     none
16970   Output:
16971     none
16972   Function value:
16973     DWORD        time stamp
16974 
16975 \********************************************************************/
16976 {
16977   return (dm.last_active);
16978 }
16979 
16980 
16981 /********************************************************************/
16982 EVENT_HEADER *dm_pointer_get(void)
16983 /********************************************************************\
16984   Routine: dm_pointer_get
16985 
16986   Purpose: Get pointer to next free location in event buffer.
16987            after 10sec tries, it times out return NULL indicating a
16988            serious problem, i.e. abort.
16989   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
16990   Input:
16991     none
16992   Output:
16993     DM_BUFFER * dm    local valid dm to work on
16994   Function value:
16995     EVENT_HEADER *    Pointer to free location
16996     NULL              cannot after several attempt get free space => abort
16997 \********************************************************************/
16998 {
16999   int timeout, status;
17000 
17001   /* Is there still space in the active area ? */
17002   if (!dm_active_full())
17003     return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17004 
17005   /* no more space => switch area */
17006 
17007   /* Tag current area with global dm.serial for order consistency */
17008   dm.pa->serial = dm.serial++;
17009 
17010   /* set active buffer time stamp */
17011   dm.last_active = ss_millitime();
17012 
17013   /* mark current area full */
17014   dm.pa->full = TRUE;
17015 
17016   /* Trigger/do data transfer (Now/don't wait) */
17017   if ((status = dm_area_send()) == RPC_NET_ERROR) {
17018     cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
17019     return NULL;
17020   }
17021 
17022   /* wait switch completion (max 10 sec) */
17023   timeout = ss_millitime();     /* with timeout */
17024   while ((ss_millitime() - timeout) < 10000) {
17025     dm.pa = dm_area_switch();
17026     if (dm.pa != NULL)
17027       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17028     ss_sleep(200);
17029 
17030 #ifdef DM_DEBUG
17031     printf
17032         (" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
17033          ss_millitime() - timeout, dm.area1.full, dm.area2.full,
17034          dm.area1.serial, dm.area2.serial, dm.serial);
17035 
17036 #endif                          /*  */
17037   }
17038 
17039   /* Time running out abort */
17040   cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
17041   return NULL;
17042 }
17043 
17044 
17045 /********************************************************************/
17046 int dm_pointer_increment(INT buffer_handle, INT event_size)
17047 /********************************************************************\
17048   Routine: dm_pointer_increment
17049 
17050   Purpose: Increment write pointer of event buffer after an event
17051            has been copied into the buffer (at an address previously
17052            obtained via dm_pointer_get)
17053   Input:
17054     INT buffer_handle         Buffer handle event should be sent to
17055     INT event_size            Event size in bytes including header
17056   Output:
17057     none
17058   Function value:
17059     CM_SUCCESS                Successful completion
17060     status                    from bm_send_event for local connection
17061 \********************************************************************/
17062 {
17063   INT aligned_event_size;
17064 
17065   /* if not connected remotely, use bm_send_event */
17066   if (_send_sock == 0) {
17067     *((INT *) dm.pa->pw) = buffer_handle;
17068     return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT),
17069                          event_size, SYNC);
17070   }
17071   aligned_event_size = ALIGN(event_size);
17072   *((INT *) dm.pa->pw) = buffer_handle;
17073 
17074   /* adjust write pointer */
17075   dm.pa->pw += sizeof(INT) + aligned_event_size;
17076 
17077   /* adjust end pointer */
17078   dm.pa->pe = dm.pa->pw;
17079   return CM_SUCCESS;
17080 }
17081 
17082 
17083 /********************************************************************/
17084 INLINE INT dm_buffer_send(DMEM_AREA * larea)
17085 /********************************************************************\
17086   Routine: dm_buffer_send
17087 
17088   Purpose: Ship data to the cache in fact!
17089            Basically the same do loop is done in the send_tcp.
17090            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
17091            Introduced for bringing tcp option to user code.
17092   Input:
17093     DMEM_AREA * larea   The area to work with.
17094   Output:
17095     none
17096   Function value:
17097     CM_SUCCESS       Successful completion
17098     DM_ACTIVE_NULL   Both area were/are full
17099     RPC_NET_ERROR    send error
17100 \********************************************************************/
17101 {
17102   INT tot_size, nwrite;
17103   char *lpt;
17104 
17105   /* if not connected remotely, use bm_send_event */
17106   if (_send_sock == 0)
17107     return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
17108 
17109   /* alias */
17110   lpt = larea->pt;
17111 
17112   /* Get overall buffer size */
17113   tot_size = (PTYPE) larea->pe - (PTYPE) lpt;
17114 
17115   /* shortcut for speed */
17116   if (tot_size == 0)
17117     return CM_SUCCESS;
17118 
17119 #ifdef DM_DEBUG
17120   printf("lpt:%p size:%i ", lpt, tot_size);
17121 
17122 #endif                          /*  */
17123   nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
17124 
17125 #ifdef DM_DEBUG
17126   printf("nwrite:%i  errno:%i\n", nwrite, errno);
17127 
17128 #endif                          /*  */
17129   if (nwrite < 0)
17130     return RPC_NET_ERROR;
17131 
17132   /* reset area */
17133   larea->pw = larea->pe = larea->pt;
17134   larea->full = FALSE;
17135   return CM_SUCCESS;
17136 }
17137 
17138 
17139 /********************************************************************/
17140 INT dm_area_send(void)
17141 /********************************************************************\
17142   Routine: dm_area_send
17143 
17144   Purpose: Empty the FULL area only in proper event order
17145            Meant to be use either in mfe.c scheduler on every event
17146 
17147   Dual memory scheme:
17148    DM_DUAL_THREAD : Trigger sem_send
17149    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
17150                     if dm.pa is NULL (were both full) and now both are empty
17151 
17152   Input:
17153     none
17154   Output:
17155     none
17156   Function value:
17157     CM_SUCCESS                Successful completion
17158     RPC_NET_ERROR             send error
17159 \********************************************************************/
17160 {
17161 
17162 #ifdef DM_DUAL_THREAD
17163   INT status;
17164 
17165   /* force a DM_SEND if possible. Don't wait for completion */
17166   dm.action = DM_SEND;
17167   ss_mutex_release(dm.sem_send);
17168 
17169 #ifdef OS_WINNT
17170   /* necessary for true MUTEX (NT) */
17171   status = ss_mutex_wait_for(dm.sem_send, 1);
17172   if (status == SS_NO_MUTEX) {
17173     printf(" timeout while waiting for sem_send\n");
17174     return RPC_NET_ERROR;
17175   }
17176 #endif                          /*  */
17177   return CM_SUCCESS;
17178 
17179 #else                           /*  */
17180   /* ---------- NOT IN DUAL THREAD ----------- */
17181   INT status = 0;
17182 
17183   /* if no DUAL thread everything is local then */
17184   /* select the full area */
17185   if (dm.area1.full && dm.area2.full)
17186     if (dm.area1.serial <= dm.area2.serial)
17187       status = dm_buffer_send(&dm.area1);
17188 
17189     else
17190       status = dm_buffer_send(&dm.area2);
17191 
17192   else if (dm.area1.full)
17193     status = dm_buffer_send(&dm.area1);
17194 
17195   else if (dm.area2.full)
17196     status = dm_buffer_send(&dm.area2);
17197   if (status != CM_SUCCESS)
17198     return status;              /* catch transfer error too */
17199   if (dm.pa == NULL) {
17200     printf(" sync send dm.pa:%p full 1%li 2%li\n", dm.pa,
17201            dm.area1.full, dm.area2.full);
17202     dm.pa = &dm.area1;
17203   }
17204   return CM_SUCCESS;
17205 
17206 #endif                          /*  */
17207 }
17208 
17209 
17210 /********************************************************************/
17211 INT dm_task(void *pointer)
17212 /********************************************************************\
17213   Routine: dm_task
17214 
17215   Purpose: async send events doing a double purpose:
17216   a) send full buffer if found (DM_SEND) set by dm_active_full
17217   b) flush full areas (DM_FLUSH) set by dm_area_flush
17218   Input:
17219   none
17220   Output:
17221   none
17222   Function value:
17223   none
17224   \********************************************************************/
17225 {
17226 
17227 #ifdef DM_DUAL_THREAD
17228   INT status, timeout;
17229 
17230   printf("Semaphores initialization ... in areaSend ");
17231 
17232   /* Check or Wait for semaphore to be setup */
17233   timeout = ss_millitime();
17234   while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
17235     ss_sleep(200);
17236   if (dm.sem_send == 0)
17237     goto kill;
17238 
17239 #ifdef OS_WINNT
17240   /* necessary for true MUTEX (NT) get semaphore */
17241   ss_mutex_wait_for(dm.sem_flush, 0);
17242 
17243 #endif                          /*  */
17244 
17245   /* Main FOREVER LOOP */
17246   printf("task areaSend ready...\n");
17247   while (1) {
17248     if (!dm_area_full()) {
17249 
17250       /* wait semaphore here ........ 0 == forever */
17251       ss_mutex_wait_for(dm.sem_send, 0);
17252 
17253 #ifdef OS_WINNT
17254       /* necessary for true MUTEX (NT) give semaphore */
17255       ss_mutex_release(dm.sem_send);
17256 
17257 #endif                          /*  */
17258     }
17259     if (dm.action == DM_SEND) {
17260 
17261 #ifdef DM_DEBUG
17262       printf("Send %i %i ", dm.area1.full, dm.area2.full);
17263 
17264 #endif                          /*  */
17265       /* DM_SEND : Empty the oldest buffer only. */
17266       if (dm.area1.full && dm.area2.full) {
17267         if (dm.area1.serial <= dm.area2.serial)
17268           status = dm_buffer_send(&dm.area1);
17269 
17270         else
17271           status = dm_buffer_send(&dm.area2);
17272       }
17273 
17274       else if (dm.area1.full)
17275         status = dm_buffer_send(&dm.area1);
17276 
17277       else if (dm.area2.full)
17278         status = dm_buffer_send(&dm.area2);
17279       if (status != CM_SUCCESS) {
17280         cm_msg(MERROR, "dm_task", "network error %i", status);
17281         goto kill;
17282       }
17283     }
17284     /* if DM_SEND */
17285     else if (dm.action == DM_FLUSH) {
17286 
17287       /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
17288          events) Empty both area in order independently of being full or not */
17289       if (dm.area1.serial <= dm.area2.serial) {
17290         status = dm_buffer_send(&dm.area1);
17291         if (status != CM_SUCCESS)
17292           goto error;
17293         status = dm_buffer_send(&dm.area2);
17294         if (status != CM_SUCCESS)
17295           goto error;
17296       }
17297 
17298       else {
17299         status = dm_buffer_send(&dm.area2);
17300         if (status != CM_SUCCESS)
17301           goto error;
17302         status = dm_buffer_send(&dm.area1);
17303         if (status != CM_SUCCESS)
17304           goto error;
17305       }
17306 
17307       /* reset counter */
17308       dm.area1.serial = 0;
17309       dm.area2.serial = dm.serial = 1;
17310 
17311 #ifdef DM_DEBUG
17312       printf("dm.action: Flushing ...\n");
17313 
17314 #endif                          /*  */
17315       /* reset area to #1 */
17316       dm.pa = &dm.area1;
17317 
17318       /* release user */
17319       ss_mutex_release(dm.sem_flush);
17320 
17321 #ifdef OS_WINNT
17322       /* necessary for true MUTEX (NT) get semaphore back */
17323       ss_mutex_wait_for(dm.sem_flush, 0);
17324 
17325 #endif                          /*  */
17326     }                           /* if FLUSH */
17327     if (dm.action == DM_KILL)
17328       goto kill;
17329   }                             /* FOREVER (go back wainting for semaphore) */
17330 
17331   /* kill spawn now */
17332 error:cm_msg(MERROR, "dm_area_flush", "aSync Net error");
17333 kill:ss_mutex_release(dm.sem_flush);
17334 
17335 #ifdef OS_WINNT
17336   ss_mutex_wait_for(dm.sem_flush, 1);
17337 
17338 #endif                          /*  */
17339   cm_msg(MERROR, "areaSend", "task areaSend exiting now");
17340   exit;
17341   return 1;
17342 
17343 #else                           /*  */
17344   printf("DM_DUAL_THREAD not defined\n");
17345   return 0;
17346 
17347 #endif                          /*  */
17348 }
17349 
17350 
17351 /********************************************************************/
17352 INT dm_area_flush(void)
17353 /********************************************************************\
17354   Routine: dm_area_flush
17355 
17356   Purpose: Flush all the events in the areas.
17357            Used in mfe for BOR events, periodic events and
17358            if rate to low in main loop once a second. The standard
17359            data transfer should be done/triggered by dm_area_send (sync/async)
17360            in dm_pointer_get().
17361   Input:
17362     none
17363   Output:
17364     none
17365   Function value:
17366     CM_SUCCESS       Successful completion
17367     RPC_NET_ERROR    send error
17368 \********************************************************************/
17369 {
17370   INT status;
17371 
17372 #ifdef DM_DUAL_THREAD
17373   /* request FULL flush */
17374   dm.action = DM_FLUSH;
17375   ss_mutex_release(dm.sem_send);
17376 
17377 #ifdef OS_WINNT
17378   /* necessary for true MUTEX (NT) get semaphore back */
17379   ss_mutex_wait_for(dm.sem_send, 0);
17380 
17381 #endif                          /*  */
17382 
17383   /* important to wait for completion before continue with timeout
17384      timeout specified milliseconds */
17385   status = ss_mutex_wait_for(dm.sem_flush, 10000);
17386 
17387 #ifdef DM_DEBUG
17388   printf("dm_area_flush after waiting %i\n", status);
17389 
17390 #endif                          /*  */
17391 #ifdef OS_WINNT
17392   ss_mutex_release(dm.sem_flush);       /* give it back now */
17393 
17394 #endif                          /*  */
17395   return status;
17396 
17397 #else                           /*  */
17398   /* full flush done here */
17399   /* select in order both area independently of being full or not */
17400   if (dm.area1.serial <= dm.area2.serial) {
17401     status = dm_buffer_send(&dm.area1);
17402     if (status != CM_SUCCESS)
17403       return status;
17404     status = dm_buffer_send(&dm.area2);
17405     if (status != CM_SUCCESS)
17406       return status;
17407   }
17408 
17409   else {
17410     status = dm_buffer_send(&dm.area2);
17411     if (status != CM_SUCCESS)
17412       return status;
17413     status = dm_buffer_send(&dm.area1);
17414     if (status != CM_SUCCESS)
17415       return status;
17416   }
17417 
17418   /* reset serial counter */
17419   dm.area1.serial = dm.area2.serial = 0;
17420   dm.last_active = ss_millitime();
17421   return CM_SUCCESS;
17422 
17423 #endif                          /*  */
17424 }
17425 
17426 
17427 /********************************************************************/
17428 
17429 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17430 
17431 /** @} */// end of dmfunctionc
17432 
17433 /** @} */// end of midasincludecode

Midas DOC Version 1.9.3 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Suzannah Daviel - Doxygen - Peter Green - Greg Hackman - Gertjan Hofman - Paul Knowles - Rudi Meier - Glenn Moloney - Dave Morris - Konstantin Olchanski - Renee Poutissou - Andreas Suter - Piotr Adam Zolnierczuk