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.224  2004/10/07 22:04:17  pierre
00010   Doxygen correction
00011 
00012   Revision 1.223  2004/10/06 22:02:25  midas
00013   Implemented multiple requests for a transition
00014 
00015   Revision 1.221  2004/10/04 17:55:24  midas
00016   Fixed bug in cm_msg() with incorrect buffer size for 'message file'
00017 
00018   Revision 1.220  2004/10/04 15:35:25  midas
00019   Removed error triggered by bm_close_all_buffers()
00020 
00021   Revision 1.219  2004/10/01 23:35:53  midas
00022   Removed PRE/POST transitions and implemented sequence order of transitions
00023 
00024   Revision 1.218  2004/09/30 19:58:11  midas
00025   Added more debugging info to cm_transition
00026 
00027   Revision 1.217  2004/09/30 18:21:28  midas
00028   Changed debug printout for transitions
00029 
00030   Revision 1.216  2004/09/28 20:21:05  midas
00031   Fixed missing LF in debug output
00032 
00033   Revision 1.215  2004/09/28 20:05:59  midas
00034   Revised debug logging for mserver
00035 
00036   Revision 1.214  2004/09/28 18:30:24  midas
00037   Changed rpc_debug_print arguments
00038 
00039   Revision 1.213  2004/09/28 17:13:21  midas
00040   Added startup debug code for mserver
00041 
00042   Revision 1.212  2004/09/17 01:00:55  midas
00043   Lock ODB during startup of programs
00044 
00045   Revision 1.211  2004/07/15 14:59:35  midas
00046   Fixed compiler warning
00047 
00048   Revision 1.210  2004/07/15 14:58:12  midas
00049   Fixed severe bug in bm_check_buffers which crashed the logger during run stop
00050 
00051   Revision 1.209  2004/07/12 12:23:03  midas
00052   Fixed small bug in hs_enum_vars()
00053 
00054   Revision 1.208  2004/07/12 11:32:13  midas
00055   Fixed types in cm_msg()
00056 
00057   Revision 1.207  2004/05/07 19:40:11  midas
00058   Replaced min/max by MIN/MAX macros
00059 
00060   Revision 1.206  2004/05/03 11:30:37  midas
00061   Implemented cm_query_transition()
00062 
00063   Revision 1.205  2004/04/30 07:26:38  midas
00064   Fixed compiler warning
00065 
00066   Revision 1.204  2004/03/31 17:35:40  olchansk
00067   fix the infamous problem with "last NN days broken" in Elog
00068   catch more memory overruns in the elog code
00069 
00070   Revision 1.203  2004/03/19 09:58:22  midas
00071   Changed client inactivity time check in cm_watchdog
00072 
00073   Revision 1.202  2004/01/17 05:35:53  olchansk
00074   replace #define ALIGN() with ALIGN8() to dodge namespace pollution under macosx
00075   hide strlcpy() & co #ifdef HAVE_STRLCPY (macosx already has strlcpy())
00076   correct inconsistent prototype of dbg_malloc() and dbg_calloc()
00077 
00078   Revision 1.201  2004/01/13 00:52:18  pierre
00079   fix dox comment for vxworks
00080 
00081   Revision 1.200  2004/01/08 08:40:10  midas
00082   Implemented standard indentation
00083 
00084   Revision 1.199  2004/01/08 06:44:00  pierre
00085   Doxygen the file
00086 
00087   Revision 1.198  2003/11/24 08:22:46  midas
00088   Changed timeouts from INT to DWORD, added ignore_timeout to cm_cleanup, adde '-f' flag to ODBEdit 'cleanup'
00089 
00090   Revision 1.197  2003/11/20 11:29:44  midas
00091   Implemented db_check_record and use it in most places instead of db_create_record
00092 
00093   Revision 1.196  2003/11/01 01:27:58  olchansk
00094   abort if cannot read /runinfo/run number
00095 
00096   Revision 1.195  2003/10/30 12:03:11  midas
00097   Removed tabs
00098 
00099   Revision 1.194  2003/10/13 00:07:40  olchansk
00100   refuse run number zero and abort on corrupted run numbers
00101 
00102   Revision 1.193  2003/10/12 22:56:33  olchansk
00103   when submitting new Elog message, add the message text to the outgoing email.
00104   add traps for some array overruns (see http://midas.triumf.ca/forum/Development%20Area/12)
00105 
00106   Revision 1.192  2003/09/04 11:47:48  midas
00107   Fixed problem with hKey in cm_transition
00108 
00109   Revision 1.191  2003/05/09 07:40:05  midas
00110   Added extra parameter to cm_get_environment
00111 
00112   Revision 1.190  2003/05/08 19:36:32  midas
00113   Changed size_t into INT
00114 
00115   Revision 1.189  2003/05/02 09:03:01  midas
00116   Fixed buffer overflows by strlcpy()
00117 
00118   Revision 1.188  2003/04/25 13:54:04  midas
00119   Fixed compiler warnings
00120 
00121   Revision 1.187  2003/04/22 12:01:29  midas
00122   Added graceful shutdown of odbedit->frontend connection
00123 
00124   Revision 1.186  2003/04/22 10:09:06  midas
00125   Added RPC_NODELAY option
00126 
00127   Revision 1.185  2003/04/16 19:34:42  pierre
00128   mv stdio.h, ctype.h into midasinc.h
00129 
00130   Revision 1.184  2003/04/15 08:37:04  midas
00131   Removed error message in rpc_client_connect on broken connection, since this is normal if the frontend for example is restarted
00132 
00133   Revision 1.183  2003/04/14 11:01:04  midas
00134   Added bk_find(), added htonl(INADDR_ANY) for bind()
00135 
00136   Revision 1.182  2003/04/09 13:42:47  midas
00137   Made file compile under C++
00138 
00139   Revision 1.181  2003/03/28 07:59:50  midas
00140   Removed old code
00141 
00142   Revision 1.180  2003/03/27 19:40:27  olchansk
00143   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.
00144 
00145   Revision 1.179  2003/03/22 07:06:47  olchansk
00146   prevent infinite loop in hs_read() and hs_dump() when reading broken history files.
00147 
00148   Revision 1.178  2003/01/14 12:19:23  midas
00149   Removed unnecessary code
00150 
00151   Revision 1.177  2003/01/14 08:14:32  midas
00152   Removed unnecessary bind()
00153 
00154   Revision 1.176  2003/01/13 17:07:01  midas
00155   Fixed problem with missing history in recent files
00156 
00157   Revision 1.175  2002/11/27 12:54:49  midas
00158   Removed unnecessary bind()
00159 
00160   Revision 1.174  2002/10/21 00:14:38  olchansk
00161   add missing error reporting
00162 
00163   Revision 1.173  2002/10/15 19:14:06  olchansk
00164   disallow recursive rpc_server_disconnect() (->rpc_call->rpc_server_disconnect)
00165 
00166   Revision 1.172  2002/10/04 09:05:39  midas
00167   Set timeout to 0 in cm_enable_watchdog(FALSE)
00168 
00169   Revision 1.171  2002/09/23 18:13:49  pierre
00170   correct cm_cleanup() rpc_call arg list
00171 
00172   Revision 1.170  2002/09/23 09:50:23  midas
00173   Fixed problem with odbedit 'cleanup' command
00174 
00175   Revision 1.169  2002/09/18 16:39:16  pierre
00176   add bk_list()
00177 
00178   Revision 1.168  2002/09/17 08:58:43  midas
00179   Fix for watchdog timeout after tape operations
00180 
00181   Revision 1.167  2002/09/13 07:32:47  midas
00182   Added client name to cm_cleanup()
00183 
00184   Revision 1.166  2002/09/12 10:42:20  midas
00185   Added note to cm_cleanup()
00186 
00187   Revision 1.165  2002/09/09 17:57:14  pierre
00188   #if !defined(OS_VXWORKS) for eb_ & hs_ section
00189 
00190   Revision 1.164  2002/06/25 19:39:48  pierre
00191   doc++ functions  strstr
00192 
00193   Revision 1.163  2002/06/25 19:00:36  pierre
00194   doc++ functions
00195 
00196   Revision 1.162  2002/05/29 18:49:37  midas
00197   Fixed bug with 'shutdown all' in odbedit
00198 
00199   Revision 1.161  2002/05/29 07:25:13  midas
00200   Fixed bug with shutting down programs
00201 
00202   Revision 1.160  2002/05/28 12:47:46  midas
00203   Shut down client connection in FTCP mode
00204 
00205   Revision 1.159  2002/05/27 14:29:10  midas
00206   Improved rpc timeout error reports
00207 
00208   Revision 1.158  2002/05/22 06:07:01  midas
00209   Call bm_defragment_event for both EVENTID_FRAG1 and EVENTID_FRAG
00210 
00211   Revision 1.157  2002/05/22 05:43:32  midas
00212   Added extra variables to hs_enum_vars for mhist to display array size
00213 
00214   Revision 1.156  2002/05/22 05:26:38  midas
00215   Fixed problem with empty history files
00216 
00217   Revision 1.155  2002/05/16 18:01:13  midas
00218   Added subdir creation in logger and improved program restart scheme
00219 
00220   Revision 1.154  2002/05/15 23:43:39  midas
00221   Added bm_defragment_event()
00222 
00223   Revision 1.153  2002/05/14 04:24:53  midas
00224   Fixed bug on nonexisting message file
00225 
00226   Revision 1.152  2002/05/11 01:22:48  midas
00227   Improved malloc/free debugging
00228 
00229   Revision 1.151  2002/05/10 16:49:37  midas
00230   Moved 'execute on start' before TR_PRESTART
00231 
00232   Revision 1.150  2002/05/10 01:41:19  midas
00233   Added optional debug output to cm_transition
00234 
00235   Revision 1.149  2002/05/10 00:17:05  midas
00236   Run start abort causes logger to delete old data file on next run start
00237 
00238   Revision 1.148  2002/05/08 22:15:24  pierre
00239   add db_get_value arg doc
00240 
00241   Revision 1.147  2002/05/08 19:54:40  midas
00242   Added extra parameter to function db_get_value()
00243 
00244   Revision 1.146  2002/05/07 22:27:56  midas
00245   Fixed bug that history files did not get closed on hs_read
00246 
00247   Revision 1.145  2002/03/13 08:38:00  midas
00248   Added periodic alarms
00249 
00250   Revision 1.144  2002/02/02 11:33:45  midas
00251   Fixed bug in hs_read with small history files
00252 
00253   Revision 1.143  2002/01/30 13:03:34  midas
00254   Fixed small bug in history function
00255 
00256   Revision 1.142  2001/12/12 18:27:03  pierre
00257   add doc++, fix comments
00258 
00259   Revision 1.141  2001/12/12 17:46:21  pierre
00260   1.8.3-2 doc++ comments
00261 
00262   Revision 1.140  2001/12/05 11:29:51  midas
00263   Changed creation of "/Loger/xxx dir"
00264 
00265   Revision 1.139  2001/11/20 14:42:15  midas
00266   Added "/logger/history dir" and "/logger/elog dir"
00267 
00268   Revision 1.138  2001/10/25 22:18:48  pierre
00269   added doc++ comments
00270 
00271   Revision 1.137  2001/10/05 22:35:46  pierre
00272   - change doc \_ to _
00273   - Change MALLOC to M_MALLOC, FREE to M_FREE macros.
00274 
00275   Revision 1.136  2001/08/07 13:07:01  midas
00276   Return error if subprocess creation in rpc_server_accept fails
00277 
00278   Revision 1.135  2001/08/07 08:07:09  midas
00279   Fixed bug in el_retrieve with attachment decoding
00280 
00281   Revision 1.134  2001/06/27 11:55:50  midas
00282   Fixed compiler warnings (came from IRIX)
00283 
00284   Revision 1.133  2001/06/15 08:49:56  midas
00285   Fixed bug when query gave no result if only messages from today are present
00286 
00287   Revision 1.132  2001/05/22 09:27:13  midas
00288   Fixed bug when searching old messages
00289 
00290   Revision 1.131  2001/04/10 01:17:44  midas
00291   Fixed bug in cm_msg_retrieve which screwed up message display in mhttpd
00292 
00293   Revision 1.130  2001/02/19 11:29:05  midas
00294   Set run stop time in ODB before run is stopped in order to have the proper
00295   value in the runxxx.odb file
00296 
00297   Revision 1.129  2001/01/30 08:28:13  midas
00298   Use va_arg(..., double) for float numbers
00299 
00300   Revision 1.128  2000/12/06 02:58:24  midas
00301   Put extended error information for bind() call
00302 
00303   Revision 1.127  2000/11/14 12:19:24  midas
00304   Fixed bug in cm_msg_retrieve, added improved "more" feature in message display
00305 
00306   Revision 1.126  2000/11/14 08:17:04  midas
00307   Added number of messages for cm_msg_retrieve and in odbedit "old" command
00308 
00309   Revision 1.125  2000/11/06 14:19:19  midas
00310   Don't return from hs_read if variable not found (could be present later...)
00311 
00312   Revision 1.124  2000/10/21 12:26:12  midas
00313   Fixed bug with cache pointer in hs_read
00314 
00315   Revision 1.123  2000/09/29 13:31:12  midas
00316   ODBEdit cleanup now deletes open record with no client attached to
00317 
00318   Revision 1.122  2000/08/21 14:18:39  midas
00319   bk_close returns bank size
00320 
00321   Revision 1.121  2000/08/21 07:05:48  midas
00322   Added cm_msg_log1(...,facility) to be compatible with older programs
00323 
00324   Revision 1.120  2000/08/11 12:16:44  midas
00325   Fixed bug with "facility" being NULL
00326 
00327   Revision 1.119  2000/08/11 11:43:51  midas
00328   Added cm_msg1 to produce messages which go to a differnt logging file
00329 
00330   Revision 1.118  2000/08/10 08:04:56  midas
00331   Create default /runinfo structure in cm_connect_experiment
00332 
00333   Revision 1.117  2000/05/16 10:38:17  midas
00334   - Set MIDAS_DIR as the default /logger/data dir on cm_connect_experiment
00335   - Remove elog file if all messages are deleted
00336 
00337   Revision 1.116  2000/05/09 09:06:12  midas
00338   Added MIDAS_EXPTAB environment variable and hashmark comments in exptab
00339 
00340   Revision 1.115  2000/05/08 14:29:38  midas
00341   Added delete option in ELog
00342 
00343   Revision 1.114  2000/05/05 14:20:05  midas
00344   Do online mode check in al_trigger_alarm
00345 
00346   Revision 1.113  2000/04/26 20:27:06  pierre
00347   -Added doc++ comments on some functions.
00348 
00349   Revision 1.112  2000/04/25 11:55:42  midas
00350   Adjusted tabs for history functions
00351 
00352   Revision 1.111  2000/04/17 16:28:21  pierre
00353   - Added arg "BOOL binary_time" to hs_dump(), hs_fdump() for mhist -b
00354 
00355   Revision 1.110  2000/03/29 09:14:47  midas
00356   Fixed bug with original message tagging having the wrong offset
00357 
00358   Revision 1.109  2000/03/17 13:00:06  midas
00359   Frontends use default timeout fo 60 sec.
00360 
00361   Revision 1.108  2000/03/17 10:55:15  midas
00362   Don't trigger internal alarms if alarm system is off
00363 
00364   Revision 1.107  2000/03/04 00:42:29  midas
00365   Delete elog & alarm mutexes correctly
00366 
00367   Revision 1.106  2000/03/03 22:46:07  midas
00368   Remove elog and alarm mutex on exit
00369 
00370   Revision 1.105  2000/03/03 01:45:13  midas
00371   Added web password for mhttpd, added webpasswd command in odbedit
00372 
00373   Revision 1.104  2000/03/01 23:06:19  midas
00374   bk_xxx functions now don't use global variable _pbk
00375 
00376   Revision 1.103  2000/02/29 21:59:05  midas
00377   Fixec bug with order of actions in cm_transition
00378 
00379   Revision 1.102  2000/02/29 02:10:26  midas
00380   Added cm_is_ctrlc_pressed and cm_ack_ctrlc_pressed
00381 
00382   Revision 1.101  2000/02/25 22:49:29  midas
00383   Increased timeouts
00384 
00385   Revision 1.100  2000/02/25 22:19:09  midas
00386   Improved Ctrl-C handling
00387 
00388   Revision 1.99  2000/02/24 23:58:29  midas
00389   Fixed problem with _requested_transition being update by hotlink too late
00390 
00391   Revision 1.98  2000/02/24 22:29:25  midas
00392   Added deferred transitions
00393 
00394   Revision 1.97  2000/02/23 21:07:44  midas
00395   Changed spaces and tabulators
00396 
00397   Revision 1.96  2000/02/15 11:07:51  midas
00398   Changed GET_xxx to bit flags
00399 
00400   Revision 1.95  2000/02/09 08:03:52  midas
00401   Fixed bracket indention
00402 
00403   Revision 1.94  1999/12/08 16:10:43  midas
00404   Fixed another watchdog bug causing remote clients to crash
00405 
00406   Revision 1.93  1999/12/08 11:44:25  midas
00407   Fixed bug with watchdog timeout
00408 
00409   Revision 1.92  1999/12/08 10:00:41  midas
00410   Changed error string to single line
00411 
00412   Revision 1.91  1999/12/08 00:25:20  pierre
00413   - add cm_get_path in cm_msg_retrieve for midas.log location.
00414   - mod dm_buffer_create for arg "max user event size".
00415   - fix dm_area_flush.
00416   - fix other compilation warnings for OSF/1
00417 
00418   Revision 1.90  1999/11/26 08:31:58  midas
00419   midas.log is now places in the same directory as the .SHM files in case
00420   there is no data dir in the ODB
00421 
00422   Revision 1.89  1999/11/25 13:29:55  midas
00423   Fixed bug in cm_msg_retrieve
00424 
00425   Revision 1.88  1999/11/23 15:52:40  midas
00426   If an event is larger than the buffer read or write cache, it bypasses the
00427   cache.
00428 
00429   Revision 1.87  1999/11/19 09:49:58  midas
00430   Fixed bug with wrong default watchdog timeout in cm_connect_experiment1
00431 
00432   Revision 1.86  1999/11/12 10:04:59  midas
00433   Fixed bug with WATCHDOG_INTERVAL
00434 
00435   Revision 1.85  1999/11/10 15:05:16  midas
00436   Did some additional database locking
00437 
00438   Revision 1.84  1999/11/10 13:56:12  midas
00439   Alarm record only gets created when old one mismatches
00440 
00441   Revision 1.83  1999/11/10 10:39:11  midas
00442   Changed initialization of alarms
00443 
00444   Revision 1.82  1999/11/10 08:30:44  midas
00445   Fixed bug when editing the last elog message
00446 
00447   Revision 1.81  1999/11/09 14:44:08  midas
00448   Changed ODB locking in cm_cleanup
00449 
00450   Revision 1.80  1999/11/09 13:17:25  midas
00451   Added secure ODB feature
00452 
00453   Revision 1.79  1999/11/08 13:56:09  midas
00454   Added different alarm types
00455 
00456   Revision 1.78  1999/10/27 15:13:56  midas
00457   Added "access(<key>)" in alarm system
00458 
00459   Revision 1.77  1999/10/27 13:37:57  midas
00460   Added event size check in bm_send_event
00461 
00462   Revision 1.76  1999/10/18 15:52:12  midas
00463   Use "alarm count" to declare programs dead if inactive for 5 minutes
00464 
00465   Revision 1.75  1999/10/18 14:41:51  midas
00466   Use /programs/<name>/Watchdog timeout in all programs as timeout value. The
00467   default value can be submitted by calling cm_connect_experiment1(..., timeout)
00468 
00469   Revision 1.74  1999/10/13 08:03:28  midas
00470   Fixed bug displaying executed message as %d
00471 
00472   Revision 1.73  1999/10/11 14:14:03  midas
00473   Use ss_system in certain places
00474 
00475   Revision 1.72  1999/10/11 13:01:22  midas
00476   Produce system message when executing an alarm script
00477 
00478   Revision 1.71  1999/10/08 22:15:03  midas
00479   Added ftruncate for LINUX
00480 
00481   Revision 1.70  1999/10/08 22:00:30  midas
00482   Finished editing of elog messages
00483 
00484   Revision 1.69  1999/10/08 15:07:06  midas
00485   Program check creates new internal alarm when triggered
00486 
00487   Revision 1.68  1999/10/08 13:21:20  midas
00488   Alarm system disabled when running offline
00489 
00490   Revision 1.67  1999/10/07 13:50:49  midas
00491   Fixed bug with date in el_submit
00492 
00493   Revision 1.66  1999/10/07 13:31:18  midas
00494   Fixed truncated date in el_submit, cut off @host in author search
00495 
00496   Revision 1.65  1999/10/06 06:56:02  midas
00497   Include weekday in elog
00498 
00499   Revision 1.64  1999/10/05 13:16:10  midas
00500   Added global alarm flag "/alarms/alarm system active"
00501 
00502   Revision 1.63  1999/10/04 11:54:14  midas
00503   Submit full alarm string to execute command
00504 
00505   Revision 1.62  1999/09/30 22:59:06  pierre
00506   - fix bk_close for BK32
00507 
00508   Revision 1.61  1999/09/29 19:23:33  pierre
00509   - Fix bk_iterate,swap,locate for bank32
00510 
00511   Revision 1.60  1999/09/27 13:49:04  midas
00512   Added bUnique parameter to cm_shutdown
00513 
00514   Revision 1.59  1999/09/27 12:54:08  midas
00515   Finished alarm system
00516 
00517   Revision 1.58  1999/09/27 08:56:53  midas
00518   Fixed bug with missing run number in elog
00519 
00520   Revision 1.57  1999/09/23 14:00:48  midas
00521   Used capital names for mutexes
00522 
00523   Revision 1.56  1999/09/23 12:45:49  midas
00524   Added 32 bit banks
00525 
00526   Revision 1.55  1999/09/22 08:57:08  midas
00527   Implemented auto start and auto stop in /programs
00528 
00529   Revision 1.54  1999/09/21 14:57:39  midas
00530   Added "execute on start/stop" under /programs
00531 
00532   Revision 1.53  1999/09/21 14:15:04  midas
00533   Replaces cm_execute by system()
00534 
00535   Revision 1.52  1999/09/21 13:48:04  midas
00536   Added programs check in al_check
00537 
00538   Revision 1.51  1999/09/17 15:59:03  midas
00539   Added internal alarms
00540 
00541   Revision 1.50  1999/09/17 15:06:48  midas
00542   Moved al_check into cm_yield() and rpc_server_thread
00543 
00544   Revision 1.49  1999/09/17 11:50:53  midas
00545   Added al_xxx functions
00546 
00547   Revision 1.48  1999/09/17 11:48:06  midas
00548   Alarm system half finished
00549 
00550   Revision 1.47  1999/09/15 13:33:34  midas
00551   Added remote el_submit functionality
00552 
00553   Revision 1.46  1999/09/14 15:15:45  midas
00554   Moved el_xxx funtions into midas.c
00555 
00556   Revision 1.45  1999/09/13 11:08:24  midas
00557   Check NULL as experiment in cm_connect_experiment
00558 
00559   Revision 1.44  1999/09/10 06:11:15  midas
00560   Used %100 for year in tms structure
00561 
00562   Revision 1.43  1999/08/03 14:41:09  midas
00563   Lock buffer in bm_skip_event
00564 
00565   Revision 1.42  1999/08/03 11:15:07  midas
00566   Added bm_skip_event
00567 
00568   Revision 1.41  1999/07/21 09:22:01  midas
00569   Added Ctrl-C handler to cm_connect_experiment and cm_yield
00570 
00571   Revision 1.40  1999/06/28 12:01:21  midas
00572   Added hs_fdump
00573 
00574   Revision 1.39  1999/06/25 12:01:54  midas
00575   Added bk_delete function
00576 
00577   Revision 1.38  1999/06/23 09:36:24  midas
00578   - Fixed "too many connections" bug
00579   - incorporated PAAs dm_xxx changes
00580 
00581   Revision 1.37  1999/05/05 12:02:33  midas
00582   Added and modified history functions, added db_set_num_values
00583 
00584   Revision 1.36  1999/04/30 14:22:01  midas
00585   Send buffer name via bm_notify_client to java application
00586 
00587   Revision 1.35  1999/04/30 13:19:54  midas
00588   Changed inter-process communication (ss_resume, bm_notify_clients, etc)
00589   to strings so that server process can receive it's own watchdog produced
00590   messages (pass buffer name insteas buffer handle)
00591 
00592   Revision 1.34  1999/04/30 10:58:58  midas
00593   Added -D debug to screen for mserver
00594 
00595   Revision 1.33  1999/04/29 10:48:02  midas
00596   Implemented "/System/Client Notify" key
00597 
00598   Revision 1.32  1999/04/28 15:27:28  midas
00599   Made hs_read working for Java
00600 
00601   Revision 1.31  1999/04/27 15:16:14  midas
00602   Increased ASCII_BUFFER_SIZE to 64500
00603 
00604   Revision 1.30  1999/04/27 11:11:26  midas
00605   Added rpc_register_client
00606 
00607   Revision 1.29  1999/04/23 11:42:52  midas
00608   Made db_get_data_index working for Java
00609 
00610   Revision 1.28  1999/04/19 07:47:00  midas
00611   Added cm_msg_retrieve
00612 
00613   Revision 1.27  1999/04/16 15:13:28  midas
00614   bm_notify_client notifies ASCII client (Java) always
00615 
00616   Revision 1.26  1999/04/15 15:43:06  midas
00617   Added functionality for bm_receive_event in ASCII mode
00618 
00619   Revision 1.25  1999/04/15 09:58:42  midas
00620   Switched if (rpc_list[i].id == 0) statements
00621 
00622   Revision 1.24  1999/04/13 12:20:43  midas
00623   Added db_get_data1 (for Java)
00624 
00625   Revision 1.23  1999/04/08 15:26:05  midas
00626   Worked on rpc_execute_ascii
00627 
00628   Revision 1.22  1999/03/23 10:37:39  midas
00629   Fixed bug in cm_set_watchdog_params which causes mtape report ODB errors
00630 
00631   Revision 1.21  1999/02/12 10:55:03  midas
00632   Accepted PAA's modification in cm_set_watchdog_params()
00633 
00634   Revision 1.20  1999/02/11 13:14:46  midas
00635   Basic ASCII protocol implemented in server
00636 
00637   Revision 1.19  1999/02/09 14:38:23  midas
00638   Added debug logging facility
00639 
00640   Revision 1.18  1999/02/06 00:17:12  pierre
00641   - Fix local watchdog timeout in cm_set_watchdog_params()
00642   - Touch dm_xxx functions for OS_WINNT
00643 
00644   Revision 1.17  1999/02/02 07:42:22  midas
00645   Only print warning about zero length bank in bk_close if bank has type TID_STRUCT
00646 
00647   Revision 1.16  1999/02/01 15:41:23  midas
00648   Added warning for zero length bank in bk_close
00649 
00650   Revision 1.15  1999/02/01 13:03:49  midas
00651   Added /system/clients/xxx/link timeout to show current TCP timeout value
00652 
00653   Revision 1.14  1999/01/22 09:31:16  midas
00654   Fixed again status return from ss_mutex_create in bm_open_buffer
00655 
00656   Revision 1.13  1999/01/21 23:09:17  pierre
00657   - Incorporate dm_semaphore_...() functionality into ss_mutex_...()
00658   - Remove dm_semaphore_...(), adjust dm_...() accordingly.
00659   - Incorporate taskSpawn into ss_thread_create (system.c).
00660   - Adjust status value returnd from ss_mutex_create().
00661 
00662   Revision 1.12  1999/01/20 08:55:44  midas
00663   - Renames ss_xxx_mutex to ss_mutex_xxx
00664   - Added timout flag to ss_mutex_wait_for
00665 
00666   Revision 1.11  1999/01/19 19:58:56  pierre
00667   - Fix compiler warning in dm_buffer_send
00668 
00669   Revision 1.10  1999/01/18 17:50:35  pierre
00670   - Added dm_...() functions for Dual Memory buffer handling.
00671 
00672   Revision 1.9  1998/12/11 17:00:02  midas
00673   Fixed a few typos
00674 
00675   Revision 1.8  1998/10/28 12:05:57  midas
00676   Fixed minor compiler warning
00677 
00678   Revision 1.7  1998/10/28 12:01:30  midas
00679   Added version number to run start notification
00680 
00681   Revision 1.6  1998/10/27 10:53:48  midas
00682   - Added run start notification
00683   - Added ss_shell() for NT
00684 
00685   Revision 1.5  1998/10/23 14:21:50  midas
00686   - Modified version scheme from 1.06 to 1.6.0
00687   - cm_get_version() now returns versino as string
00688 
00689   Revision 1.4  1998/10/13 07:34:42  midas
00690   Reopened database in case of wrong password
00691 
00692   Revision 1.3  1998/10/12 12:19:02  midas
00693   Added Log tag in header
00694 
00695   Revision 1.2  1998/10/12 11:59:10  midas
00696   Added Log tag in header
00697 
00698 \********************************************************************/
00699 
00700 #include "midas.h"
00701 #include "msystem.h"
00702 #include <assert.h>
00703 
00704 /**dox***************************************************************/
00705 /** @file midas.c
00706 The main core C-code for Midas.
00707 */
00708 
00709 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00710  */
00711 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00712  */
00713 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00714  */
00715 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00716  */
00717 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00718  */
00719 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00720  */
00721 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00722  */
00723 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00724  */
00725 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00726  */
00727 
00728 /**dox***************************************************************/
00729 /** @addtogroup midasincludecode
00730  *  
00731  *  @{  */
00732 
00733 /**dox***************************************************************/
00734 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00735 
00736 /********************************************************************/
00737 /* data type sizes */
00738 INT tid_size[] = {
00739    0,                           /* tid == 0 not defined                               */
00740    1,                           /* TID_BYTE      unsigned byte         0       255    */
00741    1,                           /* TID_SBYTE     signed byte         -128      127    */
00742    1,                           /* TID_CHAR      single character      0       255    */
00743    2,                           /* TID_WORD      two bytes             0      65535   */
00744    2,                           /* TID_SHORT     signed word        -32768    32767   */
00745    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00746    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00747    4,                           /* TID_BOOL      four bytes bool       0        1     */
00748    4,                           /* TID_FLOAT     4 Byte float format                  */
00749    8,                           /* TID_DOUBLE    8 Byte float format                  */
00750    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00751    0,                           /* TID_STRING    zero terminated string               */
00752    0,                           /* TID_ARRAY     variable length array of unkown type */
00753    0,                           /* TID_STRUCT    C structure                          */
00754    0,                           /* TID_KEY       key in online database               */
00755    0                            /* TID_LINK      link in online database              */
00756 };
00757 
00758 /* data type names */
00759 char *tid_name[] = {
00760    "NULL",
00761    "BYTE",
00762    "SBYTE",
00763    "CHAR",
00764    "WORD",
00765    "SHORT",
00766    "DWORD",
00767    "INT",
00768    "BOOL",
00769    "FLOAT",
00770    "DOUBLE",
00771    "BITFIELD",
00772    "STRING",
00773    "ARRAY",
00774    "STRUCT",
00775    "KEY",
00776    "LINK"
00777 };
00778 
00779 struct {
00780    int transition;
00781    char name[32];
00782 } trans_name[] = {
00783    {TR_START, "START",}, 
00784    {TR_STOP, "STOP",}, 
00785    {TR_PAUSE, "PAUSE",}, 
00786    {TR_RESUME, "RESUME",}, 
00787    {TR_DEFERRED, "DEFERRED",},
00788 };
00789 
00790 /* Globals */
00791 #ifdef OS_MSDOS
00792 extern unsigned _stklen = 60000U;
00793 #endif
00794 
00795 extern DATABASE *_database;
00796 extern INT _database_entries;
00797 
00798 static BUFFER *_buffer;
00799 static INT _buffer_entries = 0;
00800 
00801 static INT _msg_buffer = 0;
00802 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00803 
00804 static REQUEST_LIST *_request_list;
00805 static INT _request_list_entries = 0;
00806 
00807 static EVENT_HEADER *_event_buffer;
00808 static INT _event_buffer_size = 0;
00809 
00810 static char *_net_recv_buffer;
00811 static INT _net_recv_buffer_size = 0;
00812 
00813 static char *_net_send_buffer;
00814 static INT _net_send_buffer_size = 0;
00815 
00816 static char *_tcp_buffer = NULL;
00817 static INT _tcp_wp = 0;
00818 static INT _tcp_rp = 0;
00819 
00820 static INT _send_sock;
00821 
00822 static void (*_debug_print) (char *) = NULL;
00823 static INT _debug_mode = 0;
00824 
00825 static INT _watchdog_last_called = 0;
00826 
00827 /* table for transition functions */
00828 
00829 typedef struct {
00830    INT transition;
00831    INT sequence_number;
00832    INT(*func) (INT, char *);
00833 } TRANS_TABLE;
00834 
00835 #define MAX_TRANSITIONS 20
00836 
00837 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00838 
00839 TRANS_TABLE _deferred_trans_table[] = {
00840    {TR_START},
00841    {TR_STOP},
00842    {TR_PAUSE},
00843    {TR_RESUME},
00844    {0}
00845 };
00846 
00847 static BOOL _server_registered = FALSE;
00848 
00849 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00850 
00851 void cm_ctrlc_handler(int sig);
00852 
00853 typedef struct {
00854    INT code;
00855    char *string;
00856 } ERROR_TABLE;
00857 
00858 ERROR_TABLE _error_table[] = {
00859    {CM_WRONG_PASSWORD, "Wrong password"},
00860    {CM_UNDEF_EXP, "Experiment not defined"},
00861    {CM_UNDEF_ENVIRON,
00862     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00863    {RPC_NET_ERROR, "Cannot connect to remote host"},
00864    {0, NULL}
00865 };
00866 
00867 
00868 typedef struct {
00869    void *adr;
00870    int size;
00871    char file[80];
00872    int line;
00873 } DBG_MEM_LOC;
00874 
00875 DBG_MEM_LOC *_mem_loc = NULL;
00876 INT _n_mem = 0;
00877 
00878 void *dbg_malloc(unsigned int size, char *file, int line)
00879 {
00880    FILE *f;
00881    void *adr;
00882    int i;
00883 
00884    adr = malloc(size);
00885 
00886    /* search for deleted entry */
00887    for (i = 0; i < _n_mem; i++)
00888       if (_mem_loc[i].adr == NULL)
00889          break;
00890 
00891    if (i == _n_mem) {
00892       _n_mem++;
00893       if (!_mem_loc)
00894          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00895       else
00896          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00897    }
00898 
00899    _mem_loc[i].adr = adr;
00900    _mem_loc[i].size = size;
00901    strcpy(_mem_loc[i].file, file);
00902    _mem_loc[i].line = line;
00903 
00904    f = fopen("mem.txt", "w");
00905    for (i = 0; i < _n_mem; i++)
00906       if (_mem_loc[i].adr)
00907          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00908                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00909    fclose(f);
00910 
00911    return adr;
00912 }
00913 
00914 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00915 {
00916    void *adr;
00917 
00918    adr = dbg_malloc(size * count, file, line);
00919    if (adr)
00920       memset(adr, 0, size * count);
00921 
00922    return adr;
00923 }
00924 
00925 void dbg_free(void *adr, char *file, int line)
00926 {
00927    FILE *f;
00928    int i;
00929 
00930    free(adr);
00931 
00932    for (i = 0; i < _n_mem; i++)
00933       if (_mem_loc[i].adr == adr)
00934          break;
00935 
00936    if (i < _n_mem)
00937       _mem_loc[i].adr = NULL;
00938 
00939    f = fopen("mem.txt", "w");
00940    for (i = 0; i < _n_mem; i++)
00941       if (_mem_loc[i].adr)
00942          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00943                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00944    fclose(f);
00945 
00946 }
00947 
00948 #ifndef HAVE_STRLCPY
00949 /*---- strlcpy and strlcat to avoid buffer overflow ----------------*/
00950 
00951 /*
00952  * Copy src to string dst of size siz.  At most siz-1 characters
00953  * will be copied.  Always NUL terminates (unless size == 0).
00954  * Returns strlen(src); if retval >= siz, truncation occurred.
00955  */
00956 INT strlcpy(char *dst, const char *src, INT size)
00957 {
00958    char *d = dst;
00959    const char *s = src;
00960    INT n = size;
00961 
00962    /* Copy as many bytes as will fit */
00963    if (n != 0 && --n != 0) {
00964       do {
00965          if ((*d++ = *s++) == 0)
00966             break;
00967       } while (--n != 0);
00968    }
00969 
00970    /* Not enough room in dst, add NUL and traverse rest of src */
00971    if (n == 0) {
00972       if (size != 0)
00973          *d = '\0';             /* NUL-terminate dst */
00974       while (*s++);
00975    }
00976 
00977    return (s - src - 1);        /* count does not include NUL */
00978 }
00979 
00980 /*
00981  * Appends src to string dst of size siz (unlike strncat, siz is the
00982  * full size of dst, not space left).  At most siz-1 characters
00983  * will be copied.  Always NUL terminates (unless size <= strlen(dst)).
00984  * Returns strlen(src) + MIN(size, strlen(initial dst)).
00985  * If retval >= size, truncation occurred.
00986  */
00987 INT strlcat(char *dst, const char *src, INT size)
00988 {
00989    char *d = dst;
00990    const char *s = src;
00991    INT n = size;
00992    INT dlen;
00993 
00994    /* Find the end of dst and adjust bytes left but don't go past end */
00995    while (n-- != 0 && *d != '\0')
00996       d++;
00997    dlen = d - dst;
00998    n = size - dlen;
00999 
01000    if (n == 0)
01001       return (dlen + strlen(s));
01002    while (*s != '\0') {
01003       if (n != 1) {
01004          *d++ = *s;
01005          n--;
01006       }
01007       s++;
01008    }
01009    *d = '\0';
01010 
01011    return (dlen + (s - src));   /* count does not include NUL */
01012 }
01013 #endif
01014 
01015 /********************************************************************\
01016 *                                                                    *
01017 *              Common message functions                              *
01018 *                                                                    *
01019 \********************************************************************/
01020 
01021 static int (*_message_print) (const char *) = puts;
01022 static INT _message_mask_system = MT_ALL;
01023 static INT _message_mask_user = MT_ALL;
01024 
01025 
01026 /**dox***************************************************************/
01027 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01028 
01029 /**dox***************************************************************/
01030 /** @addtogroup msgfunctionc
01031  *  
01032  *  @{  */
01033 
01034 /********************************************************************/
01035 /**
01036 Convert error code to string. Used after cm_connect_experiment to print
01037 error string in command line programs or windows programs.
01038 @param code Error code as defined in midas.h
01039 @param string Error string
01040 @return CM_SUCCESS
01041 */
01042 INT cm_get_error(INT code, char *string)
01043 {
01044    INT i;
01045 
01046    for (i = 0; _error_table[i].code; i++)
01047       if (_error_table[i].code == code) {
01048          strcpy(string, _error_table[i].string);
01049          return CM_SUCCESS;
01050       }
01051 
01052    sprintf(string, "Unexpected error #%d", code);
01053    return CM_SUCCESS;
01054 }
01055 
01056 /********************************************************************/
01057 /** 
01058 Set message masks. When a message is generated by calling cm_msg(),
01059 it can got to two destinatinons. First a user defined callback routine
01060 and second to the "SYSMSG" buffer.
01061 
01062 A user defined callback receives all messages which satisfy the user_mask.
01063 
01064 \code
01065 int message_print(const char *msg)
01066 {
01067   char str[160];
01068 
01069   memset(str, ' ', 159);
01070   str[159] = 0;
01071   if (msg[0] == '[')
01072     msg = strchr(msg, ']')+2;
01073   memcpy(str, msg, strlen(msg));
01074   ss_printf(0, 20, str);
01075   return 0;
01076 }
01077 ...
01078   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
01079 ...
01080 \endcode
01081 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
01082 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
01083 @param func Function which receives all printout. By setting "puts",
01084        messages are just printed to the screen.
01085 @return CM_SUCCESS
01086 */
01087 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
01088 {
01089    _message_mask_system = system_mask;
01090    _message_mask_user = user_mask;
01091    _message_print = func;
01092 
01093    return BM_SUCCESS;
01094 }
01095 
01096 /********************************************************************/
01097 /**
01098 Write message to logging file. Called by cm_msg.
01099 @attention May burn your fingers
01100 @param message_type      Message type
01101 @param message          Message string
01102 @return CM_SUCCESS
01103 */
01104 INT cm_msg_log(INT message_type, const char *message)
01105 {
01106    char dir[256];
01107    char filename[256];
01108    char path[256];
01109    char str[256];
01110    FILE *f;
01111    INT status, size;
01112    HNDLE hDB, hKey;
01113 
01114    if (rpc_is_remote())
01115       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
01116 
01117    if (message_type != MT_DEBUG) {
01118       cm_get_experiment_database(&hDB, NULL);
01119 
01120       if (hDB) {
01121          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01122          if (status == DB_SUCCESS) {
01123             size = sizeof(dir);
01124             memset(dir, 0, size);
01125             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01126             if (dir[0] != 0)
01127                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01128                   strcat(dir, DIR_SEPARATOR_STR);
01129 
01130             strcpy(filename, "midas.log");
01131             size = sizeof(filename);
01132             db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01133                          TRUE);
01134 
01135             strcpy(path, dir);
01136             strcat(path, filename);
01137          } else {
01138             cm_get_path(dir);
01139             if (dir[0] != 0)
01140                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01141                   strcat(dir, DIR_SEPARATOR_STR);
01142 
01143             strcpy(path, dir);
01144             strcat(path, "midas.log");
01145          }
01146       } else
01147          strcpy(path, "midas.log");
01148 
01149       f = fopen(path, "a");
01150       if (f == NULL) {
01151          printf("Cannot open message log file %s\n", path);
01152       } else {
01153          strcpy(str, ss_asctime());
01154          fprintf(f, str);
01155          fprintf(f, " %s\n", message);
01156 
01157          fclose(f);
01158       }
01159    }
01160 
01161    return CM_SUCCESS;
01162 }
01163 
01164 /********************************************************************/
01165 /**
01166 Write message to logging file. Called by cm_msg().
01167 @internal 
01168 @param message_type      Message type
01169 @param message          Message string
01170 @param facility         Message facility, filename in which messages will be written
01171 @return CM_SUCCESS
01172 */
01173 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
01174 /********************************************************************\
01175 
01176   Routine: cm_msg_log1
01177 
01178   Purpose: Write message to logging file. Called by cm_msg.
01179            Internal use only
01180 
01181   Input:
01182     INT    message_type      Message type
01183     char   *message          Message string
01184     char   *
01185 
01186   Output:
01187     none
01188 
01189   Function value:
01190     CM_SUCCESS
01191 
01192 \********************************************************************/
01193 {
01194    char dir[256];
01195    char filename[256];
01196    char path[256];
01197    char str[256];
01198    FILE *f;
01199    INT status, size;
01200    HNDLE hDB, hKey;
01201 
01202 
01203    if (rpc_is_remote())
01204       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
01205 
01206    if (message_type != MT_DEBUG) {
01207       cm_get_experiment_database(&hDB, NULL);
01208 
01209       if (hDB) {
01210          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01211          if (status == DB_SUCCESS) {
01212             size = sizeof(dir);
01213             memset(dir, 0, size);
01214             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01215             if (dir[0] != 0)
01216                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01217                   strcat(dir, DIR_SEPARATOR_STR);
01218 
01219             if (facility[0]) {
01220                strcpy(filename, facility);
01221                strcat(filename, ".log");
01222             } else {
01223                strcpy(filename, "midas.log");
01224                size = sizeof(filename);
01225                db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01226                             TRUE);
01227             }
01228 
01229             strcpy(path, dir);
01230             strcat(path, filename);
01231          } else {
01232             cm_get_path(dir);
01233             if (dir[0] != 0)
01234                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01235                   strcat(dir, DIR_SEPARATOR_STR);
01236 
01237             strcpy(path, dir);
01238             if (facility[0]) {
01239                strcat(path, facility);
01240                strcat(path, ".log");
01241             } else
01242                strcat(path, "midas.log");
01243          }
01244       } else {
01245          if (facility[0]) {
01246             strcpy(path, facility);
01247             strcat(path, ".log");
01248          } else
01249             strcpy(path, "midas.log");
01250       }
01251 
01252       f = fopen(path, "a");
01253       if (f == NULL) {
01254          printf("Cannot open message log file %s\n", path);
01255       } else {
01256          strcpy(str, ss_asctime());
01257          fprintf(f, str);
01258          fprintf(f, " %s\n", message);
01259          fclose(f);
01260       }
01261    }
01262 
01263    return CM_SUCCESS;
01264 }
01265 
01266 /********************************************************************/
01267 /** 
01268 This routine can be called whenever an internal error occurs
01269 or an informative message is produced. Different message
01270 types can be enabled or disabled by setting the type bits
01271 via cm_set_msg_print().
01272 @attention Do not add the "\n" escape carriage control at the end of the
01273 formated line as it is already added by the client on the receiving side.
01274 \code
01275    ...
01276    cm_msg(MINFO, "my program", "This is a information message only);
01277    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
01278    cm_msg(MTALK, "my_program", My program is Done!");
01279    ...
01280 \endcode
01281 @param message_type (See @ref midas_macro).
01282 @param filename Name of source file where error occured
01283 @param line Line number where error occured
01284 @param routine Routine name.
01285 @param format message to printout, ... Parameters like for printf()
01286 @return CM_SUCCESS
01287 */
01288 INT cm_msg(INT message_type, char *filename, INT line,
01289            const char *routine, const char *format, ...)
01290 {
01291    va_list argptr;
01292    char event[1000], str[256], local_message[256], send_message[256], *pc;
01293    EVENT_HEADER *pevent;
01294    INT status;
01295    static BOOL in_routine = FALSE;
01296 
01297    /* avoid recursive calls */
01298    if (in_routine)
01299       return 0;
01300 
01301    in_routine = TRUE;
01302 
01303    /* strip path */
01304    pc = filename + strlen(filename);
01305    while (*pc != '\\' && *pc != '/' && pc != filename)
01306       pc--;
01307    if (pc != filename)
01308       pc++;
01309 
01310    /* print client name into string */
01311    if (message_type == MT_USER)
01312       sprintf(send_message, "[%s] ", routine);
01313    else {
01314       rpc_get_name(str);
01315       if (str[0])
01316          sprintf(send_message, "[%s] ", str);
01317       else
01318          send_message[0] = 0;
01319    }
01320 
01321    local_message[0] = 0;
01322 
01323    /* preceed error messages with file and line info */
01324    if (message_type == MT_ERROR) {
01325       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01326       strcat(send_message, str);
01327       strcat(local_message, str);
01328    }
01329 
01330    /* print argument list into message */
01331    va_start(argptr, format);
01332    vsprintf(str, (char *) format, argptr);
01333    va_end(argptr);
01334    strcat(send_message, str);
01335    strcat(local_message, str);
01336 
01337    /* call user function if set via cm_set_msg_print */
01338    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01339       _message_print(local_message);
01340 
01341    /* return if system mask is not set */
01342    if ((message_type & _message_mask_system) == 0) {
01343       in_routine = FALSE;
01344       return CM_SUCCESS;
01345    }
01346 
01347    /* copy message to event */
01348    pevent = (EVENT_HEADER *) event;
01349    strcpy(event + sizeof(EVENT_HEADER), send_message);
01350 
01351    /* send event if not of type MLOG */
01352    if (message_type != MT_LOG) {
01353       /* if no message buffer already opened, do so now */
01354       if (_msg_buffer == 0) {
01355          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01356          if (status != BM_SUCCESS && status != BM_CREATED) {
01357             in_routine = FALSE;
01358             return status;
01359          }
01360       }
01361 
01362       /* setup the event header and send the message */
01363       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01364                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01365       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01366    }
01367 
01368    /* log message */
01369    cm_msg_log(message_type, send_message);
01370 
01371    in_routine = FALSE;
01372 
01373    return CM_SUCCESS;
01374 }
01375 
01376 /********************************************************************/
01377 /**
01378 This routine is similar to @ref cm_msg().
01379 It differs from cm_msg() only by the logging destination being a file
01380 given through the argument list i.e:\b facility
01381 @internal
01382 @attention Do not add the "\n" escape carriage control at the end of the
01383 formated line as it is already added by the client on the receiving side.
01384 The first arg in the following example uses the predefined
01385 macro MINFO which handles automatically the first 3 arguments of the function
01386 (see @ref midas_macro).
01387 \code   ...
01388    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
01389    ...
01390 //----- File my_log_file.log
01391 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
01392 \endcode
01393 @param message_type See @ref midas_macro.
01394 @param filename Name of source file where error occured
01395 @param line Line number where error occured
01396 @param facility Logging file name
01397 @param routine Routine name
01398 @param format message to printout, ... Parameters like for printf()
01399 @return CM_SUCCESS
01400 */
01401 INT cm_msg1(INT message_type, char *filename, INT line,
01402             const char *facility, const char *routine, const char *format, ...)
01403 {
01404    va_list argptr;
01405    char event[1000], str[256], local_message[256], send_message[256], *pc;
01406    EVENT_HEADER *pevent;
01407    INT status;
01408    static BOOL in_routine = FALSE;
01409 
01410    /* avoid recursive calles */
01411    if (in_routine)
01412       return 0;
01413 
01414    in_routine = TRUE;
01415 
01416    /* strip path */
01417    pc = filename + strlen(filename);
01418    while (*pc != '\\' && *pc != '/' && pc != filename)
01419       pc--;
01420    if (pc != filename)
01421       pc++;
01422 
01423    /* print client name into string */
01424    if (message_type == MT_USER)
01425       sprintf(send_message, "[%s] ", routine);
01426    else {
01427       rpc_get_name(str);
01428       if (str[0])
01429          sprintf(send_message, "[%s] ", str);
01430       else
01431          send_message[0] = 0;
01432    }
01433 
01434    local_message[0] = 0;
01435 
01436    /* preceed error messages with file and line info */
01437    if (message_type == MT_ERROR) {
01438       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01439       strcat(send_message, str);
01440       strcat(local_message, str);
01441    }
01442 
01443    /* print argument list into message */
01444    va_start(argptr, format);
01445    vsprintf(str, (char *) format, argptr);
01446    va_end(argptr);
01447 
01448    if (facility)
01449       sprintf(local_message + strlen(local_message), "{%s} ", facility);
01450 
01451    strcat(send_message, str);
01452    strcat(local_message, str);
01453 
01454    /* call user function if set via cm_set_msg_print */
01455    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01456       _message_print(local_message);
01457 
01458    /* return if system mask is not set */
01459    if ((message_type & _message_mask_system) == 0) {
01460       in_routine = FALSE;
01461       return CM_SUCCESS;
01462    }
01463 
01464    /* copy message to event */
01465    pevent = (EVENT_HEADER *) event;
01466    strcpy(event + sizeof(EVENT_HEADER), send_message);
01467 
01468    /* send event if not of type MLOG */
01469    if (message_type != MT_LOG) {
01470       /* if no message buffer already opened, do so now */
01471       if (_msg_buffer == 0) {
01472          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01473          if (status != BM_SUCCESS && status != BM_CREATED) {
01474             in_routine = FALSE;
01475             return status;
01476          }
01477       }
01478 
01479       /* setup the event header and send the message */
01480       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01481                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01482       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01483    }
01484 
01485    /* log message */
01486    cm_msg_log1(message_type, send_message, facility);
01487 
01488    in_routine = FALSE;
01489 
01490    return CM_SUCCESS;
01491 }
01492 
01493 /********************************************************************/
01494 /** 
01495 Register a dispatch function for receiving system messages.
01496 - example code from mlxspeaker.c
01497 \code
01498 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
01499 {
01500   char str[256], *pc, *sp;
01501   // print message
01502   printf("%s\n", (char *)(message));
01503 
01504   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
01505                  ,header->event_id
01506                  ,header->trigger_mask
01507                  ,header->serial_number
01508                  ,header->data_size);
01509   pc = strchr((char *)(message),']')+2;
01510   ...
01511   // skip none talking message
01512   if (header->trigger_mask == MT_TALK ||
01513       header->trigger_mask == MT_USER)
01514    ...
01515 }
01516 
01517 int main(int argc, char *argv[])
01518 {
01519   ...
01520   // now connect to server
01521   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
01522   if (status != CM_SUCCESS)
01523     return 1;
01524   // Register callback for messages
01525   cm_msg_register(receive_message);
01526   ...
01527 }
01528 \endcode
01529 @param func Dispatch function.
01530 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
01531 */
01532 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
01533 {
01534    INT status, id;
01535 
01536    /* if no message buffer already opened, do so now */
01537    if (_msg_buffer == 0) {
01538       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01539       if (status != BM_SUCCESS && status != BM_CREATED)
01540          return status;
01541    }
01542 
01543    _msg_dispatch = func;
01544 
01545    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
01546 
01547    return status;
01548 }
01549 
01550 /********************************************************************/
01551 /**
01552 Retrieve old messages from log file 
01553 @param  n_message        Number of messages to retrieve
01554 @param  message          buf_size bytes of messages, separated
01555                          by \n characters. The returned number
01556                          of bytes is normally smaller than the
01557                          initial buf_size, since only full
01558                          lines are returned.
01559 @param *buf_size         Size of message buffer to fill
01560 @return CM_SUCCESS
01561 */
01562 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
01563 {
01564    char dir[256];
01565    char filename[256];
01566    char path[256], *p;
01567    FILE *f;
01568    INT status, size, offset, i;
01569    HNDLE hDB, hKey;
01570 
01571 
01572    if (rpc_is_remote())
01573       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
01574 
01575    cm_get_experiment_database(&hDB, NULL);
01576 
01577    if (hDB) {
01578       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01579       if (status == DB_SUCCESS) {
01580          size = sizeof(dir);
01581          memset(dir, 0, size);
01582          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01583          if (dir[0] != 0)
01584             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01585                strcat(dir, DIR_SEPARATOR_STR);
01586 
01587          strcpy(filename, "midas.log");
01588          size = sizeof(filename);
01589          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
01590 
01591          strcpy(path, dir);
01592          strcat(path, filename);
01593       } else {
01594          cm_get_path(dir);
01595          if (dir[0] != 0)
01596             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01597                strcat(dir, DIR_SEPARATOR_STR);
01598 
01599          strcpy(path, dir);
01600          strcat(path, "midas.log");
01601       }
01602    } else
01603       strcpy(path, "midas.log");
01604 
01605    f = fopen(path, "rb");
01606    if (f == NULL) {
01607       sprintf(message, "Cannot open message log file %s\n", path);
01608       *buf_size = strlen(message);
01609       return CM_DB_ERROR;
01610    } else {
01611       /* position buf_size bytes before the EOF */
01612       fseek(f, -(*buf_size - 1), SEEK_END);
01613       offset = ftell(f);
01614       if (offset != 0) {
01615          /* go to end of line */
01616          fgets(message, *buf_size - 1, f);
01617          offset = ftell(f) - offset;
01618          *buf_size -= offset;
01619       }
01620 
01621       memset(message, 0, *buf_size);
01622       fread(message, 1, *buf_size - 1, f);
01623       message[*buf_size - 1] = 0;
01624       fclose(f);
01625 
01626       p = message + (*buf_size - 2);
01627 
01628       /* goto end of buffer */
01629       while (p != message && *p == 0)
01630          p--;
01631 
01632       /* strip line break */
01633       while (p != message && (*p == '\n' || *p == '\r'))
01634          *(p--) = 0;
01635 
01636       /* trim buffer so that last n_messages remain */
01637       for (i = 0; i < n_message; i++) {
01638          while (p != message && *p != '\n')
01639             p--;
01640 
01641          while (p != message && (*p == '\n' || *p == '\r'))
01642             p--;
01643       }
01644       if (p != message) {
01645          p++;
01646          while (*p == '\n' || *p == '\r')
01647             p++;
01648       }
01649 
01650       *buf_size = (*buf_size - 1) - ((PTYPE) p - (PTYPE) message);
01651 
01652       memmove(message, p, *buf_size);
01653       message[*buf_size] = 0;
01654    }
01655 
01656    return CM_SUCCESS;
01657 }
01658 
01659 /**dox***************************************************************/
01660 /** @} */ /* end of msgfunctionc */
01661 
01662 /**dox***************************************************************/
01663 /** @addtogroup cmfunctionc
01664  *  
01665  *  @{  */
01666 
01667 /********************************************************************/
01668 /**
01669 Get time from MIDAS server and set local time.
01670 @param    seconds         Time in seconds
01671 @return CM_SUCCESS
01672 */
01673 INT cm_synchronize(DWORD * seconds)
01674 {
01675    INT sec, status;
01676 
01677    /* if connected to server, get time from there */
01678    if (rpc_is_remote()) {
01679       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01680 
01681       /* set local time */
01682       if (status == CM_SUCCESS)
01683          ss_settime(sec);
01684    }
01685 
01686    /* return time to caller */
01687    if (seconds != NULL) {
01688       *seconds = ss_time();
01689    }
01690 
01691    return CM_SUCCESS;
01692 }
01693 
01694 /********************************************************************/
01695 /**
01696 Get time from MIDAS server and set local time.
01697 @param    str            return time string
01698 @param    buf_size       Maximum size of str
01699 @return   CM_SUCCESS
01700 */
01701 INT cm_asctime(char *str, INT buf_size)
01702 {
01703    /* if connected to server, get time from there */
01704    if (rpc_is_remote())
01705       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01706 
01707    /* return local time */
01708    strcpy(str, ss_asctime());
01709 
01710    return CM_SUCCESS;
01711 }
01712 
01713 /********************************************************************/
01714 /**
01715 Get time from ss_time on server.
01716 @param    time string
01717 @return   CM_SUCCESS
01718 */
01719 INT cm_time(DWORD * time)
01720 {
01721    /* if connected to server, get time from there */
01722    if (rpc_is_remote())
01723       return rpc_call(RPC_CM_TIME, time);
01724 
01725    /* return local time */
01726    *time = ss_time();
01727 
01728    return CM_SUCCESS;
01729 }
01730 
01731 /**dox***************************************************************/
01732 /** @} */ /* end of cmfunctionc */
01733 
01734 /********************************************************************\
01735 *                                                                    *
01736 *           cm_xxx  -  Common Functions to buffer & database         *
01737 *                                                                    *
01738 \********************************************************************/
01739 
01740 /* Globals */
01741 
01742 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01743 static HNDLE _hDB = 0;          /* Database handle */
01744 static char _client_name[NAME_LENGTH];
01745 static char _path_name[MAX_STRING_LENGTH];
01746 static INT _call_watchdog = TRUE;
01747 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01748 INT _mutex_alarm, _mutex_elog;
01749 
01750 /**dox***************************************************************/
01751 /** @addtogroup cmfunctionc
01752  *  
01753  *  @{  */
01754 
01755 /**
01756 Return version number of current MIDAS library as a string
01757 @return version number * 100
01758 */
01759 char *cm_get_version()
01760 {
01761    return MIDAS_VERSION;
01762 }
01763 
01764 /********************************************************************/
01765 /**
01766 Set path to actual experiment. This function gets called
01767 by cm_connect_experiment if the connection is established
01768 to a local experiment (not through the TCP/IP server).
01769 The path is then used for all shared memory routines.
01770 @param  path             Pathname
01771 @return CM_SUCCESS
01772 */
01773 INT cm_set_path(char *path)
01774 {
01775    strcpy(_path_name, path);
01776 
01777    /* check for trailing directory seperator */
01778    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01779       strcat(_path_name, DIR_SEPARATOR_STR);
01780 
01781    return CM_SUCCESS;
01782 }
01783 
01784 /********************************************************************/
01785 /**
01786 Return the path name previously set with cm_set_path.
01787 @param  path             Pathname
01788 @return CM_SUCCESS
01789 */
01790 INT cm_get_path(char *path)
01791 {
01792    strcpy(path, _path_name);
01793 
01794    return CM_SUCCESS;
01795 }
01796 
01797 /**dox***************************************************************/
01798 /** @} */ /* end of cmfunctionc */
01799 
01800 /**dox***************************************************************/
01801 /** @addtogroup cmfunctionc
01802  *  
01803  *  @{  */
01804 
01805 /**dox***************************************************************/
01806 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01807 
01808 typedef struct {
01809    char name[NAME_LENGTH];
01810    char directory[MAX_STRING_LENGTH];
01811    char user[NAME_LENGTH];
01812 } experiment_table;
01813 
01814 static experiment_table exptab[MAX_EXPERIMENT];
01815 
01816 /**dox***************************************************************/
01817 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01818 
01819 /**
01820 Scan the "exptab" file for MIDAS experiment names and save them
01821 for later use by rpc_server_accept(). The file is first searched
01822 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01823 @return CM_SUCCESS<br>
01824         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01825 */
01826 INT cm_scan_experiments(void)
01827 {
01828    INT i;
01829    FILE *f;
01830    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01831 
01832    for (i = 0; i < MAX_EXPERIMENT; i++)
01833       exptab[i].name[0] = 0;
01834 
01835    /* MIDAS_DIR overrides exptab */
01836    if (getenv("MIDAS_DIR")) {
01837       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01838 
01839       strcpy(exptab[0].name, "Default");
01840       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01841       exptab[0].user[0] = 0;
01842 
01843       return CM_SUCCESS;
01844    }
01845 
01846    /* default directory for different OSes */
01847 #if defined (OS_WINNT)
01848    if (getenv("SystemRoot"))
01849       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01850    else if (getenv("windir"))
01851       strlcpy(str, getenv("windir"), sizeof(str));
01852    else
01853       strcpy(str, "");
01854 
01855    strcpy(alt_str, str);
01856    strcat(str, "\\system32\\exptab");
01857    strcat(alt_str, "\\system\\exptab");
01858 #elif defined (OS_UNIX)
01859    strcpy(str, "/etc/exptab");
01860    strcpy(alt_str, "/exptab");
01861 #else
01862    strcpy(str, "exptab");
01863    strcpy(alt_str, "exptab");
01864 #endif
01865 
01866    /* MIDAS_EXPTAB overrides default directory */
01867    if (getenv("MIDAS_EXPTAB")) {
01868       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01869       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01870    }
01871 
01872    /* read list of available experiments */
01873    f = fopen(str, "r");
01874    if (f == NULL) {
01875       f = fopen(alt_str, "r");
01876       if (f == NULL)
01877          return CM_UNDEF_ENVIRON;
01878    }
01879 
01880    i = 0;
01881    if (f != NULL) {
01882       do {
01883          str[0] = 0;
01884          if (fgets(str, 100, f) == NULL)
01885             break;
01886          if (str[0] && str[0] != '#') {
01887             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01888 
01889             /* check for trailing directory separator */
01890             pdir = exptab[i].directory;
01891             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01892                strcat(pdir, DIR_SEPARATOR_STR);
01893 
01894             i++;
01895          }
01896       } while (!feof(f));
01897       fclose(f);
01898    }
01899 
01900    /*
01901       for (j=0 ; j<i ; j++)
01902       {
01903       sprintf(str, "Scanned experiment %s", exptab[j].name);
01904       cm_msg(MINFO, str);
01905       }
01906     */
01907 
01908    return CM_SUCCESS;
01909 }
01910 
01911 /********************************************************************/
01912 /**
01913 Delete client info from database
01914 @param hDB               Database handle
01915 @param pid               PID of entry to delete, zero for this process.
01916 @return CM_SUCCESS
01917 */
01918 INT cm_delete_client_info(HNDLE hDB, INT pid)
01919 {
01920 #ifdef LOCAL_ROUTINES
01921 
01922    /* only do it if local */
01923    if (!rpc_is_remote()) {
01924       INT status;
01925       HNDLE hKey;
01926       char str[256];
01927 
01928       if (!pid)
01929          pid = ss_gettid();
01930 
01931       /* don't delete info from a closed database */
01932       if (_database_entries == 0)
01933          return CM_SUCCESS;
01934 
01935       /* make operation atomic by locking database */
01936       db_lock_database(hDB);
01937 
01938       sprintf(str, "System/Clients/%0d", pid);
01939       status = db_find_key1(hDB, 0, str, &hKey);
01940       if (status != DB_SUCCESS) {
01941          db_unlock_database(hDB);
01942          return status;
01943       }
01944 
01945       /* unlock client entry and delete it without locking DB */
01946       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01947       db_delete_key1(hDB, hKey, 1, TRUE);
01948 
01949       db_unlock_database(hDB);
01950 
01951       /* touch notify key to inform others */
01952       status = 0;
01953       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01954    }
01955 #endif                          /*LOCAL_ROUTINES */
01956 
01957    return CM_SUCCESS;
01958 }
01959 
01960 /********************************************************************/
01961 /**
01962 Check if a client with a /system/client/xxx entry has
01963 a valid entry in the ODB client table. If not, remove
01964 that client from the /system/client tree. 
01965 @param   hDB               Handle to online database
01966 @param   hKeyClient        Handle to client key
01967 @return  CM_SUCCESS, CM_NO_CLIENT
01968 */
01969 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01970 {
01971 #ifdef LOCAL_ROUTINES
01972 
01973    KEY key;
01974    DATABASE_HEADER *pheader;
01975    DATABASE_CLIENT *pclient;
01976    INT i, client_pid, status;
01977    char name[NAME_LENGTH];
01978 
01979    db_get_key(hDB, hKeyClient, &key);
01980    client_pid = atoi(key.name);
01981 
01982    i = sizeof(name);
01983    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01984 
01985    db_lock_database(hDB);
01986    if (_database[hDB - 1].attached) {
01987       pheader = _database[hDB - 1].database_header;
01988       pclient = pheader->client;
01989 
01990       /* loop through clients */
01991       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01992          if (pclient->tid == client_pid)
01993             break;
01994 
01995       if (i == pheader->max_client_index) {
01996          /* client not found : delete ODB stucture */
01997          db_unlock_database(hDB);
01998 
01999          status = cm_delete_client_info(hDB, client_pid);
02000          if (status != CM_SUCCESS)
02001             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
02002          else
02003             cm_msg(MINFO, "cm_check_clinet",
02004                    "Deleted /System/Clients/%d entry for client %s\n", client_pid, name);
02005 
02006          return CM_NO_CLIENT;
02007       }
02008    }
02009 
02010    db_unlock_database(hDB);
02011 
02012 #endif                          /*LOCAL_ROUTINES */
02013 
02014    return CM_SUCCESS;
02015 }
02016 
02017 /********************************************************************/
02018 /**
02019 Set client information in online database and return handle 
02020 @param  hDB              Handle to online database  
02021 @param  hKeyClient       returned key
02022 @param  host_name        server name 
02023 @param  client_name      Name of this program as it will be seen
02024                          by other clients.
02025 @param  hw_type          Type of byte order
02026 @param  password         MIDAS password  
02027 @param  watchdog_timeout Default watchdog timeout, can be overwritten
02028                          by ODB setting /programs/<name>/Watchdog timeout
02029 @return   CM_SUCCESS
02030 */
02031 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
02032                        char *client_name, INT hw_type, char *password,
02033                        DWORD watchdog_timeout)
02034 {
02035    if (rpc_is_remote())
02036       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
02037                       host_name, client_name, hw_type, password, watchdog_timeout);
02038 
02039 #ifdef LOCAL_ROUTINES
02040    {
02041       INT status, pid, data, i, index, size;
02042       HNDLE hKey, hSubkey;
02043       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
02044       BOOL call_watchdog, allow;
02045       PROGRAM_INFO_STR(program_info_str);
02046 
02047       /* check security if password is presend */
02048       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
02049       if (hKey) {
02050          /* get password */
02051          size = sizeof(pwd);
02052          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
02053 
02054          /* first check allowed hosts list */
02055          allow = FALSE;
02056          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
02057          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
02058             allow = TRUE;
02059 
02060          /* check allowed programs list */
02061          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
02062          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
02063             allow = TRUE;
02064 
02065          /* now check password */
02066          if (!allow &&
02067              strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
02068             if (password[0])
02069                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s",
02070                       host_name);
02071             db_close_all_databases();
02072             bm_close_all_buffers();
02073             _msg_buffer = 0;
02074             return CM_WRONG_PASSWORD;
02075          }
02076       }
02077 
02078       /* make following operation atomic by locking database */
02079       db_lock_database(hDB);
02080 
02081       /* check if entry with this pid exists already */
02082       pid = ss_gettid();
02083 
02084       sprintf(str, "System/Clients/%0d", pid);
02085       status = db_find_key(hDB, 0, str, &hKey);
02086       if (status == DB_SUCCESS) {
02087          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02088          db_delete_key(hDB, hKey, TRUE);
02089       }
02090 
02091       if (strlen(client_name) >= NAME_LENGTH)
02092          client_name[NAME_LENGTH] = 0;
02093 
02094       strcpy(name, client_name);
02095       strcpy(orig_name, client_name);
02096 
02097       /* check if client name already exists */
02098       status = db_find_key(hDB, 0, "System/Clients", &hKey);
02099 
02100       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
02101          for (i = 0;; i++) {
02102             status = db_enum_key(hDB, hKey, i, &hSubkey);
02103             if (status == DB_NO_MORE_SUBKEYS)
02104                break;
02105 
02106             if (status == DB_SUCCESS) {
02107                size = sizeof(str);
02108                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
02109             }
02110 
02111             /* check if client is living */
02112             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
02113                continue;
02114 
02115             if (equal_ustring(str, name)) {
02116                sprintf(name, "%s%d", client_name, index);
02117                break;
02118             }
02119          }
02120       }
02121 
02122       /* set name */
02123       sprintf(str, "System/Clients/%0d/Name", pid);
02124       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
02125       if (status != DB_SUCCESS) {
02126          db_unlock_database(hDB);
02127          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
02128          return status;
02129       }
02130 
02131       /* copy new client name */
02132       strcpy(client_name, name);
02133       db_set_client_name(hDB, client_name);
02134 
02135       /* set also as rpc name */
02136       rpc_set_name(client_name);
02137 
02138       /* use /system/clients/PID as root */
02139       sprintf(str, "System/Clients/%0d", pid);
02140       db_find_key(hDB, 0, str, &hKey);
02141 
02142       /* set host name */
02143       status =
02144           db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
02145       if (status != DB_SUCCESS){
02146          db_unlock_database(hDB);
02147          return status;
02148       }
02149 
02150       /* set computer id */
02151       status = db_set_value(hDB, hKey, "Hardware type", &hw_type,
02152                             sizeof(hw_type), 1, TID_INT);
02153       if (status != DB_SUCCESS){
02154          db_unlock_database(hDB);
02155          return status;
02156       }
02157 
02158       /* set server port */
02159       data = 0;
02160       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
02161       if (status != DB_SUCCESS){
02162          db_unlock_database(hDB);
02163          return status;
02164       }
02165 
02166       /* lock client entry */
02167       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02168 
02169       /* get (set) default watchdog timeout */
02170       size = sizeof(watchdog_timeout);
02171       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
02172       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02173 
02174       /* define /programs entry */
02175       sprintf(str, "/Programs/%s", orig_name);
02176       db_create_record(hDB, 0, str, strcomb(program_info_str));
02177 
02178       /* save handle for ODB and client */
02179       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
02180       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
02181 
02182       /* save watchdog timeout */
02183       cm_get_watchdog_params(&call_watchdog, NULL);
02184       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02185       if (call_watchdog)
02186          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02187 
02188       /* end of atomic operations */
02189       db_unlock_database(hDB);
02190 
02191       /* touch notify key to inform others */
02192       data = 0;
02193       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
02194 
02195       *hKeyClient = hKey;
02196    }
02197 #endif                          /* LOCAL_ROUTINES */
02198 
02199    return CM_SUCCESS;
02200 }
02201 
02202 /********************************************************************/
02203 /**
02204 Get info about the current client 
02205 @param  *client_name       Client name.  
02206 @return   CM_SUCCESS, CM_UNDEF_EXP  
02207 */
02208 INT cm_get_client_info(char *client_name)
02209 {
02210    INT status, length;
02211    HNDLE hDB, hKey;
02212 
02213    /* get root key of client */
02214    cm_get_experiment_database(&hDB, &hKey);
02215    if (!hDB) {
02216       client_name[0] = 0;
02217       return CM_UNDEF_EXP;
02218    }
02219 
02220    status = db_find_key(hDB, hKey, "Name", &hKey);
02221    if (status != DB_SUCCESS)
02222       return status;
02223 
02224    length = NAME_LENGTH;
02225    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
02226    if (status != DB_SUCCESS)
02227       return status;
02228 
02229    return CM_SUCCESS;
02230 }
02231 
02232 /********************************************************************/
02233 /**
02234 Returns MIDAS environment variables. 
02235 @attention This function can be used to evaluate the standard MIDAS
02236            environment variables before connecting to an experiment
02237            (see @ref Environment_variables).
02238            The usual way is that the host name and experiment name are first derived
02239            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
02240            They can then be superseded by command line parameters with -h and -e flags.
02241 \code
02242 #include <stdio.h>
02243 #include <midas.h>
02244 main(int argc, char *argv[])
02245 {
02246   INT  status, i;
02247   char host_name[256],exp_name[32];
02248 
02249   // get default values from environment
02250   cm_get_environment(host_name, exp_name);
02251 
02252   // parse command line parameters
02253   for (i=1 ; i<argc ; i++)
02254     {
02255     if (argv[i][0] == '-')
02256       {
02257       if (i+1 >= argc || argv[i+1][0] == '-')
02258         goto usage;
02259       if (argv[i][1] == 'e')
02260         strcpy(exp_name, argv[++i]);
02261       else if (argv[i][1] == 'h')
02262         strcpy(host_name, argv[++i]);
02263       else
02264         {
02265 usage:
02266         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02267         return 1;
02268         }
02269       }
02270     }
02271   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02272   if (status != CM_SUCCESS)
02273     return 1;
02274     ...do anyting...
02275   cm_disconnect_experiment();
02276 }
02277 \endcode
02278 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
02279 @param host_name_size     string length
02280 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
02281 @param exp_name_size      string length
02282 @return CM_SUCCESS
02283 */
02284 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name,
02285                        int exp_name_size)
02286 {
02287    host_name[0] = exp_name[0] = 0;
02288 
02289    if (getenv("MIDAS_SERVER_HOST"))
02290       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
02291 
02292    if (getenv("MIDAS_EXPT_NAME"))
02293       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
02294 
02295    return CM_SUCCESS;
02296 }
02297 
02298 
02299 /**dox***************************************************************/
02300 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02301 
02302 /********************************************************************/
02303 void cm_check_connect(void)
02304 {
02305    if (_hKeyClient)
02306       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
02307 }
02308 
02309 /**dox***************************************************************/
02310 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02311 
02312 /********************************************************************/
02313 /**
02314 This function connects to an existing MIDAS experiment.
02315 This must be the first call in a MIDAS application.
02316 It opens three TCP connection to the remote host (one for RPC calls,
02317 one to send events and one for hot-link notifications from the remote host)
02318 and writes client information into the ODB under /System/Clients.
02319 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
02320 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
02321 experiment name (see @ref Environment_variables).
02322 For that purpose, the function cm_get_environment()
02323 should be called prior to cm_connect_experiment(). If command line
02324 parameters -h and -e are used, the evaluation should be done between
02325 cm_get_environment() and cm_connect_experiment(). The function
02326 cm_disconnect_experiment() must be called before a MIDAS application exits.
02327 \code
02328 #include <stdio.h>
02329 #include <midas.h>
02330 main(int argc, char *argv[])
02331 {
02332   INT  status, i;
02333   char host_name[256],exp_name[32];
02334 
02335   // get default values from environment
02336   cm_get_environment(host_name, exp_name);
02337 
02338   // parse command line parameters
02339   for (i=1 ; i<argc ; i++)
02340     {
02341     if (argv[i][0] == '-')
02342       {
02343       if (i+1 >= argc || argv[i+1][0] == '-')
02344         goto usage;
02345       if (argv[i][1] == 'e')
02346         strcpy(exp_name, argv[++i]);
02347       else if (argv[i][1] == 'h')
02348         strcpy(host_name, argv[++i]);
02349       else
02350         {
02351 usage:
02352         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02353         return 1;
02354         }
02355       }
02356     }
02357   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02358   if (status != CM_SUCCESS)
02359     return 1;
02360   ...do operations...
02361   cm_disconnect_experiment();
02362 }
02363 \endcode
02364 @param host_name Specifies host to connect to. Must be a valid IP host name.
02365   The string can be empty ("") if to connect to the local computer.
02366 @param exp_name Specifies the experiment to connect to.
02367   If this string is empty, the number of defined experiments in exptab is checked.
02368   If only one experiment is defined, the function automatically connects to this
02369   one. If more than one experiment is defined, a list is presented and the user
02370   can interactively select one experiment.
02371 @param client_name Client name of the calling program as it can be seen by
02372   others (like the scl command in ODBEdit).
02373 @param func Callback function to read in a password if security has
02374   been enabled. In all command line applications this function is NULL which
02375   invokes an internal ss_gets() function to read in a password.
02376   In windows environments (MS Windows, X Windows) a function can be supplied to
02377   open a dialog box and read in the password. The argument of this function must
02378   be the returned password.
02379 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
02380 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
02381 */
02382 INT cm_connect_experiment(char *host_name, char *exp_name,
02383                           char *client_name, void (*func) (char *))
02384 {
02385    INT status;
02386    char str[256];
02387 
02388    status = cm_connect_experiment1(host_name, exp_name, client_name,
02389                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
02390    if (status != CM_SUCCESS) {
02391       cm_get_error(status, str);
02392       puts(str);
02393    }
02394 
02395    return status;
02396 }
02397 
02398 /********************************************************************/
02399 /**
02400 Connect to a MIDAS experiment (to the online database) on
02401            a specific host.
02402 @internal
02403 */
02404 INT cm_connect_experiment1(char *host_name, char *exp_name,
02405                            char *client_name, void (*func) (char *),
02406                            INT odb_size, DWORD watchdog_timeout)
02407 {
02408    INT status, i, mutex_elog, mutex_alarm, size;
02409    char local_host_name[HOST_NAME_LENGTH];
02410    char client_name1[NAME_LENGTH];
02411    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
02412    HNDLE hDB, hKeyClient;
02413    BOOL call_watchdog;
02414    RUNINFO_STR(runinfo_str);
02415 
02416    if (_hKeyClient)
02417       cm_disconnect_experiment();
02418 
02419    rpc_set_name(client_name);
02420 
02421    /* check for local host */
02422    if (equal_ustring(host_name, "local"))
02423       host_name[0] = 0;
02424 
02425 #ifdef OS_WINNT
02426    {
02427       WSADATA WSAData;
02428 
02429       /* Start windows sockets */
02430       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02431          return RPC_NET_ERROR;
02432    }
02433 #endif
02434 
02435    /* search for experiment name in exptab */
02436    if (exp_name == NULL)
02437       exp_name = "";
02438 
02439    strcpy(exp_name1, exp_name);
02440    if (exp_name1[0] == 0) {
02441       status = cm_select_experiment(host_name, exp_name1);
02442       if (status != CM_SUCCESS)
02443          return status;
02444    }
02445 
02446    /* connect to MIDAS server */
02447    if (host_name[0]) {
02448       status = rpc_server_connect(host_name, exp_name1);
02449       if (status != RPC_SUCCESS)
02450          return status;
02451 
02452       /* register MIDAS library functions */
02453       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
02454       if (status != RPC_SUCCESS)
02455          return status;
02456    } else {
02457       /* lookup path for *SHM files and save it */
02458       status = cm_scan_experiments();
02459       if (status != CM_SUCCESS)
02460          return status;
02461 
02462       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
02463          if (equal_ustring(exp_name1, exptab[i].name))
02464             break;
02465 
02466       /* return if experiment not defined */
02467       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
02468          /* message should be displayed by application
02469             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
02470             cm_msg(MERROR, str);
02471           */
02472          return CM_UNDEF_EXP;
02473       }
02474 
02475       cm_set_path(exptab[i].directory);
02476 
02477       /* create alarm and elog mutexes */
02478       status = ss_mutex_create("ALARM", &mutex_alarm);
02479       if (status != SS_CREATED && status != SS_SUCCESS) {
02480          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
02481          return status;
02482       }
02483       status = ss_mutex_create("ELOG", &mutex_elog);
02484       if (status != SS_CREATED && status != SS_SUCCESS) {
02485          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
02486          return status;
02487       }
02488       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
02489    }
02490 
02491    /* open ODB */
02492    if (odb_size == 0)
02493       odb_size = DEFAULT_ODB_SIZE;
02494 
02495    status = db_open_database("ODB", odb_size, &hDB, client_name);
02496    if (status != DB_SUCCESS && status != DB_CREATED) {
02497       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02498       return status;
02499    }
02500 
02501    /* now setup client info */
02502    gethostname(local_host_name, sizeof(local_host_name));
02503 
02504    /* check watchdog timeout */
02505    if (watchdog_timeout == 0)
02506       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
02507 
02508    strcpy(client_name1, client_name);
02509    password[0] = 0;
02510    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02511                                client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02512                                password, watchdog_timeout);
02513 
02514    if (status == CM_WRONG_PASSWORD) {
02515       if (func == NULL)
02516          strcpy(str, ss_getpass("Password: "));
02517       else
02518          func(str);
02519 
02520       /* re-open database */
02521       status = db_open_database("ODB", odb_size, &hDB, client_name);
02522       if (status != DB_SUCCESS && status != DB_CREATED) {
02523          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02524          return status;
02525       }
02526 
02527       strcpy(password, ss_crypt(str, "mi"));
02528       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02529                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02530                                   password, watchdog_timeout);
02531       if (status != CM_SUCCESS) {
02532          /* disconnect */
02533          if (rpc_is_remote())
02534             rpc_server_disconnect();
02535 
02536          return status;
02537       }
02538    }
02539 
02540    cm_set_experiment_database(hDB, hKeyClient);
02541 
02542    /* set experiment name in ODB */
02543    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
02544 
02545    /* set data dir in ODB */
02546    cm_get_path(str);
02547    size = sizeof(str);
02548    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
02549 
02550    /* check /runinfo structure */
02551    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), FALSE);
02552    if (status == DB_STRUCT_MISMATCH) {
02553       cm_msg(MERROR, "cm_connect_experiment1",
02554              "Aborting on mismatching /Runinfo structure");
02555       cm_disconnect_experiment();
02556       abort();
02557    }
02558 
02559    /* register server to be able to be called by other clients */
02560    status = cm_register_server();
02561    if (status != CM_SUCCESS)
02562       return status;
02563 
02564    /* set watchdog timeout */
02565    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
02566    size = sizeof(watchdog_timeout);
02567    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
02568    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02569    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02570 
02571    /* send startup notification */
02572    if (strchr(local_host_name, '.'))
02573       *strchr(local_host_name, '.') = 0;
02574 
02575    /* startup message is not displayed */
02576    _message_print = NULL;
02577 
02578    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started",
02579           client_name, local_host_name);
02580 
02581    /* enable system and user messages to stdout as default */
02582    cm_set_msg_print(MT_ALL, MT_ALL, puts);
02583 
02584    /* call cm_check_connect when exiting */
02585    atexit((void (*)(void)) cm_check_connect);
02586 
02587    /* register ctrl-c handler */
02588    ss_ctrlc_handler(cm_ctrlc_handler);
02589 
02590    return CM_SUCCESS;
02591 }
02592 
02593 /********************************************************************/
02594 /** 
02595 Connect to a MIDAS server and return all defined
02596            experiments in *exp_name[MAX_EXPERIMENTS]
02597 @param  host_name         Internet host name.
02598 @param  exp_name          list of experiment names
02599 @return CM_SUCCESS, RPC_NET_ERROR
02600 */
02601 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
02602 {
02603    INT i, status;
02604    struct sockaddr_in bind_addr;
02605    INT sock;
02606    char str[MAX_EXPERIMENT * NAME_LENGTH];
02607    struct hostent *phe;
02608 
02609    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
02610       status = cm_scan_experiments();
02611       if (status != CM_SUCCESS)
02612          return status;
02613 
02614       for (i = 0; i < MAX_EXPERIMENT; i++)
02615          strcpy(exp_name[i], exptab[i].name);
02616 
02617       return CM_SUCCESS;
02618    }
02619 #ifdef OS_WINNT
02620    {
02621       WSADATA WSAData;
02622 
02623       /* Start windows sockets */
02624       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02625          return RPC_NET_ERROR;
02626    }
02627 #endif
02628 
02629    /* create a new socket for connecting to remote server */
02630    sock = socket(AF_INET, SOCK_STREAM, 0);
02631    if (sock == -1) {
02632       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02633       return RPC_NET_ERROR;
02634    }
02635 
02636    /* connect to remote node */
02637    memset(&bind_addr, 0, sizeof(bind_addr));
02638    bind_addr.sin_family = AF_INET;
02639    bind_addr.sin_addr.s_addr = 0;
02640    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
02641 
02642 #ifdef OS_VXWORKS
02643    {
02644       INT host_addr;
02645 
02646       host_addr = hostGetByName(host_name);
02647       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02648    }
02649 #else
02650    phe = gethostbyname(host_name);
02651    if (phe == NULL) {
02652       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
02653       return RPC_NET_ERROR;
02654    }
02655    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02656 #endif
02657 
02658 #ifdef OS_UNIX
02659    do {
02660       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02661 
02662       /* don't return if an alarm signal was cought */
02663    } while (status == -1 && errno == EINTR);
02664 #else
02665    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02666 #endif
02667 
02668    if (status != 0) {
02669 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02670       return RPC_NET_ERROR;
02671    }
02672 
02673    /* request experiment list */
02674    send(sock, "I", 2, 0);
02675 
02676    for (i = 0; i < MAX_EXPERIMENT; i++) {
02677       exp_name[i][0] = 0;
02678       status = recv_string(sock, str, sizeof(str), 1000);
02679 
02680       if (status < 0)
02681          return RPC_NET_ERROR;
02682 
02683       if (status == 0)
02684          break;
02685 
02686       strcpy(exp_name[i], str);
02687    }
02688 
02689    exp_name[i][0] = 0;
02690    closesocket(sock);
02691 
02692    return CM_SUCCESS;
02693 }
02694 
02695 /********************************************************************/
02696 /**
02697 Connect to a MIDAS server and select an experiment
02698            from the experiments available on this server
02699 @internal
02700 @param  host_name         Internet host name.
02701 @param  exp_name          list of experiment names
02702 @return CM_SUCCESS, RPC_NET_ERROR
02703 */
02704 INT cm_select_experiment(char *host_name, char *exp_name)
02705 {
02706    INT status, i;
02707    char expts[MAX_EXPERIMENT][NAME_LENGTH];
02708    char str[32];
02709 
02710    /* retrieve list of experiments and make selection */
02711    status = cm_list_experiments(host_name, expts);
02712    if (status != CM_SUCCESS)
02713       return status;
02714 
02715    if (expts[1][0]) {
02716       if (host_name[0])
02717          printf("Available experiments on server %s:\n", host_name);
02718       else
02719          printf("Available experiments on local computer:\n");
02720 
02721       for (i = 0; expts[i][0]; i++)
02722          printf("%d : %s\n", i, expts[i]);
02723       printf("Select number: ");
02724       ss_gets(str, 32);
02725       i = atoi(str);
02726       strcpy(exp_name, expts[i]);
02727    } else
02728       strcpy(exp_name, expts[0]);
02729 
02730    return CM_SUCCESS;
02731 }
02732 
02733 /********************************************************************/
02734 /**
02735 Connect to a MIDAS client of the current experiment
02736 @internal
02737 @param  client_name       Name of client to connect to. This name
02738                             is set by the other client via the
02739                             cm_connect_experiment call.
02740 @param  hConn            Connection handle
02741 @return CM_SUCCESS, CM_NO_CLIENT
02742 */
02743 INT cm_connect_client(char *client_name, HNDLE * hConn)
02744 {
02745    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02746    INT status, i, length, port;
02747    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02748 
02749    /* find client entry in ODB */
02750    cm_get_experiment_database(&hDB, &hKey);
02751 
02752    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02753    if (status != DB_SUCCESS)
02754       return status;
02755 
02756    i = 0;
02757    do {
02758       /* search for client with specific name */
02759       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02760       if (status == DB_NO_MORE_SUBKEYS)
02761          return CM_NO_CLIENT;
02762 
02763       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02764       if (status != DB_SUCCESS)
02765          return status;
02766 
02767       length = NAME_LENGTH;
02768       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02769       if (status != DB_SUCCESS)
02770          return status;
02771 
02772       if (equal_ustring(name, client_name)) {
02773          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02774          if (status != DB_SUCCESS)
02775             return status;
02776 
02777          length = sizeof(INT);
02778          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02779          if (status != DB_SUCCESS)
02780             return status;
02781 
02782          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02783          if (status != DB_SUCCESS)
02784             return status;
02785 
02786          length = sizeof(host_name);
02787          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02788          if (status != DB_SUCCESS)
02789             return status;
02790 
02791          /* client found -> connect to its server port */
02792          return rpc_client_connect(host_name, port, client_name, hConn);
02793       }
02794 
02795 
02796    } while (TRUE);
02797 }
02798 
02799 /********************************************************************/
02800 /**
02801 Disconnect from a MIDAS client 
02802 @param   hConn             Connection handle obtained via
02803                              cm_connect_client()
02804 @param   bShutdown         If TRUE, disconnect from client and
02805                              shut it down (exit the client program)
02806                              by sending a RPC_SHUTDOWN message
02807 @return   see rpc_client_disconnect()
02808 */
02809 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02810 {
02811    return rpc_client_disconnect(hConn, bShutdown);
02812 }
02813 
02814 /********************************************************************/
02815 /**
02816 Disconnect from a MIDAS experiment.
02817 @attention Should be the last call to a MIDAS library function in an
02818 application before it exits. This function removes the client information
02819 from the ODB, disconnects all TCP connections and frees all internal
02820 allocated memory. See cm_connect_experiment() for example.
02821 @return CM_SUCCESS
02822 */
02823 INT cm_disconnect_experiment(void)
02824 {
02825    HNDLE hDB, hKey;
02826    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02827 
02828    /* send shutdown notification */
02829    rpc_get_name(client_name);
02830    gethostname(local_host_name, sizeof(local_host_name));
02831    if (strchr(local_host_name, '.'))
02832       *strchr(local_host_name, '.') = 0;
02833 
02834    /* disconnect message not displayed */
02835    _message_print = NULL;
02836 
02837    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped",
02838           client_name, local_host_name);
02839 
02840    if (rpc_is_remote()) {
02841       /* close open records */
02842       db_close_all_records();
02843 
02844       rpc_client_disconnect(-1, FALSE);
02845       rpc_server_disconnect();
02846    } else {
02847       rpc_client_disconnect(-1, FALSE);
02848 
02849 #ifdef LOCAL_ROUTINES
02850       ss_alarm(0, cm_watchdog);
02851       _watchdog_last_called = 0;
02852 #endif                          /* LOCAL_ROUTINES */
02853 
02854       /* delete client info */
02855       cm_get_experiment_database(&hDB, &hKey);
02856 
02857       if (hDB)
02858          cm_delete_client_info(hDB, 0);
02859 
02860       bm_close_all_buffers();
02861       db_close_all_databases();
02862    }
02863 
02864    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02865       rpc_server_shutdown();
02866 
02867    /* free RPC list */
02868    rpc_deregister_functions();
02869 
02870    cm_set_experiment_database(0, 0);
02871 
02872    _msg_buffer = 0;
02873 
02874    /* free memory buffers */
02875    if (_event_buffer_size > 0) {
02876       M_FREE(_event_buffer);
02877       _event_buffer_size = 0;
02878    }
02879 
02880    if (_net_recv_buffer_size > 0) {
02881       M_FREE(_net_recv_buffer);
02882       _net_recv_buffer_size = 0;
02883    }
02884 
02885    if (_net_send_buffer_size > 0) {
02886       M_FREE(_net_send_buffer);
02887       _net_send_buffer_size = 0;
02888    }
02889 
02890    if (_tcp_buffer != NULL) {
02891       M_FREE(_tcp_buffer);
02892       _tcp_buffer = NULL;
02893    }
02894 
02895    return CM_SUCCESS;
02896 }
02897 
02898 /********************************************************************/
02899 /**
02900 Set the handle to the ODB for the currently connected experiment
02901 @param hDB              Database handle
02902 @param hKeyClient       Key handle of client structure
02903 @return CM_SUCCESS
02904 */
02905 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02906 {
02907    _hDB = hDB;
02908    _hKeyClient = hKeyClient;
02909 
02910    return CM_SUCCESS;
02911 }
02912 
02913 
02914 
02915 /**dox***************************************************************/
02916 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02917 
02918 /********************************************************************/
02919 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02920 /********************************************************************\
02921 
02922   Routine: cm_set_experiment_mutex
02923 
02924   Purpose: Set the handle to the experiment wide mutexes
02925 
02926   Input:
02927     INT    mutex_alarm      Alarm mutex
02928     INT    mutex_elog       Elog mutex
02929 
02930   Output:
02931     none
02932 
02933   Function value:
02934     CM_SUCCESS              Successful completion
02935 
02936 \********************************************************************/
02937 {
02938    _mutex_alarm = mutex_alarm;
02939    _mutex_elog = mutex_elog;
02940 
02941    return CM_SUCCESS;
02942 }
02943 
02944 /**dox***************************************************************/
02945 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02946 
02947 /********************************************************************/
02948 /** 
02949 Get the handle to the ODB from the currently connected experiment.
02950 
02951 @attention This function returns the handle of the online database (ODB) which
02952 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02953 to access the client information in the ODB. If the client key handle is not needed,
02954 the parameter can be NULL.
02955 \code
02956 HNDLE hDB, hkeyclient;
02957  char  name[32];
02958  int   size;
02959  db_get_experiment_database(&hdb, &hkeyclient);
02960  size = sizeof(name);
02961  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02962  printf("My name is %s\n", name);
02963 \endcode
02964 @param hDB Database handle.
02965 @param hKeyClient Handle for key where search starts, zero for root.
02966 @return CM_SUCCESS
02967 */
02968 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02969 {
02970    if (_hDB) {
02971       if (hDB != NULL)
02972          *hDB = _hDB;
02973       if (hKeyClient != NULL)
02974          *hKeyClient = _hKeyClient;
02975    } else {
02976       if (hDB != NULL)
02977          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02978       if (hKeyClient != NULL)
02979          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02980    }
02981 
02982    return CM_SUCCESS;
02983 }
02984 
02985 /**dox***************************************************************/
02986 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02987 
02988 /********************************************************************/
02989 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02990 /********************************************************************\
02991 
02992   Routine: cm_get_experiment_mutex
02993 
02994   Purpose: Get the handle to the experiment wide mutexes
02995 
02996   Input:
02997     none
02998 
02999   Output:
03000     INT    mutex_alarm      Alarm mutex
03001     INT    mutex_elog       Elog mutex
03002 
03003   Function value:
03004     CM_SUCCESS              Successful completion
03005 
03006 \********************************************************************/
03007 {
03008    if (mutex_alarm)
03009       *mutex_alarm = _mutex_alarm;
03010    if (mutex_elog)
03011       *mutex_elog = _mutex_elog;
03012 
03013    return CM_SUCCESS;
03014 }
03015 
03016 /**dox***************************************************************/
03017 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03018 
03019 /********************************************************************/
03020 /**
03021 Sets the internal watchdog flags and the own timeout.
03022 If call_watchdog is TRUE, the cm_watchdog routine is called
03023 periodically from the system to show other clients that
03024 this application is "alive". On UNIX systems, the
03025 alarm() timer is used which is then not available for
03026 user purposes.
03027 
03028 The timeout specifies the time, after which the calling
03029 application should be considered "dead" by other clients.
03030 Normally, the cm_watchdog() routines is called periodically.
03031 If a client crashes, this does not occur any more. Then
03032 other clients can detect this and clear all buffer and
03033 database entries of this application so they are not
03034 blocked any more. If this application should not checked
03035 by others, the timeout can be specified as zero.
03036 It might be useful for debugging purposes to do so,
03037 because if a debugger comes to a breakpoint and stops
03038 the application, the periodic call of cm_watchdog
03039 is disabled and the client looks like dead.
03040 
03041 If the timeout is not zero, but the watchdog is not
03042 called (call_watchdog == FALSE), the user must ensure
03043 to call cm_watchdog periodically with a period of
03044 WATCHDOG_INTERVAL milliseconds or less.
03045 
03046 An application which calles system routines which block
03047 the alarm signal for some time, might increase the
03048 timeout to the maximum expected blocking time before
03049 issuing the calls. One example is the logger doing
03050 Exabyte tape IO, which can take up to one minute.
03051 @param    call_watchdog   Call the cm_watchdog routine periodically
03052 @param    timeout         Timeout for this application in ms
03053 @return   CM_SUCCESS
03054 */
03055 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
03056 {
03057    INT i;
03058 
03059    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
03060    _watchdog_timeout = timeout;
03061 
03062    if (rpc_is_remote())
03063       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
03064 
03065 #ifdef LOCAL_ROUTINES
03066 
03067    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
03068       HNDLE hDB, hKey;
03069 
03070       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
03071 
03072       /* write timeout value to client enty in ODB */
03073       cm_get_experiment_database(&hDB, &hKey);
03074 
03075       if (hDB) {
03076          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03077          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
03078          db_set_mode(hDB, hKey, MODE_READ, TRUE);
03079       }
03080    } else {
03081       _call_watchdog = call_watchdog;
03082       _watchdog_timeout = timeout;
03083 
03084       /* set watchdog flag of all open buffers */
03085       for (i = _buffer_entries; i > 0; i--) {
03086          BUFFER_CLIENT *pclient;
03087          BUFFER_HEADER *pheader;
03088          INT index;
03089 
03090          index = _buffer[i - 1].client_index;
03091          pheader = _buffer[i - 1].buffer_header;
03092          pclient = &pheader->client[index];
03093 
03094          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03095              _buffer[i - 1].index != rpc_get_server_acception())
03096             continue;
03097 
03098          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03099              _buffer[i - 1].index != ss_gettid())
03100             continue;
03101 
03102          if (!_buffer[i - 1].attached)
03103             continue;
03104 
03105          /* clear entry from client structure in buffer header */
03106          pclient->watchdog_timeout = timeout;
03107 
03108          /* show activity */
03109          pclient->last_activity = ss_millitime();
03110       }
03111 
03112       /* set watchdog flag of alll open databases */
03113       for (i = _database_entries; i > 0; i--) {
03114          DATABASE_HEADER *pheader;
03115          DATABASE_CLIENT *pclient;
03116          INT index;
03117 
03118          db_lock_database(i);
03119          index = _database[i - 1].client_index;
03120          pheader = _database[i - 1].database_header;
03121          pclient = &pheader->client[index];
03122 
03123          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03124              _database[i - 1].index != rpc_get_server_acception()) {
03125             db_unlock_database(i);
03126             continue;
03127          }
03128 
03129          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03130              _database[i - 1].index != ss_gettid()) {
03131             db_unlock_database(i);
03132             continue;
03133          }
03134 
03135          if (!_database[i - 1].attached) {
03136             db_unlock_database(i);
03137             continue;
03138          }
03139 
03140          /* clear entry from client structure in buffer header */
03141          pclient->watchdog_timeout = timeout;
03142 
03143          /* show activity */
03144          pclient->last_activity = ss_millitime();
03145 
03146          db_unlock_database(i);
03147       }
03148 
03149       if (call_watchdog)
03150          /* restart watchdog */
03151          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
03152       else
03153          /* kill current timer */
03154          ss_alarm(0, cm_watchdog);
03155    }
03156 
03157 #endif                          /* LOCAL_ROUTINES */
03158 
03159    return CM_SUCCESS;
03160 }
03161 
03162 /********************************************************************/
03163 /**
03164 Return the current watchdog parameters
03165 @param call_watchdog   Call the cm_watchdog routine periodically
03166 @param timeout         Timeout for this application in seconds
03167 @return   CM_SUCCESS
03168 */
03169 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
03170 {
03171    if (call_watchdog)
03172       *call_watchdog = _call_watchdog;
03173    if (timeout)
03174       *timeout = _watchdog_timeout;
03175 
03176    return CM_SUCCESS;
03177 }
03178 
03179 /********************************************************************/
03180 /**
03181 Return watchdog information about specific client
03182 @param    hDB              ODB handle
03183 @param    client_name     ODB client name
03184 @param    timeout         Timeout for this application in seconds
03185 @param    last            Last time watchdog was called in msec
03186 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
03187 */
03188 
03189 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
03190 {
03191    if (rpc_is_remote())
03192       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
03193 
03194 #ifdef LOCAL_ROUTINES
03195    {
03196       DATABASE_HEADER *pheader;
03197       DATABASE_CLIENT *pclient;
03198       INT i;
03199 
03200       if (hDB > _database_entries || hDB <= 0) {
03201          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03202          return DB_INVALID_HANDLE;
03203       }
03204 
03205       if (!_database[hDB - 1].attached) {
03206          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03207          return DB_INVALID_HANDLE;
03208       }
03209 
03210       /* lock database */
03211       db_lock_database(hDB);
03212 
03213       pheader = _database[hDB - 1].database_header;
03214       pclient = pheader->client;
03215 
03216       /* find client */
03217       for (i = 0; i < pheader->max_client_index; i++, pclient++)
03218          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
03219             *timeout = pclient->watchdog_timeout;
03220             *last = ss_millitime() - pclient->last_activity;
03221             db_unlock_database(hDB);
03222             return CM_SUCCESS;
03223          }
03224 
03225       *timeout = *last = 0;
03226 
03227       db_unlock_database(hDB);
03228 
03229       return CM_NO_CLIENT;
03230    }
03231 #else                           /* LOCAL_ROUTINES */
03232    return CM_SUCCESS;
03233 #endif                          /* LOCAL_ROUTINES */
03234 }
03235 
03236 
03237 /**dox***************************************************************/
03238 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03239 
03240 /********************************************************************/
03241 INT cm_register_server(void)
03242 /********************************************************************\
03243 
03244   Routine: cm_register_server
03245 
03246   Purpose: Register a server which can be called from other clients
03247            of a specific experiment.
03248 
03249   Input:
03250     none
03251 
03252   Output:
03253     none
03254 
03255   Function value:
03256     CM_SUCCESS              Successful completion
03257 
03258 \********************************************************************/
03259 {
03260    INT status, port;
03261    HNDLE hDB, hKey;
03262 
03263    if (!_server_registered) {
03264       port = 0;
03265       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
03266       if (status != RPC_SUCCESS)
03267          return status;
03268       _server_registered = TRUE;
03269 
03270       /* register MIDAS library functions */
03271       rpc_register_functions(rpc_get_internal_list(1), NULL);
03272 
03273       /* store port number in ODB */
03274       cm_get_experiment_database(&hDB, &hKey);
03275 
03276       status = db_find_key(hDB, hKey, "Server Port", &hKey);
03277       if (status != DB_SUCCESS)
03278          return status;
03279 
03280       /* unlock database */
03281       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03282 
03283       /* set value */
03284       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
03285       if (status != DB_SUCCESS)
03286          return status;
03287 
03288       /* lock database */
03289       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03290    }
03291 
03292    return CM_SUCCESS;
03293 }
03294 
03295 /**dox***************************************************************/
03296 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03297 
03298 /********************************************************************/
03299 /**
03300 Registers a callback function for run transitions.
03301 This function internally registers the transition callback
03302 function and publishes its request for transition notification by writing
03303 a transition request to /System/Clients/<pid>/Transition XXX.
03304 Other clients making a transition scan the transition requests of all clients
03305 and call their transition callbacks via RPC.
03306 
03307 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
03308 sequence. All sequence numbers given in the registration are sorted on 
03309 a transition and the clients are contacted in ascending order. By default,
03310 all programs register with a sequence number of 500. The logger however
03311 uses 200 for start, so that it can open files before the other clients
03312 are contacted, and 800 for stop, so that the files get closed when all
03313 other clients have gone already through the stop trantition.
03314 
03315 The callback function returns CM_SUCCESS if it can perform the transition or
03316 a value larger than one in case of error. An error string can be copied
03317 into the error variable.
03318 @attention The callback function will be called on transitions from inside the
03319     cm_yield() function which therefore must be contained in the main program loop.
03320 \code
03321 INT start(INT run_number, char *error)
03322 {
03323   if (<not ok>)
03324     {
03325     strcpy(error, "Cannot start because ...");
03326     return 2;
03327     }
03328   printf("Starting run %d\n", run_number);
03329   return CM_SUCCESS;
03330 }
03331 main()
03332 {
03333   ...
03334   cm_register_transition(TR_START, start, 500);
03335   do
03336     {
03337     status = cm_yield(1000);
03338     } while (status != RPC_SHUTDOWN &&
03339              status != SS_ABORT);
03340   ...
03341 }
03342 \endcode
03343 @param transition Transition to register for (see @ref state_transition)
03344 @param func Callback function.
03345 @param sequence_number Sequence number for that transition (1..1000)
03346 @return CM_SUCCESS
03347 */
03348 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
03349 {
03350    INT status, i;
03351    HNDLE hDB, hKey, hKeyTrans;
03352    KEY key;
03353    char str[256];
03354 
03355    /* check for valid transition */
03356    if (transition != TR_START && transition != TR_STOP && 
03357        transition != TR_PAUSE && transition != TR_RESUME) {
03358          cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03359          return CM_INVALID_TRANSITION;
03360       }
03361 
03362    cm_get_experiment_database(&hDB, &hKey);
03363 
03364    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
03365 
03366    /* find empty slot */
03367    for (i = 0; i < MAX_TRANSITIONS ; i++)
03368       if (!_trans_table[i].transition) 
03369          break;
03370 
03371    if (i == MAX_TRANSITIONS) {
03372       cm_msg(MERROR, "cm_register_transition", 
03373          "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
03374       return CM_TOO_MANY_REQUESTS;
03375    }
03376 
03377    _trans_table[i].transition = transition;
03378    _trans_table[i].func = func;
03379    _trans_table[i].sequence_number = sequence_number;
03380 
03381    for (i = 0; i < 13; i++)
03382       if (trans_name[i].transition == transition)
03383          break;
03384 
03385    sprintf(str, "Transition %s", trans_name[i].name);
03386 
03387    /* unlock database */
03388    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03389 
03390    /* set value */
03391    status = db_find_key(hDB, hKey, str, &hKeyTrans);
03392    if (!hKeyTrans) {
03393       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
03394       if (status != DB_SUCCESS)
03395          return status;
03396    } else {
03397       status = db_get_key(hDB, hKeyTrans, &key);
03398       if (status != DB_SUCCESS)
03399          return status;
03400       status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT);      
03401       if (status != DB_SUCCESS)
03402          return status;
03403    }
03404 
03405    /* re-lock database */
03406    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03407 
03408    return CM_SUCCESS;
03409 }
03410 
03411 /********************************************************************/
03412 /**
03413 Change the transition sequence for the calling program.
03414 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03415 @param sequence_number New sequence number, should be between 1 and 1000
03416 @return     CM_SUCCESS
03417 */
03418 INT cm_set_transition_sequence(INT transition, INT sequence_number)
03419 {
03420    INT status, i;
03421    HNDLE hDB, hKey;
03422    char str[256];
03423 
03424    /* check for valid transition */
03425    if (transition != TR_START && transition != TR_STOP && 
03426        transition != TR_PAUSE && transition != TR_RESUME) {
03427          cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03428          return CM_INVALID_TRANSITION;
03429       }
03430 
03431    cm_get_experiment_database(&hDB, &hKey);
03432 
03433    for (i = 0; i < 13; i++)
03434       if (trans_name[i].transition == transition)
03435          break;
03436 
03437    sprintf(str, "Transition %s", trans_name[i].name);
03438 
03439    /* unlock database */
03440    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03441 
03442    /* set value */
03443    status =
03444        db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
03445    if (status != DB_SUCCESS)
03446       return status;
03447 
03448    /* re-lock database */
03449    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03450 
03451    return CM_SUCCESS;
03452 
03453 }
03454 
03455 /**dox***************************************************************/
03456 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03457 
03458 static INT _requested_transition;
03459 static DWORD _deferred_transition_mask;
03460 
03461 /**dox***************************************************************/
03462 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03463 
03464 /********************************************************************/
03465 /**
03466 Register a deferred transition handler. If a client is
03467 registered as a deferred transition handler, it may defer
03468 a requested transition by returning FALSE until a certain
03469 condition (like a motor reaches its end position) is
03470 reached.
03471 @param transition      One of TR_xxx
03472 @param (*func)         Function which gets called whenever
03473                        a transition is requested. If it returns
03474                        FALSE, the transition is not performed.
03475 @return CM_SUCCESS,    <error> Error from ODB access
03476 */
03477 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
03478 {
03479    INT status, i, size;
03480    char tr_key_name[256];
03481    HNDLE hDB, hKey;
03482 
03483    cm_get_experiment_database(&hDB, &hKey);
03484 
03485    for (i = 0; _deferred_trans_table[i].transition; i++)
03486       if (_deferred_trans_table[i].transition == transition)
03487          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
03488 
03489    /* set new transition mask */
03490    _deferred_transition_mask |= transition;
03491 
03492    for (i = 0; i < 13; i++)
03493       if (trans_name[i].transition == transition)
03494          break;
03495 
03496    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
03497 
03498    /* unlock database */
03499    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03500 
03501    /* set value */
03502    i = 0;
03503    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
03504    if (status != DB_SUCCESS)
03505       return status;
03506 
03507    /* re-lock database */
03508    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03509 
03510    /* hot link requested transition */
03511    size = sizeof(_requested_transition);
03512    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size,
03513                 TID_INT, TRUE);
03514    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
03515    status =
03516        db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL,
03517                       NULL);
03518    if (status != DB_SUCCESS) {
03519       cm_msg(MERROR, "cm_register_deferred_transition",
03520              "Cannot hotlink /Runinfo/Requested Transition");
03521       return status;
03522    }
03523 
03524    return CM_SUCCESS;
03525 }
03526 
03527 /********************************************************************/
03528 /**
03529 Check for any deferred transition. If a deferred transition
03530 handler has been registered via the
03531 cm_register_deferred_transition function, this routine
03532 should be called regularly. It checks if a transition
03533 request is pending. If so, it calld the registered handler
03534 if the transition should be done and then actually does
03535 the transition.
03536 @return     CM_SUCCESS, <error>  Error from cm_transition()
03537 */
03538 INT cm_check_deferred_transition()
03539 {
03540    INT i, status;
03541    char str[256];
03542    static BOOL first;
03543 
03544    if (_requested_transition == 0)
03545       first = TRUE;
03546 
03547    if (_requested_transition & _deferred_transition_mask) {
03548       for (i = 0; _deferred_trans_table[i].transition; i++)
03549          if (_deferred_trans_table[i].transition == _requested_transition)
03550             break;
03551 
03552       if (_deferred_trans_table[i].transition == _requested_transition) {
03553          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition,
03554                                                                    first)) {
03555             status =
03556                 cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str),
03557                               SYNC, FALSE);
03558             if (status != CM_SUCCESS)
03559                cm_msg(MERROR, "cm_check_deferred_transition",
03560                       "Cannot perform deferred transition: %s", str);
03561 
03562             /* bypass hotlink and set _requested_transition directly to zero */
03563             _requested_transition = 0;
03564 
03565             return status;
03566          }
03567          first = FALSE;
03568       }
03569    }
03570 
03571    return SUCCESS;
03572 }
03573 
03574 
03575 /**dox***************************************************************/
03576 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03577 
03578 /********************************************************************/
03579 
03580 /**dox***************************************************************/
03581 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03582 
03583 typedef struct {
03584    int  sequence_number;
03585    char host_name[HOST_NAME_LENGTH];
03586    char client_name[NAME_LENGTH];
03587    int  port;
03588 } TR_CLIENT;
03589 
03590 int tr_compare(const void *arg1, const void *arg2) 
03591 {
03592    return ((TR_CLIENT *)arg1)->sequence_number - ((TR_CLIENT*)arg2)->sequence_number;
03593 }
03594 
03595 /********************************************************************/
03596 /**
03597 Performs a run transition (Start/Stop/Pause/Resume).
03598 
03599 Synchronous/Asynchronous flag.
03600 If set to ASYNC, the transition is done
03601 asynchronously, meaning that clients are connected and told to execute their
03602 callback routine, but no result is awaited. The return value is
03603 specified by the transition callback function on the remote clients. If all callbacks
03604 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03605 perform the transition, the return value of this callback is returned from
03606 cm_transition().
03607 The async_flag is usually FALSE so that transition callbacks can block a
03608 run transition in case of problems and return an error string. The only exception are
03609 situations where a run transition is performed automatically by a program which
03610 cannot block in a transition. For example the logger can cause a run stop when a
03611 disk is nearly full but it cannot block in the cm_transition() function since it
03612 has its own run stop callback which must flush buffers and close disk files and
03613 tapes.
03614 \code
03615 ...
03616     i = 1;
03617     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03618 
03619       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03620       if (status != CM_SUCCESS)
03621       {
03622         // in case of error
03623         printf("Error: %s\n", str);
03624       }
03625     ...
03626 \endcode
03627 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03628 @param run_number New run number. If zero, use current run number plus one.
03629 @param perror returned error string.
03630 @param strsize Size of error string.
03631 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03632 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
03633 @return CM_SUCCESS, <error> error code from remote client
03634 */
03635 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize,
03636                   INT async_flag, INT debug_flag)
03637 {
03638    INT i, j, status, index, size, sequence_number, port, state, old_timeout, n_tr_clients;
03639    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
03640    DWORD seconds;
03641    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH],
03642      str[256], error[256], tr_key_name[256];
03643    KEY key;
03644    BOOL deferred;
03645    PROGRAM_INFO program_info;
03646    TR_CLIENT *tr_client;
03647 
03648    deferred = (transition & TR_DEFERRED) > 0;
03649    transition &= ~TR_DEFERRED;
03650 
03651    /* check for valid transition */
03652    if (transition != TR_START && transition != TR_STOP &&
03653       transition != TR_PAUSE && transition != TR_RESUME) {
03654          cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03655          return CM_INVALID_TRANSITION;
03656       }
03657 
03658    /* get key of local client */
03659    cm_get_experiment_database(&hDB, &hKeylocal);
03660 
03661    if (perror != NULL)
03662       strcpy(perror, "Success");
03663 
03664    /* if no run number is given, get it from DB */
03665    if (run_number == 0) {
03666       size = sizeof(run_number);
03667       status =
03668           db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03669       assert(status == SUCCESS);
03670    }
03671 
03672    if (run_number <= 0) {
03673       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d",
03674              run_number);
03675       abort();
03676    }
03677 
03678    /* Set new run number in ODB */
03679    if (transition == TR_START) {
03680       if (debug_flag == 1)
03681          printf("Setting run number %d in ODB\n", run_number);
03682       if (debug_flag == 2)
03683          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
03684 
03685       status = db_set_value(hDB, 0, "Runinfo/Run number",
03686                             &run_number, sizeof(run_number), 1, TID_INT);
03687       assert(status == SUCCESS);
03688       if (status != DB_SUCCESS)
03689          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03690    }
03691 
03692    if (deferred) {
03693       /* remove transition request */
03694       i = 0;
03695       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03696    } else {
03697       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03698       if (status != DB_SUCCESS) {
03699          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03700          return status;
03701       }
03702 
03703       /* check if deferred transition already in progress */
03704       size = sizeof(INT);
03705       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03706       if (i) {
03707          if (perror)
03708             sprintf(perror, "Deferred transition already in progress");
03709 
03710          return CM_TRANSITION_IN_PROGRESS;
03711       }
03712 
03713       for (i = 0; i < 13; i++)
03714          if (trans_name[i].transition == transition)
03715             break;
03716 
03717       sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
03718 
03719       /* search database for clients with deferred transition request */
03720       for (i = 0, status = 0;; i++) {
03721          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03722          if (status == DB_NO_MORE_SUBKEYS)
03723             break;
03724 
03725          if (status == DB_SUCCESS) {
03726             size = sizeof(sequence_number);
03727             status = db_get_value(hDB, hSubkey, tr_key_name,
03728                                  &sequence_number, &size, TID_INT, FALSE);
03729 
03730             /* if registered for deferred transition, set flag in ODB and return */
03731             if (status == DB_SUCCESS) {
03732                size = NAME_LENGTH;
03733                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03734                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition,
03735                             sizeof(int), 1, TID_INT);
03736 
03737                if (debug_flag == 1)
03738                   printf("---- Transition %s deferred by client \"%s\" ----\n", trans_name[i].name, str);
03739                if (debug_flag == 2)
03740                   cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", 
03741                      trans_name[i].name, str);
03742 
03743                if (perror)
03744                   sprintf(perror, "Transition deferred by client \"%s\"", str);
03745 
03746                return CM_DEFERRED_TRANSITION;
03747             }
03748          }
03749       }
03750    }
03751 
03752    /* execute programs on start */
03753    if (transition == TR_START) {
03754       str[0] = 0;
03755       size = sizeof(str);
03756       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING,
03757                    TRUE);
03758       if (str[0])
03759          ss_system(str);
03760 
03761       db_find_key(hDB, 0, "/Programs", &hRootKey);
03762       if (hRootKey) {
03763          for (i = 0;; i++) {
03764             status = db_enum_key(hDB, hRootKey, i, &hKey);
03765             if (status == DB_NO_MORE_SUBKEYS)
03766                break;
03767 
03768             db_get_key(hDB, hKey, &key);
03769 
03770             /* don't check "execute on xxx" */
03771             if (key.type != TID_KEY)
03772                continue;
03773 
03774             size = sizeof(program_info);
03775             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03776             if (status != DB_SUCCESS) {
03777                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03778                continue;
03779             }
03780 
03781             if (program_info.auto_start && program_info.start_command[0])
03782                ss_system(program_info.start_command);
03783          }
03784       }
03785    }
03786 
03787    /* set new start time in database */
03788    if (transition == TR_START) {
03789       /* ASCII format */
03790       cm_asctime(str, sizeof(str));
03791       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03792 
03793       /* reset stop time */
03794       seconds = 0;
03795       db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03796                    &seconds, sizeof(seconds), 1, TID_DWORD);
03797 
03798       /* Seconds since 1.1.1970 */
03799       cm_time(&seconds);
03800       db_set_value(hDB, 0, "Runinfo/Start Time binary",
03801                    &seconds, sizeof(seconds), 1, TID_DWORD);
03802    }
03803 
03804    /* set stop time in database */
03805    if (transition == TR_STOP) {
03806       size = sizeof(state);
03807       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03808       if (status != DB_SUCCESS)
03809          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03810 
03811       if (state != STATE_STOPPED) {
03812          /* stop time binary */
03813          cm_time(&seconds);
03814          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03815                                &seconds, sizeof(seconds), 1, TID_DWORD);
03816          if (status != DB_SUCCESS)
03817             cm_msg(MERROR, "cm_transition",
03818                    "cannot set \"Runinfo/Stop Time binary\" in database");
03819 
03820          /* stop time ascii */
03821          cm_asctime(str, sizeof(str));
03822          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03823          if (status != DB_SUCCESS)
03824             cm_msg(MERROR, "cm_transition",
03825                    "cannot set \"Runinfo/Stop Time\" in database");
03826       }
03827    }
03828 
03829    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03830    if (status != DB_SUCCESS) {
03831       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03832       return status;
03833    }
03834 
03835    for (i = 0; i < 13; i++)
03836       if (trans_name[i].transition == transition)
03837          break;
03838 
03839    if (debug_flag == 1)
03840       printf("---- Transition %s started ----\n", trans_name[i].name);
03841    if (debug_flag == 2)
03842       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----", trans_name[i].name);
03843 
03844    sprintf(tr_key_name, "Transition %s", trans_name[i].name);
03845 
03846    /* search database for clients which registered for transition */
03847    n_tr_clients = 0;
03848    tr_client = NULL;
03849 
03850    for (i = 0, status = 0;; i++) {
03851       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03852       if (status == DB_NO_MORE_SUBKEYS)
03853          break;
03854 
03855       if (status == DB_SUCCESS) {
03856          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03857 
03858          if (status == DB_SUCCESS) {
03859 
03860             db_get_key(hDB, hKeyTrans, &key);
03861 
03862             for (j=0 ; j<key.num_values ; j++) {
03863                size = sizeof(sequence_number);
03864                status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03865                assert(status == DB_SUCCESS);
03866 
03867                if (tr_client == NULL)
03868                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03869                else
03870                   tr_client = (TR_CLIENT *) realloc(tr_client, sizeof(TR_CLIENT)*(n_tr_clients+1));
03871                assert(tr_client);
03872 
03873                tr_client[n_tr_clients].sequence_number = sequence_number;
03874 
03875                if (hSubkey == hKeylocal) {
03876                   /* remember own client */
03877                   tr_client[n_tr_clients].port = 0;
03878                } else {
03879                   /* get client info */
03880                   size = sizeof(client_name);
03881                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
03882                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03883 
03884                   size = sizeof(port);
03885                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03886                   tr_client[n_tr_clients].port = port;
03887 
03888                   size = sizeof(host_name);
03889                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03890                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03891                }
03892 
03893                n_tr_clients++;
03894             }
03895          }
03896       }
03897    }
03898 
03899    /* sort clients according to sequence number */
03900    if (n_tr_clients > 1)
03901       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03902 
03903    /* contact ordered clients for transition */
03904    for (index=0 ; index<n_tr_clients; index++) {
03905       /* erase error string */
03906       error[0] = 0;
03907 
03908       if (debug_flag == 1)
03909          printf("\n==== Found client \"%s\" with sequence number %d\n", tr_client[index].client_name, 
03910             tr_client[index].sequence_number);
03911       if (debug_flag == 2)
03912          cm_msg(MDEBUG, "cm_transition", "cm_transition: ==== Found client \"%s\" with sequence number %d",
03913             tr_client[index].client_name, tr_client[index].sequence_number);
03914 
03915       /* if own client call transition callback directly */
03916       if (tr_client[index].port == 0) {
03917          for (i = 0; _trans_table[i].transition; i++)
03918             if (_trans_table[i].transition == transition)
03919                break;
03920 
03921          /* call registerd function */
03922          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03923             if (debug_flag == 1)
03924                printf("Calling local transition callback\n");
03925             if (debug_flag == 2)
03926                cm_msg(MDEBUG, "cm_transition", "cm_transition: Calling local transition callback");
03927 
03928             status = _trans_table[i].func(run_number, error);
03929 
03930             if (debug_flag == 1)
03931                printf("Local transition callback finished\n");
03932             if (debug_flag == 2)
03933                cm_msg(MDEBUG, "cm_transition", "cm_transition: Local transition callback finished");
03934          } else
03935             status = CM_SUCCESS;
03936 
03937          if (perror != NULL)
03938             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03939                      strlen(error) + 1 : strsize);
03940 
03941          if (status != CM_SUCCESS) {
03942             free(tr_client);
03943             return status;
03944          }
03945 
03946       } else {
03947          
03948          /* contact client if transition mask set */
03949          if (debug_flag == 1)
03950             printf("Connecting to client \"%s\" on host %s...\n", 
03951                tr_client[index].client_name, tr_client[index].host_name);
03952          if (debug_flag == 2)
03953             cm_msg(MDEBUG, "cm_transition", 
03954                "cm_transition: Connecting to client \"%s\" on host %s...", 
03955                tr_client[index].client_name, tr_client[index].host_name);
03956 
03957          /* client found -> connect to its server port */
03958          status = rpc_client_connect(tr_client[index].host_name, tr_client[index].port, 
03959                                      tr_client[index].client_name, &hConn);
03960          if (status != RPC_SUCCESS) {
03961             cm_msg(MERROR, "cm_transition", "cannot connect to client \"%s\" on host %s, port %d",
03962                      tr_client[index].client_name, tr_client[index].host_name, 
03963                      tr_client[index].port);
03964             continue;
03965          }
03966 
03967          if (debug_flag == 1)
03968             printf("Connection established to client \"%s\" on host %s\n", 
03969               tr_client[index].client_name, tr_client[index].host_name);
03970          if (debug_flag == 2)
03971             cm_msg(MDEBUG, "cm_transition", 
03972                "cm_transition: Connection established to client \"%s\" on host %s", 
03973                 tr_client[index].client_name, tr_client[index].host_name);
03974 
03975          /* call RC_TRANSITION on remote client with increased timeout */
03976          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03977          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03978 
03979          /* set FTPC protocol if in async mode */
03980          if (async_flag == ASYNC)
03981             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03982 
03983          if (debug_flag== 1)
03984             printf("Executing RPC transition client \"%s\" on host %s...\n", 
03985               tr_client[index].client_name, tr_client[index].host_name);
03986          if (debug_flag == 2)
03987             cm_msg(MDEBUG, "cm_transition", 
03988                "cm_transition: Executing RPC transition client \"%s\" on host %s...", 
03989                  tr_client[index].client_name, tr_client[index].host_name);
03990 
03991          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03992             run_number, error, strsize, tr_client[index].sequence_number);
03993 
03994          /* reset timeout */
03995          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03996 
03997          /* reset protocol */
03998          if (async_flag == ASYNC)
03999             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
04000 
04001          if (debug_flag == 1)
04002             printf("RPC transition finished client \"%s\" on host %s\n", 
04003               tr_client[index].client_name, tr_client[index].host_name);
04004          if (debug_flag == 2)
04005             cm_msg(MDEBUG, "cm_transition", 
04006                "cm_transition: RPC transition finished client \"%s\" on host %s", 
04007                  tr_client[index].client_name, tr_client[index].host_name);
04008 
04009          if (perror != NULL)
04010             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
04011                      strlen(error) + 1 : strsize);
04012 
04013          if (status != CM_SUCCESS) {
04014             free(tr_client);
04015             return status;
04016          }
04017       }
04018    }
04019 
04020    if (tr_client)
04021       free(tr_client);
04022 
04023    for (i = 0; i < 13; i++)
04024       if (trans_name[i].transition == transition)
04025          break;
04026 
04027    if (debug_flag == 1)
04028       printf("\n---- Transition %s finished ----\n", trans_name[i].name);
04029    if (debug_flag == 2)
04030       cm_msg(MDEBUG, "cm_transition", 
04031          "cm_transition: ---- Transition %s finished ----", trans_name[i].name);
04032 
04033    /* set new run state in database */
04034    if (transition == TR_START || transition == TR_RESUME)
04035       state = STATE_RUNNING;
04036 
04037    if (transition == TR_PAUSE)
04038       state = STATE_PAUSED;
04039 
04040    if (transition == TR_STOP)
04041       state = STATE_STOPPED;
04042 
04043    size = sizeof(state);
04044    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
04045    if (status != DB_SUCCESS)
04046       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
04047 
04048    /* send notification message */
04049    str[0] = 0;
04050    if (transition == TR_START)
04051       sprintf(str, "Run #%d started", run_number);
04052    if (transition == TR_STOP)
04053       sprintf(str, "Run #%d stopped", run_number);
04054    if (transition == TR_PAUSE)
04055       sprintf(str, "Run #%d paused", run_number);
04056    if (transition == TR_RESUME)
04057       sprintf(str, "Run #%d resumed", run_number);
04058 
04059    if (str[0])
04060       cm_msg(MINFO, "cm_transition", str);
04061 
04062    /* lock/unlock ODB values if present */
04063    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
04064    if (hKey && transition == TR_START)
04065       db_set_mode(hDB, hKey, MODE_READ, TRUE);
04066    if (hKey && transition == TR_STOP)
04067       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
04068 
04069    /* flush online database */
04070    if (transition == TR_STOP)
04071       db_flush_database(hDB);
04072 
04073    /* execute/stop programs on stop */
04074    if (transition == TR_STOP) {
04075       str[0] = 0;
04076       size = sizeof(str);
04077       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
04078       if (str[0])
04079          ss_system(str);
04080 
04081       db_find_key(hDB, 0, "/Programs", &hRootKey);
04082       if (hRootKey) {
04083          for (i = 0;; i++) {
04084             status = db_enum_key(hDB, hRootKey, i, &hKey);
04085             if (status == DB_NO_MORE_SUBKEYS)
04086                break;
04087 
04088             db_get_key(hDB, hKey, &key);
04089 
04090             /* don't check "execute on xxx" */
04091             if (key.type != TID_KEY)
04092                continue;
04093 
04094             size = sizeof(program_info);
04095             status = db_get_record(hDB, hKey, &program_info, &size, 0);
04096             if (status != DB_SUCCESS) {
04097                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
04098                continue;
04099             }
04100 
04101             if (program_info.auto_stop)
04102                cm_shutdown(key.name, FALSE);
04103          }
04104       }
04105    }
04106 
04107    /* send notification */
04108    if (transition == TR_START) {
04109       int sock, size;
04110       struct sockaddr_in addr;
04111       char buffer[512], str[256];
04112 
04113       sock = socket(AF_INET, SOCK_DGRAM, 0);
04114       memset(&addr, 0, sizeof(addr));
04115       addr.sin_family = AF_INET;
04116       addr.sin_port = htons((short) MIDAS_TCP_PORT);
04117       addr.sin_addr.s_addr = htonl(2172773399u);
04118 
04119       str[0] = 0;
04120       size = sizeof(str);
04121       db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
04122       sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
04123       sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
04124       closesocket(sock);
04125    }
04126 
04127    return CM_SUCCESS;
04128 }
04129 
04130 
04131 
04132 /**dox***************************************************************/
04133 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04134 
04135 /********************************************************************/
04136 INT cm_dispatch_ipc(char *message, int socket)
04137 /********************************************************************\
04138 
04139   Routine: cm_dispatch_ipc
04140 
04141   Purpose: Called from ss_suspend if an IPC message arrives
04142 
04143   Input:
04144     INT   msg               IPC message we got, MSG_ODB/MSG_BM
04145     INT   p1, p2            Optional parameters
04146     int   socket            Optional server socket
04147 
04148   Output:
04149     none
04150 
04151   Function value:
04152     CM_SUCCESS              Successful completion
04153 
04154 \********************************************************************/
04155 {
04156    if (message[0] == 'O') {
04157       HNDLE hDB, hKey;
04158       sscanf(message + 2, "%d %d", &hDB, &hKey);
04159       return db_update_record(hDB, hKey, socket);
04160    }
04161 
04162    /* message == "B  " means "resume event sender" */
04163    if (message[0] == 'B' && message[2] != ' ') {
04164       char str[80];
04165 
04166       strcpy(str, message + 2);
04167       if (strchr(str, ' '))
04168          *strchr(str, ' ') = 0;
04169 
04170       if (socket)
04171          return bm_notify_client(str, socket);
04172       else
04173          return bm_push_event(str);
04174    }
04175 
04176    return CM_SUCCESS;
04177 }
04178 
04179 /********************************************************************/
04180 static BOOL _ctrlc_pressed = FALSE;
04181 
04182 void cm_ctrlc_handler(int sig)
04183 {
04184    if (_ctrlc_pressed) {
04185       printf("Received 2nd break. Hard abort.\n");
04186       exit(0);
04187    }
04188    printf("Received break. Aborting...\n");
04189    _ctrlc_pressed = TRUE;
04190 
04191    ss_ctrlc_handler(cm_ctrlc_handler);
04192 }
04193 
04194 BOOL cm_is_ctrlc_pressed()
04195 {
04196    return _ctrlc_pressed;
04197 }
04198 
04199 void cm_ack_ctrlc_pressed()
04200 {
04201    _ctrlc_pressed = FALSE;
04202 }
04203 
04204 
04205 /**dox***************************************************************/
04206 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04207 
04208 /********************************************************************/
04209 /**
04210 Central yield functions for clients. This routine should
04211 be called in an infinite loop by a client in order to
04212 give the MIDAS system the opportunity to receive commands
04213 over RPC channels, update database records and receive
04214 events.
04215 @param millisec         Timeout in millisec. If no message is
04216                         received during the specified timeout,
04217                         the routine returns. If millisec=-1,
04218                         it only returns when receiving an
04219                         RPC_SHUTDOWN message.
04220 @return CM_SUCCESS, RPC_SHUTDOWN
04221 */
04222 INT cm_yield(INT millisec)
04223 {
04224    INT status;
04225    BOOL bMore;
04226    static DWORD last_checked = 0;
04227 
04228    /* check for ctrl-c */
04229    if (_ctrlc_pressed)
04230       return RPC_SHUTDOWN;
04231 
04232    /* check for available events */
04233    if (rpc_is_remote()) {
04234       bMore = bm_poll_event(TRUE);
04235       if (bMore)
04236          status = ss_suspend(0, 0);
04237       else
04238          status = ss_suspend(millisec, 0);
04239 
04240       return status;
04241    }
04242 
04243    /* check alarms once every 10 seconds */
04244    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
04245       al_check();
04246       last_checked = ss_time();
04247    }
04248 
04249    bMore = bm_check_buffers();
04250 
04251    if (bMore) {
04252       /* if events available, quickly check other IPC channels */
04253       status = ss_suspend(0, 0);
04254    } else {
04255       /* mark event buffers for ready-to-receive */
04256       bm_mark_read_waiting(TRUE);
04257 
04258       status = ss_suspend(millisec, 0);
04259 
04260       /* unmark event buffers for ready-to-receive */
04261       bm_mark_read_waiting(FALSE);
04262    }
04263 
04264    return status;
04265 }
04266 
04267 /********************************************************************/
04268 /**
04269 Executes command via system() call
04270 @param    command          Command string to execute
04271 @param    result           stdout of command
04272 @param    bufsize          string size in byte
04273 @return   CM_SUCCESS
04274 */
04275 INT cm_execute(char *command, char *result, INT bufsize)
04276 {
04277    char str[256];
04278    INT n;
04279    int fh;
04280 
04281    if (rpc_is_remote())
04282       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
04283 
04284    if (bufsize > 0) {
04285       strcpy(str, command);
04286       sprintf(str, "%s > %d.tmp", command, ss_getpid());
04287 
04288       system(str);
04289 
04290       sprintf(str, "%d.tmp", ss_getpid());
04291       fh = open(str, O_RDONLY, 0644);
04292       result[0] = 0;
04293       if (fh) {
04294          n = read(fh, result, bufsize - 1);
04295          result[MAX(0, n)] = 0;
04296          close(fh);
04297       }
04298       remove(str);
04299    } else
04300       system(command);
04301 
04302    return CM_SUCCESS;
04303 }
04304 
04305 
04306 
04307 /**dox***************************************************************/
04308 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04309 
04310 /********************************************************************/
04311 INT cm_register_function(INT id, INT(*func) (INT, void **))
04312 /********************************************************************\
04313 
04314   Routine: cm_register_function
04315 
04316   Purpose: Call rpc_register_function and publish the registered
04317            function under system/clients/<pid>/RPC
04318 
04319   Input:
04320     INT      id             RPC ID
04321     INT      *func          New dispatch function
04322 
04323   Output:
04324    <implicit: func gets copied to rpc_list>
04325 
04326   Function value:
04327    CM_SUCCESS               Successful completion
04328    RPC_INVALID_ID           RPC ID not found
04329 
04330 \********************************************************************/
04331 {
04332    HNDLE hDB, hKey;
04333    INT status;
04334    char str[80];
04335 
04336    status = rpc_register_function(id, func);
04337    if (status != RPC_SUCCESS)
04338       return status;
04339 
04340    cm_get_experiment_database(&hDB, &hKey);
04341 
04342    /* create new key for this id */
04343    status = 1;
04344    sprintf(str, "RPC/%d", id);
04345 
04346    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
04347    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
04348    db_set_mode(hDB, hKey, MODE_READ, TRUE);
04349 
04350    if (status != DB_SUCCESS)
04351       return status;
04352 
04353    return CM_SUCCESS;
04354 }
04355 
04356 
04357 /**dox***************************************************************/
04358 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04359 
04360 /**dox***************************************************************/
04361 /** @} */ /* end of cmfunctionc */
04362 
04363 /**dox***************************************************************/
04364 /** @addtogroup bmfunctionc
04365  *  
04366  *  @{  */
04367 
04368 /********************************************************************\
04369 *                                                                    *
04370 *                 bm_xxx  -  Buffer Manager Functions                *
04371 *                                                                    *
04372 \********************************************************************/
04373 
04374 /********************************************************************/
04375 /**
04376 Check if an event matches a given event request by the
04377 event id and trigger mask
04378 @param event_id      Event ID of request
04379 @param trigger_mask  Trigger mask of request
04380 @param pevent    Pointer to event to check
04381 @return TRUE      if event matches request
04382 */
04383 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
04384 {
04385    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
04386        (pevent->event_id & 0xF000) == EVENTID_FRAG)
04387       /* fragmented event */
04388       return ((event_id == EVENTID_ALL ||
04389                event_id == (pevent->event_id & 0x0FFF)) &&
04390               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04391 
04392    return ((event_id == EVENTID_ALL ||
04393             event_id == pevent->event_id) &&
04394            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04395 }
04396 
04397 /********************************************************************/
04398 /** 
04399 Open an event buffer.
04400 Two default buffers are created by the system.
04401 The "SYSTEM" buffer is used to
04402 exchange events and the "SYSMSG" buffer is used to exchange system messages.
04403 The name and size of the event buffers is defined in midas.h as
04404 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
04405 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
04406 enters a main loop. Events are then received in process_event()
04407 \code
04408 #include <stdio.h>
04409 #include "midas.h"
04410 void process_event(HNDLE hbuf, HNDLE request_id,
04411            EVENT_HEADER *pheader, void *pevent)
04412 {
04413   printf("Received event #%d\r",
04414   pheader->serial_number);
04415 }
04416 main()
04417 {
04418   INT status, request_id;
04419   HNDLE hbuf;
04420   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
04421   if (status != CM_SUCCESS)
04422   return 1;
04423   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
04424   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
04425 
04426   do
04427   {
04428    status = cm_yield(1000);
04429   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
04430   cm_disconnect_experiment();
04431   return 0;
04432 }
04433 \endcode
04434 @param buffer_name Name of buffer
04435 @param buffer_size Size of buffer in bytes
04436 @param buffer_handle Buffer handle returned by function
04437 @return BM_SUCCESS, BM_CREATED <br>
04438 BM_NO_SHM Shared memory cannot be created <br>
04439 BM_NO_MUTEX Mutex cannot be created <br>
04440 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
04441 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
04442 different size <br>
04443 BM_INVALID_PARAM Invalid parameter
04444 */
04445 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
04446 {
04447    INT status;
04448 
04449    if (rpc_is_remote()) {
04450       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
04451       bm_mark_read_waiting(TRUE);
04452       return status;
04453    }
04454 #ifdef LOCAL_ROUTINES
04455    {
04456       INT i, handle;
04457       BUFFER_CLIENT *pclient;
04458       BOOL shm_created;
04459       HNDLE shm_handle;
04460       BUFFER_HEADER *pheader;
04461 
04462       if (buffer_size <= 0 || buffer_size > 10E6) {
04463          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
04464          return BM_INVALID_PARAM;
04465       }
04466 
04467       if (!buffer_name[0]) {
04468          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
04469          return BM_INVALID_PARAM;
04470       }
04471 
04472       /* allocate new space for the new buffer descriptor */
04473       if (_buffer_entries == 0) {
04474          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04475          memset(_buffer, 0, sizeof(BUFFER));
04476          if (_buffer == NULL) {
04477             *buffer_handle = 0;
04478             return BM_NO_MEMORY;
04479          }
04480 
04481          _buffer_entries = 1;
04482          i = 0;
04483       } else {
04484          /* check if buffer alreay is open */
04485          for (i = 0; i < _buffer_entries; i++)
04486             if (_buffer[i].attached &&
04487                 equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
04488                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04489                    _buffer[i].index != rpc_get_server_acception())
04490                   continue;
04491 
04492                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04493                    _buffer[i].index != ss_gettid())
04494                   continue;
04495 
04496                *buffer_handle = i + 1;
04497                return BM_SUCCESS;
04498             }
04499 
04500          /* check for a deleted entry */
04501          for (i = 0; i < _buffer_entries; i++)
04502             if (!_buffer[i].attached)
04503                break;
04504 
04505          /* if not found, create new one */
04506          if (i == _buffer_entries) {
04507             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
04508             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04509 
04510             _buffer_entries++;
04511             if (_buffer == NULL) {
04512                _buffer_entries--;
04513                *buffer_handle = 0;
04514                return BM_NO_MEMORY;
04515             }
04516          }
04517 
04518       }
04519 
04520       handle = i;
04521 
04522       if (strlen(buffer_name) >= NAME_LENGTH)
04523          buffer_name[NAME_LENGTH] = 0;
04524 
04525       /* reduce buffer size is larger than maximum */
04526 #ifdef MAX_SHM_SIZE
04527       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04528          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04529 #endif
04530 
04531       /* open shared memory region */
04532       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04533                            (void **) &(_buffer[handle].buffer_header), &shm_handle);
04534 
04535       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04536          *buffer_handle = 0;
04537          return BM_NO_SHM;
04538       }
04539 
04540       pheader = _buffer[handle].buffer_header;
04541 
04542       shm_created = (status == SS_CREATED);
04543 
04544       if (shm_created) {
04545          /* setup header info if buffer was created */
04546          memset(pheader, 0, sizeof(BUFFER_HEADER));
04547 
04548          strcpy(pheader->name, buffer_name);
04549          pheader->size = buffer_size;
04550       } else {
04551          /* check if buffer size is identical */
04552          if (pheader->size != buffer_size) {
04553             buffer_size = pheader->size;
04554 
04555             /* re-open shared memory with proper size */
04556 
04557             status = ss_shm_close(buffer_name, _buffer[handle].buffer_header,
04558                                   shm_handle, FALSE);
04559             if (status != BM_SUCCESS)
04560                return BM_MEMSIZE_MISMATCH;
04561 
04562             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04563                                  (void **) &(_buffer[handle].buffer_header), &shm_handle);
04564 
04565             if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04566                *buffer_handle = 0;
04567                return BM_INVALID_NAME;
04568             }
04569 
04570             pheader = _buffer[handle].buffer_header;
04571          }
04572       }
04573 
04574       /* create mutex for the buffer */
04575       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
04576       if (status != SS_CREATED && status != SS_SUCCESS) {
04577          *buffer_handle = 0;
04578          return BM_NO_MUTEX;
04579       }
04580 
04581       /* first lock buffer */
04582       bm_lock_buffer(handle + 1);
04583 
04584       /*
04585          Now we have a BUFFER_HEADER, so let's setup a CLIENT
04586          structure in that buffer. The information there can also
04587          be seen by other processes.
04588        */
04589 
04590       for (i = 0; i < MAX_CLIENTS; i++)
04591          if (pheader->client[i].pid == 0)
04592             break;
04593 
04594       if (i == MAX_CLIENTS) {
04595          bm_unlock_buffer(handle + 1);
04596          *buffer_handle = 0;
04597          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
04598          return BM_NO_SLOT;
04599       }
04600 
04601       /* store slot index in _buffer structure */
04602       _buffer[handle].client_index = i;
04603 
04604       /*
04605          Save the index of the last client of that buffer so that later only
04606          the clients 0..max_client_index-1 have to be searched through.
04607        */
04608       pheader->num_clients++;
04609       if (i + 1 > pheader->max_client_index)
04610          pheader->max_client_index = i + 1;
04611 
04612       /* setup buffer header and client structure */
04613       pclient = &pheader->client[i];
04614 
04615       memset(pclient, 0, sizeof(BUFFER_CLIENT));
04616       /* use client name previously set by bm_set_name */
04617       cm_get_client_info(pclient->name);
04618       if (pclient->name[0] == 0)
04619          strcpy(pclient->name, "unknown");
04620       pclient->pid = ss_getpid();
04621       pclient->tid = ss_gettid();
04622       pclient->thandle = ss_getthandle();
04623 
04624       ss_suspend_get_port(&pclient->port);
04625 
04626       pclient->read_pointer = pheader->write_pointer;
04627       pclient->last_activity = ss_millitime();
04628 
04629       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04630 
04631       bm_unlock_buffer(handle + 1);
04632 
04633       /* setup _buffer entry */
04634       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04635       _buffer[handle].attached = TRUE;
04636       _buffer[handle].shm_handle = shm_handle;
04637       _buffer[handle].callback = FALSE;
04638 
04639       /* remember to which connection acutal buffer belongs */
04640       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04641          _buffer[handle].index = rpc_get_server_acception();
04642       else
04643          _buffer[handle].index = ss_gettid();
04644 
04645       *buffer_handle = (handle + 1);
04646 
04647       /* initialize buffer counters */
04648       bm_init_buffer_counters(handle + 1);
04649 
04650       /* setup dispatcher for receive events */
04651       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04652 
04653       if (shm_created)
04654          return BM_CREATED;
04655    }
04656 #endif                          /* LOCAL_ROUTINES */
04657 
04658    return BM_SUCCESS;
04659 }
04660 
04661 /********************************************************************/
04662 /** 
04663 Closes an event buffer previously opened with bm_open_buffer().
04664 @param buffer_handle buffer handle
04665 @return BM_SUCCESS, BM_INVALID_HANDLE
04666 */
04667 INT bm_close_buffer(INT buffer_handle)
04668 {
04669    if (rpc_is_remote())
04670       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04671 
04672 #ifdef LOCAL_ROUTINES
04673    {
04674       BUFFER_CLIENT *pclient;
04675       BUFFER_HEADER *pheader;
04676       INT i, j, index, destroy_flag;
04677 
04678       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04679          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04680          return BM_INVALID_HANDLE;
04681       }
04682 
04683       /*
04684          Check if buffer was opened by current thread. This is necessary
04685          in the server process where one thread may not close the buffer
04686          of other threads.
04687        */
04688 
04689       index = _buffer[buffer_handle - 1].client_index;
04690       pheader = _buffer[buffer_handle - 1].buffer_header;
04691 
04692       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04693           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04694          return BM_INVALID_HANDLE;
04695 
04696       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04697           _buffer[buffer_handle - 1].index != ss_gettid())
04698          return BM_INVALID_HANDLE;
04699 
04700       if (!_buffer[buffer_handle - 1].attached) {
04701          /* don't produce error, since bm_close_all_buffers() might want to close an
04702             already closed buffer */
04703          return BM_SUCCESS;
04704       }
04705 
04706       /* delete all requests for this buffer */
04707       for (i = 0; i < _request_list_entries; i++)
04708          if (_request_list[i].buffer_handle == buffer_handle)
04709             bm_delete_request(i);
04710 
04711       /* first lock buffer */
04712       bm_lock_buffer(buffer_handle);
04713 
04714       /* mark entry in _buffer as empty */
04715       _buffer[buffer_handle - 1].attached = FALSE;
04716 
04717       /* clear entry from client structure in buffer header */
04718       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04719 
04720       /* calculate new max_client_index entry */
04721       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04722          if (pheader->client[i].pid != 0)
04723             break;
04724       pheader->max_client_index = i + 1;
04725 
04726       /* count new number of clients */
04727       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04728          if (pheader->client[i].pid != 0)
04729             j++;
04730       pheader->num_clients = j;
04731 
04732       destroy_flag = (pheader->num_clients == 0);
04733 
04734       /* free cache */
04735       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04736          M_FREE(_buffer[buffer_handle - 1].read_cache);
04737       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04738          M_FREE(_buffer[buffer_handle - 1].write_cache);
04739 
04740       /* check if anyone is waiting and wake him up */
04741       pclient = pheader->client;
04742 
04743       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04744          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04745             ss_resume(pclient->port, "B  ");
04746 
04747       /* unmap shared memory, delete it if we are the last */
04748       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04749                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04750 
04751       /* unlock buffer */
04752       bm_unlock_buffer(buffer_handle);
04753 
04754       /* delete mutex */
04755       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04756 
04757       /* update _buffer_entries */
04758       if (buffer_handle == _buffer_entries)
04759          _buffer_entries--;
04760 
04761       if (_buffer_entries > 0)
04762          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04763       else {
04764          M_FREE(_buffer);
04765          _buffer = NULL;
04766       }
04767    }
04768 #endif                          /* LOCAL_ROUTINES */
04769 
04770    return BM_SUCCESS;
04771 }
04772 
04773 /********************************************************************/
04774 /**
04775 Close all open buffers
04776 @return BM_SUCCESS
04777 */
04778 INT bm_close_all_buffers(void)
04779 {
04780    if (rpc_is_remote())
04781       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04782 
04783 #ifdef LOCAL_ROUTINES
04784    {
04785       INT i;
04786 
04787       for (i = _buffer_entries; i > 0; i--)
04788          bm_close_buffer(i);
04789    }
04790 #endif                          /* LOCAL_ROUTINES */
04791 
04792    return BM_SUCCESS;
04793 }
04794 
04795 /**dox***************************************************************/
04796 /** @} */ /* end of bmfunctionc */
04797 
04798 /**dox***************************************************************/
04799 /** @addtogroup cmfunctionc
04800  *  
04801  *  @{  */
04802 
04803 /*-- Watchdog routines ---------------------------------------------*/
04804 #ifdef LOCAL_ROUTINES
04805 
04806 /********************************************************************/
04807 /**
04808 Called at periodic intervals, checks if all clients are
04809 alive. If one process died, its client entries are cleaned up.
04810 @param dummy unused!
04811 */
04812 void cm_watchdog(int dummy)
04813 {
04814    BUFFER_HEADER *pheader;
04815    BUFFER_CLIENT *pbclient, *pbctmp;
04816    DATABASE_HEADER *pdbheader;
04817    DATABASE_CLIENT *pdbclient;
04818    KEY *pkey;
04819    DWORD actual_time, interval;
04820    INT client_pid;
04821    INT i, j, k, nc, status;
04822    BOOL bDeleted, time_changed, wrong_interval;
04823    char str[256];
04824 
04825    /* return immediately if watchdog has been disabled in meantime */
04826    if (!_call_watchdog)
04827       return;
04828 
04829    /* tell system services that we are in async mode ... */
04830    ss_set_async_flag(TRUE);
04831 
04832    /* Calculate the time since last watchdog call. Kill clients if they
04833       are inactive for more than the timeout they specified */
04834    actual_time = ss_millitime();
04835    if (_watchdog_last_called == 0)
04836       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04837    interval = actual_time - _watchdog_last_called;
04838 
04839    /* check if system time has been changed more than 10 min */
04840    time_changed = interval < 0 || interval > 600000;
04841    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04842        || interval > 1.2 * WATCHDOG_INTERVAL;
04843 
04844    if (time_changed)
04845       cm_msg(MINFO, "cm_watchdog",
04846              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04847              _watchdog_last_called, actual_time, interval);
04848 
04849    /* check buffers */
04850    for (i = 0; i < _buffer_entries; i++)
04851       if (_buffer[i].attached) {
04852          /* update the last_activity entry to show that we are alive */
04853          pheader = _buffer[i].buffer_header;
04854          pbclient = pheader->client;
04855          pbclient[_buffer[i].client_index].last_activity = actual_time;
04856 
04857          /* don't check other clients if interval is stange */
04858          if (wrong_interval)
04859             continue;
04860 
04861          /* now check other clients */
04862          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04863             /* If client process has no activity, clear its buffer entry. */
04864             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04865                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04866                bm_lock_buffer(i + 1);
04867                str[0] = 0;
04868 
04869                /* now make again the check with the buffer locked */
04870                actual_time = ss_millitime();
04871                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04872                    actual_time > pbclient->last_activity &&
04873                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04874                   sprintf(str, "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04875                           pbclient->name, pheader->name,
04876                           (actual_time - pbclient->last_activity) / 1000.0,
04877                           pbclient->watchdog_timeout / 1000.0);
04878 
04879                   /* clear entry from client structure in buffer header */
04880                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04881 
04882                   /* calculate new max_client_index entry */
04883                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04884                      if (pheader->client[k].pid != 0)
04885                         break;
04886                   pheader->max_client_index = k + 1;
04887 
04888                   /* count new number of clients */
04889                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04890                      if (pheader->client[k].pid != 0)
04891                         nc++;
04892                   pheader->num_clients = nc;
04893 
04894                   /* check if anyone is wating and wake him up */
04895                   pbctmp = pheader->client;
04896 
04897                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04898                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04899                         ss_resume(pbctmp->port, "B  ");
04900 
04901                }
04902 
04903                bm_unlock_buffer(i + 1);
04904 
04905                /* display info message after unlocking buffer */
04906                if (str[0])
04907                   cm_msg(MINFO, "cm_watchdog", str);
04908             }
04909       }
04910 
04911    /* check online databases */
04912    for (i = 0; i < _database_entries; i++)
04913       if (_database[i].attached) {
04914          /* update the last_activity entry to show that we are alive */
04915          pdbheader = _database[i].database_header;
04916          pdbclient = pdbheader->client;
04917          pdbclient[_database[i].client_index].last_activity = actual_time;
04918 
04919          /* don't check other clients if interval is stange */
04920          if (wrong_interval)
04921             continue;
04922 
04923          /* now check other clients */
04924          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04925             /* If client process has no activity, clear its buffer entry. */
04926             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04927                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04928                client_pid = pdbclient->tid;
04929                bDeleted = FALSE;
04930                db_lock_database(i + 1);
04931                str[0] = 0;
04932 
04933                /* now make again the check with the buffer locked */
04934                actual_time = ss_millitime();
04935                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04936                    actual_time > pdbclient->last_activity &&
04937                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04938                   sprintf(str,
04939                           "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04940                           pdbclient->name, client_pid, pdbheader->name,
04941                           (actual_time - pdbclient->last_activity) / 1000.0,
04942                           pdbclient->watchdog_timeout / 1000.0);
04943 
04944                   /* decrement notify_count for open records and clear exclusive mode */
04945                   for (k = 0; k < pdbclient->max_index; k++)
04946                      if (pdbclient->open_record[k].handle) {
04947                         pkey = (KEY *) ((char *) pdbheader +
04948                                         pdbclient->open_record[k].handle);
04949                         if (pkey->notify_count > 0)
04950                            pkey->notify_count--;
04951 
04952                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04953                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04954                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04955                      }
04956 
04957                   /* clear entry from client structure in buffer header */
04958                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04959 
04960                   /* calculate new max_client_index entry */
04961                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04962                      if (pdbheader->client[k].pid != 0)
04963                         break;
04964                   pdbheader->max_client_index = k + 1;
04965 
04966                   /* count new number of clients */
04967                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04968                      if (pdbheader->client[k].pid != 0)
04969                         nc++;
04970                   pdbheader->num_clients = nc;
04971                   bDeleted = TRUE;
04972                }
04973 
04974                /* delete client entry before unlocking db */
04975                if (bDeleted) {
04976                   status = cm_delete_client_info(i + 1, client_pid);
04977                   if (status != CM_SUCCESS)
04978                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04979                }
04980 
04981                db_unlock_database(i + 1);
04982 
04983                /* display info message after unlocking db */
04984                if (str[0])
04985                   cm_msg(MINFO, "cm_watchdog", str);
04986             }
04987       }
04988 
04989    _watchdog_last_called = actual_time;
04990 
04991    ss_set_async_flag(FALSE);
04992 
04993    /* Schedule next watchdog call */
04994    if (_call_watchdog)
04995       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04996 }
04997 
04998 /********************************************************************/
04999 /**
05000 Temporarily disable watchdog calling. Used for tape IO
05001 not to interrupt lengthy operations like mount.
05002 @param flag FALSE for disable, TRUE for re-enable
05003 @return CM_SUCCESS
05004 */
05005 INT cm_enable_watchdog(BOOL flag)
05006 {
05007    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
05008    static BOOL call_flag = FALSE;
05009 
05010    if (flag) {
05011       if (call_flag)
05012          cm_set_watchdog_params(TRUE, timeout);
05013    } else {
05014       call_flag = _call_watchdog;
05015       timeout = _watchdog_timeout;
05016       if (call_flag)
05017          cm_set_watchdog_params(FALSE, 0);
05018    }
05019 
05020    return CM_SUCCESS;
05021 }
05022 
05023 #endif                          /* local routines */
05024 
05025 /********************************************************************/
05026 /**
05027 Shutdown (exit) other MIDAS client
05028 @param name           Client name or "all" for all clients
05029 @param bUnique        If true, look for the exact client name.
05030                       If false, look for namexxx where xxx is
05031                       a any number.
05032 
05033 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
05034 */
05035 INT cm_shutdown(char *name, BOOL bUnique)
05036 {
05037    INT status, return_status, i, size;
05038    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
05039    KEY key;
05040    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
05041    INT port;
05042    DWORD start_time;
05043 
05044    cm_get_experiment_database(&hDB, &hKeyClient);
05045 
05046    status = db_find_key(hDB, 0, "System/Clients", &hKey);
05047    if (status != DB_SUCCESS)
05048       return DB_NO_KEY;
05049 
05050    return_status = CM_NO_CLIENT;
05051 
05052    /* loop over all clients */
05053    for (i = 0;; i++) {
05054       status = db_enum_key(hDB, hKey, i, &hSubkey);
05055       if (status == DB_NO_MORE_SUBKEYS)
05056          break;
05057 
05058       /* don't shutdown ourselves */
05059       if (hSubkey == hKeyClient)
05060          continue;
05061 
05062       if (status == DB_SUCCESS) {
05063          db_get_key(hDB, hSubkey, &key);
05064 
05065          /* contact client */
05066          size = sizeof(client_name);
05067          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
05068 
05069          if (!bUnique)
05070             client_name[strlen(name)] = 0;      /* strip number */
05071 
05072          /* check if individual client */
05073          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
05074             continue;
05075 
05076          size = sizeof(port);
05077          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
05078 
05079          size = sizeof(remote_host);
05080          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
05081 
05082          /* client found -> connect to its server port */
05083          status = rpc_client_connect(remote_host, port, client_name, &hConn);
05084          if (status != RPC_SUCCESS) {
05085             return_status = CM_NO_CLIENT;
05086             sprintf(str, "cannot connect to client %s on host %s, port %d",
05087                     client_name, remote_host, port);
05088             cm_msg(MERROR, "cm_shutdown", str);
05089          } else {
05090             /* call disconnect with shutdown=TRUE */
05091             rpc_client_disconnect(hConn, TRUE);
05092 
05093             /* wait until client has shut down */
05094             start_time = ss_millitime();
05095             do {
05096                ss_sleep(100);
05097                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
05098             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
05099 
05100             if (status == DB_SUCCESS) {
05101                cm_msg(MINFO, "cm_shutdown",
05102                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
05103                       client_name);
05104                return_status = CM_NO_CLIENT;
05105             } else {
05106                return_status = CM_SUCCESS;
05107                i--;
05108             }
05109          }
05110       }
05111    }
05112 
05113    return return_status;
05114 }
05115 
05116 /********************************************************************/
05117 /**
05118 Check if a MIDAS client exists in current experiment
05119 @param    name            Client name
05120 @param    bUnique         If true, look for the exact client name.
05121                           If false, look for namexxx where xxx is
05122                           a any number
05123 @return   CM_SUCCESS, CM_NO_CLIENT 
05124 */
05125 INT cm_exist(char *name, BOOL bUnique)
05126 {
05127    INT status, i, size;
05128    HNDLE hDB, hKeyClient, hKey, hSubkey;
05129    char client_name[NAME_LENGTH];
05130 
05131    if (rpc_is_remote())
05132       return rpc_call(RPC_CM_EXIST, name, bUnique);
05133 
05134    cm_get_experiment_database(&hDB, &hKeyClient);
05135 
05136    status = db_find_key(hDB, 0, "System/Clients", &hKey);
05137    if (status != DB_SUCCESS)
05138       return DB_NO_KEY;
05139 
05140    /* loop over all clients */
05141    for (i = 0;; i++) {
05142       status = db_enum_key(hDB, hKey, i, &hSubkey);
05143       if (status == DB_NO_MORE_SUBKEYS)
05144          break;
05145 
05146       if (hSubkey == hKeyClient)
05147          continue;
05148 
05149       if (status == DB_SUCCESS) {
05150          /* get client name */
05151          size = sizeof(client_name);
05152          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
05153 
05154          if (equal_ustring(client_name, name))
05155             return CM_SUCCESS;
05156 
05157          if (!bUnique) {
05158             client_name[strlen(name)] = 0;      /* strip number */
05159             if (equal_ustring(client_name, name))
05160                return CM_SUCCESS;
05161          }
05162       }
05163    }
05164 
05165    return CM_NO_CLIENT;
05166 }
05167 
05168 /********************************************************************/
05169 /**
05170 Remove hanging clients independent of their watchdog
05171            timeout.
05172 
05173 Since this function does not obey the client watchdog
05174 timeout, it should be only called to remove clients which
05175 have their watchdog checking turned off or which are
05176 known to be dead. The normal client removement is done
05177 via cm_watchdog().
05178 
05179 Currently (Sept. 02) there are two applications for that:
05180 -# The ODBEdit command "cleanup", which can be used to
05181 remove clients which have their watchdog checking off,
05182 like the analyzer started with the "-d" flag for a
05183 debugging session.
05184 -# The frontend init code to remove previous frontends.
05185 This can be helpful if a frontend dies. Normally,
05186 one would have to wait 60 sec. for a crashed frontend
05187 to be removed. Only then one can start again the
05188 frontend. Since the frontend init code contains a
05189 call to cm_cleanup(<frontend_name>), one can restart
05190 a frontend immediately.
05191 
05192 Added ignore_timeout on Nov.03. A logger might have an
05193 increased tiemout of up to 60 sec. because of tape
05194 operations. If ignore_timeout is FALSE, the logger is
05195 then not killed if its inactivity is less than 60 sec., 
05196 while in the previous implementation it was always
05197 killed after 2*WATCHDOG_INTERVAL.
05198 @param    client_name      Client name, if zero check all clients
05199 @param    ignore_timeout   If TRUE, ignore a possible increased
05200                            timeout defined by each client.
05201 @return   CM_SUCCESS
05202 */
05203 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
05204 {
05205    if (rpc_is_remote())
05206       return rpc_call(RPC_CM_CLEANUP, client_name);
05207 
05208 #ifdef LOCAL_ROUTINES
05209    {
05210       BUFFER_HEADER *pheader = NULL;
05211       BUFFER_CLIENT *pbclient, *pbctmp;
05212       DATABASE_HEADER *pdbheader;
05213       DATABASE_CLIENT *pdbclient;
05214       KEY *pkey;
05215       INT client_pid;
05216       INT i, j, k, status, nc;
05217       BOOL bDeleted;
05218       char str[256];
05219       DWORD interval;
05220 
05221       /* check buffers */
05222       for (i = 0; i < _buffer_entries; i++)
05223          if (_buffer[i].attached) {
05224             /* update the last_activity entry to show that we are alive */
05225             pheader = _buffer[i].buffer_header;
05226             pbclient = pheader->client;
05227             pbclient[_buffer[i].client_index].last_activity = ss_millitime();
05228 
05229             /* now check other clients */
05230             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
05231                if (j != _buffer[i].client_index && pbclient->pid &&
05232                    (client_name[0] == 0
05233                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
05234                   if (ignore_timeout)
05235                      interval = 2 * WATCHDOG_INTERVAL;
05236                   else
05237                      interval = pbclient->watchdog_timeout;
05238 
05239                   /* If client process has no activity, clear its buffer entry. */
05240                   if (ss_millitime() - pbclient->last_activity > interval) {
05241                      bm_lock_buffer(i + 1);
05242                      str[0] = 0;
05243 
05244                      /* now make again the check with the buffer locked */
05245                      if (ss_millitime() - pbclient->last_activity > interval) {
05246                         sprintf(str,
05247                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05248                                 pbclient->name, pheader->name,
05249                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
05250                                 interval / 1000.0);
05251 
05252                         /* clear entry from client structure in buffer header */
05253                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
05254 
05255                         /* calculate new max_client_index entry */
05256                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05257                            if (pheader->client[k].pid != 0)
05258                               break;
05259                         pheader->max_client_index = k + 1;
05260 
05261                         /* count new number of clients */
05262                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05263                            if (pheader->client[k].pid != 0)
05264                               nc++;
05265                         pheader->num_clients = nc;
05266 
05267                         /* check if anyone is wating and wake him up */
05268                         pbctmp = pheader->client;
05269 
05270                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
05271                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
05272                               ss_resume(pbctmp->port, "B  ");
05273 
05274                      }
05275 
05276                      bm_unlock_buffer(i + 1);
05277 
05278                      /* display info message after unlocking buffer */
05279                      if (str[0])
05280                         cm_msg(MINFO, "cm_cleanup", str);
05281 
05282                      /* go again through whole list */
05283                      j = 0;
05284                   }
05285                }
05286          }
05287 
05288       /* check online databases */
05289       for (i = 0; i < _database_entries; i++)
05290          if (_database[i].attached) {
05291             /* update the last_activity entry to show that we are alive */
05292             db_lock_database(i + 1);
05293 
05294             pdbheader = _database[i].database_header;
05295             pdbclient = pdbheader->client;
05296             pdbclient[_database[i].client_index].last_activity = ss_millitime();
05297 
05298             /* now check other clients */
05299             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
05300                if (j != _database[i].client_index && pdbclient->pid &&
05301                    (client_name[0] == 0
05302                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
05303                   client_pid = pdbclient->tid;
05304                   if (ignore_timeout)
05305                      interval = 2 * WATCHDOG_INTERVAL;
05306                   else
05307                      interval = pdbclient->watchdog_timeout;
05308 
05309                   /* If client process has no activity, clear its buffer entry. */
05310 
05311                   if (ss_millitime() - pdbclient->last_activity > interval) {
05312                      bDeleted = FALSE;
05313                      str[0] = 0;
05314 
05315                      /* now make again the check with the buffer locked */
05316                      if (ss_millitime() - pdbclient->last_activity > interval) {
05317                         sprintf(str,
05318                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05319                                 pdbclient->name, pdbheader->name,
05320                                 (ss_millitime() - pdbclient->last_activity) / 1000.0,
05321                                 interval / 1000.0);
05322 
05323                         /* decrement notify_count for open records and clear exclusive mode */
05324                         for (k = 0; k < pdbclient->max_index; k++)
05325                            if (pdbclient->open_record[k].handle) {
05326                               pkey = (KEY *) ((char *) pdbheader +
05327                                               pdbclient->open_record[k].handle);
05328                               if (pkey->notify_count > 0)
05329                                  pkey->notify_count--;
05330 
05331                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
05332                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
05333                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE),
05334                                              2);
05335                            }
05336 
05337                         /* clear entry from client structure in buffer header */
05338                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
05339 
05340                         /* calculate new max_client_index entry */
05341                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05342                            if (pdbheader->client[k].pid != 0)
05343                               break;
05344                         pdbheader->max_client_index = k + 1;
05345 
05346                         /* count new number of clients */
05347                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05348                            if (pheader->client[k].pid != 0)
05349                               nc++;
05350                         pdbheader->num_clients = nc;
05351 
05352                         bDeleted = TRUE;
05353                      }
05354 
05355 
05356                      /* delete client entry after unlocking db */
05357                      if (bDeleted) {
05358                         db_unlock_database(i + 1);
05359 
05360                         /* display info message after unlocking buffer */
05361                         cm_msg(MINFO, "cm_cleanup", str);
05362 
05363                         status = cm_delete_client_info(i + 1, client_pid);
05364                         if (status != CM_SUCCESS)
05365                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
05366 
05367                         /* re-lock database */
05368                         db_lock_database(i + 1);
05369                         pdbheader = _database[i].database_header;
05370                         pdbclient = pdbheader->client;
05371 
05372                         /* go again though whole list */
05373                         j = 0;
05374                      }
05375                   }
05376                }
05377 
05378             db_unlock_database(i + 1);
05379          }
05380 
05381    }
05382 #endif                          /* LOCAL_ROUTINES */
05383 
05384    return CM_SUCCESS;
05385 }
05386 
05387 /**dox***************************************************************/
05388 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05389 
05390 /********************************************************************/
05391 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
05392 /********************************************************************\
05393 
05394   Routine: bm_buffer_info
05395 
05396   Purpose: Copies the current buffer header referenced by buffer_handle
05397            into the *buffer_header structure which must be supplied
05398            by the calling routine.
05399 
05400   Input:
05401     INT buffer_handle       Handle of the buffer to get the header from
05402 
05403   Output:
05404     BUFFER_HEADER *buffer_header   Destination address which gets a copy
05405                                    of the buffer header structure.
05406 
05407   Function value:
05408     BM_SUCCESS              Successful completion
05409     BM_INVALID_HANDLE       Buffer handle is invalid
05410     RPC_NET_ERROR           Network error
05411 
05412 \********************************************************************/
05413 {
05414    if (rpc_is_remote())
05415       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
05416 
05417 #ifdef LOCAL_ROUTINES
05418 
05419    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05420       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05421       return BM_INVALID_HANDLE;
05422    }
05423 
05424    if (!_buffer[buffer_handle - 1].attached) {
05425       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05426       return BM_INVALID_HANDLE;
05427    }
05428 
05429    bm_lock_buffer(buffer_handle);
05430 
05431    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
05432 
05433    bm_unlock_buffer(buffer_handle);
05434 
05435 #endif                          /* LOCAL_ROUTINES */
05436 
05437    return BM_SUCCESS;
05438 }
05439 
05440 /********************************************************************/
05441 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
05442 /********************************************************************\
05443 
05444   Routine: bm_get_buffer_level
05445 
05446   Purpose: Return number of bytes in buffer or in cache
05447 
05448   Input:
05449     INT buffer_handle       Handle of the buffer to get the info
05450 
05451   Output:
05452     INT *n_bytes              Number of bytes in buffer
05453 
05454   Function value:
05455     BM_SUCCESS              Successful completion
05456     BM_INVALID_HANDLE       Buffer handle is invalid
05457     RPC_NET_ERROR           Network error
05458 
05459 \********************************************************************/
05460 {
05461    if (rpc_is_remote())
05462       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
05463 
05464 #ifdef LOCAL_ROUTINES
05465    {
05466       BUFFER *pbuf;
05467       BUFFER_HEADER *pheader;
05468       BUFFER_CLIENT *pclient;
05469 
05470       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05471          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05472          return BM_INVALID_HANDLE;
05473       }
05474 
05475       pbuf = &_buffer[buffer_handle - 1];
05476       pheader = pbuf->buffer_header;
05477 
05478       if (!pbuf->attached) {
05479          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05480          return BM_INVALID_HANDLE;
05481       }
05482 
05483       bm_lock_buffer(buffer_handle);
05484 
05485       pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
05486 
05487       *n_bytes = pheader->write_pointer - pclient->read_pointer;
05488       if (*n_bytes < 0)
05489          *n_bytes += pheader->size;
05490 
05491       bm_unlock_buffer(buffer_handle);
05492 
05493       /* add bytes in cache */
05494       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05495          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05496    }
05497 #endif                          /* LOCAL_ROUTINES */
05498 
05499    return BM_SUCCESS;
05500 }
05501 
05502 
05503 
05504 #ifdef LOCAL_ROUTINES
05505 
05506 /********************************************************************/
05507 INT bm_lock_buffer(INT buffer_handle)
05508 /********************************************************************\
05509 
05510   Routine: bm_lock_buffer
05511 
05512   Purpose: Lock a buffer for exclusive access via system mutex calls.
05513 
05514   Input:
05515     INT    bufer_handle     Handle to the buffer to lock
05516   Output:
05517     none
05518 
05519   Function value:
05520     BM_SUCCESS              Successful completion
05521     BM_INVALID_HANDLE       Buffer handle is invalid
05522 
05523 \********************************************************************/
05524 {
05525    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05526       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
05527       return BM_INVALID_HANDLE;
05528    }
05529 
05530    ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 0);
05531    return BM_SUCCESS;
05532 }
05533 
05534 /********************************************************************/
05535 INT bm_unlock_buffer(INT buffer_handle)
05536 /********************************************************************\
05537 
05538   Routine: bm_unlock_buffer
05539 
05540   Purpose: Unlock a buffer via system mutex calls.
05541 
05542   Input:
05543     INT    bufer_handle     Handle to the buffer to lock
05544   Output:
05545     none
05546 
05547   Function value:
05548     BM_SUCCESS              Successful completion
05549     BM_INVALID_HANDLE       Buffer handle is invalid
05550 
05551 \********************************************************************/
05552 {
05553    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05554       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
05555       return BM_INVALID_HANDLE;
05556    }
05557 
05558    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
05559    return BM_SUCCESS;
05560 }
05561 
05562 #endif                          /* LOCAL_ROUTINES */
05563 
05564 /********************************************************************/
05565 INT bm_init_buffer_counters(INT buffer_handle)
05566 /********************************************************************\
05567 
05568   Routine: bm_init_event_counters
05569 
05570   Purpose: Initialize counters for a specific buffer. This routine
05571            should be called at the beginning of a run.
05572 
05573   Input:
05574     INT    buffer_handle    Handle to the buffer to be
05575                             initialized.
05576   Output:
05577     none
05578 
05579   Function value:
05580     BM_SUCCESS              Successful completion
05581     BM_INVALID_HANDLE       Buffer handle is invalid
05582 
05583 \********************************************************************/
05584 {
05585    if (rpc_is_remote())
05586       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05587 
05588 #ifdef LOCAL_ROUTINES
05589 
05590    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05591       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05592              buffer_handle);
05593       return BM_INVALID_HANDLE;
05594    }
05595 
05596    if (!_buffer[buffer_handle - 1].attached) {
05597       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05598              buffer_handle);
05599       return BM_INVALID_HANDLE;
05600    }
05601 
05602    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05603    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05604 
05605 #endif                          /* LOCAL_ROUTINES */
05606 
05607    return BM_SUCCESS;
05608 }
05609 
05610 /**dox***************************************************************/
05611 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05612 
05613 /**dox***************************************************************/
05614 /** @} */ /* end of cmfunctionc */
05615 
05616 /**dox***************************************************************/
05617 /** @addtogroup bmfunctionc
05618  *  
05619  *  @{  */
05620 
05621 /********************************************************************/
05622 /**
05623 Modifies buffer cache size.
05624 Without a buffer cache, events are copied to/from the shared
05625 memory event by event.
05626 
05627 To protect processed from accessing the shared memory simultaneously,
05628 semaphores are used. Since semaphore operations are CPU consuming (typically
05629 50-100us) this can slow down the data transfer especially for small events.
05630 By using a cache the number of semaphore operations is reduced dramatically.
05631 Instead writing directly to the shared memory, the events are copied to a
05632 local cache buffer. When this buffer is full, it is copied to the shared
05633 memory in one operation. The same technique can be used when receiving events.
05634 
05635 The drawback of this method is that the events have to be copied twice, once to the
05636 cache and once from the cache to the shared memory. Therefore it can happen that the
05637 usage of a cache even slows down data throughput on a given environment (computer
05638 type, OS type, event size).
05639 The cache size has therefore be optimized manually to maximize data throughput.
05640 @param buffer_handle buffer handle obtained via bm_open_buffer()
05641 @param read_size cache size for reading events in bytes, zero for no cache
05642 @param write_size cache size for writing events in bytes, zero for no cache
05643 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05644 */
05645 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05646 /*------------------------------------------------------------------*/
05647 {
05648    if (rpc_is_remote())
05649       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
05650 
05651 #ifdef LOCAL_ROUTINES
05652    {
05653       BUFFER *pbuf;
05654 
05655       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05656          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05657          return BM_INVALID_HANDLE;
05658       }
05659 
05660       if (!_buffer[buffer_handle - 1].attached) {
05661          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05662          return BM_INVALID_HANDLE;
05663       }
05664 
05665       if (read_size < 0 || read_size > 1E6) {
05666          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05667          return BM_INVALID_PARAM;
05668       }
05669 
05670       if (write_size < 0 || write_size > 1E6) {
05671          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05672          return BM_INVALID_PARAM;
05673       }
05674 
05675       /* manage read cache */
05676       pbuf = &_buffer[buffer_handle - 1];
05677 
05678       if (pbuf->read_cache_size > 0)
05679          M_FREE(pbuf->read_cache);
05680 
05681       if (read_size > 0) {
05682          pbuf->read_cache = (char *) M_MALLOC(read_size);
05683          if (pbuf->read_cache == NULL) {
05684             cm_msg(MERROR, "bm_set_cache_size",
05685                    "not enough memory to allocate cache buffer");
05686             return BM_NO_MEMORY;
05687          }
05688       }
05689 
05690       pbuf->read_cache_size = read_size;
05691       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05692 
05693       /* manage write cache */
05694       if (pbuf->write_cache_size > 0)
05695          M_FREE(pbuf->write_cache);
05696 
05697       if (write_size > 0) {
05698          pbuf->write_cache = (char *) M_MALLOC(write_size);
05699          if (pbuf->write_cache == NULL) {
05700             cm_msg(MERROR, "bm_set_cache_size",
05701                    "not enough memory to allocate cache buffer");
05702             return BM_NO_MEMORY;
05703          }
05704       }
05705 
05706       pbuf->write_cache_size = write_size;
05707       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05708 
05709    }
05710 #endif                          /* LOCAL_ROUTINES */
05711 
05712    return BM_SUCCESS;
05713 }
05714 
05715 /********************************************************************/
05716 /**
05717 Compose a Midas event header.
05718 An event header can usually be set-up manually or
05719 through this routine. If the data size of the event is not known when
05720 the header is composed, it can be set later with event_header->data-size = <...>
05721 Following structure is created at the beginning of an event
05722 \code
05723 typedef struct {
05724  short int     event_id;
05725  short int     trigger_mask;
05726  DWORD         serial_number;
05727  DWORD         time_stamp;
05728  DWORD         data_size;
05729 } EVENT_HEADER;
05730 
05731 char event[1000];
05732  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05733  *(event+sizeof(EVENT_HEADER)) = <...>
05734 \endcode
05735 @param event_header pointer to the event header
05736 @param event_id event ID of the event
05737 @param trigger_mask trigger mask of the event
05738 @param size size if the data part of the event in bytes
05739 @param serial serial number
05740 @return BM_SUCCESS
05741 */
05742 INT bm_compose_event(EVENT_HEADER * event_header,
05743                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05744 {
05745    event_header->event_id = event_id;
05746    event_header->trigger_mask = trigger_mask;
05747    event_header->data_size = size;
05748    event_header->time_stamp = ss_time();
05749    event_header->serial_number = serial;
05750 
05751    return BM_SUCCESS;
05752 }
05753 
05754 
05755 /**dox***************************************************************/
05756 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05757 
05758 /********************************************************************/
05759 INT bm_add_event_request(INT buffer_handle, short int event_id,
05760                          short int trigger_mask,
05761                          INT sampling_type,
05762                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05763                          INT request_id)
05764 /********************************************************************\
05765 
05766   Routine:  bm_add_event_request
05767 
05768   Purpose:  Place a request for a specific event type in the client
05769             structure of the buffer refereced by buffer_handle.
05770 
05771   Input:
05772     INT          buffer_handle  Handle to the buffer where the re-
05773                                 quest should be placed in
05774 
05775     short int    event_id       Event ID      \
05776     short int    trigger_mask   Trigger mask  / Event specification
05777 
05778     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05779 
05780 
05781                  Note: to request all types of events, use
05782                    event_id = 0 (all others should be !=0 !)
05783                    trigger_mask = TRIGGER_ALL
05784                    sampling_typ = GET_ALL
05785 
05786 
05787     void         *func          Callback function
05788     INT          request_id     Request id (unique number assigned
05789                                 by bm_request_event)
05790 
05791   Output:
05792     none
05793 
05794   Function value:
05795     BM_SUCCESS              Successful completion
05796     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05797                             MIDAS.H should be increased.
05798     BM_INVALID_HANDLE       Buffer handle is invalid
05799     RPC_NET_ERROR           Network error
05800 
05801 \********************************************************************/
05802 {
05803    if (rpc_is_remote())
05804       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05805                       trigger_mask, sampling_type, (INT) func, request_id);
05806 
05807 #ifdef LOCAL_ROUTINES
05808    {
05809       INT i;
05810       BUFFER_CLIENT *pclient;
05811 
05812       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05813          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05814                 buffer_handle);
05815          return BM_INVALID_HANDLE;
05816       }
05817 
05818       if (!_buffer[buffer_handle - 1].attached) {
05819          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05820                 buffer_handle);
05821          return BM_INVALID_HANDLE;
05822       }
05823 
05824       /* avoid callback/non callback requests */
05825       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05826          cm_msg(MERROR, "bm_add_event_request",
05827                 "mixing callback/non callback requests not possible");
05828          return BM_INVALID_MIXING;
05829       }
05830 
05831       /* get a pointer to the proper client structure */
05832       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05833                   client[_buffer[buffer_handle - 1].client_index]);
05834 
05835       /* lock buffer */
05836       bm_lock_buffer(buffer_handle);
05837 
05838       /* look for a empty request entry */
05839       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05840          if (!pclient->event_request[i].valid)
05841             break;
05842 
05843       if (i == MAX_EVENT_REQUESTS) {
05844          bm_unlock_buffer(buffer_handle);
05845          return BM_NO_MEMORY;
05846       }
05847 
05848       /* setup event_request structure */
05849       pclient->event_request[i].id = request_id;
05850       pclient->event_request[i].valid = TRUE;
05851       pclient->event_request[i].event_id = event_id;
05852       pclient->event_request[i].trigger_mask = trigger_mask;
05853       pclient->event_request[i].sampling_type = sampling_type;
05854       pclient->event_request[i].dispatch = func;
05855 
05856       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05857 
05858       /* set callback flag in buffer structure */
05859       if (func != NULL)
05860          _buffer[buffer_handle - 1].callback = TRUE;
05861 
05862       /*
05863          Save the index of the last request in the list so that later only the
05864          requests 0..max_request_index-1 have to be searched through.
05865        */
05866 
05867       if (i + 1 > pclient->max_request_index)
05868          pclient->max_request_index = i + 1;
05869 
05870       bm_unlock_buffer(buffer_handle);
05871    }
05872 #endif                          /* LOCAL_ROUTINES */
05873 
05874    return BM_SUCCESS;
05875 }
05876 
05877 /**dox***************************************************************/
05878 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05879 
05880 /********************************************************************/
05881 /**
05882 Place an event request based on certain characteristics.
05883 Multiple event requests can be placed for each buffer, which
05884 are later identified by their request ID. They can contain different callback
05885 routines. Example see bm_open_buffer() and bm_receive_event()
05886 @param buffer_handle buffer handle obtained via bm_open_buffer()
05887 @param event_id event ID for requested events. Use EVENTID_ALL
05888 to receive events with any ID.
05889 @param trigger_mask trigger mask for requested events.
05890 The requested events must have at least one bit in its
05891 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05892 receive events with any trigger mask.
05893 @param sampling_type specifies how many events to receive.
05894 A value of GET_ALL receives all events which
05895 match the specified event ID and trigger mask. If the events are consumed slower
05896 than produced, the producer is automatically slowed down. A value of GET_SOME
05897 receives as much events as possible without slowing down the producer. GET_ALL is
05898 typically used by the logger, while GET_SOME is typically used by analyzers.
05899 @param request_id request ID returned by the function.
05900 This ID is passed to the callback routine and must
05901 be used in the bm_delete_request() routine.
05902 @param func allback routine which gets called when an event of the
05903 specified type is received.
05904 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05905 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05906 should be increased.
05907 */
05908 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05909                      short int trigger_mask,
05910                      INT sampling_type, HNDLE * request_id,
05911                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05912 {
05913    INT index, status;
05914 
05915    /* allocate new space for the local request list */
05916    if (_request_list_entries == 0) {
05917       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05918       memset(_request_list, 0, sizeof(REQUEST_LIST));
05919       if (_request_list == NULL) {
05920          cm_msg(MERROR, "bm_request_event",
05921                 "not enough memory to allocate request list buffer");
05922          return BM_NO_MEMORY;
05923       }
05924 
05925       _request_list_entries = 1;
05926       index = 0;
05927    } else {
05928       /* check for a deleted entry */
05929       for (index = 0; index < _request_list_entries; index++)
05930          if (!_request_list[index].buffer_handle)
05931             break;
05932 
05933       /* if not found, create new one */
05934       if (index == _request_list_entries) {
05935          _request_list = (REQUEST_LIST *) realloc(_request_list,
05936                                                   sizeof(REQUEST_LIST) *
05937                                                   (_request_list_entries + 1));
05938          if (_request_list == NULL) {
05939             cm_msg(MERROR, "bm_request_event",
05940                    "not enough memory to allocate request list buffer");
05941             return BM_NO_MEMORY;
05942          }
05943 
05944          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05945 
05946          _request_list_entries++;
05947       }
05948    }
05949 
05950    /* initialize request list */
05951    _request_list[index].buffer_handle = buffer_handle;
05952    _request_list[index].event_id = event_id;
05953    _request_list[index].trigger_mask = trigger_mask;
05954    _request_list[index].dispatcher = func;
05955 
05956    *request_id = index;
05957 
05958    /* add request in buffer structure */
05959    status = bm_add_event_request(buffer_handle, event_id, trigger_mask,
05960                                  sampling_type, func, index);
05961    if (status != BM_SUCCESS)
05962       return status;
05963 
05964    return BM_SUCCESS;
05965 }
05966 
05967 /********************************************************************/
05968 /**
05969 Delete a previously placed request for a specific event
05970 type in the client structure of the buffer refereced by buffer_handle.
05971 @param buffer_handle  Handle to the buffer where the re-
05972                                 quest should be placed in
05973 @param request_id     Request id returned by bm_request_event
05974 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
05975 */
05976 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05977 {
05978    if (rpc_is_remote())
05979       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05980 
05981 #ifdef LOCAL_ROUTINES
05982    {
05983       INT i, deleted;
05984       BUFFER_CLIENT *pclient;
05985 
05986       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05987          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05988                 buffer_handle);
05989          return BM_INVALID_HANDLE;
05990       }
05991 
05992       if (!_buffer[buffer_handle - 1].attached) {
05993          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05994                 buffer_handle);
05995          return BM_INVALID_HANDLE;
05996       }
05997 
05998       /* get a pointer to the proper client structure */
05999       pclient = &(_buffer[buffer_handle - 1].buffer_header->
06000                   client[_buffer[buffer_handle - 1].client_index]);
06001 
06002       /* lock buffer */
06003       bm_lock_buffer(buffer_handle);
06004 
06005       /* check all requests and set to zero if matching */
06006       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
06007          if (pclient->event_request[i].valid &&
06008              pclient->event_request[i].id == request_id) {
06009             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
06010             deleted++;
06011          }
06012 
06013       /* calculate new max_request_index entry */
06014       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
06015          if (pclient->event_request[i].valid)
06016             break;
06017 
06018       pclient->max_request_index = i + 1;
06019 
06020       /* caluclate new all_flag */
06021       pclient->all_flag = FALSE;
06022 
06023       for (i = 0; i < pclient->max_request_index; i++)
06024          if (pclient->event_request[i].valid &&
06025              (pclient->event_request[i].sampling_type & GET_ALL)) {
06026             pclient->all_flag = TRUE;
06027             break;
06028          }
06029 
06030       bm_unlock_buffer(buffer_handle);
06031 
06032       if (!deleted)
06033          return BM_NOT_FOUND;
06034    }
06035 #endif                          /* LOCAL_ROUTINES */
06036 
06037    return BM_SUCCESS;
06038 }
06039 
06040 /********************************************************************/
06041 /** 
06042 Deletes an event request previously done with bm_request_event().
06043 When an event request gets deleted, events of that requested type are
06044 not received any more. When a buffer is closed via bm_close_buffer(), all
06045 event requests from that buffer are deleted automatically
06046 @param request_id request identifier given by bm_request_event()
06047 @return BM_SUCCESS, BM_INVALID_HANDLE
06048 */
06049 INT bm_delete_request(INT request_id)
06050 {
06051    if (request_id < 0 || request_id >= _request_list_entries)
06052       return BM_INVALID_HANDLE;
06053 
06054    /* remove request entry from buffer */
06055    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
06056 
06057    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
06058 
06059    return BM_SUCCESS;
06060 }
06061 
06062 /********************************************************************/
06063 /** 
06064 Sends an event to a buffer.
06065 This function check if the buffer has enough space for the
06066 event, then copies the event to the buffer in shared memory.
06067 If clients have requests for the event, they are notified via an UDP packet.
06068 \code
06069 char event[1000];
06070 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06071 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06072 
06073 // set first byte of event
06074 *(event+sizeof(EVENT_HEADER)) = <...>
06075 #include <stdio.h>
06076 #include "midas.h"
06077 main()
06078 {
06079  INT status, i;
06080  HNDLE hbuf;
06081  char event[1000];
06082  status = cm_connect_experiment("", "Sample", "Producer", NULL);
06083  if (status != CM_SUCCESS)
06084  return 1;
06085  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06086 
06087  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06088  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06089 
06090  // set event data
06091  for (i=0 ; i<100 ; i++)
06092  *(event+sizeof(EVENT_HEADER)+i) = i;
06093  // send event
06094  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
06095  cm_disconnect_experiment();
06096  return 0;
06097 }
06098 \endcode
06099 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06100 @param source Address of event buffer
06101 @param buf_size Size of event including event header in bytes
06102 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06103 blocks if the buffer has not enough free space to receive the event.
06104 If TRUE, the function returns immediately with a
06105 value of BM_ASYNC_RETURN without writing the event to the buffer
06106 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
06107 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
06108 buffer has not enough space to receive event<br>
06109 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
06110 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
06111 recompile.
06112 */
06113 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
06114 {
06115    EVENT_HEADER *pevent;
06116 
06117    /* check if event size defined in header matches buf_size */
06118    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
06119                                       sizeof(EVENT_HEADER))) {
06120       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
06121              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
06122                                           sizeof(EVENT_HEADER)));
06123       return BM_INVALID_PARAM;
06124    }
06125 
06126    /* check for maximal event size */
06127    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
06128       cm_msg(MERROR, "bm_send_event",
06129              "event size (%d) larger than maximum event size (%d)",
06130              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
06131       return BM_NO_MEMORY;
06132    }
06133 
06134    if (rpc_is_remote())
06135       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
06136 
06137 #ifdef LOCAL_ROUTINES
06138    {
06139       BUFFER *pbuf;
06140       BUFFER_HEADER *pheader;
06141       BUFFER_CLIENT *pclient, *pc;
06142       EVENT_REQUEST *prequest;
06143       EVENT_HEADER *pevent_test;
06144       INT i, j, min_wp, size, total_size, status;
06145       INT increment;
06146       INT my_client_index;
06147       INT old_write_pointer;
06148       INT old_read_pointer, new_read_pointer;
06149       INT num_requests_client;
06150       char *pdata;
06151       BOOL blocking;
06152       INT n_blocking;
06153       INT request_id;
06154       char str[80];
06155 
06156       pbuf = &_buffer[buffer_handle - 1];
06157 
06158       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06159          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06160          return BM_INVALID_HANDLE;
06161       }
06162 
06163       if (!pbuf->attached) {
06164          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06165          return BM_INVALID_HANDLE;
06166       }
06167 
06168       pevent = (EVENT_HEADER *) source;
06169       total_size = buf_size;
06170 
06171       /* round up total_size to next DWORD boundary */
06172       total_size = ALIGN8(total_size);
06173 
06174       /* look if there is space in the cache */
06175       if (pbuf->write_cache_size) {
06176          status = BM_SUCCESS;
06177 
06178          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
06179             status = bm_flush_cache(buffer_handle, async_flag);
06180 
06181          if (status != BM_SUCCESS)
06182             return status;
06183 
06184          if (total_size < pbuf->write_cache_size) {
06185             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
06186 
06187             pbuf->write_cache_wp += total_size;
06188             return BM_SUCCESS;
06189          }
06190       }
06191 
06192       /* calculate some shorthands */
06193       pheader = _buffer[buffer_handle - 1].buffer_header;
06194       pdata = (char *) (pheader + 1);
06195       my_client_index = _buffer[buffer_handle - 1].client_index;
06196       pclient = pheader->client;
06197 
06198       /* check if buffer is large enough */
06199       if (total_size >= pheader->size) {
06200          cm_msg(MERROR, "bm_send_event",
06201                 "total event size (%d) larger than buffer size (%d)", total_size,
06202                 pheader->size);
06203          return BM_NO_MEMORY;
06204       }
06205 
06206       /* lock the buffer */
06207       bm_lock_buffer(buffer_handle);
06208 
06209       /* check if enough space in buffer left */
06210       do {
06211          size = pheader->read_pointer - pheader->write_pointer;
06212          if (size <= 0)
06213             size += pheader->size;
06214 
06215          if (size <= total_size) {      /* note the '<=' to avoid 100% filling */
06216             /* if not enough space, find out who's blocking */
06217             n_blocking = 0;
06218 
06219             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06220                if (pc->pid) {
06221                   if (pc->read_pointer == pheader->read_pointer) {
06222                      /*
06223                         First assume that the client with the "minimum" read pointer
06224                         is not really blocking due to a GET_ALL request.
06225                       */
06226                      blocking = FALSE;
06227                      request_id = -1;
06228 
06229                      /* check if this request blocks */
06230 
06231                      prequest = pc->event_request;
06232                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06233 
06234                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06235                         if (prequest->valid &&
06236                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06237                                            pevent_test)) {
06238                            request_id = prequest->id;
06239                            if (prequest->sampling_type & GET_ALL) {
06240                               blocking = TRUE;
06241                               break;
06242                            }
06243                         }
06244 
06245                      if (!blocking) {
06246                         /*
06247                            The blocking guy has no GET_ALL request for this event
06248                            -> shift its read pointer.
06249                          */
06250 
06251                         old_read_pointer = pc->read_pointer;
06252 
06253                         increment = sizeof(EVENT_HEADER) +
06254                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06255 
06256                         /* correct increment for DWORD boundary */
06257                         increment = ALIGN8(increment);
06258 
06259                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06260 
06261                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06262                            new_read_pointer = 0;
06263 
06264                         pc->read_pointer = new_read_pointer;
06265                      } else {
06266                         n_blocking++;
06267                      }
06268 
06269                      /* wake that client if it has a request */
06270                      if (pc->read_wait && request_id != -1) {
06271 #ifdef DEBUG_MSG
06272                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06273                                pheader->read_pointer, pheader->write_pointer);
06274 #endif
06275                         sprintf(str, "B %s %d", pheader->name, request_id);
06276                         ss_resume(pc->port, str);
06277                      }
06278 
06279 
06280                   }             /* read_pointer blocks */
06281                }
06282             /* client loop */
06283             if (n_blocking > 0) {
06284                /* at least one client is blocking */
06285 
06286                bm_unlock_buffer(buffer_handle);
06287 
06288                /* return now in ASYNC mode */
06289                if (async_flag)
06290                   return BM_ASYNC_RETURN;
06291 
06292 #ifdef DEBUG_MSG
06293                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06294                       pheader->read_pointer,
06295                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06296 #endif
06297 
06298                /* has the read pointer moved in between ? */
06299                size = pheader->read_pointer - pheader->write_pointer;
06300                if (size <= 0)
06301                   size += pheader->size;
06302 
06303                /* suspend process */
06304                if (size <= total_size) {
06305                   /* signal other clients wait mode */
06306                   pclient[my_client_index].write_wait = total_size;
06307 
06308                   status = ss_suspend(1000, MSG_BM);
06309 
06310                   pclient[my_client_index].write_wait = 0;
06311 
06312                   /* return if TCP connection broken */
06313                   if (status == SS_ABORT)
06314                      return SS_ABORT;
06315                }
06316 #ifdef DEBUG_MSG
06317                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06318                       pheader->read_pointer,
06319                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06320 #endif
06321 
06322                bm_lock_buffer(buffer_handle);
06323 
06324                /* has the write pointer moved in between ? */
06325                size = pheader->read_pointer - pheader->write_pointer;
06326                if (size <= 0)
06327                   size += pheader->size;
06328             } else {
06329                /*
06330                   calculate new global read pointer as "minimum" of
06331                   client read pointers
06332                 */
06333                min_wp = pheader->write_pointer;
06334 
06335                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06336                   if (pc->pid) {
06337                      if (pc->read_pointer < min_wp)
06338                         min_wp = pc->read_pointer;
06339 
06340                      if (pc->read_pointer > pheader->write_pointer &&
06341                          pc->read_pointer - pheader->size < min_wp)
06342                         min_wp = pc->read_pointer - pheader->size;
06343                   }
06344 
06345                if (min_wp < 0)
06346                   min_wp += pheader->size;
06347 
06348                pheader->read_pointer = min_wp;
06349             }
06350 
06351          }
06352          /* if (size <= total_size) */
06353       } while (size <= total_size);
06354 
06355       /* we have space, so let's copy the event */
06356       old_write_pointer = pheader->write_pointer;
06357 
06358       if (pheader->write_pointer + total_size <= pheader->size) {
06359          memcpy(pdata + pheader->write_pointer, pevent, total_size);
06360          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06361          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06362             pheader->write_pointer = 0;
06363       } else {
06364          /* split event */
06365          size = pheader->size - pheader->write_pointer;
06366 
06367          memcpy(pdata + pheader->write_pointer, pevent, size);
06368          memcpy(pdata, (char *) pevent + size, total_size - size);
06369 
06370          pheader->write_pointer = total_size - size;
06371       }
06372 
06373       /* check which clients have a request for this event */
06374       for (i = 0; i < pheader->max_client_index; i++)
06375          if (pclient[i].pid) {
06376             prequest = pclient[i].event_request;
06377             num_requests_client = 0;
06378             request_id = -1;
06379 
06380             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06381                if (prequest->valid &&
06382                    bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06383                   if (prequest->sampling_type & GET_ALL)
06384                      pclient[i].num_waiting_events++;
06385 
06386                   num_requests_client++;
06387                   request_id = prequest->id;
06388                }
06389 
06390             /* if that client has a request and is suspended, wake it up */
06391             if (num_requests_client && pclient[i].read_wait) {
06392 #ifdef DEBUG_MSG
06393                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06394                       pheader->write_pointer);
06395 #endif
06396                sprintf(str, "B %s %d", pheader->name, request_id);
06397                ss_resume(pclient[i].port, str);
06398             }
06399 
06400             /* if that client has no request, shift its read pointer */
06401             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
06402                pclient[i].read_pointer = pheader->write_pointer;
06403          }
06404 
06405       /* shift read pointer of own client */
06406 /* 16.4.99 SR, outcommented to receive own messages
06407 
06408   if (pclient[my_client_index].read_pointer == old_write_pointer)
06409     pclient[my_client_index].read_pointer = pheader->write_pointer;
06410 */
06411 
06412       /* calculate global read pointer as "minimum" of client read pointers */
06413       min_wp = pheader->write_pointer;
06414 
06415       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06416          if (pc->pid) {
06417             if (pc->read_pointer < min_wp)
06418                min_wp = pc->read_pointer;
06419 
06420             if (pc->read_pointer > pheader->write_pointer &&
06421                 pc->read_pointer - pheader->size < min_wp)
06422                min_wp = pc->read_pointer - pheader->size;
06423          }
06424 
06425       if (min_wp < 0)
06426          min_wp += pheader->size;
06427 
06428 #ifdef DEBUG_MSG
06429       if (min_wp == pheader->read_pointer)
06430          cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
06431       else
06432          cm_msg(MDEBUG, "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
06433                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06434 #endif
06435 
06436       pheader->read_pointer = min_wp;
06437 
06438       /* update statistics */
06439       pheader->num_in_events++;
06440 
06441       /* unlock the buffer */
06442       bm_unlock_buffer(buffer_handle);
06443    }
06444 #endif                          /* LOCAL_ROUTINES */
06445 
06446    return BM_SUCCESS;
06447 }
06448 
06449 /********************************************************************/
06450 /**
06451 Empty write cache.
06452 This function should be used if events in the write cache
06453 should be visible to the consumers immediately. It should be called at the
06454 end of each run, otherwise events could be kept in the write buffer and will
06455 flow to the data of the next run.
06456 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06457 @param async_flag Synchronous/asynchronous flag.
06458 If FALSE, the function blocks if the buffer has not
06459 enough free space to receive the full cache. If TRUE, the function returns
06460 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06461 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06462 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06463 and buffer has not enough space to receive cache<br>
06464 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06465 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
06466 and recompile.
06467 */
06468 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06469 {
06470    if (rpc_is_remote())
06471       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06472 
06473 #ifdef LOCAL_ROUTINES
06474    {
06475       BUFFER *pbuf;
06476       BUFFER_HEADER *pheader;
06477       BUFFER_CLIENT *pclient, *pc;
06478       EVENT_REQUEST *prequest;
06479       EVENT_HEADER *pevent, *pevent_test;
06480       INT i, j, min_wp, size, total_size, status;
06481       INT increment;
06482       INT my_client_index;
06483       INT old_write_pointer;
06484       INT old_read_pointer, new_read_pointer;
06485       char *pdata;
06486       BOOL blocking;
06487       INT n_blocking;
06488       INT request_id;
06489       char str[80];
06490 
06491       pbuf = &_buffer[buffer_handle - 1];
06492 
06493       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06494          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06495          return BM_INVALID_HANDLE;
06496       }
06497 
06498       if (!pbuf->attached) {
06499          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06500          return BM_INVALID_HANDLE;
06501       }
06502 
06503       if (pbuf->write_cache_size == 0)
06504          return BM_SUCCESS;
06505 
06506       /* check if anything needs to be flushed */
06507       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06508          return BM_SUCCESS;
06509 
06510       /* calculate some shorthands */
06511       pheader = _buffer[buffer_handle - 1].buffer_header;
06512       pdata = (char *) (pheader + 1);
06513       my_client_index = _buffer[buffer_handle - 1].client_index;
06514       pclient = pheader->client;
06515       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06516 
06517       /* lock the buffer */
06518       bm_lock_buffer(buffer_handle);
06519 
06520 #ifdef DEBUG_MSG
06521       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer,
06522              pheader->write_pointer);
06523 #endif
06524 
06525       /* check if enough space in buffer left */
06526       do {
06527          size = pheader->read_pointer - pheader->write_pointer;
06528          if (size <= 0)
06529             size += pheader->size;
06530 
06531          if (size <= pbuf->write_cache_wp) {
06532             /* if not enough space, find out who's blocking */
06533             n_blocking = 0;
06534 
06535             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06536                if (pc->pid) {
06537                   if (pc->read_pointer == pheader->read_pointer) {
06538                      /*
06539                         First assume that the client with the "minimum" read pointer
06540                         is not really blocking due to a GET_ALL request.
06541                       */
06542                      blocking = FALSE;
06543                      request_id = -1;
06544 
06545                      /* check if this request blocks. */
06546                      prequest = pc->event_request;
06547                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06548 
06549                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06550                         if (prequest->valid &&
06551                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06552                                            pevent_test)) {
06553                            request_id = prequest->id;
06554                            if (prequest->sampling_type & GET_ALL) {
06555                               blocking = TRUE;
06556                               break;
06557                            }
06558                         }
06559 
06560                      if (!blocking) {
06561                         /*
06562                            The blocking guy has no GET_ALL request for this event
06563                            -> shift its read pointer.
06564                          */
06565 
06566                         old_read_pointer = pc->read_pointer;
06567 
06568                         increment = sizeof(EVENT_HEADER) +
06569                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06570 
06571                         /* correct increment for DWORD boundary */
06572                         increment = ALIGN8(increment);
06573 
06574                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06575 
06576                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06577                            new_read_pointer = 0;
06578 
06579 #ifdef DEBUG_MSG
06580                         cm_msg(MDEBUG, "bm_flush_cache: shift client %d rp=%d -> =%d", i,
06581                                pc->read_pointer, new_read_pointer);
06582 #endif
06583 
06584                         pc->read_pointer = new_read_pointer;
06585                      } else {
06586                         n_blocking++;
06587                      }
06588 
06589                      /* wake that client if it has a request */
06590                      if (pc->read_wait && request_id != -1) {
06591 #ifdef DEBUG_MSG
06592                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06593                                pheader->read_pointer, pheader->write_pointer);
06594 #endif
06595                         sprintf(str, "B %s %d", pheader->name, request_id);
06596                         ss_resume(pc->port, str);
06597                      }
06598 
06599 
06600                   }             /* read_pointer blocks */
06601                }
06602             /* client loop */
06603             if (n_blocking > 0) {
06604                /* at least one client is blocking */
06605 
06606                bm_unlock_buffer(buffer_handle);
06607 
06608                /* return now in ASYNC mode */
06609                if (async_flag)
06610                   return BM_ASYNC_RETURN;
06611 
06612 #ifdef DEBUG_MSG
06613                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06614                       pheader->read_pointer,
06615                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06616 #endif
06617 
06618                /* has the read pointer moved in between ? */
06619                size = pheader->read_pointer - pheader->write_pointer;
06620                if (size <= 0)
06621                   size += pheader->size;
06622 
06623                /* suspend process */
06624                if (size <= pbuf->write_cache_wp) {
06625                   /* signal other clients wait mode */
06626                   pclient[my_client_index].write_wait = pbuf->write_cache_wp;
06627 
06628                   status = ss_suspend(1000, MSG_BM);
06629 
06630                   pclient[my_client_index].write_wait = 0;
06631 
06632                   /* return if TCP connection broken */
06633                   if (status == SS_ABORT)
06634                      return SS_ABORT;
06635                }
06636 #ifdef DEBUG_MSG
06637                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06638                       pheader->read_pointer,
06639                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06640 #endif
06641 
06642                bm_lock_buffer(buffer_handle);
06643 
06644                /* has the write pointer moved in between ? */
06645                size = pheader->read_pointer - pheader->write_pointer;
06646                if (size <= 0)
06647                   size += pheader->size;
06648             } else {
06649                /*
06650                   calculate new global read pointer as "minimum" of
06651                   client read pointers
06652                 */
06653                min_wp = pheader->write_pointer;
06654 
06655                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06656                   if (pc->pid) {
06657                      if (pc->read_pointer < min_wp)
06658                         min_wp = pc->read_pointer;
06659 
06660                      if (pc->read_pointer > pheader->write_pointer &&
06661                          pc->read_pointer - pheader->size < min_wp)
06662                         min_wp = pc->read_pointer - pheader->size;
06663                   }
06664 
06665                if (min_wp < 0)
06666                   min_wp += pheader->size;
06667 
06668                pheader->read_pointer = min_wp;
06669             }
06670 
06671          }
06672          /* if (size <= total_size) */
06673       } while (size <= pbuf->write_cache_wp);
06674 
06675 
06676       /* we have space, so let's copy the event */
06677       old_write_pointer = pheader->write_pointer;
06678 
06679 #ifdef DEBUG_MSG
06680       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06681              pheader->write_pointer);
06682 #endif
06683 
06684       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06685          /* loop over all events in cache */
06686 
06687          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06688          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06689 
06690          /* correct size for DWORD boundary */
06691          total_size = ALIGN8(total_size);
06692 
06693          if (pheader->write_pointer + total_size <= pheader->size) {
06694             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06695             pheader->write_pointer = (pheader->write_pointer + total_size) %
06696                 pheader->size;
06697             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06698                pheader->write_pointer = 0;
06699          } else {
06700             /* split event */
06701             size = pheader->size - pheader->write_pointer;
06702 
06703             memcpy(pdata + pheader->write_pointer, pevent, size);
06704             memcpy(pdata, (char *) pevent + size, total_size - size);
06705 
06706             pheader->write_pointer = total_size - size;
06707          }
06708 
06709          pbuf->write_cache_rp += total_size;
06710       }
06711 
06712       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06713 
06714       /* check which clients are waiting */
06715       for (i = 0; i < pheader->max_client_index; i++)
06716          if (pclient[i].pid && pclient[i].read_wait) {
06717 #ifdef DEBUG_MSG
06718             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06719                    pheader->write_pointer);
06720 #endif
06721             sprintf(str, "B %s %d", pheader->name, -1);
06722             ss_resume(pclient[i].port, str);
06723          }
06724 
06725       /* shift read pointer of own client */
06726 /* 16.4.99 SR, outcommented to receive own messages
06727 
06728   if (pclient[my_client_index].read_pointer == old_write_pointer)
06729     pclient[my_client_index].read_pointer = pheader->write_pointer;
06730 */
06731 
06732       /* calculate global read pointer as "minimum" of client read pointers */
06733       min_wp = pheader->write_pointer;
06734 
06735       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06736          if (pc->pid) {
06737 #ifdef DEBUG_MSG
06738             cm_msg(MDEBUG, "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06739 #endif
06740             if (pc->read_pointer < min_wp)
06741                min_wp = pc->read_pointer;
06742 
06743             if (pc->read_pointer > pheader->write_pointer &&
06744                 pc->read_pointer - pheader->size < min_wp)
06745                min_wp = pc->read_pointer - pheader->size;
06746          }
06747 
06748       if (min_wp < 0)
06749          min_wp += pheader->size;
06750 
06751 #ifdef DEBUG_MSG
06752       if (min_wp == pheader->read_pointer)
06753          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06754       else
06755          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06756                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06757 #endif
06758 
06759       pheader->read_pointer = min_wp;
06760 
06761       /* update statistics */
06762       pheader->num_in_events++;
06763 
06764       /* unlock the buffer */
06765       bm_unlock_buffer(buffer_handle);
06766    }
06767 #endif                          /* LOCAL_ROUTINES */
06768 
06769    return BM_SUCCESS;
06770 }
06771 
06772 /********************************************************************/
06773 /**
06774 Receives events directly.
06775 This function is an alternative way to receive events without
06776 a main loop.
06777 
06778 It can be used in analysis systems which actively receive events,
06779 rather than using callbacks. A analysis package could for example contain its own
06780 command line interface. A command
06781 like "receive 1000 events" could make it necessary to call bm_receive_event()
06782 1000 times in a row to receive these events and then return back to the
06783 command line prompt.
06784 The according bm_request_event() call contains NULL as the
06785 callback routine to indicate that bm_receive_event() is called to receive
06786 events.
06787 \code
06788 #include <stdio.h>
06789 #include "midas.h"
06790 void process_event(EVENT_HEADER *pheader)
06791 {
06792  printf("Received event #%d\r",
06793  pheader->serial_number);
06794 }
06795 main()
06796 {
06797   INT status, request_id;
06798   HNDLE hbuf;
06799   char event_buffer[1000];
06800   status = cm_connect_experiment("", "Sample",
06801   "Simple Analyzer", NULL);
06802   if (status != CM_SUCCESS)
06803    return 1;
06804   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06805   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06806 
06807   do
06808   {
06809    size = sizeof(event_buffer);
06810    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06811   if (status == CM_SUCCESS)
06812    process_event((EVENT_HEADER *) event_buffer);
06813    <...do something else...>
06814    status = cm_yield(0);
06815   } while (status != RPC_SHUTDOWN &&
06816   status != SS_ABORT);
06817   cm_disconnect_experiment();
06818   return 0;
06819 }
06820 \endcode
06821 @param buffer_handle buffer handle
06822 @param destination destination address where event is written to
06823 @param buf_size size of destination buffer on input, size of event plus
06824 header on return.
06825 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06826 blocks if no event is available. If TRUE, the function returns immediately
06827 with a value of BM_ASYNC_RETURN without receiving any event.
06828 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06829 BM_TRUNCATED   The event is larger than the destination buffer and was
06830                therefore truncated <br>
06831 BM_ASYNC_RETURN No event available
06832 */
06833 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06834 {
06835    if (rpc_is_remote()) {
06836       if (*buf_size > NET_BUFFER_SIZE) {
06837          cm_msg(MERROR, "bm_receive_event",
06838                 "max. event size larger than NET_BUFFER_SIZE");
06839          return RPC_NET_ERROR;
06840       }
06841 
06842       return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06843                       destination, buf_size, async_flag);
06844    }
06845 #ifdef LOCAL_ROUTINES
06846    {
06847       BUFFER *pbuf;
06848       BUFFER_HEADER *pheader;
06849       BUFFER_CLIENT *pclient, *pc, *pctmp;
06850       EVENT_REQUEST *prequest;
06851       EVENT_HEADER *pevent;
06852       char *pdata;
06853       INT convert_flags;
06854       INT i, min_wp, size, max_size, total_size, status = 0;
06855       INT my_client_index;
06856       BOOL found;
06857       INT old_read_pointer, new_read_pointer;
06858 
06859       pbuf = &_buffer[buffer_handle - 1];
06860 
06861       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06862          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06863          return BM_INVALID_HANDLE;
06864       }
06865 
06866       if (!pbuf->attached) {
06867          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06868          return BM_INVALID_HANDLE;
06869       }
06870 
06871       max_size = *buf_size;
06872       *buf_size = 0;
06873 
06874       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06875          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06876       else
06877          convert_flags = 0;
06878 
06879     CACHE_READ:
06880 
06881       /* look if there is anything in the cache */
06882       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06883          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06884          size = pevent->data_size + sizeof(EVENT_HEADER);
06885 
06886          if (size > max_size) {
06887             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, max_size);
06888             cm_msg(MERROR, "bm_receive_event", "event size larger than buffer size");
06889             *buf_size = max_size;
06890             status = BM_TRUNCATED;
06891          } else {
06892             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06893             *buf_size = size;
06894             status = BM_SUCCESS;
06895          }
06896 
06897          /* now convert event header */
06898          if (convert_flags) {
06899             pevent = (EVENT_HEADER *) destination;
06900             rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06901             rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06902                                convert_flags);
06903             rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06904                                convert_flags);
06905             rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06906                                convert_flags);
06907             rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06908                                convert_flags);
06909          }
06910 
06911          /* correct size for DWORD boundary */
06912          size = ALIGN8(size);
06913 
06914          pbuf->read_cache_rp += size;
06915 
06916          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06917             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06918 
06919          return status;
06920       }
06921 
06922       /* calculate some shorthands */
06923       pheader = pbuf->buffer_header;
06924       pdata = (char *) (pheader + 1);
06925       my_client_index = pbuf->client_index;
06926       pclient = pheader->client;
06927       pc = pheader->client + my_client_index;
06928 
06929       /* first do a quick check without locking the buffer */
06930       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06931          return BM_ASYNC_RETURN;
06932 
06933       /* lock the buffer */
06934       bm_lock_buffer(buffer_handle);
06935 
06936     LOOP:
06937 
06938       while (pheader->write_pointer == pc->read_pointer) {
06939          bm_unlock_buffer(buffer_handle);
06940 
06941          /* return now in ASYNC mode */
06942          if (async_flag == ASYNC)
06943             return BM_ASYNC_RETURN;
06944 
06945          pc->read_wait = TRUE;
06946 
06947          /* check again pointers (may have moved in between) */
06948          if (pheader->write_pointer == pc->read_pointer) {
06949 #ifdef DEBUG_MSG
06950             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06951                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06952 #endif
06953 
06954             status = ss_suspend(1000, MSG_BM);
06955 
06956 #ifdef DEBUG_MSG
06957             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
06958                    pheader->read_pointer, pheader->write_pointer);
06959 #endif
06960 
06961             /* return if TCP connection broken */
06962             if (status == SS_ABORT)
06963                return SS_ABORT;
06964          }
06965 
06966          pc->read_wait = FALSE;
06967 
06968          bm_lock_buffer(buffer_handle);
06969       }
06970 
06971       /* check if event at current read pointer matches a request */
06972       found = FALSE;
06973 
06974       do {
06975          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06976 
06977          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06978          total_size = ALIGN8(total_size);
06979 
06980          prequest = pc->event_request;
06981 
06982          for (i = 0; i < pc->max_request_index; i++, prequest++)
06983             if (prequest->valid &&
06984                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06985                /* we found one, so copy it */
06986 
06987                if (pbuf->read_cache_size > 0 &&
06988                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06989                   if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
06990                      goto CACHE_FULL;
06991 
06992                   if (pc->read_pointer + total_size <= pheader->size) {
06993                      /* copy event to cache */
06994                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06995                   } else {
06996                      /* event is splitted */
06997                      size = pheader->size - pc->read_pointer;
06998                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06999                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
07000                             pdata, total_size - size);
07001                   }
07002                } else {
07003                   if (pc->read_pointer + total_size <= pheader->size) {
07004                      /* event is not splitted */
07005                      if (total_size > max_size)
07006                         memcpy(destination, pevent, max_size);
07007                      else
07008                         memcpy(destination, pevent, total_size);
07009                   } else {
07010                      /* event is splitted */
07011                      size = pheader->size - pc->read_pointer;
07012 
07013                      if (size > max_size)
07014                         memcpy(destination, pevent, max_size);
07015                      else
07016                         memcpy(destination, pevent, size);
07017 
07018                      if (total_size > max_size) {
07019                         if (size <= max_size)
07020                            memcpy((char *) destination + size, pdata, max_size - size);
07021                      } else
07022                         memcpy((char *) destination + size, pdata, total_size - size);
07023                   }
07024 
07025                   if (total_size < max_size)
07026                      *buf_size = total_size;
07027                   else
07028                      *buf_size = max_size;
07029 
07030                   /* now convert event header */
07031                   if (convert_flags) {
07032                      pevent = (EVENT_HEADER *) destination;
07033                      rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING,
07034                                         convert_flags);
07035                      rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
07036                                         convert_flags);
07037                      rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
07038                                         convert_flags);
07039                      rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
07040                                         convert_flags);
07041                      rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
07042                                         convert_flags);
07043                   }
07044                }
07045 
07046                if (pbuf->read_cache_size > 0 &&
07047                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07048                   pbuf->read_cache_wp += total_size;
07049                } else {
07050                   if (total_size > max_size) {
07051                      cm_msg(MERROR, "bm_receive_event",
07052                             "event size larger than buffer size");
07053                      status = BM_TRUNCATED;
07054                   } else
07055                      status = BM_SUCCESS;
07056                }
07057 
07058                /* update statistics */
07059                found = TRUE;
07060                pheader->num_out_events++;
07061                break;
07062             }
07063 
07064          old_read_pointer = pc->read_pointer;
07065 
07066          /* shift read pointer */
07067          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07068 
07069          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07070             new_read_pointer = 0;
07071 
07072 #ifdef DEBUG_MSG
07073          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07074                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
07075                 total_size);
07076 #endif
07077 
07078          pc->read_pointer = new_read_pointer;
07079 
07080          /*
07081             Repeat until a requested event is found or no more events
07082             are available.
07083           */
07084 
07085          if (pbuf->read_cache_size == 0 && found)
07086             break;
07087 
07088          /* break if event has bypassed read cache */
07089          if (pbuf->read_cache_size > 0 &&
07090              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
07091             break;
07092 
07093       } while (pheader->write_pointer != pc->read_pointer);
07094 
07095     CACHE_FULL:
07096 
07097       /* calculate global read pointer as "minimum" of client read pointers */
07098       min_wp = pheader->write_pointer;
07099 
07100       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07101          if (pctmp->pid) {
07102             if (pctmp->read_pointer < min_wp)
07103                min_wp = pctmp->read_pointer;
07104 
07105             if (pctmp->read_pointer > pheader->write_pointer &&
07106                 pctmp->read_pointer - pheader->size < min_wp)
07107                min_wp = pctmp->read_pointer - pheader->size;
07108          }
07109 
07110       if (min_wp < 0)
07111          min_wp += pheader->size;
07112 
07113       pheader->read_pointer = min_wp;
07114 
07115       /*
07116          If read pointer has been changed, it may have freed up some space
07117          for waiting producers. So check if free space is now more than 50%
07118          of the buffer size and wake waiting producers.
07119        */
07120       size = pc->read_pointer - pheader->write_pointer;
07121       if (size <= 0)
07122          size += pheader->size;
07123 
07124       if (size >= pheader->size * 0.5)
07125          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07126             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
07127                                                              (pctmp->pid == ss_getpid()
07128                                                               && pctmp->tid !=
07129                                                               ss_gettid()))) {
07130 #ifdef DEBUG_MSG
07131                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07132                       pheader->read_pointer,
07133                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
07134 #endif
07135                ss_resume(pctmp->port, "B  ");
07136             }
07137 
07138       /* if no matching event found, start again */
07139       if (!found)
07140          goto LOOP;
07141 
07142       bm_unlock_buffer(buffer_handle);
07143 
07144       if (pbuf->read_cache_size > 0 &&
07145           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
07146          goto CACHE_READ;
07147 
07148       return status;
07149    }
07150 #else                           /* LOCAL_ROUTINES */
07151 
07152    return SS_SUCCESS;
07153 #endif
07154 }
07155 
07156 /********************************************************************/
07157 /**
07158 Skip all events in current buffer.
07159 
07160 Useful for single event displays to see the newest events
07161 @param buffer_handle      Handle of the buffer. Must be obtained
07162                           via bm_open_buffer.
07163 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
07164 */
07165 INT bm_skip_event(INT buffer_handle)
07166 {
07167    if (rpc_is_remote())
07168       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
07169 
07170 #ifdef LOCAL_ROUTINES
07171    {
07172       BUFFER *pbuf;
07173       BUFFER_HEADER *pheader;
07174       BUFFER_CLIENT *pclient;
07175 
07176       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
07177          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07178          return BM_INVALID_HANDLE;
07179       }
07180 
07181       pbuf = &_buffer[buffer_handle - 1];
07182       pheader = pbuf->buffer_header;
07183 
07184       if (!pbuf->attached) {
07185          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07186          return BM_INVALID_HANDLE;
07187       }
07188 
07189       /* clear cache */
07190       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
07191          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07192 
07193       bm_lock_buffer(buffer_handle);
07194 
07195       /* forward read pointer to global write pointer */
07196       pclient = pheader->client + pbuf->client_index;
07197       pclient->read_pointer = pheader->write_pointer;
07198 
07199       bm_unlock_buffer(buffer_handle);
07200    }
07201 #endif
07202 
07203    return BM_SUCCESS;
07204 }
07205 
07206 /********************************************************************/
07207 /**
07208 Check a buffer if an event is available and call the dispatch function if found.
07209 @param buffer_name       Name of buffer
07210 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
07211                     RPC_NET_ERROR
07212 */
07213 INT bm_push_event(char *buffer_name)
07214 {
07215 #ifdef LOCAL_ROUTINES
07216    {
07217       BUFFER *pbuf;
07218       BUFFER_HEADER *pheader;
07219       BUFFER_CLIENT *pclient, *pc, *pctmp;
07220       EVENT_REQUEST *prequest;
07221       EVENT_HEADER *pevent;
07222       char *pdata;
07223       INT i, min_wp, size, total_size, buffer_handle;
07224       INT my_client_index;
07225       BOOL found;
07226       INT old_read_pointer, new_read_pointer;
07227 
07228       for (i = 0; i < _buffer_entries; i++)
07229          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07230             break;
07231       if (i == _buffer_entries)
07232          return BM_INVALID_HANDLE;
07233 
07234       buffer_handle = i + 1;
07235       pbuf = &_buffer[buffer_handle - 1];
07236 
07237       if (!pbuf->attached)
07238          return BM_INVALID_HANDLE;
07239 
07240       /* return immediately if no callback routine is defined */
07241       if (!pbuf->callback)
07242          return BM_SUCCESS;
07243 
07244       if (_event_buffer_size == 0) {
07245          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
07246          if (_event_buffer == NULL) {
07247             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
07248             return BM_NO_MEMORY;
07249          }
07250          _event_buffer_size = 1000;
07251       }
07252 
07253     CACHE_READ:
07254 
07255       /* look if there is anything in the cache */
07256       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
07257          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
07258          size = pevent->data_size + sizeof(EVENT_HEADER);
07259 
07260          /* correct size for DWORD boundary */
07261          size = ALIGN8(size);
07262 
07263          /* increment read pointer */
07264          pbuf->read_cache_rp += size;
07265          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
07266             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07267 
07268          /* call dispatcher */
07269          for (i = 0; i < _request_list_entries; i++)
07270             if (_request_list[i].buffer_handle == buffer_handle &&
07271                 bm_match_event(_request_list[i].event_id,
07272                                _request_list[i].trigger_mask, pevent)) {
07273                /* if event is fragmented, call defragmenter */
07274                if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
07275                    (pevent->event_id & 0xF000) == EVENTID_FRAG)
07276                   bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1),
07277                                       _request_list[i].dispatcher);
07278                else
07279                   _request_list[i].dispatcher(buffer_handle, i, pevent,
07280                                               (void *) (pevent + 1));
07281             }
07282 
07283          return BM_MORE_EVENTS;
07284       }
07285 
07286       /* calculate some shorthands */
07287       pheader = pbuf->buffer_header;
07288       pdata = (char *) (pheader + 1);
07289       my_client_index = pbuf->client_index;
07290       pclient = pheader->client;
07291       pc = pheader->client + my_client_index;
07292 
07293       /* first do a quick check without locking the buffer */
07294       if (pheader->write_pointer == pc->read_pointer)
07295          return BM_SUCCESS;
07296 
07297       /* lock the buffer */
07298       bm_lock_buffer(buffer_handle);
07299 
07300     LOOP:
07301 
07302       if (pheader->write_pointer == pc->read_pointer) {
07303          bm_unlock_buffer(buffer_handle);
07304 
07305          /* return if no event available */
07306          return BM_SUCCESS;
07307       }
07308 
07309       /* check if event at current read pointer matches a request */
07310       found = FALSE;
07311 
07312       do {
07313          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07314 
07315          total_size = pevent->data_size + sizeof(EVENT_HEADER);
07316          total_size = ALIGN8(total_size);
07317 
07318          prequest = pc->event_request;
07319 
07320          for (i = 0; i < pc->max_request_index; i++, prequest++)
07321             if (prequest->valid &&
07322                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
07323                /* we found one, so copy it */
07324 
07325                if (pbuf->read_cache_size > 0 &&
07326                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07327                   /* copy dispatch function and event to cache */
07328 
07329                   if (pbuf->read_cache_size - pbuf->read_cache_wp <
07330                       total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
07331                      goto CACHE_FULL;
07332 
07333                   if (pc->read_pointer + total_size <= pheader->size) {
07334                      /* copy event to cache */
07335                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
07336                   } else {
07337                      /* event is splitted */
07338 
07339                      size = pheader->size - pc->read_pointer;
07340                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07341                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
07342                             pdata, total_size - size);
07343                   }
07344 
07345                   pbuf->read_cache_wp += total_size;
07346                } else {
07347                   /* copy event to copy buffer, save dispatcher */
07348 
07349                   if (total_size > _event_buffer_size) {
07350                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
07351                      _event_buffer_size = total_size;
07352                   }
07353 
07354                   if (pc->read_pointer + total_size <= pheader->size) {
07355                      memcpy(_event_buffer, pevent, total_size);
07356                   } else {
07357                      /* event is splitted */
07358                      size = pheader->size - pc->read_pointer;
07359 
07360                      memcpy(_event_buffer, pevent, size);
07361                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
07362                   }
07363                }
07364 
07365                /* update statistics */
07366                found = TRUE;
07367                pheader->num_out_events++;
07368                break;
07369             }
07370 
07371          old_read_pointer = pc->read_pointer;
07372 
07373          /* shift read pointer */
07374          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07375 
07376          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07377             new_read_pointer = 0;
07378 
07379 #ifdef DEBUG_MSG
07380          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07381                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
07382                 total_size);
07383 #endif
07384 
07385          pc->read_pointer = new_read_pointer;
07386 
07387          /*
07388             Repeat until a requested event is found or no more events
07389             are available or large event received.
07390           */
07391 
07392          if (pbuf->read_cache_size == 0 && found)
07393             break;
07394 
07395          /* break if event has bypassed read cache */
07396          if (pbuf->read_cache_size > 0 &&
07397              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
07398             break;
07399 
07400       } while (pheader->write_pointer != pc->read_pointer);
07401 
07402     CACHE_FULL:
07403 
07404       /* calculate global read pointer as "minimum" of client read pointers */
07405       min_wp = pheader->write_pointer;
07406 
07407       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07408          if (pctmp->pid) {
07409             if (pctmp->read_pointer < min_wp)
07410                min_wp = pctmp->read_pointer;
07411 
07412             if (pctmp->read_pointer > pheader->write_pointer &&
07413                 pctmp->read_pointer - pheader->size < min_wp)
07414                min_wp = pctmp->read_pointer - pheader->size;
07415          }
07416 
07417       if (min_wp < 0)
07418          min_wp += pheader->size;
07419 
07420       pheader->read_pointer = min_wp;
07421 
07422       /*
07423          If read pointer has been changed, it may have freed up some space
07424          for waiting producers. So check if free space is now more than 50%
07425          of the buffer size and wake waiting producers.
07426        */
07427       size = pc->read_pointer - pheader->write_pointer;
07428       if (size <= 0)
07429          size += pheader->size;
07430 
07431       if (size >= pheader->size * 0.5)
07432          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07433             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
07434                                                              (pctmp->pid == ss_getpid()
07435                                                               && pctmp->tid !=
07436                                                               ss_gettid()))) {
07437 #ifdef DEBUG_MSG
07438                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07439                       pheader->read_pointer,
07440                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
07441 #endif
07442                ss_resume(pctmp->port, "B  ");
07443             }
07444 
07445       /* if no matching event found, start again */
07446       if (!found)
07447          goto LOOP;
07448 
07449       bm_unlock_buffer(buffer_handle);
07450 
07451       if (pbuf->read_cache_size > 0 &&
07452           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
07453          goto CACHE_READ;
07454 
07455       /* call dispatcher */
07456       for (i = 0; i < _request_list_entries; i++)
07457          if (_request_list[i].buffer_handle == buffer_handle &&
07458              bm_match_event(_request_list[i].event_id,
07459                             _request_list[i].trigger_mask, _event_buffer)) {
07460             if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07461                 (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07462                bm_defragment_event(buffer_handle, i, _event_buffer,
07463                                    (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07464                                    _request_list[i].dispatcher);
07465             else
07466                _request_list[i].dispatcher(buffer_handle, i, _event_buffer,
07467                                            (void *) (((EVENT_HEADER *) _event_buffer) +
07468                                                      1));
07469          }
07470 
07471       return BM_MORE_EVENTS;
07472    }
07473 #else                           /* LOCAL_ROUTINES */
07474 
07475    return BM_SUCCESS;
07476 #endif
07477 }
07478 
07479 /********************************************************************/
07480 /**
07481 Check if any requested event is waiting in a buffer
07482 @return TRUE             More events are waiting<br>
07483         FALSE            No more events are waiting
07484 */
07485 INT bm_check_buffers()
07486 {
07487 #ifdef LOCAL_ROUTINES
07488    {
07489       INT index, status = 0;
07490       INT server_type, server_conn, tid;
07491       BOOL bMore;
07492       DWORD start_time;
07493 
07494       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07495       server_conn = rpc_get_server_acception();
07496       tid = ss_gettid();
07497 
07498       /* if running as a server, buffer checking is done by client
07499          via ASYNC bm_receive_event */
07500       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
07501          return FALSE;
07502 
07503       bMore = FALSE;
07504       start_time = ss_millitime();
07505 
07506       /* go through all buffers */
07507       for (index = 0; index < _buffer_entries; index++) {
07508          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07509             continue;
07510 
07511          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07512             continue;
07513 
07514          if (!_buffer[index].attached)
07515             continue;
07516 
07517          do {
07518 
07519             /* one bm_push_event could cause a run stop and a buffer close, which
07520                would crash the next call to bm_push_event(). So check for valid
07521                buffer on each call */
07522             if (index < _buffer_entries && _buffer[index].buffer_header->name != NULL)
07523                status = bm_push_event(_buffer[index].buffer_header->name);
07524 
07525             if (status != BM_MORE_EVENTS)
07526                break;
07527 
07528             /* stop after one second */
07529             if (ss_millitime() - start_time > 1000) {
07530                bMore = TRUE;
07531                break;
07532             }
07533 
07534          } while (TRUE);
07535       }
07536 
07537       return bMore;
07538 
07539    }
07540 #else                           /* LOCAL_ROUTINES */
07541 
07542    return FALSE;
07543 
07544 #endif
07545 }
07546 
07547 /**dox***************************************************************/
07548 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07549 
07550 /********************************************************************/
07551 INT bm_mark_read_waiting(BOOL flag)
07552 /********************************************************************\
07553 
07554   Routine: bm_mark_read_waiting
07555 
07556   Purpose: Mark all open buffers ready for receiving events.
07557            Called internally by ss_suspend
07558 
07559 
07560   Input:
07561     BOOL flag               TRUE for waiting, FALSE for not waiting
07562 
07563   Output:
07564     none
07565 
07566   Function value:
07567     BM_SUCCESS              Successful completion
07568 
07569 \********************************************************************/
07570 {
07571    if (rpc_is_remote())
07572       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07573 
07574 #ifdef LOCAL_ROUTINES
07575    {
07576       INT i;
07577       BUFFER_HEADER *pheader;
07578       BUFFER_CLIENT *pclient;
07579 
07580       /* Mark all buffer for read waiting */
07581       for (i = 0; i < _buffer_entries; i++) {
07582          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
07583              _buffer[i].index != rpc_get_server_acception())
07584             continue;
07585 
07586          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
07587              _buffer[i].index != ss_gettid())
07588             continue;
07589 
07590          if (!_buffer[i].attached)
07591             continue;
07592 
07593          pheader = _buffer[i].buffer_header;
07594          pclient = pheader->client + _buffer[i].client_index;
07595          pclient->read_wait = flag;
07596       }
07597    }
07598 #endif                          /* LOCAL_ROUTINES */
07599 
07600    return BM_SUCCESS;
07601 }
07602 
07603 /********************************************************************/
07604 INT bm_notify_client(char *buffer_name, int socket)
07605 /********************************************************************\
07606 
07607   Routine: bm_notify_client
07608 
07609   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07610            the connected client
07611 
07612   Input:
07613     char  *buffer_name      Name of buffer
07614     int   socket            Network socket to client
07615 
07616   Output:
07617     none
07618 
07619   Function value:
07620     BM_SUCCESS              Successful completion
07621 
07622 \********************************************************************/
07623 {
07624    char buffer[32];
07625    NET_COMMAND *nc;
07626    INT i, convert_flags;
07627    static DWORD last_time = 0;
07628 
07629    for (i = 0; i < _buffer_entries; i++)
07630       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07631          break;
07632    if (i == _buffer_entries)
07633       return BM_INVALID_HANDLE;
07634 
07635    /* don't send notification if client has no callback defined
07636       to receive events -> client calls bm_receive_event manually */
07637    if (!_buffer[i].callback)
07638       return DB_SUCCESS;
07639 
07640    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07641 
07642    /* only send notification once each 500ms */
07643    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07644       return DB_SUCCESS;
07645 
07646    last_time = ss_millitime();
07647 
07648    if (convert_flags & CF_ASCII) {
07649       sprintf(buffer, "MSG_BM&%s", buffer_name);
07650       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
07651    } else {
07652       nc = (NET_COMMAND *) buffer;
07653 
07654       nc->header.routine_id = MSG_BM;
07655       nc->header.param_size = 0;
07656 
07657       if (convert_flags) {
07658          rpc_convert_single(&nc->header.routine_id, TID_DWORD,
07659                             RPC_OUTGOING, convert_flags);
07660          rpc_convert_single(&nc->header.param_size, TID_DWORD,
07661                             RPC_OUTGOING, convert_flags);
07662       }
07663 
07664       /* send the update notification to the client */
07665       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07666    }
07667 
07668    return BM_SUCCESS;
07669 }
07670 
07671 /********************************************************************/
07672 INT bm_poll_event(INT flag)
07673 /********************************************************************\
07674 
07675   Routine: bm_poll_event
07676 
07677   Purpose: Poll an event from a remote server. Gets called by
07678            rpc_client_dispatch
07679 
07680   Input:
07681     INT flag         TRUE if called from cm_yield
07682 
07683   Output:
07684     none
07685 
07686   Function value:
07687     TRUE             More events are waiting
07688     FALSE            No more events are waiting
07689     SS_ABORT         Network connection broken
07690 
07691 \********************************************************************/
07692 {
07693    INT status, size, i, request_id;
07694    DWORD start_time;
07695    BOOL bMore;
07696    static BOOL bMoreLast = FALSE;
07697 
07698    if (_event_buffer_size == 0) {
07699       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07700       if (!_event_buffer) {
07701          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
07702          return SS_ABORT;
07703       }
07704       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07705    }
07706 
07707    start_time = ss_millitime();
07708 
07709    /* if we got event notification, turn off read_wait */
07710    if (!flag)
07711       bm_mark_read_waiting(FALSE);
07712 
07713    /* if called from yield, return if no more events */
07714    if (flag) {
07715       if (!bMoreLast)
07716          return FALSE;
07717    }
07718 
07719    bMore = FALSE;
07720 
07721    /* loop over all requests */
07722    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07723       /* continue if no dispatcher set (manual bm_receive_event) */
07724       if (_request_list[request_id].dispatcher == NULL)
07725          continue;
07726 
07727       do {
07728          /* receive event */
07729          size = _event_buffer_size;
07730          status = bm_receive_event(_request_list[request_id].buffer_handle,
07731                                    _event_buffer, &size, ASYNC);
07732 
07733          /* call user function if successful */
07734          if (status == BM_SUCCESS)
07735             /* search proper request for this event */
07736             for (i = 0; i < _request_list_entries; i++)
07737                if ((_request_list[i].buffer_handle ==
07738                     _request_list[request_id].buffer_handle) &&
07739                    bm_match_event(_request_list[i].event_id,
07740                                   _request_list[i].trigger_mask, _event_buffer)) {
07741                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07742                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07743                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07744                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07745                                          _request_list[i].dispatcher);
07746                   else
07747                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07748                                                  _event_buffer,
07749                                                  (void
07750                                                   *) (((EVENT_HEADER *) _event_buffer) +
07751                                                       1));
07752                }
07753 
07754          /* break if no more events */
07755          if (status == BM_ASYNC_RETURN)
07756             break;
07757 
07758          /* break if server died */
07759          if (status == RPC_NET_ERROR)
07760             return SS_ABORT;
07761 
07762          /* stop after one second */
07763          if (ss_millitime() - start_time > 1000) {
07764             bMore = TRUE;
07765             break;
07766          }
07767 
07768       } while (TRUE);
07769    }
07770 
07771    if (!bMore)
07772       bm_mark_read_waiting(TRUE);
07773 
07774    bMoreLast = bMore;
07775 
07776    return bMore;
07777 }
07778 
07779 /**dox***************************************************************/
07780 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07781 
07782 /********************************************************************/
07783 /** 
07784 Clears event buffer and cache.
07785 If an event buffer is large and a consumer is slow in analyzing
07786 events, events are usually received some time after they are produced.
07787 This effect is even more experienced if a read cache is used
07788 (via bm_set_cache_size()).
07789 When changes to the hardware are made in the experience, the consumer will then
07790 still analyze old events before any new event which reflects the hardware change.
07791 Users can be fooled by looking at histograms which reflect the hardware change
07792 many seconds after they have been made.
07793 
07794 To overcome this potential problem, the analyzer can call
07795 bm_empty_buffers() just after the hardware change has been made which
07796 skips all old events contained in event buffers and read caches.
07797 Technically this is done by forwarding the read pointer of the client.
07798 No events are really deleted, they are still visible to other clients like
07799 the logger.
07800 
07801 Note that the front-end also contains write buffers which can delay the
07802 delivery of events.
07803 The standard front-end framework mfe.c reduces this effect by flushing
07804 all buffers once every second.
07805 @return BM_SUCCESS
07806 */
07807 INT bm_empty_buffers()
07808 {
07809    if (rpc_is_remote())
07810       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07811 
07812 #ifdef LOCAL_ROUTINES
07813    {
07814       INT index, server_type, server_conn, tid;
07815       BUFFER *pbuf;
07816       BUFFER_CLIENT *pclient;
07817 
07818       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07819       server_conn = rpc_get_server_acception();
07820       tid = ss_gettid();
07821 
07822       /* go through all buffers */
07823       for (index = 0; index < _buffer_entries; index++) {
07824          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07825             continue;
07826 
07827          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07828             continue;
07829 
07830          if (!_buffer[index].attached)
07831             continue;
07832 
07833          pbuf = &_buffer[index];
07834 
07835          /* empty cache */
07836          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07837 
07838          /* set read pointer to write pointer */
07839          pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07840          bm_lock_buffer(index + 1);
07841          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07842          bm_unlock_buffer(index + 1);
07843       }
07844 
07845    }
07846 #endif                          /* LOCAL_ROUTINES */
07847 
07848    return BM_SUCCESS;
07849 }
07850 
07851 /**dox***************************************************************/
07852 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07853 
07854 #define MAX_DEFRAG_EVENTS 10
07855 
07856 typedef struct {
07857    WORD event_id;
07858    DWORD data_size;
07859    DWORD received;
07860    EVENT_HEADER *pevent;
07861 } EVENT_DEFRAG_BUFFER;
07862 
07863 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07864 
07865 /********************************************************************/
07866 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07867                          EVENT_HEADER * pevent, void *pdata,
07868                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07869 /********************************************************************\
07870 
07871   Routine: bm_defragment_event
07872 
07873   Purpose: Called internally from the event receiving routines
07874            bm_push_event and bm_poll_event to recombine event
07875            fragments and call the user callback routine upon
07876            completion.
07877 
07878   Input:
07879     HNDLE buffer_handle  Handle for the buffer containing event
07880     HNDLE request_id     Handle for event request
07881     EVENT_HEADER *pevent Pointer to event header
07882     void *pata           Pointer to event data
07883     dispatcher()         User callback routine
07884 
07885   Output:
07886     <calls dispatcher() after successfull recombination of event>
07887 
07888   Function value:
07889     void
07890 
07891 \********************************************************************/
07892 {
07893    INT i;
07894    static int j=-1;
07895    
07896    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07897     /*---- start new event ----*/
07898 
07899       printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
07900       /* check if fragments already stored */
07901       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07902          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07903             break;
07904 
07905       if (i < MAX_DEFRAG_EVENTS) {
07906          free(defrag_buffer[i].pevent);
07907          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07908          cm_msg(MERROR, "bm_defragement_event",
07909                 "Received new event with ID %d while old fragments were not completed",
07910                 (pevent->event_id & 0x0FFF));
07911       }
07912 
07913       /* search new slot */
07914       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07915          if (defrag_buffer[i].event_id == 0)
07916             break;
07917 
07918       if (i == MAX_DEFRAG_EVENTS) {
07919          cm_msg(MERROR, "bm_defragment_event",
07920                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07921          return;
07922       }
07923 
07924       /* check event size */
07925       if (pevent->data_size != sizeof(DWORD)) {
07926          cm_msg(MERROR, "bm_defragment_event",
07927                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07928                 pevent->data_size, sizeof(DWORD));
07929          return;
07930       }
07931 
07932       /* setup defragment buffer */
07933       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07934       defrag_buffer[i].data_size = *(DWORD *) pdata;
07935       defrag_buffer[i].received = 0;
07936       defrag_buffer[i].pevent =
07937           (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07938 
07939       if (defrag_buffer[i].pevent == NULL) {
07940          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07941          cm_msg(MERROR, "bm_defragement_event",
07942                 "Not enough memory to allocate event defragment buffer");
07943          return;
07944       }
07945 
07946       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07947       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07948       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07949 
07950       printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n"
07951              , i, defrag_buffer[i].event_id
07952              , pevent->serial_number
07953              , defrag_buffer[i].data_size);
07954       j=0;
07955       
07956       return;
07957    }
07958 
07959    /* search buffer for that event */
07960    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07961       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07962          break;
07963 
07964    if (i == MAX_DEFRAG_EVENTS) {
07965       /* no buffer available -> no first fragment received */
07966       free(defrag_buffer[i].pevent);
07967       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07968       cm_msg(MERROR, "bm_defragement_event",
07969              "Received fragment without first fragment (ID %d) Ser#:%d",
07970              pevent->event_id & 0x0FFF, pevent->serial_number );
07971       printf("Received fragment without first fragment (ID 0x%x) Ser#:%d Sz:%d\n"
07972              , pevent->event_id
07973              , pevent->serial_number
07974              , pevent->data_size );
07975       return;
07976    }
07977 
07978    /* add fragment to buffer */
07979    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07980       free(defrag_buffer[i].pevent);
07981       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07982       cm_msg(MERROR, "bm_defragement_event",
07983              "Received fragments with more data (%d) than event size (%d)"
07984              , pevent->data_size + defrag_buffer[i].received
07985              , defrag_buffer[i].data_size);
07986       return;
07987    }
07988 
07989    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07990           defrag_buffer[i].received, pdata, pevent->data_size);
07991 
07992    defrag_buffer[i].received += pevent->data_size;
07993    
07994    printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n"
07995           , i, j++, defrag_buffer[i].event_id
07996           , pevent->serial_number
07997           , pevent->data_size);
07998    
07999    
08000    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
08001      /* event complete */
08002      dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent,
08003                 defrag_buffer[i].pevent + 1);
08004       free(defrag_buffer[i].pevent);
08005       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
08006    }
08007 }
08008 
08009 /**dox***************************************************************/
08010 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08011 
08012 /**dox***************************************************************/
08013 /** @} */ /* end of bmfunctionc */
08014 
08015 /**dox***************************************************************/
08016 /** @addtogroup rpcfunctionc
08017  *  
08018  *  @{  */
08019 
08020 /**dox***************************************************************/
08021 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08022 
08023 /********************************************************************\
08024 *                                                                    *
08025 *                         RPC functions                              *
08026 *                                                                    *
08027 \********************************************************************/
08028 
08029 /* globals */
08030 
08031 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
08032 RPC_SERVER_CONNECTION _server_connection;
08033 
08034 static int _lsock;
08035 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
08036 static INT _server_acception_index = 0;
08037 static INT _server_type;
08038 static char _server_name[256];
08039 
08040 static RPC_LIST *rpc_list = NULL;
08041 
08042 int _opt_tcp_size = OPT_TCP_SIZE;
08043 
08044 
08045 /********************************************************************\
08046 *                       conversion functions                         *
08047 \********************************************************************/
08048 
08049 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
08050 {
08051    *convert_flags = 0;
08052 
08053    /* big/little endian conversion */
08054    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
08055         (hw_type & DRI_LITTLE_ENDIAN)) ||
08056        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
08057       *convert_flags |= CF_ENDIAN;
08058 
08059    /* float conversion between IEEE and VAX G */
08060    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
08061       *convert_flags |= CF_VAX2IEEE;
08062 
08063    /* float conversion between VAX G and IEEE */
08064    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
08065       *convert_flags |= CF_IEEE2VAX;
08066 
08067    /* ASCII format */
08068    if (remote_hw_type & DR_ASCII)
08069       *convert_flags |= CF_ASCII;
08070 }
08071 
08072 /********************************************************************/
08073 void rpc_get_convert_flags(INT * convert_flags)
08074 {
08075    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
08076                           _server_connection.remote_hw_type, convert_flags);
08077 }
08078 
08079 /********************************************************************/
08080 void rpc_ieee2vax_float(float *var)
08081 {
08082    unsigned short int lo, hi;
08083 
08084    /* swap hi and lo word */
08085    lo = *((short int *) (var) + 1);
08086    hi = *((short int *) (var));
08087 
08088    /* correct exponent */
08089    if (lo != 0)
08090       lo += 0x100;
08091 
08092    *((short int *) (var) + 1) = hi;
08093    *((short int *) (var)) = lo;
08094 }
08095 
08096 void rpc_vax2ieee_float(float *var)
08097 {
08098    unsigned short int lo, hi;
08099 
08100    /* swap hi and lo word */
08101    lo = *((short int *) (var) + 1);
08102    hi = *((short int *) (var));
08103 
08104    /* correct exponent */
08105    if (hi != 0)
08106       hi -= 0x100;
08107 
08108    *((short int *) (var) + 1) = hi;
08109    *((short int *) (var)) = lo;
08110 
08111 }
08112 
08113 void rpc_vax2ieee_double(double *var)
08114 {
08115    unsigned short int i1, i2, i3, i4;
08116 
08117    /* swap words */
08118    i1 = *((short int *) (var) + 3);
08119    i2 = *((short int *) (var) + 2);
08120    i3 = *((short int *) (var) + 1);
08121    i4 = *((short int *) (var));
08122 
08123    /* correct exponent */
08124    if (i4 != 0)
08125       i4 -= 0x20;
08126 
08127    *((short int *) (var) + 3) = i4;
08128    *((short int *) (var) + 2) = i3;
08129    *((short int *) (var) + 1) = i2;
08130    *((short int *) (var)) = i1;
08131 }
08132 
08133 void rpc_ieee2vax_double(double *var)
08134 {
08135    unsigned short int i1, i2, i3, i4;
08136 
08137    /* swap words */
08138    i1 = *((short int *) (var) + 3);
08139    i2 = *((short int *) (var) + 2);
08140    i3 = *((short int *) (var) + 1);
08141    i4 = *((short int *) (var));
08142 
08143    /* correct exponent */
08144    if (i1 != 0)
08145       i1 += 0x20;
08146 
08147    *((short int *) (var) + 3) = i4;
08148    *((short int *) (var) + 2) = i3;
08149    *((short int *) (var) + 1) = i2;
08150    *((short int *) (var)) = i1;
08151 }
08152 
08153 /********************************************************************/
08154 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
08155 {
08156 
08157    if (convert_flags & CF_ENDIAN) {
08158       if (tid == TID_WORD || tid == TID_SHORT)
08159          WORD_SWAP(data);
08160       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
08161          DWORD_SWAP(data);
08162       if (tid == TID_DOUBLE)
08163          QWORD_SWAP(data);
08164    }
08165 
08166    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
08167        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
08168       if (tid == TID_FLOAT)
08169          rpc_ieee2vax_float((float *) data);
08170       if (tid == TID_DOUBLE)
08171          rpc_ieee2vax_double((double *) data);
08172    }
08173 
08174    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
08175        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
08176       if (tid == TID_FLOAT)
08177          rpc_vax2ieee_float((float *) data);
08178       if (tid == TID_DOUBLE)
08179          rpc_vax2ieee_double((double *) data);
08180    }
08181 }
08182 
08183 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
08184 /********************************************************************\
08185 
08186   Routine: rpc_convert_data
08187 
08188   Purpose: Convert data format between differenct computers
08189 
08190   Input:
08191     void   *data            Pointer to data
08192     INT    tid              Type ID of data, one of TID_xxx
08193     INT    flags            Combination of following flags:
08194                               RPC_IN: data is input parameter
08195                               RPC_OUT: data is output variable
08196                               RPC_FIXARRAY, RPC_VARARRAY: data is array
08197                                 of "size" bytes (see next param.)
08198                               RPC_OUTGOING: data is outgoing
08199     INT    total_size       Size of bytes of data. Used for variable
08200                             length arrays.
08201     INT    convert_flags    Flags for data conversion
08202 
08203   Output:
08204     void   *data            Is converted according to _convert_flag
08205                             value
08206 
08207   Function value:
08208     RPC_SUCCESS             Successful completion
08209 
08210 \********************************************************************/
08211 {
08212    INT i, n, single_size;
08213    char *p;
08214 
08215    /* convert array */
08216    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
08217       single_size = tid_size[tid];
08218       /* don't convert TID_ARRAY & TID_STRUCT */
08219       if (single_size == 0)
08220          return;
08221 
08222       n = total_size / single_size;
08223 
08224       for (i = 0; i < n; i++) {
08225          p = (char *) data + (i * single_size);
08226          rpc_convert_single(p, tid, flags, convert_flags);
08227       }
08228    } else {
08229       rpc_convert_single(data, tid, flags, convert_flags);
08230    }
08231 }
08232 
08233 /********************************************************************\
08234 *                       type ID functions                            *
08235 \********************************************************************/
08236 
08237 INT rpc_tid_size(INT id)
08238 {
08239    if (id < TID_LAST)
08240       return tid_size[id];
08241 
08242    return 0;
08243 }
08244 
08245 char *rpc_tid_name(INT id)
08246 {
08247    if (id < TID_LAST)
08248       return tid_name[id];
08249    else
08250       return "<unknown>";
08251 }
08252 
08253 
08254 /**dox***************************************************************/
08255 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08256 
08257 /********************************************************************\
08258 *                        client functions                            *
08259 \********************************************************************/
08260 
08261 /********************************************************************/
08262 /**
08263 Register RPC client for standalone mode (without standard
08264            midas server)
08265 @param list           Array of RPC_LIST structures containing
08266                             function IDs and parameter definitions.
08267                             The end of the list must be indicated by
08268                             a function ID of zero.
08269 @param name          Name of this client
08270 @return RPC_SUCCESS
08271 */
08272 INT rpc_register_client(char *name, RPC_LIST * list)
08273 {
08274    rpc_set_name(name);
08275    rpc_register_functions(rpc_get_internal_list(0), NULL);
08276    rpc_register_functions(list, NULL);
08277 
08278    return RPC_SUCCESS;
08279 }
08280 
08281 /********************************************************************/
08282 /**
08283 Register a set of RPC functions (both as clients or servers)
08284 @param new_list       Array of RPC_LIST structures containing
08285                             function IDs and parameter definitions.
08286                             The end of the list must be indicated by
08287                             a function ID of zero.
08288 @param func          Default dispatch function
08289 
08290 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
08291 */
08292 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
08293 {
08294    INT i, j, iold, inew;
08295 
08296    /* count number of new functions */
08297    for (i = 0; new_list[i].id != 0; i++) {
08298       /* check double defined functions */
08299       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
08300          if (rpc_list[j].id == new_list[i].id)
08301             return RPC_DOUBLE_DEFINED;
08302    }
08303    inew = i;
08304 
08305    /* count number of existing functions */
08306    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
08307    iold = i;
08308 
08309    /* allocate new memory for rpc_list */
08310    if (rpc_list == NULL)
08311       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
08312    else
08313       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
08314 
08315    if (rpc_list == NULL) {
08316       cm_msg(MERROR, "rpc_register_functions", "out of memory");
08317       return RPC_NO_MEMORY;
08318    }
08319 
08320    /* append new functions */
08321    for (i = iold; i < iold + inew; i++) {
08322       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
08323 
08324       /* set default dispatcher */
08325       if (rpc_list[i].dispatch == NULL)
08326          rpc_list[i].dispatch = func;
08327 
08328       /* check valid ID for user functions */
08329       if (new_list != rpc_get_internal_list(0) &&
08330           new_list != rpc_get_internal_list(1) &&
08331           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
08332          cm_msg(MERROR, "rpc_register_functions",
08333                 "registered RPC function with invalid ID");
08334    }
08335 
08336    /* mark end of list */
08337    rpc_list[i].id = 0;
08338 
08339    return RPC_SUCCESS;
08340 }
08341 
08342 
08343 
08344 /**dox***************************************************************/
08345 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08346 
08347 /********************************************************************/
08348 INT rpc_deregister_functions()
08349 /********************************************************************\
08350 
08351   Routine: rpc_deregister_functions
08352 
08353   Purpose: Free memory of previously registered functions
08354 
08355   Input:
08356     none
08357 
08358   Output:
08359     none
08360 
08361   Function value:
08362     RPC_SUCCESS              Successful completion
08363 
08364 \********************************************************************/
08365 {
08366    if (rpc_list)
08367       M_FREE(rpc_list);
08368    rpc_list = NULL;
08369 
08370    return RPC_SUCCESS;
08371 }
08372 
08373 
08374 /********************************************************************/
08375 INT rpc_register_function(INT id, INT(*func) (INT, void **))
08376 /********************************************************************\
08377 
08378   Routine: rpc_register_function
08379 
08380   Purpose: Replace a dispatch function for a specific rpc routine
08381 
08382   Input:
08383     INT      id             RPC ID
08384     INT      *func          New dispatch function
08385 
08386   Output:
08387    <implicit: func gets copied to rpc_list>
08388 
08389   Function value:
08390    RPC_SUCCESS              Successful completion
08391    RPC_INVALID_ID           RPC ID not found
08392 
08393 \********************************************************************/
08394 {
08395    INT i;
08396 
08397    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
08398       if (rpc_list[i].id == id)
08399          break;
08400 
08401    if (rpc_list[i].id == id)
08402       rpc_list[i].dispatch = func;
08403    else
08404       return RPC_INVALID_ID;
08405 
08406    return RPC_SUCCESS;
08407 }
08408 
08409 
08410 /********************************************************************/
08411 INT rpc_client_dispatch(int sock)
08412 /********************************************************************\
08413 
08414   Routine: rpc_client_dispatch
08415 
08416   Purpose: Gets called whenever a client receives data from the
08417            server. Get set via rpc_connect. Internal use only.
08418 
08419 \********************************************************************/
08420 {
08421    INT hDB, hKey, n;
08422    NET_COMMAND *nc;
08423    INT status = 0;
08424    char net_buffer[256];
08425 
08426    nc = (NET_COMMAND *) net_buffer;
08427 
08428    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08429    if (n <= 0)
08430       return SS_ABORT;
08431 
08432    if (nc->header.routine_id == MSG_ODB) {
08433       /* update a changed record */
08434       hDB = *((INT *) nc->param);
08435       hKey = *((INT *) nc->param + 1);
08436       status = db_update_record(hDB, hKey, 0);
08437    }
08438 
08439    else if (nc->header.routine_id == MSG_WATCHDOG) {
08440       nc->header.routine_id = 1;
08441       nc->header.param_size = 0;
08442       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08443       status = RPC_SUCCESS;
08444    }
08445 
08446    else if (nc->header.routine_id == MSG_BM) {
08447       fd_set readfds;
08448       struct timeval timeout;
08449 
08450       /* receive further messages to empty TCP queue */
08451       do {
08452          FD_ZERO(&readfds);
08453          FD_SET(sock, &readfds);
08454 
08455          timeout.tv_sec = 0;
08456          timeout.tv_usec = 0;
08457 
08458          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08459 
08460          if (FD_ISSET(sock, &readfds)) {
08461             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08462             if (n <= 0)
08463                return SS_ABORT;
08464 
08465             if (nc->header.routine_id == MSG_ODB) {
08466                /* update a changed record */
08467                hDB = *((INT *) nc->param);
08468                hKey = *((INT *) nc->param + 1);
08469                status = db_update_record(hDB, hKey, 0);
08470             }
08471 
08472             else if (nc->header.routine_id == MSG_WATCHDOG) {
08473                nc->header.routine_id = 1;
08474                nc->header.param_size = 0;
08475                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08476                status = RPC_SUCCESS;
08477             }
08478          }
08479 
08480       } while (FD_ISSET(sock, &readfds));
08481 
08482       /* poll event from server */
08483       status = bm_poll_event(FALSE);
08484    }
08485 
08486    return status;
08487 }
08488 
08489 
08490 /********************************************************************/
08491 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
08492 /********************************************************************\
08493 
08494   Routine: rpc_client_connect
08495 
08496   Purpose: Establish a network connection to a remote client
08497 
08498   Input:
08499     char *host_name          IP address of host to connect to.
08500     INT  port                TPC port to connect to.
08501     char *clinet_name        Client program name
08502 
08503   Output:
08504     HNDLE *hConnection       Handle for new connection which can be used
08505                              in future rpc_call(hConnection....) calls
08506 
08507   Function value:
08508     RPC_SUCCESS              Successful completion
08509     RPC_NET_ERROR            Error in socket call
08510     RPC_NO_CONNECTION        Maximum number of connections reached
08511     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08512 
08513 \********************************************************************/
08514 {
08515    INT i, status, index;
08516    struct sockaddr_in bind_addr;
08517    INT sock;
08518    INT remote_hw_type, hw_type;
08519    char str[200];
08520    char version[32], v1[32];
08521    char local_prog_name[NAME_LENGTH];
08522    char local_host_name[HOST_NAME_LENGTH];
08523    struct hostent *phe;
08524 
08525 #ifdef OS_WINNT
08526    {
08527       WSADATA WSAData;
08528 
08529       /* Start windows sockets */
08530       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08531          return RPC_NET_ERROR;
08532    }
08533 #endif
08534 
08535    /* check if cm_connect_experiment was called */
08536    if (_client_name[0] == 0) {
08537       cm_msg(MERROR, "rpc_client_connect",
08538              "cm_connect_experiment/rpc_set_name not called");
08539       return RPC_NOT_REGISTERED;
08540    }
08541 
08542    /* check for broken connections */
08543    rpc_client_check();
08544 
08545    /* check if connection already exists */
08546    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08547       if (_client_connection[i].send_sock != 0 &&
08548           strcmp(_client_connection[i].host_name, host_name) == 0 &&
08549           _client_connection[i].port == port) {
08550          *hConnection = i + 1;
08551          return RPC_SUCCESS;
08552       }
08553 
08554    /* search for free entry */
08555    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08556       if (_client_connection[i].send_sock == 0)
08557          break;
08558 
08559    /* open new network connection */
08560    if (i == MAX_RPC_CONNECTION) {
08561       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
08562       return RPC_NO_CONNECTION;
08563    }
08564 
08565    /* create a new socket for connecting to remote server */
08566    sock = socket(AF_INET, SOCK_STREAM, 0);
08567    if (sock == -1) {
08568       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08569       return RPC_NET_ERROR;
08570    }
08571 
08572    index = i;
08573    strcpy(_client_connection[index].host_name, host_name);
08574    strcpy(_client_connection[index].client_name, client_name);
08575    _client_connection[index].port = port;
08576    _client_connection[index].exp_name[0] = 0;
08577    _client_connection[index].transport = RPC_TCP;
08578    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08579    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08580 
08581    /* connect to remote node */
08582    memset(&bind_addr, 0, sizeof(bind_addr));
08583    bind_addr.sin_family = AF_INET;
08584    bind_addr.sin_addr.s_addr = 0;
08585    bind_addr.sin_port = htons((short) port);
08586 
08587 #ifdef OS_VXWORKS
08588    {
08589       INT host_addr;
08590 
08591       host_addr = hostGetByName(host_name);
08592       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08593    }
08594 #else
08595    phe = gethostbyname(host_name);
08596    if (phe == NULL) {
08597       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08598       return RPC_NET_ERROR;
08599    }
08600    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08601 #endif
08602 
08603 #ifdef OS_UNIX
08604    do {
08605       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08606 
08607       /* don't return if an alarm signal was cought */
08608    } while (status == -1 && errno == EINTR);
08609 #else
08610    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08611 #endif
08612 
08613    if (status != 0) {
08614       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08615          message should be displayed by application */
08616       return RPC_NET_ERROR;
08617    }
08618 
08619    /* set TCP_NODELAY option for better performance */
08620 #ifdef OS_VXWORKS
08621    i = 1;
08622    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08623 #endif
08624 
08625    /* send local computer info */
08626    rpc_get_name(local_prog_name);
08627    gethostname(local_host_name, sizeof(local_host_name));
08628 
08629    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08630    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name,
08631            local_host_name);
08632 
08633    send(sock, str, strlen(str) + 1, 0);
08634 
08635    /* receive remote computer info */
08636    i = recv_string(sock, str, sizeof(str), 10000);
08637    if (i <= 0) {
08638       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s",
08639              str);
08640       return RPC_NET_ERROR;
08641    }
08642 
08643    remote_hw_type = version[0] = 0;
08644    sscanf(str, "%d %s", &remote_hw_type, version);
08645    _client_connection[index].remote_hw_type = remote_hw_type;
08646    _client_connection[index].send_sock = sock;
08647 
08648    /* print warning if version patch level doesn't agree */
08649    strcpy(v1, version);
08650    if (strchr(v1, '.'))
08651       if (strchr(strchr(v1, '.') + 1, '.'))
08652          *strchr(strchr(v1, '.') + 1, '.') = 0;
08653 
08654    strcpy(str, cm_get_version());
08655    if (strchr(str, '.'))
08656       if (strchr(strchr(str, '.') + 1, '.'))
08657          *strchr(strchr(str, '.') + 1, '.') = 0;
08658 
08659    if (strcmp(v1, str) != 0) {
08660       sprintf(str, "remote MIDAS version %s differs from local version %s",
08661               version, cm_get_version());
08662       cm_msg(MERROR, "rpc_client_connect", str);
08663    }
08664 
08665    *hConnection = index + 1;
08666 
08667    return RPC_SUCCESS;
08668 }
08669 
08670 
08671 /********************************************************************/
08672 void rpc_client_check()
08673 /********************************************************************\
08674 
08675   Routine: rpc_client_check
08676 
08677   Purpose: Check all client connections if remote client closed link
08678 
08679   Function value:
08680     RPC_SUCCESS              Successful completion
08681     RPC_NET_ERROR            Error in socket call
08682     RPC_NO_CONNECTION        Maximum number of connections reached
08683     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08684 
08685 \********************************************************************/
08686 {
08687    INT i, status;
08688 
08689    /* check for broken connections */
08690    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08691       if (_client_connection[i].send_sock != 0) {
08692          int sock;
08693          fd_set readfds;
08694          struct timeval timeout;
08695          char buffer[64];
08696 
08697          sock = _client_connection[i].send_sock;
08698          FD_ZERO(&readfds);
08699          FD_SET(sock, &readfds);
08700 
08701          timeout.tv_sec = 0;
08702          timeout.tv_usec = 0;
08703 
08704          do {
08705             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08706          } while (status == -1);        /* dont return if an alarm signal was cought */
08707 
08708          if (FD_ISSET(sock, &readfds)) {
08709             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08710 
08711             if (equal_ustring(buffer, "EXIT")) {
08712                /* normal exit */
08713                closesocket(sock);
08714                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08715             }
08716 
08717             if (status <= 0) {
08718                cm_msg(MERROR, "rpc_client_check",
08719                       "Connection broken to \"%s\" on host %s",
08720                       _client_connection[i].client_name, _client_connection[i].host_name);
08721 
08722                /* connection broken -> reset */
08723                closesocket(sock);
08724                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08725             }
08726          }
08727       }
08728 }
08729 
08730 
08731 /********************************************************************/
08732 INT rpc_server_connect(char *host_name, char *exp_name)
08733 /********************************************************************\
08734 
08735   Routine: rpc_server_connect
08736 
08737   Purpose: Extablish a network connection to a remote MIDAS
08738            server using a callback scheme.
08739 
08740   Input:
08741     char *host_name         IP address of host to connect to.
08742 
08743     INT  port               TPC port to connect to.
08744 
08745     char *exp_name          Name of experiment to connect to. By using
08746                             this name, several experiments (e.g. online
08747                             DAQ and offline analysis) can run simultan-
08748                             eously on the same host.
08749 
08750   Output:
08751     none
08752 
08753   Function value:
08754     RPC_SUCCESS              Successful completion
08755     RPC_NET_ERROR            Error in socket call
08756     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08757     CM_UNDEF_EXP             Undefined experiment on server
08758 
08759 \********************************************************************/
08760 {
08761    INT i, status, flag;
08762    struct sockaddr_in bind_addr;
08763    INT sock, lsock1, lsock2, lsock3;
08764    INT listen_port1, listen_port2, listen_port3;
08765    INT remote_hw_type, hw_type;
08766    int size;
08767    char str[200], version[32], v1[32];
08768    char local_prog_name[NAME_LENGTH];
08769    struct hostent *phe;
08770 
08771 #ifdef OS_WINNT
08772    {
08773       WSADATA WSAData;
08774 
08775       /* Start windows sockets */
08776       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08777          return RPC_NET_ERROR;
08778    }
08779 #endif
08780 
08781    /* check if local connection */
08782    if (host_name[0] == 0)
08783       return RPC_SUCCESS;
08784 
08785    /* register system functions */
08786    rpc_register_functions(rpc_get_internal_list(0), NULL);
08787 
08788    /* check if cm_connect_experiment was called */
08789    if (_client_name[0] == 0) {
08790       cm_msg(MERROR, "rpc_server_connect",
08791              "cm_connect_experiment/rpc_set_name not called");
08792       return RPC_NOT_REGISTERED;
08793    }
08794 
08795    /* check if connection already exists */
08796    if (_server_connection.send_sock != 0)
08797       return RPC_SUCCESS;
08798 
08799    strcpy(_server_connection.host_name, host_name);
08800    strcpy(_server_connection.exp_name, exp_name);
08801    _server_connection.transport = RPC_TCP;
08802    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08803 
08804    /* create new TCP sockets for listening */
08805    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08806    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08807    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08808    if (lsock3 == -1) {
08809       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08810       return RPC_NET_ERROR;
08811    }
08812 
08813    flag = 1;
08814    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08815    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08816    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08817 
08818    /* let OS choose any port number */
08819    memset(&bind_addr, 0, sizeof(bind_addr));
08820    bind_addr.sin_family = AF_INET;
08821    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08822    bind_addr.sin_port = 0;
08823 
08824    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08825    bind_addr.sin_port = 0;
08826    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08827    bind_addr.sin_port = 0;
08828    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08829    if (status < 0) {
08830       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08831       return RPC_NET_ERROR;
08832    }
08833 
08834    /* listen for connection */
08835    status = listen(lsock1, 1);
08836    status = listen(lsock2, 1);
08837    status = listen(lsock3, 1);
08838    if (status < 0) {
08839       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08840       return RPC_NET_ERROR;
08841    }
08842 
08843    /* find out which port OS has chosen */
08844    size = sizeof(bind_addr);
08845    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08846    listen_port1 = ntohs(bind_addr.sin_port);
08847    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08848    listen_port2 = ntohs(bind_addr.sin_port);
08849    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08850    listen_port3 = ntohs(bind_addr.sin_port);
08851 
08852    /* create a new socket for connecting to remote server */
08853    sock = socket(AF_INET, SOCK_STREAM, 0);
08854    if (sock == -1) {
08855       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08856       return RPC_NET_ERROR;
08857    }
08858 
08859    /* connect to remote node */
08860    memset(&bind_addr, 0, sizeof(bind_addr));
08861    bind_addr.sin_family = AF_INET;
08862    bind_addr.sin_addr.s_addr = 0;
08863    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08864 
08865 #ifdef OS_VXWORKS
08866    {
08867       INT host_addr;
08868 
08869       host_addr = hostGetByName(host_name);
08870       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08871    }
08872 #else
08873    phe = gethostbyname(host_name);
08874    if (phe == NULL) {
08875       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08876       return RPC_NET_ERROR;
08877    }
08878    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08879 #endif
08880 
08881 #ifdef OS_UNIX
08882    do {
08883       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08884 
08885       /* don't return if an alarm signal was cought */
08886    } while (status == -1 && errno == EINTR);
08887 #else
08888    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08889 #endif
08890 
08891    if (status != 0) {
08892 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08893       return RPC_NET_ERROR;
08894    }
08895 
08896    /* connect to experiment */
08897    if (exp_name[0] == 0)
08898       sprintf(str, "C %d %d %d %s Default",
08899               listen_port1, listen_port2, listen_port3, cm_get_version());
08900    else
08901       sprintf(str, "C %d %d %d %s %s",
08902               listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08903 
08904    send(sock, str, strlen(str) + 1, 0);
08905    i = recv_string(sock, str, sizeof(str), 10000);
08906    closesocket(sock);
08907    if (i <= 0) {
08908       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08909       return RPC_NET_ERROR;
08910    }
08911 
08912    status = version[0] = 0;
08913    sscanf(str, "%d %s", &status, version);
08914 
08915    if (status == 2) {
08916 /*  message "undefined experiment" should be displayed by application */
08917       return CM_UNDEF_EXP;
08918    }
08919 
08920    /* print warning if version patch level doesn't agree */
08921    strcpy(v1, version);
08922    if (strchr(v1, '.'))
08923       if (strchr(strchr(v1, '.') + 1, '.'))
08924          *strchr(strchr(v1, '.') + 1, '.') = 0;
08925 
08926    strcpy(str, cm_get_version());
08927    if (strchr(str, '.'))
08928       if (strchr(strchr(str, '.') + 1, '.'))
08929          *strchr(strchr(str, '.') + 1, '.') = 0;
08930 
08931    if (strcmp(v1, str) != 0) {
08932       sprintf(str, "remote MIDAS version %s differs from local version %s",
08933               version, cm_get_version());
08934       cm_msg(MERROR, "rpc_server_connect", str);
08935    }
08936 
08937    /* wait for callback on send and recv socket */
08938    size = sizeof(bind_addr);
08939    _server_connection.send_sock =
08940        accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08941 
08942    _server_connection.recv_sock =
08943        accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08944 
08945    _server_connection.event_sock =
08946        accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08947 
08948    if (_server_connection.send_sock == -1 ||
08949        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08950       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08951       return RPC_NET_ERROR;
08952    }
08953 
08954    closesocket(lsock1);
08955    closesocket(lsock2);
08956    closesocket(lsock3);
08957 
08958    /* set TCP_NODELAY option for better performance */
08959 #ifdef OS_VXWORKS
08960    flag = 1;
08961    setsockopt(_server_connection.send_sock,
08962               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08963    setsockopt(_server_connection.event_sock,
08964               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08965 #endif
08966 
08967    /* increase send buffer size to 64kB */
08968    flag = 0x10000;
08969    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
08970               (char *) &flag, sizeof(flag));
08971 
08972    /* send local computer info */
08973    rpc_get_name(local_prog_name);
08974    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08975    sprintf(str, "%d %s", hw_type, local_prog_name);
08976 
08977    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08978 
08979    /* receive remote computer info */
08980    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08981    if (i <= 0) {
08982       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08983       return RPC_NET_ERROR;
08984    }
08985 
08986    sscanf(str, "%d", &remote_hw_type);
08987    _server_connection.remote_hw_type = remote_hw_type;
08988 
08989    /* set dispatcher which receives database updates */
08990    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
08991                            (int (*)(void)) rpc_client_dispatch);
08992 
08993    return RPC_SUCCESS;
08994 }
08995 
08996 
08997 /********************************************************************/
08998 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08999 /********************************************************************\
09000 
09001   Routine: rpc_client_disconnect
09002 
09003   Purpose: Close a rpc connection to a MIDAS client
09004 
09005   Input:
09006     HNDLE  hConn           Handle of connection
09007     BOOL   bShutdown       Shut down remote server if TRUE
09008 
09009   Output:
09010     none
09011 
09012   Function value:
09013    RPC_SUCCESS             Successful completion
09014 
09015 \********************************************************************/
09016 {
09017    INT i;
09018 
09019    if (hConn == -1) {
09020       /* close all open connections */
09021       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
09022          if (_client_connection[i].send_sock != 0)
09023             rpc_client_disconnect(i + 1, FALSE);
09024 
09025       /* close server connection from other clients */
09026       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09027          if (_server_acception[i].recv_sock) {
09028             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
09029             closesocket(_server_acception[i].recv_sock);
09030          }
09031    } else {
09032       /* notify server about exit */
09033 
09034       /* set FTCP mode (helps for rebooted VxWorks nodes) */
09035       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
09036       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
09037 
09038       /* close socket */
09039       if (_client_connection[hConn - 1].send_sock)
09040          closesocket(_client_connection[hConn - 1].send_sock);
09041 
09042       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
09043    }
09044 
09045    return RPC_SUCCESS;
09046 }
09047 
09048 
09049 /********************************************************************/
09050 INT rpc_server_disconnect()
09051 /********************************************************************\
09052 
09053   Routine: rpc_server_disconnect
09054 
09055   Purpose: Close a rpc connection to a MIDAS server and close all
09056            server connections from other clients
09057 
09058   Input:
09059     none
09060 
09061   Output:
09062     none
09063 
09064   Function value:
09065    RPC_SUCCESS             Successful completion
09066    RPC_NET_ERROR           Error in socket call
09067    RPC_NO_CONNECTION       Maximum number of connections reached
09068 
09069 \********************************************************************/
09070 {
09071    static int rpc_server_disconnect_recursion_level = 0;
09072 
09073    if (rpc_server_disconnect_recursion_level)
09074       return RPC_SUCCESS;
09075 
09076    rpc_server_disconnect_recursion_level = 1;
09077 
09078    /* flush remaining events */
09079    rpc_flush_event();
09080 
09081    /* notify server about exit */
09082    rpc_call(RPC_ID_EXIT);
09083 
09084    /* close sockets */
09085    closesocket(_server_connection.send_sock);
09086    closesocket(_server_connection.recv_sock);
09087    closesocket(_server_connection.event_sock);
09088 
09089    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
09090 
09091    rpc_server_disconnect_recursion_level = 0;
09092    return RPC_SUCCESS;
09093 }
09094 
09095 
09096 /********************************************************************/
09097 INT rpc_is_remote(void)
09098 /********************************************************************\
09099 
09100   Routine: rpc_is_remote
09101 
09102   Purpose: Return true if program is connected to a remote server
09103 
09104   Input:
09105    none
09106 
09107   Output:
09108     none
09109 
09110   Function value:
09111     INT    RPC connection index
09112 
09113 \********************************************************************/
09114 {
09115    return _server_connection.send_sock != 0;
09116 }
09117 
09118 
09119 /********************************************************************/
09120 INT rpc_get_server_acception(void)
09121 /********************************************************************\
09122 
09123   Routine: rpc_get_server_acception
09124 
09125   Purpose: Return actual RPC server connection index
09126 
09127   Input:
09128    none
09129 
09130   Output:
09131     none
09132 
09133   Function value:
09134     INT    RPC server connection index
09135 
09136 \********************************************************************/
09137 {
09138    return _server_acception_index;
09139 }
09140 
09141 
09142 /********************************************************************/
09143 INT rpc_set_server_acception(INT index)
09144 /********************************************************************\
09145 
09146   Routine: rpc_set_server_acception
09147 
09148   Purpose: Set actual RPC server connection index
09149 
09150   Input:
09151     INT  index              Server index
09152 
09153   Output:
09154     none
09155 
09156   Function value:
09157     RPC_SUCCESS             Successful completion
09158 
09159 \********************************************************************/
09160 {
09161    _server_acception_index = index;
09162    return RPC_SUCCESS;
09163 }
09164 
09165 
09166 /********************************************************************/
09167 INT rpc_get_option(HNDLE hConn, INT item)
09168 /********************************************************************\
09169 
09170   Routine: rpc_get_option
09171 
09172   Purpose: Get actual RPC option
09173 
09174   Input:
09175     HNDLE hConn             RPC connection handle
09176     INT   item              One of RPC_Oxxx
09177 
09178   Output:
09179     none
09180 
09181   Function value:
09182     INT                     Actual option
09183 
09184 \********************************************************************/
09185 {
09186    switch (item) {
09187    case RPC_OTIMEOUT:
09188       if (hConn == -1)
09189          return _server_connection.rpc_timeout;
09190       return _client_connection[hConn - 1].rpc_timeout;
09191 
09192    case RPC_OTRANSPORT:
09193       if (hConn == -1)
09194          return _server_connection.transport;
09195       return _client_connection[hConn - 1].transport;
09196 
09197    case RPC_OHW_TYPE:
09198       {
09199          INT tmp_type, size;
09200          DWORD dummy;
09201          unsigned char *p;
09202          float f;
09203          double d;
09204 
09205          tmp_type = 0;
09206 
09207          /* test pointer size */
09208          size = sizeof(p);
09209          if (size == 2)
09210             tmp_type |= DRI_16;
09211          if (size == 4)
09212             tmp_type |= DRI_32;
09213          if (size == 8)
09214             tmp_type |= DRI_64;
09215 
09216          /* test if little or big endian machine */
09217          dummy = 0x12345678;
09218          p = (unsigned char *) &dummy;
09219          if (*p == 0x78)
09220             tmp_type |= DRI_LITTLE_ENDIAN;
09221          else if (*p == 0x12)
09222             tmp_type |= DRI_BIG_ENDIAN;
09223          else
09224             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
09225 
09226          /* floating point format */
09227          f = (float) 1.2345;
09228          dummy = 0;
09229          memcpy(&dummy, &f, sizeof(f));
09230          if ((dummy & 0xFF) == 0x19 &&
09231              ((dummy >> 8) & 0xFF) == 0x04 &&
09232              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
09233             tmp_type |= DRF_IEEE;
09234          else if ((dummy & 0xFF) == 0x9E &&
09235                   ((dummy >> 8) & 0xFF) == 0x40 &&
09236                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
09237             tmp_type |= DRF_G_FLOAT;
09238          else
09239             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09240 
09241          d = (double) 1.2345;
09242          dummy = 0;
09243          memcpy(&dummy, &d, sizeof(f));
09244          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
09245              ((dummy >> 8) & 0xFF) == 0x97 &&
09246              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
09247             tmp_type |= DRF_IEEE;
09248          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
09249                   ((dummy >> 8) & 0xFF) == 0xC0 &&
09250                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
09251             tmp_type |= DRF_IEEE;
09252          else if ((dummy & 0xFF) == 0x13 &&
09253                   ((dummy >> 8) & 0xFF) == 0x40 &&
09254                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
09255             tmp_type |= DRF_G_FLOAT;
09256          else if ((dummy & 0xFF) == 0x9E &&
09257                   ((dummy >> 8) & 0xFF) == 0x40 &&
09258                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
09259             cm_msg(MERROR, "rpc_get_option",
09260                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
09261          else
09262             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09263 
09264          return tmp_type;
09265       }
09266 
09267    default:
09268       cm_msg(MERROR, "rpc_get_option", "invalid argument");
09269       break;
09270    }
09271 
09272    return 0;
09273 }
09274 
09275 /**dox***************************************************************/
09276 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09277 
09278 /********************************************************************/
09279 /**
09280 Set RPC option
09281 @param hConn              RPC connection handle
09282 @param item               One of RPC_Oxxx
09283 @param value              Value to set
09284 @return RPC_SUCCESS
09285 */
09286 INT rpc_set_option(HNDLE hConn, INT item, INT value)
09287 {
09288    switch (item) {
09289    case RPC_OTIMEOUT:
09290       if (hConn == -1)
09291          _server_connection.rpc_timeout = value;
09292       else
09293          _client_connection[hConn - 1].rpc_timeout = value;
09294       break;
09295 
09296    case RPC_OTRANSPORT:
09297       if (hConn == -1)
09298          _server_connection.transport = value;
09299       else
09300          _client_connection[hConn - 1].transport = value;
09301       break;
09302 
09303    case RPC_NODELAY:
09304       if (hConn == -1)
09305          setsockopt(_server_connection.send_sock, IPPROTO_TCP,
09306                     TCP_NODELAY, (char *) &value, sizeof(value));
09307       else
09308          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
09309                     TCP_NODELAY, (char *) &value, sizeof(value));
09310       break;
09311 
09312    default:
09313       cm_msg(MERROR, "rpc_set_option", "invalid argument");
09314       break;
09315    }
09316 
09317    return 0;
09318 }
09319 
09320 
09321 /**dox***************************************************************/
09322 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09323 
09324 /********************************************************************/
09325 PTYPE rpc_get_server_option(INT item)
09326 /********************************************************************\
09327 
09328   Routine: rpc_get_server_option
09329 
09330   Purpose: Get actual RPC option for server connection
09331 
09332   Input:
09333     INT  item               One of RPC_Oxxx
09334 
09335   Output:
09336     none
09337 
09338   Function value:
09339     INT                     Actual option
09340 
09341 \********************************************************************/
09342 {
09343    INT i;
09344 
09345    if (item == RPC_OSERVER_TYPE)
09346       return _server_type;
09347 
09348    if (item == RPC_OSERVER_NAME)
09349       return (PTYPE) _server_name;
09350 
09351    /* return 0 for local calls */
09352    if (_server_type == ST_NONE)
09353       return 0;
09354 
09355    /* check which connections belongs to caller */
09356    if (_server_type == ST_MTHREAD) {
09357       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09358          if (_server_acception[i].tid == ss_gettid())
09359             break;
09360    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09361       i = MAX(0, _server_acception_index - 1);
09362    else
09363       i = 0;
09364 
09365    switch (item) {
09366    case RPC_CONVERT_FLAGS:
09367       return _server_acception[i].convert_flags;
09368    case RPC_ODB_HANDLE:
09369       return _server_acception[i].odb_handle;
09370    case RPC_CLIENT_HANDLE:
09371       return _server_acception[i].client_handle;
09372    case RPC_SEND_SOCK:
09373       return _server_acception[i].send_sock;
09374    case RPC_WATCHDOG_TIMEOUT:
09375       return _server_acception[i].watchdog_timeout;
09376    }
09377 
09378    return 0;
09379 }
09380 
09381 
09382 /********************************************************************/
09383 INT rpc_set_server_option(INT item, PTYPE value)
09384 /********************************************************************\
09385 
09386   Routine: rpc_set_server_option
09387 
09388   Purpose: Set RPC option for server connection
09389 
09390   Input:
09391    INT  item               One of RPC_Oxxx
09392    INT  value              Value to set
09393 
09394   Output:
09395     none
09396 
09397   Function value:
09398     RPC_SUCCESS             Successful completion
09399 
09400 \********************************************************************/
09401 {
09402    INT i;
09403 
09404    if (item == RPC_OSERVER_TYPE) {
09405       _server_type = value;
09406       return RPC_SUCCESS;
09407    }
09408    if (item == RPC_OSERVER_NAME) {
09409       strcpy(_server_name, (char *) value);
09410       return RPC_SUCCESS;
09411    }
09412 
09413    /* check which connections belongs to caller */
09414    if (_server_type == ST_MTHREAD) {
09415       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09416          if (_server_acception[i].tid == ss_gettid())
09417             break;
09418    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09419       i = MAX(0, _server_acception_index - 1);
09420    else
09421       i = 0;
09422 
09423    switch (item) {
09424    case RPC_CONVERT_FLAGS:
09425       _server_acception[i].convert_flags = value;
09426       break;
09427    case RPC_ODB_HANDLE:
09428       _server_acception[i].odb_handle = value;
09429       break;
09430    case RPC_CLIENT_HANDLE:
09431       _server_acception[i].client_handle = value;
09432       break;
09433    case RPC_WATCHDOG_TIMEOUT:
09434       _server_acception[i].watchdog_timeout = value;
09435       break;
09436    }
09437 
09438    return RPC_SUCCESS;
09439 }
09440 
09441 
09442 /********************************************************************/
09443 INT rpc_get_name(char *name)
09444 /********************************************************************\
09445 
09446   Routine: rpc_get_name
09447 
09448   Purpose: Get name set by rpc_set_name
09449 
09450   Input:
09451     none
09452 
09453   Output:
09454     char*  name             The location pointed by *name receives a
09455                             copy of the _prog_name
09456 
09457   Function value:
09458     RPC_SUCCESS             Successful completion
09459 
09460 \********************************************************************/
09461 {
09462    strcpy(name, _client_name);
09463 
09464    return RPC_SUCCESS;
09465 }
09466 
09467 
09468 /********************************************************************/
09469 INT rpc_set_name(char *name)
09470 /********************************************************************\
09471 
09472   Routine: rpc_set_name
09473 
09474   Purpose: Set name of actual program for further rpc connections
09475 
09476   Input:
09477    char *name               Program name, up to NAME_LENGTH chars,
09478                             no blanks
09479 
09480   Output:
09481     none
09482 
09483   Function value:
09484     RPC_SUCCESS             Successful completion
09485 
09486 \********************************************************************/
09487 {
09488    strcpy(_client_name, name);
09489 
09490    return RPC_SUCCESS;
09491 }
09492 
09493 
09494 /********************************************************************/
09495 INT rpc_set_debug(void (*func) (char *), INT mode)
09496 /********************************************************************\
09497 
09498   Routine: rpc_set_debug
09499 
09500   Purpose: Set a function which is called on every RPC call to
09501            display the function name and parameters of the RPC
09502            call.
09503 
09504   Input:
09505    void *func(char*)        Pointer to function.
09506    INT  mode                Debug mode
09507 
09508   Output:
09509     none
09510 
09511   Function value:
09512     RPC_SUCCESS             Successful completion
09513 
09514 \********************************************************************/
09515 {
09516    _debug_print = func;
09517    _debug_mode = mode;
09518    return RPC_SUCCESS;
09519 }
09520 
09521 /********************************************************************/
09522 void rpc_debug_printf(char *format, ...)
09523 /********************************************************************\
09524 
09525   Routine: rpc_debug_print
09526 
09527   Purpose: Calls function set via rpc_set_debug to output a string.
09528 
09529   Input:
09530    char *str                Debug string
09531 
09532   Output:
09533     none
09534 
09535 \********************************************************************/
09536 {
09537    va_list argptr;
09538    char str[1000];
09539 
09540    if (_debug_mode) {
09541       va_start(argptr, format);
09542       vsprintf(str, (char *) format, argptr);
09543       va_end(argptr);
09544 
09545       if (_debug_print) {
09546          strcat(str, "\n");
09547          _debug_print(str);
09548       } else
09549          puts(str);
09550    }
09551 }
09552 
09553 /********************************************************************/
09554 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09555 {
09556    switch (arg_type) {
09557       /* On the stack, the minimum parameter size is sizeof(int).
09558          To avoid problems on little endian systems, treat all
09559          smaller parameters as int's */
09560    case TID_BYTE:
09561    case TID_SBYTE:
09562    case TID_CHAR:
09563    case TID_WORD:
09564    case TID_SHORT:
09565       *((int *) arg) = va_arg(*arg_ptr, int);
09566       break;
09567 
09568    case TID_INT:
09569    case TID_BOOL:
09570       *((INT *) arg) = va_arg(*arg_ptr, INT);
09571       break;
09572 
09573    case TID_DWORD:
09574       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09575       break;
09576 
09577       /* float variables are passed as double by the compiler */
09578    case TID_FLOAT:
09579       *((float *) arg) = (float) va_arg(*arg_ptr, double);
09580       break;
09581 
09582    case TID_DOUBLE:
09583       *((double *) arg) = va_arg(*arg_ptr, double);
09584       break;
09585 
09586    case TID_ARRAY:
09587       *((char **) arg) = va_arg(*arg_ptr, char *);
09588       break;
09589    }
09590 }
09591 
09592 
09593 /********************************************************************/
09594 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09595 /********************************************************************\
09596 
09597   Routine: rpc_client_call
09598 
09599   Purpose: Call a function on a MIDAS client
09600 
09601   Input:
09602     INT  hConn              Client connection
09603     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09604 
09605     ...                     variable argument list
09606 
09607   Output:
09608     (depends on argument list)
09609 
09610   Function value:
09611     RPC_SUCCESS             Successful completion
09612     RPC_NET_ERROR           Error in socket call
09613     RPC_NO_CONNECTION       No active connection
09614     RPC_TIMEOUT             Timeout in RPC call
09615     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09616     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09617 
09618 \********************************************************************/
09619 {
09620    va_list ap, aptmp;
09621    char arg[8], arg_tmp[8];
09622    INT arg_type, transport, rpc_timeout;
09623    INT i, index, status, rpc_index;
09624    INT param_size, arg_size, send_size;
09625    INT tid, flags;
09626    fd_set readfds;
09627    struct timeval timeout;
09628    char *param_ptr, str[80];
09629    BOOL bpointer, bbig;
09630    NET_COMMAND *nc;
09631    int send_sock;
09632 
09633    index = hConn - 1;
09634 
09635    if (_client_connection[index].send_sock == 0) {
09636       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09637       return RPC_NO_CONNECTION;
09638    }
09639 
09640    send_sock = _client_connection[index].send_sock;
09641    rpc_timeout = _client_connection[index].rpc_timeout;
09642    transport = _client_connection[index].transport;
09643 
09644    /* init network buffer */
09645    if (_net_send_buffer_size == 0) {
09646       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09647       if (_net_send_buffer == NULL) {
09648          cm_msg(MERROR, "rpc_client_call",
09649                 "not enough memory to allocate network buffer");
09650          return RPC_EXCEED_BUFFER;
09651       }
09652       _net_send_buffer_size = NET_BUFFER_SIZE;
09653    }
09654 
09655    nc = (NET_COMMAND *) _net_send_buffer;
09656    nc->header.routine_id = routine_id;
09657 
09658    if (transport == RPC_FTCP)
09659       nc->header.routine_id |= TCP_FAST;
09660 
09661    for (i = 0;; i++)
09662       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09663          break;
09664    rpc_index = i;
09665    if (rpc_list[i].id == 0) {
09666       sprintf(str, "invalid rpc ID (%d)", routine_id);
09667       cm_msg(MERROR, "rpc_client_call", str);
09668       return RPC_INVALID_ID;
09669    }
09670 
09671    /* examine variable argument list and convert it to parameter array */
09672    va_start(ap, routine_id);
09673 
09674    /* find out if we are on a big endian system */
09675    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09676 
09677    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09678       tid = rpc_list[rpc_index].param[i].tid;
09679       flags = rpc_list[rpc_index].param[i].flags;
09680 
09681       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09682           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09683           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09684 
09685       if (bpointer)
09686          arg_type = TID_ARRAY;
09687       else
09688          arg_type = tid;
09689 
09690       /* floats are passed as doubles, at least under NT */
09691       if (tid == TID_FLOAT && !bpointer)
09692          arg_type = TID_DOUBLE;
09693 
09694       /* get pointer to argument */
09695       rpc_va_arg(&ap, arg_type, arg);
09696 
09697       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09698       if (bbig) {
09699          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09700             arg[0] = arg[3];
09701          }
09702          if (tid == TID_WORD || tid == TID_SHORT) {
09703             arg[0] = arg[2];
09704             arg[1] = arg[3];
09705          }
09706       }
09707 
09708       if (flags & RPC_IN) {
09709          if (bpointer)
09710             arg_size = tid_size[tid];
09711          else
09712             arg_size = tid_size[arg_type];
09713 
09714          /* for strings, the argument size depends on the string length */
09715          if (tid == TID_STRING || tid == TID_LINK)
09716             arg_size = 1 + strlen((char *) *((char **) arg));
09717 
09718          /* for varibale length arrays, the size is given by
09719             the next parameter on the stack */
09720          if (flags & RPC_VARARRAY) {
09721             memcpy(&aptmp, &ap, sizeof(ap));
09722             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09723 
09724             if (flags & RPC_OUT)
09725                arg_size = *((INT *) * ((void **) arg_tmp));
09726             else
09727                arg_size = *((INT *) arg_tmp);
09728 
09729             *((INT *) param_ptr) = ALIGN8(arg_size);
09730             param_ptr += ALIGN8(sizeof(INT));
09731          }
09732 
09733          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09734             arg_size = rpc_list[rpc_index].param[i].n;
09735 
09736          /* always align parameter size */
09737          param_size = ALIGN8(arg_size);
09738 
09739          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09740             cm_msg(MERROR, "rpc_client_call",
09741                    "parameters (%d) too large for network buffer (%d)",
09742                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09743             return RPC_EXCEED_BUFFER;
09744          }
09745 
09746          if (bpointer)
09747             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09748          else {
09749             /* floats are passed as doubles on most systems */
09750             if (tid != TID_FLOAT)
09751                memcpy(param_ptr, arg, arg_size);
09752             else
09753                *((float *) param_ptr) = (float) *((double *) arg);
09754          }
09755 
09756          param_ptr += param_size;
09757 
09758       }
09759    }
09760 
09761    va_end(ap);
09762 
09763    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09764 
09765    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09766 
09767    /* in FAST TCP mode, only send call and return immediately */
09768    if (transport == RPC_FTCP) {
09769       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09770 
09771       if (i != send_size) {
09772          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09773          return RPC_NET_ERROR;
09774       }
09775 
09776       return RPC_SUCCESS;
09777    }
09778 
09779    /* in TCP mode, send and wait for reply on send socket */
09780    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09781    if (i != send_size) {
09782       cm_msg(MERROR, "rpc_client_call",
09783              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09784              rpc_list[rpc_index].name, _client_connection[index].host_name);
09785       return RPC_NET_ERROR;
09786    }
09787 
09788    /* make some timeout checking */
09789    if (rpc_timeout > 0) {
09790       FD_ZERO(&readfds);
09791       FD_SET(send_sock, &readfds);
09792 
09793       timeout.tv_sec = rpc_timeout / 1000;
09794       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09795 
09796       do {
09797          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09798 
09799          /* if an alarm signal was cought, restart select with reduced timeout */
09800          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09801             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09802 
09803       } while (status == -1);   /* dont return if an alarm signal was cought */
09804 
09805       if (!FD_ISSET(send_sock, &readfds)) {
09806          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09807                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09808 
09809          /* disconnect to avoid that the reply to this rpc_call comes at
09810             the next rpc_call */
09811          rpc_client_disconnect(hConn, FALSE);
09812 
09813          return RPC_TIMEOUT;
09814       }
09815    }
09816 
09817    /* receive result on send socket */
09818    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09819 
09820    if (i <= 0) {
09821       cm_msg(MERROR, "rpc_client_call",
09822              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09823              rpc_list[rpc_index].name, _client_connection[index].host_name);
09824       return RPC_NET_ERROR;
09825    }
09826 
09827    /* extract result variables and place it to argument list */
09828    status = nc->header.routine_id;
09829 
09830    va_start(ap, routine_id);
09831 
09832    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09833       tid = rpc_list[rpc_index].param[i].tid;
09834       flags = rpc_list[rpc_index].param[i].flags;
09835 
09836       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09837           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09838           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09839 
09840       if (bpointer)
09841          arg_type = TID_ARRAY;
09842       else
09843          arg_type = rpc_list[rpc_index].param[i].tid;
09844 
09845       if (tid == TID_FLOAT && !bpointer)
09846          arg_type = TID_DOUBLE;
09847 
09848       rpc_va_arg(&ap, arg_type, arg);
09849 
09850       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09851          tid = rpc_list[rpc_index].param[i].tid;
09852          flags = rpc_list[rpc_index].param[i].flags;
09853 
09854          arg_size = tid_size[tid];
09855 
09856          if (tid == TID_STRING || tid == TID_LINK)
09857             arg_size = strlen((char *) (param_ptr)) + 1;
09858 
09859          if (flags & RPC_VARARRAY) {
09860             arg_size = *((INT *) param_ptr);
09861             param_ptr += ALIGN8(sizeof(INT));
09862          }
09863 
09864          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09865             arg_size = rpc_list[rpc_index].param[i].n;
09866 
09867          /* return parameters are always pointers */
09868          if (*((char **) arg))
09869             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09870 
09871          /* parameter size is always aligned */
09872          param_size = ALIGN8(arg_size);
09873 
09874          param_ptr += param_size;
09875       }
09876    }
09877 
09878    va_end(ap);
09879 
09880    return status;
09881 }
09882 
09883 
09884 /********************************************************************/
09885 INT rpc_call(const INT routine_id, ...)
09886 /********************************************************************\
09887 
09888   Routine: rpc_call
09889 
09890   Purpose: Call a function on a MIDAS server
09891 
09892   Input:
09893     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09894 
09895     ...                     variable argument list
09896 
09897   Output:
09898     (depends on argument list)
09899 
09900   Function value:
09901     RPC_SUCCESS             Successful completion
09902     RPC_NET_ERROR           Error in socket call
09903     RPC_NO_CONNECTION       No active connection
09904     RPC_TIMEOUT             Timeout in RPC call
09905     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09906     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09907 
09908 \********************************************************************/
09909 {
09910    va_list ap, aptmp;
09911    char arg[8], arg_tmp[8];
09912    INT arg_type, transport, rpc_timeout;
09913    INT i, index, status;
09914    INT param_size, arg_size, send_size;
09915    INT tid, flags;
09916    fd_set readfds;
09917    struct timeval timeout;
09918    char *param_ptr, str[80];
09919    BOOL bpointer, bbig;
09920    NET_COMMAND *nc;
09921    int send_sock;
09922 
09923    send_sock = _server_connection.send_sock;
09924    transport = _server_connection.transport;
09925    rpc_timeout = _server_connection.rpc_timeout;
09926 
09927    /* init network buffer */
09928    if (_net_send_buffer_size == 0) {
09929       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09930       if (_net_send_buffer == NULL) {
09931          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09932          return RPC_EXCEED_BUFFER;
09933       }
09934       _net_send_buffer_size = NET_BUFFER_SIZE;
09935    }
09936 
09937    nc = (NET_COMMAND *) _net_send_buffer;
09938    nc->header.routine_id = routine_id;
09939 
09940    if (transport == RPC_FTCP)
09941       nc->header.routine_id |= TCP_FAST;
09942 
09943    for (i = 0;; i++)
09944       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09945          break;
09946    index = i;
09947    if (rpc_list[i].id == 0) {
09948       sprintf(str, "invalid rpc ID (%d)", routine_id);
09949       cm_msg(MERROR, "rpc_call", str);
09950       return RPC_INVALID_ID;
09951    }
09952 
09953    /* examine variable argument list and convert it to parameter array */
09954    va_start(ap, routine_id);
09955 
09956    /* find out if we are on a big endian system */
09957    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09958 
09959    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09960       tid = rpc_list[index].param[i].tid;
09961       flags = rpc_list[index].param[i].flags;
09962 
09963       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09964           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09965           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09966 
09967       if (bpointer)
09968          arg_type = TID_ARRAY;
09969       else
09970          arg_type = tid;
09971 
09972       /* floats are passed as doubles, at least under NT */
09973       if (tid == TID_FLOAT && !bpointer)
09974          arg_type = TID_DOUBLE;
09975 
09976       /* get pointer to argument */
09977       rpc_va_arg(&ap, arg_type, arg);
09978 
09979       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09980       if (bbig) {
09981          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09982             arg[0] = arg[3];
09983          }
09984          if (tid == TID_WORD || tid == TID_SHORT) {
09985             arg[0] = arg[2];
09986             arg[1] = arg[3];
09987          }
09988       }
09989 
09990       if (flags & RPC_IN) {
09991          if (bpointer)
09992             arg_size = tid_size[tid];
09993          else
09994             arg_size = tid_size[arg_type];
09995 
09996          /* for strings, the argument size depends on the string length */
09997          if (tid == TID_STRING || tid == TID_LINK)
09998             arg_size = 1 + strlen((char *) *((char **) arg));
09999 
10000          /* for varibale length arrays, the size is given by
10001             the next parameter on the stack */
10002          if (flags & RPC_VARARRAY) {
10003             memcpy(&aptmp, &ap, sizeof(ap));
10004             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
10005 
10006             if (flags & RPC_OUT)
10007                arg_size = *((INT *) * ((void **) arg_tmp));
10008             else
10009                arg_size = *((INT *) arg_tmp);
10010 
10011             *((INT *) param_ptr) = ALIGN8(arg_size);
10012             param_ptr += ALIGN8(sizeof(INT));
10013          }
10014 
10015          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
10016             arg_size = rpc_list[index].param[i].n;
10017 
10018          /* always align parameter size */
10019          param_size = ALIGN8(arg_size);
10020 
10021          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
10022             cm_msg(MERROR, "rpc_call",
10023                    "parameters (%d) too large for network buffer (%d)",
10024                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
10025             return RPC_EXCEED_BUFFER;
10026          }
10027 
10028          if (bpointer)
10029             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
10030          else {
10031             /* floats are passed as doubles on most systems */
10032             if (tid != TID_FLOAT)
10033                memcpy(param_ptr, arg, arg_size);
10034             else
10035                *((float *) param_ptr) = (float) *((double *) arg);
10036          }
10037 
10038          param_ptr += param_size;
10039 
10040       }
10041    }
10042 
10043    va_end(ap);
10044 
10045    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
10046 
10047    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10048 
10049    /* in FAST TCP mode, only send call and return immediately */
10050    if (transport == RPC_FTCP) {
10051       i = send_tcp(send_sock, (char *) nc, send_size, 0);
10052 
10053       if (i != send_size) {
10054          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
10055          return RPC_NET_ERROR;
10056       }
10057 
10058       return RPC_SUCCESS;
10059    }
10060 
10061    /* in TCP mode, send and wait for reply on send socket */
10062    i = send_tcp(send_sock, (char *) nc, send_size, 0);
10063    if (i != send_size) {
10064       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
10065       return RPC_NET_ERROR;
10066    }
10067 
10068    /* make some timeout checking */
10069    if (rpc_timeout > 0) {
10070       FD_ZERO(&readfds);
10071       FD_SET(send_sock, &readfds);
10072 
10073       timeout.tv_sec = rpc_timeout / 1000;
10074       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
10075 
10076       do {
10077          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
10078 
10079          /* if an alarm signal was cought, restart select with reduced timeout */
10080          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
10081             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
10082 
10083       } while (status == -1);   /* dont return if an alarm signal was cought */
10084 
10085       if (!FD_ISSET(send_sock, &readfds)) {
10086          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"",
10087                 rpc_list[index].name);
10088 
10089          /* disconnect to avoid that the reply to this rpc_call comes at
10090             the next rpc_call */
10091          rpc_server_disconnect();
10092 
10093          return RPC_TIMEOUT;
10094       }
10095    }
10096 
10097    /* receive result on send socket */
10098    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
10099 
10100    if (i <= 0) {
10101       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"",
10102              rpc_list[index].name);
10103       return RPC_NET_ERROR;
10104    }
10105 
10106    /* extract result variables and place it to argument list */
10107    status = nc->header.routine_id;
10108 
10109    va_start(ap, routine_id);
10110 
10111    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
10112       tid = rpc_list[index].param[i].tid;
10113       flags = rpc_list[index].param[i].flags;
10114 
10115       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
10116           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
10117           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
10118 
10119       if (bpointer)
10120          arg_type = TID_ARRAY;
10121       else
10122          arg_type = rpc_list[index].param[i].tid;
10123 
10124       if (tid == TID_FLOAT && !bpointer)
10125          arg_type = TID_DOUBLE;
10126 
10127       rpc_va_arg(&ap, arg_type, arg);
10128 
10129       if (rpc_list[index].param[i].flags & RPC_OUT) {
10130          tid = rpc_list[index].param[i].tid;
10131          arg_size = tid_size[tid];
10132 
10133          if (tid == TID_STRING || tid == TID_LINK)
10134             arg_size = strlen((char *) (param_ptr)) + 1;
10135 
10136          if (flags & RPC_VARARRAY) {
10137             arg_size = *((INT *) param_ptr);
10138             param_ptr += ALIGN8(sizeof(INT));
10139          }
10140 
10141          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
10142             arg_size = rpc_list[index].param[i].n;
10143 
10144          /* return parameters are always pointers */
10145          if (*((char **) arg))
10146             memcpy((void *) *((char **) arg), param_ptr, arg_size);
10147 
10148          /* parameter size is always aligned */
10149          param_size = ALIGN8(arg_size);
10150 
10151          param_ptr += param_size;
10152       }
10153    }
10154 
10155    va_end(ap);
10156 
10157    return status;
10158 }
10159 
10160 
10161 /********************************************************************/
10162 INT rpc_set_opt_tcp_size(INT tcp_size)
10163 {
10164    INT old;
10165 
10166    old = _opt_tcp_size;
10167    _opt_tcp_size = tcp_size;
10168    return old;
10169 }
10170 
10171 INT rpc_get_opt_tcp_size()
10172 {
10173    return _opt_tcp_size;
10174 }
10175 
10176 /**dox***************************************************************/
10177 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10178 
10179 /********************************************************************/
10180 /**
10181 Fast send_event routine which bypasses the RPC layer and
10182            sends the event directly at the TCP level.
10183 @param buffer_handle      Handle of the buffer to send the event to.
10184                             Must be obtained via bm_open_buffer.
10185 @param source            Address of the event to send. It must have
10186                             a proper event header.
10187 @param buf_size           Size of event in bytes with header.
10188 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
10189                             function returns immediately if it cannot
10190                             send the event over the network. In SYNC
10191                             mode, it waits until the packet is sent
10192                             (blocking).
10193 
10194 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
10195         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
10196 */
10197 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
10198 {
10199    INT i;
10200    NET_COMMAND *nc;
10201    unsigned long flag;
10202    BOOL would_block = 0;
10203    DWORD aligned_buf_size;
10204 
10205    aligned_buf_size = ALIGN8(buf_size);
10206 
10207    if (aligned_buf_size !=
10208        (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
10209       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
10210       return BM_INVALID_PARAM;
10211    }
10212    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
10213       cm_msg(MERROR, "rpc_send_event",
10214              "event size (%d) larger than maximum event size (%d)",
10215              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
10216       return RPC_EXCEED_BUFFER;
10217    }
10218 
10219    if (!rpc_is_remote())
10220       return bm_send_event(buffer_handle, source, buf_size, async_flag);
10221 
10222    /* init network buffer */
10223    if (!_tcp_buffer)
10224       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
10225    if (!_tcp_buffer) {
10226       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
10227       return RPC_EXCEED_BUFFER;
10228    }
10229 
10230    /* check if not enough space in TCP buffer */
10231    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
10232        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
10233       /* set socket to nonblocking IO */
10234       if (async_flag == ASYNC) {
10235          flag = 1;
10236 #ifdef OS_VXWORKS
10237          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10238 #else
10239          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10240 #endif
10241       }
10242 
10243       i = send_tcp(_server_connection.send_sock,
10244                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10245 
10246       if (i < 0)
10247 #ifdef OS_WINNT
10248          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
10249 #else
10250          would_block = (errno == EWOULDBLOCK);
10251 #endif
10252 
10253       /* set socket back to blocking IO */
10254       if (async_flag == ASYNC) {
10255          flag = 0;
10256 #ifdef OS_VXWORKS
10257          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10258 #else
10259          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10260 #endif
10261       }
10262 
10263       /* increment read pointer */
10264       if (i > 0)
10265          _tcp_rp += i;
10266 
10267       /* check if whole buffer is sent */
10268       if (_tcp_rp == _tcp_wp)
10269          _tcp_rp = _tcp_wp = 0;
10270 
10271       if (i < 0 && !would_block) {
10272          printf("send_tcp() returned %d\n", i);
10273          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
10274          return RPC_NET_ERROR;
10275       }
10276 
10277       /* return if buffer is not emptied */
10278       if (_tcp_wp > 0)
10279          return BM_ASYNC_RETURN;
10280    }
10281 
10282    nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
10283    nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
10284    nc->header.param_size = 4 * 8 + aligned_buf_size;
10285 
10286    /* assemble parameters manually */
10287    *((INT *) (&nc->param[0])) = buffer_handle;
10288    *((INT *) (&nc->param[8])) = buf_size;
10289 
10290    /* send events larger than optimal buffer size directly */
10291    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
10292       /* send header */
10293       send_tcp(_server_connection.send_sock,
10294                _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
10295 
10296       /* send data */
10297       send_tcp(_server_connection.send_sock, (char *) source, aligned_buf_size, 0);
10298 
10299       /* send last two parameters */
10300       *((INT *) (&nc->param[0])) = buf_size;
10301       *((INT *) (&nc->param[8])) = 0;
10302       send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
10303    } else {
10304       /* copy event */
10305       memcpy(&nc->param[16], source, buf_size);
10306 
10307       /* last two parameters (buf_size and async_flag */
10308       *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
10309       *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
10310 
10311       _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10312    }
10313 
10314    return RPC_SUCCESS;
10315 }
10316 
10317 
10318 
10319 /**dox***************************************************************/
10320 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10321 
10322 /********************************************************************/
10323 int rpc_get_send_sock()
10324 /********************************************************************\
10325 
10326   Routine: rpc_get_send_sock
10327 
10328   Purpose: Return send socket to MIDAS server. Used by MFE.C for
10329            optimized event sending.
10330 
10331   Input:
10332     none
10333 
10334   Output:
10335     none
10336 
10337   Function value:
10338     int    socket
10339 
10340 \********************************************************************/
10341 {
10342    return _server_connection.send_sock;
10343 }
10344 
10345 
10346 /********************************************************************/
10347 int rpc_get_event_sock()
10348 /********************************************************************\
10349 
10350   Routine: rpc_get_event_sock
10351 
10352   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
10353            optimized event sending.
10354 
10355   Input:
10356     none
10357 
10358   Output:
10359     none
10360 
10361   Function value:
10362     int    socket
10363 
10364 \********************************************************************/
10365 {
10366    return _server_connection.event_sock;
10367 }
10368 
10369 /**dox***************************************************************/
10370 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10371 
10372 /********************************************************************/
10373 /**
10374 Send event residing in the TCP cache buffer filled by
10375            rpc_send_event. This routine should be called when a
10376            run is stopped.
10377 
10378 @return RPC_SUCCESS, RPC_NET_ERROR
10379 */
10380 INT rpc_flush_event()
10381 {
10382    INT i;
10383 
10384    if (!rpc_is_remote())
10385       return RPC_SUCCESS;
10386 
10387    /* return if rpc_send_event was not called */
10388    if (!_tcp_buffer || _tcp_wp == 0)
10389       return RPC_SUCCESS;
10390 
10391    /* empty TCP buffer */
10392    if (_tcp_wp > 0) {
10393       i = send_tcp(_server_connection.send_sock,
10394                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10395 
10396       if (i != _tcp_wp - _tcp_rp) {
10397          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
10398          return RPC_NET_ERROR;
10399       }
10400    }
10401 
10402    _tcp_rp = _tcp_wp = 0;
10403 
10404    return RPC_SUCCESS;
10405 }
10406 
10407 /**dox***************************************************************/
10408 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10409 
10410 /********************************************************************/
10411 
10412 typedef struct {
10413    int transition;
10414    int run_number;
10415    time_t trans_time;
10416    int sequence_number;
10417 } TR_FIFO;
10418 
10419 static TR_FIFO tr_fifo[10];
10420 static int trf_wp, trf_rp;
10421 
10422 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
10423 /********************************************************************\
10424 
10425   Routine: rpc_transition_dispatch
10426 
10427   Purpose: Gets called when a transition function was registered and
10428            a transition occured. Internal use only.
10429 
10430   Input:
10431     INT    index            RPC function ID
10432     void   *prpc_param      RPC parameters
10433 
10434   Output:
10435     none
10436 
10437   Function value:
10438     INT    return value from called user routine
10439 
10440 \********************************************************************/
10441 {
10442    INT status, i;
10443 
10444    /* erase error string */
10445    *(CSTRING(2)) = 0;
10446 
10447    if (index == RPC_RC_TRANSITION) {
10448       for (i = 0; i<MAX_TRANSITIONS ; i++)
10449          if (_trans_table[i].transition == CINT(0) &&
10450              _trans_table[i].sequence_number == CINT(4))
10451             break;
10452 
10453       /* call registerd function */
10454       if (i < MAX_TRANSITIONS) {
10455          if (_trans_table[i].func)
10456             /* execute callback if defined */
10457             status = _trans_table[i].func(CINT(1), CSTRING(2));
10458          else {
10459             /* store transition in FIFO */
10460             tr_fifo[trf_wp].transition = CINT(0);
10461             tr_fifo[trf_wp].run_number = CINT(1);
10462             tr_fifo[trf_wp].trans_time = time(NULL);
10463             tr_fifo[trf_wp].sequence_number = CINT(4);
10464             trf_wp = (trf_wp + 1) % 10;
10465             status = RPC_SUCCESS;
10466          }
10467       }
10468       else
10469          status = RPC_SUCCESS;
10470 
10471    } else {
10472       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
10473       status = RPC_INVALID_ID;
10474    }
10475 
10476    return status;
10477 }
10478 
10479 /********************************************************************/
10480 int cm_query_transition(int *transition, int *run_number, int *trans_time)
10481 /********************************************************************\
10482 
10483   Routine: cm_query_transition
10484 
10485   Purpose: Query system if transition has occured. Normally, one 
10486            registers callbacks for transitions via 
10487            cm_register_transition. In some environments however,
10488            callbacks are not possible. In that case one spciefies
10489            a NULL pointer as the callback routine and can query
10490            transitions "manually" by calling this functions. A small
10491            FIFO takes care that no transition is lost if this functions
10492            did not get called between some transitions.
10493 
10494   Output:
10495     INT   *transition        Type of transition, one of TR_xxx
10496     INT   *run_nuber         Run number for transition
10497     time_t *trans_time       Time (in UNIX time) of transition
10498 
10499   Function value:
10500     FALSE  No transition occured since last call
10501     TRUE   Transition occured
10502 
10503 \********************************************************************/
10504 {
10505 
10506    if (trf_wp == trf_rp)
10507       return FALSE;
10508 
10509    if (transition)
10510       *transition = tr_fifo[trf_rp].transition;
10511 
10512    if (run_number)
10513       *run_number = tr_fifo[trf_rp].run_number;
10514 
10515    if (trans_time)
10516       *trans_time = (int)tr_fifo[trf_rp].trans_time;
10517 
10518    trf_rp = (trf_rp + 1) % 10;
10519 
10520    return TRUE;
10521 }
10522 
10523 /********************************************************************\
10524 *                        server functions                            *
10525 \********************************************************************/
10526 
10527 
10528 /********************************************************************/
10529 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10530                     INT * remaining)
10531 /********************************************************************\
10532 
10533   Routine: recv_tcp_server
10534 
10535   Purpose: TCP receive routine with local cache. To speed up network
10536            performance, a 64k buffer is read in at once and split into
10537            several RPC command on successive calls to recv_tcp_server.
10538            Therefore, the number of recv() calls is minimized.
10539 
10540            This routine is ment to be called by the server process.
10541            Clients should call recv_tcp instead.
10542 
10543   Input:
10544     INT   index              Index of server connection
10545     DWORD buffer_size        Size of the buffer in bytes.
10546     INT   flags              Flags passed to recv()
10547     INT   convert_flags      Convert flags needed for big/little
10548                              endian conversion
10549 
10550   Output:
10551     char  *buffer            Network receive buffer.
10552     INT   *remaining         Remaining data in cache
10553 
10554   Function value:
10555     INT                      Same as recv()
10556 
10557 \********************************************************************/
10558 {
10559    INT size, param_size;
10560    NET_COMMAND *nc;
10561    INT write_ptr, read_ptr, misalign;
10562    char *net_buffer;
10563    INT copied, status;
10564    INT sock;
10565 
10566    sock = _server_acception[index].recv_sock;
10567 
10568    if (flags & MSG_PEEK) {
10569       status = recv(sock, buffer, buffer_size, flags);
10570       if (status == -1)
10571          cm_msg(MERROR, "recv_tcp_server",
10572                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10573                 errno, strerror(errno));
10574       return status;
10575    }
10576 
10577    if (!_server_acception[index].net_buffer) {
10578       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10579          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
10580       else
10581          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
10582 
10583       _server_acception[index].net_buffer =
10584           (char *) M_MALLOC(_server_acception[index].net_buffer_size);
10585       _server_acception[index].write_ptr = 0;
10586       _server_acception[index].read_ptr = 0;
10587       _server_acception[index].misalign = 0;
10588    }
10589    if (!_server_acception[index].net_buffer) {
10590       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
10591       return -1;
10592    }
10593 
10594    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10595       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10596       return -1;
10597    }
10598 
10599    copied = 0;
10600    param_size = -1;
10601 
10602    write_ptr = _server_acception[index].write_ptr;
10603    read_ptr = _server_acception[index].read_ptr;
10604    misalign = _server_acception[index].misalign;
10605    net_buffer = _server_acception[index].net_buffer;
10606 
10607    do {
10608       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10609          if (param_size == -1) {
10610             if (copied > 0) {
10611                /* assemble split header */
10612                memcpy(buffer + copied, net_buffer + read_ptr,
10613                       (INT) sizeof(NET_COMMAND_HEADER) - copied);
10614                nc = (NET_COMMAND *) (buffer);
10615             } else
10616                nc = (NET_COMMAND *) (net_buffer + read_ptr);
10617 
10618             param_size = (INT) nc->header.param_size;
10619 
10620             if (_server_acception[index].convert_flags)
10621                rpc_convert_single(&param_size, TID_DWORD, 0,
10622                                   _server_acception[index].convert_flags);
10623          }
10624 
10625          /* check if parameters fit in buffer */
10626          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10627             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10628             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
10629             return -1;
10630          }
10631 
10632          /* check if we have all parameters in buffer */
10633          if (write_ptr - read_ptr >=
10634              param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10635             break;
10636       }
10637 
10638       /* not enough data, so copy partially and get new */
10639       size = write_ptr - read_ptr;
10640 
10641       if (size > 0) {
10642          memcpy(buffer + copied, net_buffer + read_ptr, size);
10643          copied += size;
10644          read_ptr = write_ptr;
10645       }
10646 #ifdef OS_UNIX
10647       do {
10648          write_ptr =
10649              recv(sock, net_buffer + misalign,
10650                   _server_acception[index].net_buffer_size - 8, flags);
10651 
10652          /* don't return if an alarm signal was cought */
10653       } while (write_ptr == -1 && errno == EINTR);
10654 #else
10655       write_ptr =
10656           recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8,
10657                flags);
10658 #endif
10659 
10660       /* abort if connection broken */
10661       if (write_ptr <= 0) {
10662          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
10663                 write_ptr, errno, strerror(errno));
10664 
10665          if (remaining)
10666             *remaining = 0;
10667 
10668          return write_ptr;
10669       }
10670 
10671       read_ptr = misalign;
10672       write_ptr += misalign;
10673 
10674       misalign = write_ptr % 8;
10675    } while (TRUE);
10676 
10677    /* copy rest of parameters */
10678    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10679    memcpy(buffer + copied, net_buffer + read_ptr, size);
10680    read_ptr += size;
10681 
10682    if (remaining) {
10683       /* don't keep rpc_server_receive in an infinite loop */
10684       if (write_ptr - read_ptr < param_size)
10685          *remaining = 0;
10686       else
10687          *remaining = write_ptr - read_ptr;
10688    }
10689 
10690    _server_acception[index].write_ptr = write_ptr;
10691    _server_acception[index].read_ptr = read_ptr;
10692    _server_acception[index].misalign = misalign;
10693 
10694    return size + copied;
10695 }
10696 
10697 
10698 /********************************************************************/
10699 INT recv_tcp_check(int sock)
10700 /********************************************************************\
10701 
10702   Routine: recv_tcp_check
10703 
10704   Purpose: Check if in TCP receive buffer associated with sock is
10705            some data. Called by ss_suspend.
10706 
10707   Input:
10708     INT   sock               TCP receive socket
10709 
10710   Output:
10711     none
10712 
10713   Function value:
10714     INT   count              Number of bytes remaining in TCP buffer
10715 
10716 \********************************************************************/
10717 {
10718    INT index;
10719 
10720    /* figure out to which connection socket belongs */
10721    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10722       if (_server_acception[index].recv_sock == sock)
10723          break;
10724 
10725    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10726 }
10727 
10728 
10729 /********************************************************************/
10730 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10731                       INT * remaining)
10732 /********************************************************************\
10733 
10734   Routine: recv_event_server
10735 
10736   Purpose: TCP event receive routine with local cache. To speed up
10737            network performance, a 64k buffer is read in at once and
10738            split into several RPC command on successive calls to
10739            recv_event_server. Therefore, the number of recv() calls
10740            is minimized.
10741 
10742            This routine is ment to be called by the server process.
10743            Clients should call recv_tcp instead.
10744 
10745   Input:
10746     INT   index              Index of server connection
10747     DWORD buffer_size        Size of the buffer in bytes.
10748     INT   flags              Flags passed to recv()
10749     INT   convert_flags      Convert flags needed for big/little
10750                              endian conversion
10751 
10752   Output:
10753     char  *buffer            Network receive buffer.
10754     INT   *remaining         Remaining data in cache
10755 
10756   Function value:
10757     INT                      Same as recv()
10758 
10759 \********************************************************************/
10760 {
10761    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10762    EVENT_HEADER *pevent;
10763    INT write_ptr, read_ptr, misalign;
10764    char *net_buffer;
10765    INT copied, status;
10766    INT sock;
10767    RPC_SERVER_ACCEPTION *psa;
10768 
10769    psa = &_server_acception[index];
10770    sock = psa->event_sock;
10771 
10772    if (flags & MSG_PEEK) {
10773       status = recv(sock, buffer, buffer_size, flags);
10774       if (status == -1)
10775          cm_msg(MERROR, "recv_event_server",
10776                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10777                 errno, strerror(errno));
10778       return status;
10779    }
10780 
10781    if (!psa->ev_net_buffer) {
10782       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10783          psa->net_buffer_size = NET_TCP_SIZE;
10784       else
10785          psa->net_buffer_size = NET_BUFFER_SIZE;
10786 
10787       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10788       psa->ev_write_ptr = 0;
10789       psa->ev_read_ptr = 0;
10790       psa->ev_misalign = 0;
10791    }
10792    if (!psa->ev_net_buffer) {
10793       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10794       return -1;
10795    }
10796 
10797    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10798 
10799    if ((INT) buffer_size < header_size) {
10800       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10801       return -1;
10802    }
10803 
10804    copied = 0;
10805    event_size = -1;
10806 
10807    write_ptr = psa->ev_write_ptr;
10808    read_ptr = psa->ev_read_ptr;
10809    misalign = psa->ev_misalign;
10810    net_buffer = psa->ev_net_buffer;
10811 
10812    do {
10813       if (write_ptr - read_ptr >= header_size - copied) {
10814          if (event_size == -1) {
10815             if (copied > 0) {
10816                /* assemble split header */
10817                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10818                pbh = (INT *) buffer;
10819             } else
10820                pbh = (INT *) (net_buffer + read_ptr);
10821 
10822             pevent = (EVENT_HEADER *) (pbh + 1);
10823 
10824             event_size = pevent->data_size;
10825             if (psa->convert_flags)
10826                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10827 
10828             aligned_event_size = ALIGN8(event_size);
10829          }
10830 
10831          /* check if data part fits in buffer */
10832          if ((INT) buffer_size < aligned_event_size + header_size) {
10833             cm_msg(MERROR, "recv_event_server",
10834                    "parameters too large for network buffer");
10835             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10836             return -1;
10837          }
10838 
10839          /* check if we have whole event in buffer */
10840          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10841             break;
10842       }
10843 
10844       /* not enough data, so copy partially and get new */
10845       size = write_ptr - read_ptr;
10846 
10847       if (size > 0) {
10848          memcpy(buffer + copied, net_buffer + read_ptr, size);
10849          copied += size;
10850          read_ptr = write_ptr;
10851       }
10852 #ifdef OS_UNIX
10853       do {
10854          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10855 
10856          /* don't return if an alarm signal was cought */
10857       } while (write_ptr == -1 && errno == EINTR);
10858 #else
10859       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10860 #endif
10861 
10862       /* abort if connection broken */
10863       if (write_ptr <= 0) {
10864          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10865                 write_ptr, errno, strerror(errno));
10866 
10867          if (remaining)
10868             *remaining = 0;
10869 
10870          return write_ptr;
10871       }
10872 
10873       read_ptr = misalign;
10874       write_ptr += misalign;
10875 
10876       misalign = write_ptr % 8;
10877    } while (TRUE);
10878 
10879    /* copy rest of event */
10880    size = aligned_event_size + header_size - copied;
10881    if (size > 0) {
10882       memcpy(buffer + copied, net_buffer + read_ptr, size);
10883       read_ptr += size;
10884    }
10885 
10886    if (remaining)
10887       *remaining = write_ptr - read_ptr;
10888 
10889    psa->ev_write_ptr = write_ptr;
10890    psa->ev_read_ptr = read_ptr;
10891    psa->ev_misalign = misalign;
10892 
10893    /* convert header little endian/big endian */
10894    if (psa->convert_flags) {
10895       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10896 
10897       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10898       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10899       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10900       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10901       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10902       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10903    }
10904 
10905    return header_size + event_size;
10906 }
10907 
10908 
10909 /********************************************************************/
10910 INT recv_event_check(int sock)
10911 /********************************************************************\
10912 
10913   Routine: recv_event_check
10914 
10915   Purpose: Check if in TCP event receive buffer associated with sock
10916            is some data. Called by ss_suspend.
10917 
10918   Input:
10919     INT   sock               TCP receive socket
10920 
10921   Output:
10922     none
10923 
10924   Function value:
10925     INT   count              Number of bytes remaining in TCP buffer
10926 
10927 \********************************************************************/
10928 {
10929    INT index;
10930 
10931    /* figure out to which connection socket belongs */
10932    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10933       if (_server_acception[index].event_sock == sock)
10934          break;
10935 
10936    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10937 }
10938 
10939 
10940 /********************************************************************/
10941 INT rpc_register_server(INT server_type, char *name, INT * port,
10942                         INT(*func) (INT, void **))
10943 /********************************************************************\
10944 
10945   Routine: rpc_register_server
10946 
10947   Purpose: Register the calling process as a MIDAS RPC server. Note
10948            that cm_connnect_experiment must be called prior to any call of
10949            rpc_register_server.
10950 
10951   Input:
10952     INT   server_type       One of the following constants:
10953                             ST_SINGLE: register a single process server
10954                             ST_MTHREAD: for each connection, start
10955                                         a new thread to serve it
10956                             ST_MPROCESS: for each connection, start
10957                                          a new process to server it
10958                             ST_SUBPROCESS: the routine was called from
10959                                            a multi process server
10960                             ST_REMOTE: register a client program server
10961                                        connected to the ODB
10962     char  *name             Name of .EXE file to start in MPROCESS mode
10963     INT   *port             TCP port for listen. NULL if listen as main
10964                             server (MIDAS_TCP_PORT is then used). If *port=0,
10965                             the OS chooses a free port and returns it. If
10966                             *port != 0, this port is used.
10967     INT   *func             Default dispatch function
10968 
10969   Output:
10970     INT   *port             Port under which server is listening.
10971 
10972   Function value:
10973     RPC_SUCCESS             Successful completion
10974     RPC_NET_ERROR           Error in socket call
10975     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10976 
10977 \********************************************************************/
10978 {
10979    struct sockaddr_in bind_addr;
10980    INT status, flag;
10981    int size;
10982 
10983 #ifdef OS_WINNT
10984    {
10985       WSADATA WSAData;
10986 
10987       /* Start windows sockets */
10988       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10989          return RPC_NET_ERROR;
10990    }
10991 #endif
10992 
10993    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10994 
10995    /* register system functions */
10996    rpc_register_functions(rpc_get_internal_list(0), func);
10997 
10998    if (name != NULL)
10999       rpc_set_server_option(RPC_OSERVER_NAME, (PTYPE) name);
11000 
11001    /* in subprocess mode, don't start listener */
11002    if (server_type == ST_SUBPROCESS)
11003       return RPC_SUCCESS;
11004 
11005    /* create a socket for listening */
11006    _lsock = socket(AF_INET, SOCK_STREAM, 0);
11007    if (_lsock == -1) {
11008       cm_msg(MERROR, "rpc_register_server", "socket() failed");
11009       return RPC_NET_ERROR;
11010    }
11011 
11012    /* reuse address, needed if previous server stopped (30s timeout!) */
11013    flag = 1;
11014    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
11015    if (status < 0) {
11016       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
11017       return RPC_NET_ERROR;
11018    }
11019 
11020    /* bind local node name and port to socket */
11021    memset(&bind_addr, 0, sizeof(bind_addr));
11022    bind_addr.sin_family = AF_INET;
11023    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
11024 
11025    if (!port)
11026       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
11027    else
11028       bind_addr.sin_port = htons((short) (*port));
11029 
11030    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11031    if (status < 0) {
11032       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
11033       return RPC_NET_ERROR;
11034    }
11035 
11036    /* listen for connection */
11037 #ifdef OS_MSDOS
11038    status = listen(_lsock, 1);
11039 #else
11040    status = listen(_lsock, SOMAXCONN);
11041 #endif
11042    if (status < 0) {
11043       cm_msg(MERROR, "rpc_register_server", "listen() failed");
11044       return RPC_NET_ERROR;
11045    }
11046 
11047    /* return port wich OS has choosen */
11048    if (port && *port == 0) {
11049       size = sizeof(bind_addr);
11050       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
11051       *port = ntohs(bind_addr.sin_port);
11052    }
11053 
11054    /* define callbacks for ss_suspend */
11055    if (server_type == ST_REMOTE)
11056       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
11057    else
11058       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
11059 
11060    return RPC_SUCCESS;
11061 }
11062 
11063 
11064 /********************************************************************/
11065 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
11066 /********************************************************************\
11067 
11068   Routine: rpc_execute
11069 
11070   Purpose: Execute a RPC command received over the network
11071 
11072   Input:
11073     INT  sock               TCP socket to which the result should be
11074                             send back
11075 
11076     char *buffer            Command buffer
11077     INT  convert_flags      Flags for data conversion
11078 
11079   Output:
11080     none
11081 
11082   Function value:
11083     RPC_SUCCESS             Successful completion
11084     RPC_INVALID_ID          Invalid routine_id received
11085     RPC_NET_ERROR           Error in socket call
11086     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11087     RPC_SHUTDOWN            Shutdown requested
11088     SS_ABORT                TCP connection broken
11089     SS_EXIT                 TCP connection closed
11090 
11091 \********************************************************************/
11092 {
11093    INT i, index, routine_id, status;
11094    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11095    INT tid, flags;
11096    NET_COMMAND *nc_in, *nc_out;
11097    INT param_size, max_size;
11098    void *prpc_param[20];
11099    char str[1024], debug_line[1024];
11100 
11101 /* return buffer must be auto for multi-thread servers */
11102    char return_buffer[NET_BUFFER_SIZE];
11103 
11104 
11105    /* extract pointer array to parameters */
11106    nc_in = (NET_COMMAND *) buffer;
11107    nc_out = (NET_COMMAND *) return_buffer;
11108 
11109    /* convert header format (byte swapping) */
11110    if (convert_flags) {
11111       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
11112       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
11113    }
11114 
11115    /* no result return in FAST TCP mode */
11116    if (nc_in->header.routine_id & TCP_FAST)
11117       sock = 0;
11118 
11119    /* find entry in rpc_list */
11120    routine_id = nc_in->header.routine_id & ~TCP_FAST;
11121 
11122    for (i = 0;; i++)
11123       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
11124          break;
11125    index = i;
11126    if (rpc_list[i].id == 0) {
11127       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
11128       return RPC_INVALID_ID;
11129    }
11130 
11131    in_param_ptr = nc_in->param;
11132    out_param_ptr = nc_out->param;
11133 
11134    sprintf(debug_line, "%s(", rpc_list[index].name);
11135 
11136    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11137       tid = rpc_list[index].param[i].tid;
11138       flags = rpc_list[index].param[i].flags;
11139 
11140       if (flags & RPC_IN) {
11141          param_size = ALIGN8(tid_size[tid]);
11142 
11143          if (tid == TID_STRING || tid == TID_LINK)
11144             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11145 
11146          if (flags & RPC_VARARRAY) {
11147             /* for arrays, the size is stored as a INT in front of the array */
11148             param_size = *((INT *) in_param_ptr);
11149             if (convert_flags)
11150                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
11151             param_size = ALIGN8(param_size);
11152 
11153             in_param_ptr += ALIGN8(sizeof(INT));
11154          }
11155 
11156          if (tid == TID_STRUCT)
11157             param_size = ALIGN8(rpc_list[index].param[i].n);
11158 
11159          prpc_param[i] = in_param_ptr;
11160 
11161          /* convert data format */
11162          if (convert_flags) {
11163             if (flags & RPC_VARARRAY)
11164                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
11165             else
11166                rpc_convert_data(in_param_ptr, tid, flags,
11167                                 rpc_list[index].param[i].n * tid_size[tid],
11168                                 convert_flags);
11169          }
11170 
11171          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
11172          if (rpc_list[index].param[i].tid == TID_STRING) {
11173             /* check for long strings (db_create_record...) */
11174             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11175                strcat(debug_line, "\"");
11176                strcat(debug_line, str);
11177                strcat(debug_line, "\"");
11178             } else
11179                strcat(debug_line, "...");
11180          } else
11181             strcat(debug_line, str);
11182 
11183          in_param_ptr += param_size;
11184       }
11185 
11186       if (flags & RPC_OUT) {
11187          param_size = ALIGN8(tid_size[tid]);
11188 
11189          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11190             /* save maximum array length */
11191             max_size = *((INT *) in_param_ptr);
11192             if (convert_flags)
11193                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
11194             max_size = ALIGN8(max_size);
11195 
11196             *((INT *) out_param_ptr) = max_size;
11197 
11198             /* save space for return array length */
11199             out_param_ptr += ALIGN8(sizeof(INT));
11200 
11201             /* use maximum array length from input */
11202             param_size += max_size;
11203          }
11204 
11205          if (rpc_list[index].param[i].tid == TID_STRUCT)
11206             param_size = ALIGN8(rpc_list[index].param[i].n);
11207 
11208          if ((PTYPE) out_param_ptr - (PTYPE) nc_out + param_size > NET_BUFFER_SIZE) {
11209             cm_msg(MERROR, "rpc_execute",
11210                    "return parameters (%d) too large for network buffer (%d)",
11211                    (PTYPE) out_param_ptr - (PTYPE) nc_out + param_size, NET_BUFFER_SIZE);
11212             return RPC_EXCEED_BUFFER;
11213          }
11214 
11215          /* if parameter goes both directions, copy input to output */
11216          if (rpc_list[index].param[i].flags & RPC_IN)
11217             memcpy(out_param_ptr, prpc_param[i], param_size);
11218 
11219          if (_debug_print && !(flags & RPC_IN))
11220             strcat(debug_line, "-");
11221 
11222          prpc_param[i] = out_param_ptr;
11223          out_param_ptr += param_size;
11224       }
11225 
11226       if (rpc_list[index].param[i + 1].tid)
11227          strcat(debug_line, ", ");
11228    }
11229 
11230    strcat(debug_line, ")");
11231    rpc_debug_printf(debug_line);
11232 
11233    last_param_ptr = out_param_ptr;
11234 
11235   /*********************************\
11236   *   call dispatch function        *
11237   \*********************************/
11238    if (rpc_list[index].dispatch)
11239       status = rpc_list[index].dispatch(routine_id, prpc_param);
11240    else
11241       status = RPC_INVALID_ID;
11242 
11243    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
11244        routine_id == RPC_ID_WATCHDOG)
11245       status = RPC_SUCCESS;
11246 
11247    /* return immediately for closed down client connections */
11248    if (!sock && routine_id == RPC_ID_EXIT)
11249       return SS_EXIT;
11250 
11251    if (!sock && routine_id == RPC_ID_SHUTDOWN)
11252       return RPC_SHUTDOWN;
11253 
11254    /* Return if TCP connection broken */
11255    if (status == SS_ABORT)
11256       return SS_ABORT;
11257 
11258    /* if sock == 0, we are in FTCP mode and may not sent results */
11259    if (!sock)
11260       return RPC_SUCCESS;
11261 
11262    /* compress variable length arrays */
11263    out_param_ptr = nc_out->param;
11264    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11265       if (rpc_list[index].param[i].flags & RPC_OUT) {
11266          tid = rpc_list[index].param[i].tid;
11267          flags = rpc_list[index].param[i].flags;
11268          param_size = ALIGN8(tid_size[tid]);
11269 
11270          if (tid == TID_STRING) {
11271             max_size = *((INT *) out_param_ptr);
11272             param_size = strlen((char *) prpc_param[i]) + 1;
11273             param_size = ALIGN8(param_size);
11274 
11275             /* move string ALIGN8(sizeof(INT)) left */
11276             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
11277 
11278             /* move remaining parameters to end of string */
11279             memcpy(out_param_ptr + param_size,
11280                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11281                    (PTYPE) last_param_ptr -
11282                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11283          }
11284 
11285          if (flags & RPC_VARARRAY) {
11286             /* store array length at current out_param_ptr */
11287             max_size = *((INT *) out_param_ptr);
11288             param_size = *((INT *) prpc_param[i + 1]);
11289             *((INT *) out_param_ptr) = param_size;
11290             if (convert_flags)
11291                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
11292 
11293             out_param_ptr += ALIGN8(sizeof(INT));
11294 
11295             param_size = ALIGN8(param_size);
11296 
11297             /* move remaining parameters to end of array */
11298             memcpy(out_param_ptr + param_size,
11299                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11300                    (PTYPE) last_param_ptr -
11301                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11302          }
11303 
11304          if (tid == TID_STRUCT)
11305             param_size = ALIGN8(rpc_list[index].param[i].n);
11306 
11307          /* convert data format */
11308          if (convert_flags) {
11309             if (flags & RPC_VARARRAY)
11310                rpc_convert_data(out_param_ptr, tid,
11311                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11312                                 param_size, convert_flags);
11313             else
11314                rpc_convert_data(out_param_ptr, tid,
11315                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11316                                 rpc_list[index].param[i].n * tid_size[tid],
11317                                 convert_flags);
11318          }
11319 
11320          out_param_ptr += param_size;
11321       }
11322 
11323    /* send return parameters */
11324    param_size = (PTYPE) out_param_ptr - (PTYPE) nc_out->param;
11325    nc_out->header.routine_id = status;
11326    nc_out->header.param_size = param_size;
11327 
11328    /* convert header format (byte swapping) if necessary */
11329    if (convert_flags) {
11330       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
11331                          RPC_OUTGOING, convert_flags);
11332       rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
11333                          RPC_OUTGOING, convert_flags);
11334    }
11335 
11336    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
11337 
11338    if (status < 0) {
11339       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11340       return RPC_NET_ERROR;
11341    }
11342 
11343    /* print return buffer */
11344 /*
11345   printf("Return buffer, ID %d:\n", routine_id);
11346   for (i=0; i<param_size ; i++)
11347     {
11348     status = (char) nc_out->param[i];
11349     printf("%02X ", status);
11350     if (i%8 == 7)
11351       printf("\n");
11352     }
11353 */
11354    /* return SS_EXIT if RPC_EXIT is called */
11355    if (routine_id == RPC_ID_EXIT)
11356       return SS_EXIT;
11357 
11358    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11359    if (routine_id == RPC_ID_SHUTDOWN)
11360       return RPC_SHUTDOWN;
11361 
11362    return RPC_SUCCESS;
11363 }
11364 
11365 
11366 /********************************************************************/
11367 INT rpc_execute_ascii(INT sock, char *buffer)
11368 /********************************************************************\
11369 
11370   Routine: rpc_execute_ascii
11371 
11372   Purpose: Execute a RPC command received over the network in ASCII
11373            mode
11374 
11375   Input:
11376     INT  sock               TCP socket to which the result should be
11377                             send back
11378 
11379     char *buffer            Command buffer
11380 
11381   Output:
11382     none
11383 
11384   Function value:
11385     RPC_SUCCESS             Successful completion
11386     RPC_INVALID_ID          Invalid routine_id received
11387     RPC_NET_ERROR           Error in socket call
11388     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11389     RPC_SHUTDOWN            Shutdown requested
11390     SS_ABORT                TCP connection broken
11391     SS_EXIT                 TCP connection closed
11392 
11393 \********************************************************************/
11394 {
11395 #define ASCII_BUFFER_SIZE 64500
11396 #define N_APARAM           1024
11397 
11398    INT i, j, index, status, index_in;
11399    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11400    INT routine_id, tid, flags, array_tid, n_param;
11401    INT param_size, item_size, num_values;
11402    void *prpc_param[20];
11403    char *arpc_param[N_APARAM], *pc;
11404    char str[1024], debug_line[1024];
11405    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
11406    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
11407    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
11408 
11409    /* parse arguments */
11410    arpc_param[0] = buffer;
11411    for (i = 1; i < N_APARAM; i++) {
11412       arpc_param[i] = strchr(arpc_param[i - 1], '&');
11413       if (arpc_param[i] == NULL)
11414          break;
11415       *arpc_param[i] = 0;
11416       arpc_param[i]++;
11417    }
11418 
11419    /* decode '%' */
11420    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
11421       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
11422          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
11423             str[0] = pc[1];
11424             str[1] = pc[2];
11425             str[2] = 0;
11426             sscanf(str, "%02X", &i);
11427 
11428             *pc++ = i;
11429             while (pc[2]) {
11430                pc[0] = pc[2];
11431                pc++;
11432             }
11433          }
11434       }
11435 
11436    /* find entry in rpc_list */
11437    for (i = 0;; i++)
11438       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
11439          break;
11440    index = i;
11441    routine_id = rpc_list[i].id;
11442    if (rpc_list[i].id == 0) {
11443       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
11444       return RPC_INVALID_ID;
11445    }
11446 
11447    in_param_ptr = buffer1;
11448    out_param_ptr = buffer2;
11449    index_in = 1;
11450 
11451    sprintf(debug_line, "%s(", rpc_list[index].name);
11452 
11453    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11454       tid = rpc_list[index].param[i].tid;
11455       flags = rpc_list[index].param[i].flags;
11456 
11457       if (flags & RPC_IN) {
11458          if (flags & RPC_VARARRAY) {
11459             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
11460 
11461             prpc_param[i] = in_param_ptr;
11462             for (j = 0; j < n_param; j++) {
11463                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
11464                in_param_ptr += param_size;
11465             }
11466             in_param_ptr = (char *) ALIGN8(((PTYPE) in_param_ptr));
11467 
11468             strcat(debug_line, "<array>");
11469          } else {
11470             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
11471             param_size = ALIGN8(param_size);
11472 
11473             if (tid == TID_STRING || tid == TID_LINK)
11474                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11475 
11476             /*
11477                if (tid == TID_STRUCT)
11478                param_size = ALIGN8( rpc_list[index].param[i].n );
11479              */
11480             prpc_param[i] = in_param_ptr;
11481 
11482             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
11483             if (rpc_list[index].param[i].tid == TID_STRING) {
11484                /* check for long strings (db_create_record...) */
11485                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11486                   strcat(debug_line, "\"");
11487                   strcat(debug_line, str);
11488                   strcat(debug_line, "\"");
11489                } else
11490                   strcat(debug_line, "...");
11491             } else
11492                strcat(debug_line, str);
11493 
11494             in_param_ptr += param_size;
11495          }
11496 
11497          if ((PTYPE) in_param_ptr - (PTYPE) buffer1 > ASCII_BUFFER_SIZE) {
11498             cm_msg(MERROR, "rpc_ascii_execute",
11499                    "parameters (%d) too large for network buffer (%d)", param_size,
11500                    ASCII_BUFFER_SIZE);
11501             return RPC_EXCEED_BUFFER;
11502          }
11503 
11504       }
11505 
11506       if (flags & RPC_OUT) {
11507          param_size = ALIGN8(tid_size[tid]);
11508 
11509          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11510             /* reserve maximum array length */
11511             param_size = atoi(arpc_param[index_in]);
11512             param_size = ALIGN8(param_size);
11513          }
11514 
11515 /*
11516       if (rpc_list[index].param[i].tid == TID_STRUCT)
11517         param_size = ALIGN8( rpc_list[index].param[i].n );
11518 */
11519          if ((PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size > ASCII_BUFFER_SIZE) {
11520             cm_msg(MERROR, "rpc_execute",
11521                    "return parameters (%d) too large for network buffer (%d)",
11522                    (PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size,
11523                    ASCII_BUFFER_SIZE);
11524             return RPC_EXCEED_BUFFER;
11525          }
11526 
11527          /* if parameter goes both directions, copy input to output */
11528          if (rpc_list[index].param[i].flags & RPC_IN)
11529             memcpy(out_param_ptr, prpc_param[i], param_size);
11530 
11531          if (!(flags & RPC_IN))
11532             strcat(debug_line, "-");
11533 
11534          prpc_param[i] = out_param_ptr;
11535          out_param_ptr += param_size;
11536       }
11537 
11538       if (rpc_list[index].param[i + 1].tid)
11539          strcat(debug_line, ", ");
11540    }
11541 
11542    strcat(debug_line, ")");
11543    rpc_debug_printf(debug_line);
11544 
11545    last_param_ptr = out_param_ptr;
11546 
11547    /*********************************\
11548    *   call dispatch function        *
11549    \*********************************/
11550   
11551    if (rpc_list[index].dispatch)
11552       status = rpc_list[index].dispatch(routine_id, prpc_param);
11553    else
11554       status = RPC_INVALID_ID;
11555 
11556    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
11557        routine_id == RPC_ID_WATCHDOG)
11558       status = RPC_SUCCESS;
11559 
11560    /* Return if TCP connection broken */
11561    if (status == SS_ABORT)
11562       return SS_ABORT;
11563 
11564    /* if sock == 0, we are in FTCP mode and may not sent results */
11565    if (!sock)
11566       return RPC_SUCCESS;
11567 
11568    /* send return status */
11569    out_param_ptr = return_buffer;
11570    sprintf(out_param_ptr, "%d", status);
11571    out_param_ptr += strlen(out_param_ptr);
11572 
11573    /* convert return parameters */
11574    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11575       if (rpc_list[index].param[i].flags & RPC_OUT) {
11576          *out_param_ptr++ = '&';
11577 
11578          tid = rpc_list[index].param[i].tid;
11579          flags = rpc_list[index].param[i].flags;
11580          param_size = ALIGN8(tid_size[tid]);
11581 
11582          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11583             strcpy(out_param_ptr, (char *) prpc_param[i]);
11584             param_size = strlen((char *) prpc_param[i]);
11585          }
11586 
11587          else if (flags & RPC_VARARRAY) {
11588             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
11589                param_size = *((INT *) prpc_param[i + 1]);
11590                /* write number of bytes to output */
11591                sprintf(out_param_ptr, "%d", param_size);
11592                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
11593                memcpy(out_param_ptr, prpc_param[i], param_size);
11594                out_param_ptr += param_size;
11595                *out_param_ptr = 0;
11596             } else {
11597                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
11598                   param_size = *((INT *) prpc_param[i + 1]);
11599                   array_tid = *((INT *) prpc_param[i + 2]);
11600                   num_values = *((INT *) prpc_param[i + 3]);
11601                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
11602                   param_size = *((INT *) prpc_param[i + 1]);
11603                   array_tid = *((INT *) prpc_param[i + 3]);
11604                   num_values = 1;
11605                } else if (rpc_list[index].id == RPC_HS_READ) {
11606                   param_size = *((INT *) prpc_param[i + 1]);
11607                   if (i == 6) {
11608                      array_tid = TID_DWORD;
11609                      num_values = param_size / sizeof(DWORD);
11610                   } else {
11611                      array_tid = *((INT *) prpc_param[10]);
11612                      num_values = *((INT *) prpc_param[11]);
11613                   }
11614                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11615 
11616                   param_size = *((INT *) prpc_param[i + 1]);
11617                   array_tid = tid;
11618                   if (tid == TID_STRING)
11619                      num_values = param_size / NAME_LENGTH;
11620                   else
11621                      num_values = param_size / tid_size[tid];
11622                }
11623 
11624                /* derive size of individual item */
11625                if (array_tid == TID_STRING)
11626                   item_size = param_size / num_values;
11627                else
11628                   item_size = tid_size[array_tid];
11629 
11630                /* write number of elements to output */
11631                sprintf(out_param_ptr, "%d", num_values);
11632                out_param_ptr += strlen(out_param_ptr);
11633 
11634                /* write array of values to output */
11635                for (j = 0; j < num_values; j++) {
11636                   *out_param_ptr++ = '&';
11637                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
11638                   out_param_ptr += strlen(out_param_ptr);
11639                }
11640             }
11641          }
11642 
11643 /*
11644       else if (tid == TID_STRUCT)
11645         param_size = ALIGN8( rpc_list[index].param[i].n );
11646 */
11647          else
11648             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11649 
11650          out_param_ptr += strlen(out_param_ptr);
11651 
11652          if ((PTYPE) out_param_ptr - (PTYPE) return_buffer > ASCII_BUFFER_SIZE) {
11653             cm_msg(MERROR, "rpc_execute",
11654                    "return parameter (%d) too large for network buffer (%d)", param_size,
11655                    ASCII_BUFFER_SIZE);
11656             return RPC_EXCEED_BUFFER;
11657          }
11658       }
11659 
11660    /* send return parameters */
11661    param_size = (PTYPE) out_param_ptr - (PTYPE) return_buffer + 1;
11662 
11663    status = send_tcp(sock, return_buffer, param_size, 0);
11664 
11665    if (status < 0) {
11666       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11667       return RPC_NET_ERROR;
11668    }
11669 
11670    /* print return buffer */
11671    if (strlen(return_buffer) > sizeof(debug_line)) {
11672       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11673       strcat(debug_line, "...");
11674    } else
11675       sprintf(debug_line, "-> %s", return_buffer);
11676    rpc_debug_printf(debug_line);
11677 
11678    /* return SS_EXIT if RPC_EXIT is called */
11679    if (routine_id == RPC_ID_EXIT)
11680       return SS_EXIT;
11681 
11682    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11683    if (routine_id == RPC_ID_SHUTDOWN)
11684       return RPC_SHUTDOWN;
11685 
11686    return RPC_SUCCESS;
11687 }
11688 
11689 
11690 /********************************************************************/
11691 INT rpc_server_accept(int lsock)
11692 /********************************************************************\
11693 
11694   Routine: rpc_server_accept
11695 
11696   Purpose: Accept new incoming connections
11697 
11698   Input:
11699     INT    lscok            Listen socket
11700 
11701   Output:
11702     none
11703 
11704   Function value:
11705     RPC_SUCCESS             Successful completion
11706     RPC_NET_ERROR           Error in socket call
11707     RPC_CONNCLOSED          Connection was closed
11708     RPC_SHUTDOWN            Listener shutdown
11709     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11710 
11711 \********************************************************************/
11712 {
11713    INT index, i;
11714    int size, status;
11715    char command, version[32], v1[32];
11716    INT sock, port1, port2, port3;
11717    struct sockaddr_in acc_addr;
11718    struct hostent *phe;
11719    char str[100];
11720    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11721    char debug_str[30];
11722    char *argv[10];
11723    char net_buffer[256];
11724    struct linger ling;
11725 
11726    static struct callback_addr callback;
11727 
11728    if (lsock > 0) {
11729       size = sizeof(acc_addr);
11730       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11731 
11732       if (sock == -1)
11733          return RPC_NET_ERROR;
11734    } else {
11735       /* lsock is stdin -> already connected from inetd */
11736 
11737       size = sizeof(acc_addr);
11738       sock = lsock;
11739       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11740    }
11741 
11742    /* receive string with timeout */
11743    i = recv_string(sock, net_buffer, 256, 10000);
11744    rpc_debug_printf("Received command: %s", net_buffer);
11745  
11746    if (i > 0) {
11747       command = (char) toupper(net_buffer[0]);
11748 
11749       switch (command) {
11750       case 'S':
11751                
11752          /*----------- shutdown listener ----------------------*/
11753          closesocket(sock);
11754          return RPC_SHUTDOWN;
11755 
11756       case 7:
11757          ss_shell(sock);
11758          closesocket(sock);
11759          break;
11760 
11761       case 'I':
11762                
11763          /*----------- return available experiments -----------*/
11764          cm_scan_experiments();
11765          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11766             sprintf(str, "%s", exptab[i].name);
11767             send(sock, str, strlen(str) + 1, 0);
11768          }
11769          send(sock, "", 1, 0);
11770          closesocket(sock);
11771          break;
11772 
11773       case 'C':
11774                
11775          /*----------- connect to experiment -----------*/
11776 
11777          /* get callback information */
11778          callback.experiment[0] = 0;
11779          port1 = port2 = version[0] = 0;
11780          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11781          strcpy(callback.experiment,
11782                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1,
11783                        ' ') + 1);
11784 
11785          /* print warning if version patch level doesn't agree */
11786          strcpy(v1, version);
11787          if (strchr(v1, '.'))
11788             if (strchr(strchr(v1, '.') + 1, '.'))
11789                *strchr(strchr(v1, '.') + 1, '.') = 0;
11790 
11791          strcpy(str, cm_get_version());
11792          if (strchr(str, '.'))
11793             if (strchr(strchr(str, '.') + 1, '.'))
11794                *strchr(strchr(str, '.') + 1, '.') = 0;
11795 
11796          if (strcmp(v1, str) != 0) {
11797             sprintf(str, "client MIDAS version %s differs from local version %s",
11798                     version, cm_get_version());
11799             cm_msg(MERROR, "rpc_server_accept", str);
11800 
11801             sprintf(str, "received string: %s", net_buffer + 2);
11802             cm_msg(MERROR, "rpc_server_accept", str);
11803          }
11804 
11805          callback.host_port1 = (short) port1;
11806          callback.host_port2 = (short) port2;
11807          callback.host_port3 = (short) port3;
11808          callback.debug = _debug_mode;
11809 
11810          /* get the name of the remote host */
11811 #ifdef OS_VXWORKS
11812          {
11813             INT status;
11814             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11815             if (status != 0) {
11816                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11817                break;
11818             }
11819          }
11820 #else
11821          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11822          if (phe == NULL) {
11823             /* use IP number instead */
11824             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11825          } else
11826             strcpy(callback.host_name, phe->h_name);
11827 #endif
11828 
11829          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11830             /* update experiment definition */
11831             cm_scan_experiments();
11832 
11833             /* lookup experiment */
11834             if (equal_ustring(callback.experiment, "Default"))
11835                index = 0;
11836             else
11837                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11838                   if (equal_ustring(callback.experiment, exptab[index].name))
11839                      break;
11840 
11841             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11842                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11843                cm_msg(MERROR, "rpc_server_accept", str);
11844 
11845                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11846                closesocket(sock);
11847                break;
11848             }
11849 
11850             strcpy(callback.directory, exptab[index].directory);
11851             strcpy(callback.user, exptab[index].user);
11852 
11853             /* create a new process */
11854             sprintf(host_port1_str, "%d", callback.host_port1);
11855             sprintf(host_port2_str, "%d", callback.host_port2);
11856             sprintf(host_port3_str, "%d", callback.host_port3);
11857             sprintf(debug_str, "%d", callback.debug);
11858 
11859             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11860             argv[1] = callback.host_name;
11861             argv[2] = host_port1_str;
11862             argv[3] = host_port2_str;
11863             argv[4] = host_port3_str;
11864             argv[5] = debug_str;
11865             argv[6] = callback.experiment;
11866             argv[7] = callback.directory;
11867             argv[8] = callback.user;
11868             argv[9] = NULL;
11869 
11870             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11871                argv[0], argv[1], argv[2], argv[3], argv[4],
11872                argv[5], argv[6], argv[7], argv[8], argv[9]);
11873 
11874             status = ss_spawnv(P_NOWAIT, 
11875                           (char *) rpc_get_server_option(RPC_OSERVER_NAME),
11876                           argv);
11877 
11878             if (status != SS_SUCCESS) {
11879                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11880 
11881                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11882                send(sock, str, strlen(str) + 1, 0);
11883                closesocket(sock);
11884                break;
11885             }
11886 
11887             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11888             send(sock, str, strlen(str) + 1, 0);
11889             closesocket(sock);
11890          } else {
11891             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11892             send(sock, str, strlen(str) + 1, 0);
11893             closesocket(sock);
11894          }
11895 
11896          /* look for next free entry */
11897          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11898             if (_server_acception[index].recv_sock == 0)
11899                break;
11900          if (index == MAX_RPC_CONNECTION)
11901             return RPC_NET_ERROR;
11902          callback.index = index;
11903 
11904         /*----- multi thread server ------------------------*/
11905          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11906             ss_thread_create(rpc_server_thread, (void *) (&callback));
11907 
11908         /*----- single thread server -----------------------*/
11909          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11910              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11911             rpc_server_callback(&callback);
11912 
11913          break;
11914 
11915       default:
11916          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11917          closesocket(sock);
11918          break;
11919 
11920       }
11921    } else {                     /* if i>0 */
11922 
11923       /* lingering needed for PCTCP */
11924       ling.l_onoff = 1;
11925       ling.l_linger = 0;
11926       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11927       closesocket(sock);
11928    }
11929 
11930    return RPC_SUCCESS;
11931 }
11932 
11933 
11934 /********************************************************************/
11935 INT rpc_client_accept(int lsock)
11936 /********************************************************************\
11937 
11938   Routine: rpc_client_accept
11939 
11940   Purpose: Accept new incoming connections as a client
11941 
11942   Input:
11943     INT    lsock            Listen socket
11944 
11945   Output:
11946     none
11947 
11948   Function value:
11949     RPC_SUCCESS             Successful completion
11950     RPC_NET_ERROR           Error in socket call
11951     RPC_CONNCLOSED          Connection was closed
11952     RPC_SHUTDOWN            Listener shutdown
11953     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11954 
11955 \********************************************************************/
11956 {
11957    INT index, i, version, status;
11958    int size, sock;
11959    struct sockaddr_in acc_addr;
11960    INT client_hw_type = 0, hw_type;
11961    char str[100], client_program[NAME_LENGTH];
11962    char host_name[HOST_NAME_LENGTH];
11963    INT convert_flags;
11964    char net_buffer[256], *p;
11965 
11966    size = sizeof(acc_addr);
11967    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11968 
11969    if (sock == -1)
11970       return RPC_NET_ERROR;
11971 
11972    /* get the name of the calling host */
11973 /* outcommented for speed reasons SR 7.10.98
11974 #ifdef OS_VXWORKS
11975   {
11976   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11977   if (status != 0)
11978     strcpy(host_name, "unknown");
11979   }
11980 #else
11981   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11982   if (phe == NULL)
11983     strcpy(host_name, "unknown");
11984   strcpy(host_name, phe->h_name);
11985 #endif
11986 */
11987    strcpy(host_name, "");
11988 
11989    /* look for next free entry */
11990    for (index = 0; index < MAX_RPC_CONNECTION; index++)
11991       if (_server_acception[index].recv_sock == 0)
11992          break;
11993    if (index == MAX_RPC_CONNECTION) {
11994       closesocket(sock);
11995       return RPC_NET_ERROR;
11996    }
11997 
11998    /* receive string with timeout */
11999    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
12000    if (i <= 0) {
12001       closesocket(sock);
12002       return RPC_NET_ERROR;
12003    }
12004 
12005    /* get remote computer info */
12006    p = strtok(net_buffer, " ");
12007    if (p != NULL) {
12008       client_hw_type = atoi(p);
12009       p = strtok(NULL, " ");
12010    }
12011    if (p != NULL) {
12012       version = atoi(p);
12013       p = strtok(NULL, " ");
12014    }
12015    if (p != NULL) {
12016       strcpy(client_program, p);
12017       p = strtok(NULL, " ");
12018    }
12019    if (p != NULL) {
12020       strcpy(host_name, p);
12021       p = strtok(NULL, " ");
12022    }
12023 
12024    /* save information in _server_acception structure */
12025    _server_acception[index].recv_sock = sock;
12026    _server_acception[index].send_sock = 0;
12027    _server_acception[index].event_sock = 0;
12028    _server_acception[index].remote_hw_type = client_hw_type;
12029    strcpy(_server_acception[index].host_name, host_name);
12030    strcpy(_server_acception[index].prog_name, client_program);
12031    _server_acception[index].tid = ss_gettid();
12032    _server_acception[index].last_activity = ss_millitime();
12033    _server_acception[index].watchdog_timeout = 0;
12034 
12035    /* send my own computer id */
12036    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12037    sprintf(str, "%d %s", hw_type, cm_get_version());
12038    status = send(sock, str, strlen(str) + 1, 0);
12039    if (status != (INT) strlen(str) + 1)
12040       return RPC_NET_ERROR;
12041 
12042    rpc_set_server_acception(index + 1);
12043    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12044    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12045 
12046    /* set callback function for ss_suspend */
12047    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
12048                            (int (*)(void)) rpc_server_receive);
12049 
12050    return RPC_SUCCESS;
12051 }
12052 
12053 
12054 /********************************************************************/
12055 INT rpc_server_callback(struct callback_addr * pcallback)
12056 /********************************************************************\
12057 
12058   Routine: rpc_server_callback
12059 
12060   Purpose: Callback a remote client. Setup _server_acception entry
12061            with optional conversion flags and establish two-way
12062            TCP connection.
12063 
12064   Input:
12065     callback_addr pcallback Pointer to a callback structure
12066 
12067   Output:
12068     none
12069 
12070   Function value:
12071     RPC_SUCCESS             Successful completion
12072 
12073 \********************************************************************/
12074 {
12075    INT index, status;
12076    int recv_sock, send_sock, event_sock;
12077    struct sockaddr_in bind_addr;
12078    struct hostent *phe;
12079    char str[100], client_program[NAME_LENGTH];
12080    char host_name[HOST_NAME_LENGTH];
12081    INT client_hw_type, hw_type;
12082    INT convert_flags;
12083    char net_buffer[256];
12084    struct callback_addr callback;
12085    int flag;
12086 
12087    /* copy callback information */
12088    memcpy(&callback, pcallback, sizeof(callback));
12089    index = callback.index;
12090 
12091    /* create new sockets for TCP */
12092    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
12093    send_sock = socket(AF_INET, SOCK_STREAM, 0);
12094    event_sock = socket(AF_INET, SOCK_STREAM, 0);
12095    if (event_sock == -1)
12096       return RPC_NET_ERROR;
12097 
12098    /* callback to remote node */
12099    memset(&bind_addr, 0, sizeof(bind_addr));
12100    bind_addr.sin_family = AF_INET;
12101    bind_addr.sin_port = htons(callback.host_port1);
12102 
12103 #ifdef OS_VXWORKS
12104    {
12105       INT host_addr;
12106 
12107       host_addr = hostGetByName(callback.host_name);
12108       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
12109    }
12110 #else
12111    phe = gethostbyname(callback.host_name);
12112    if (phe == NULL) {
12113       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
12114       return RPC_NET_ERROR;
12115    }
12116    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
12117 #endif
12118 
12119    /* connect receive socket */
12120 #ifdef OS_UNIX
12121    do {
12122       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
12123 
12124       /* don't return if an alarm signal was cought */
12125    } while (status == -1 && errno == EINTR);
12126 #else
12127    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12128 #endif
12129 
12130    if (status != 0) {
12131       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
12132       goto error;
12133    }
12134 
12135    bind_addr.sin_port = htons(callback.host_port2);
12136 
12137    /* connect send socket */
12138 #ifdef OS_UNIX
12139    do {
12140       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12141 
12142       /* don't return if an alarm signal was cought */
12143    } while (status == -1 && errno == EINTR);
12144 #else
12145    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12146 #endif
12147 
12148    if (status != 0) {
12149       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
12150       goto error;
12151    }
12152 
12153    bind_addr.sin_port = htons(callback.host_port3);
12154 
12155    /* connect event socket */
12156 #ifdef OS_UNIX
12157    do {
12158       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12159 
12160       /* don't return if an alarm signal was cought */
12161    } while (status == -1 && errno == EINTR);
12162 #else
12163    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12164 #endif
12165 
12166    if (status != 0) {
12167       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
12168       goto error;
12169    }
12170 
12171    /* increase receive buffer size to 64k */
12172 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
12173    flag = 0x10000;
12174    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
12175 #endif
12176 
12177    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
12178       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
12179       goto error;
12180    }
12181 
12182    /* get remote computer info */
12183    sscanf(net_buffer, "%d", &client_hw_type);
12184 
12185    strcpy(client_program, strchr(net_buffer, ' ') + 1);
12186 
12187    /* get the name of the remote host */
12188 #ifdef OS_VXWORKS
12189    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
12190    if (status != 0)
12191       strcpy(host_name, "unknown");
12192 #else
12193    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
12194    if (phe == NULL)
12195       strcpy(host_name, "unknown");
12196    else
12197       strcpy(host_name, phe->h_name);
12198 #endif
12199 
12200    /* save information in _server_acception structure */
12201    _server_acception[index].recv_sock = recv_sock;
12202    _server_acception[index].send_sock = send_sock;
12203    _server_acception[index].event_sock = event_sock;
12204    _server_acception[index].remote_hw_type = client_hw_type;
12205    strcpy(_server_acception[index].host_name, host_name);
12206    strcpy(_server_acception[index].prog_name, client_program);
12207    _server_acception[index].tid = ss_gettid();
12208    _server_acception[index].last_activity = ss_millitime();
12209    _server_acception[index].watchdog_timeout = 0;
12210 
12211    /* send my own computer id */
12212    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12213    sprintf(str, "%d", hw_type);
12214    send(recv_sock, str, strlen(str) + 1, 0);
12215 
12216    rpc_set_server_acception(index + 1);
12217    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12218    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12219 
12220    /* set callback function for ss_suspend */
12221    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
12222                            (int (*)(void)) rpc_server_receive);
12223 
12224    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12225       rpc_debug_printf("Connection to %s:%s established\n",
12226              _server_acception[index].host_name, _server_acception[index].prog_name);
12227 
12228    return RPC_SUCCESS;
12229 
12230  error:
12231 
12232    closesocket(recv_sock);
12233    closesocket(send_sock);
12234    closesocket(event_sock);
12235 
12236    return RPC_NET_ERROR;
12237 }
12238 
12239 
12240 /********************************************************************/
12241 INT rpc_server_thread(void *pointer)
12242 /********************************************************************\
12243 
12244   Routine: rpc_server_thread
12245 
12246   Purpose: New thread for a multi-threaded server. Callback to the
12247            client and process RPC requests.
12248 
12249   Input:
12250     vcoid  pointer          pointer to callback_addr structure.
12251 
12252   Output:
12253     none
12254 
12255   Function value:
12256     RPC_SUCCESS             Successful completion
12257 
12258 \********************************************************************/
12259 {
12260    struct callback_addr callback;
12261    int status, mutex_alarm, mutex_elog;
12262    static DWORD last_checked = 0;
12263 
12264    memcpy(&callback, pointer, sizeof(callback));
12265 
12266    status = rpc_server_callback(&callback);
12267 
12268    if (status != RPC_SUCCESS)
12269       return status;
12270 
12271    /* create alarm and elog mutexes */
12272    ss_mutex_create("ALARM", &mutex_alarm);
12273    ss_mutex_create("ELOG", &mutex_elog);
12274    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
12275 
12276    do {
12277       status = ss_suspend(5000, 0);
12278 
12279       if (rpc_check_channels() == RPC_NET_ERROR)
12280          break;
12281 
12282       /* check alarms every 10 seconds */
12283       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
12284          al_check();
12285          last_checked = ss_time();
12286       }
12287 
12288    } while (status != SS_ABORT && status != SS_EXIT);
12289 
12290    /* delete entry in suspend table for this thread */
12291    ss_suspend_exit();
12292 
12293    return RPC_SUCCESS;
12294 }
12295 
12296 
12297 /********************************************************************/
12298 INT rpc_server_receive(INT index, int sock, BOOL check)
12299 /********************************************************************\
12300 
12301   Routine: rpc_server_receive
12302 
12303   Purpose: Receive rpc commands and execute them. Close the connection
12304            if client has broken TCP pipe.
12305 
12306   Input:
12307     INT    index            Index to _server_acception structure in-
12308                             dicating which connection got data.
12309     int    sock             Socket which got data
12310     BOOL   check            If TRUE, only check if connection is
12311                             broken. This may be called via
12312                             bm_receive_event/ss_suspend(..,MSG_BM)
12313 
12314   Output:
12315     none
12316 
12317   Function value:
12318     RPC_SUCCESS             Successful completion
12319     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
12320     SS_EXIT                 Server connection was closed
12321     SS_ABORT                Server connection was broken
12322 
12323 \********************************************************************/
12324 {
12325    INT status, n_received;
12326    INT remaining, *pbh, start_time;
12327    char test_buffer[256], str[80];
12328    EVENT_HEADER *pevent;
12329 
12330    /* init network buffer */
12331    if (_net_recv_buffer_size == 0) {
12332       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
12333       if (_net_recv_buffer == NULL) {
12334          cm_msg(MERROR, "rpc_server_receive",
12335                 "not enough memory to allocate network buffer");
12336          return RPC_EXCEED_BUFFER;
12337       }
12338       _net_recv_buffer_size = NET_BUFFER_SIZE;
12339    }
12340 
12341    /* only check if TCP connection is broken */
12342    if (check) {
12343       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
12344 
12345       if (n_received == -1)
12346          cm_msg(MERROR, "rpc_server_receive",
12347                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
12348                 n_received, errno, strerror(errno));
12349 
12350       if (n_received <= 0)
12351          return SS_ABORT;
12352 
12353       return SS_SUCCESS;
12354    }
12355 
12356    remaining = 0;
12357 
12358    /* receive command */
12359    if (sock == _server_acception[index].recv_sock) {
12360       do {
12361          if (_server_acception[index].remote_hw_type == DR_ASCII)
12362             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
12363                                      NET_BUFFER_SIZE, 10000);
12364          else
12365             n_received =
12366                 recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
12367 
12368          if (n_received <= 0) {
12369             status = SS_ABORT;
12370             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort",
12371                    n_received);
12372             goto error;
12373          }
12374 
12375          rpc_set_server_acception(index + 1);
12376 
12377          if (_server_acception[index].remote_hw_type == DR_ASCII)
12378             status = rpc_execute_ascii(_server_acception[index].recv_sock,
12379                                        _net_recv_buffer);
12380          else
12381             status = rpc_execute(_server_acception[index].recv_sock,
12382                                  _net_recv_buffer,
12383                                  _server_acception[index].convert_flags);
12384 
12385          if (status == SS_ABORT) {
12386             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort",
12387                    status);
12388             goto error;
12389          }
12390 
12391          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
12392             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12393                rpc_debug_printf("Connection to %s:%s closed\n",
12394                       _server_acception[index].host_name,
12395                       _server_acception[index].prog_name);
12396             goto exit;
12397          }
12398 
12399       } while (remaining);
12400    } else {
12401       /* receive event */
12402       if (sock == _server_acception[index].event_sock) {
12403          start_time = ss_millitime();
12404 
12405          do {
12406             n_received =
12407                 recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0,
12408                                   &remaining);
12409 
12410             if (n_received <= 0) {
12411                status = SS_ABORT;
12412                cm_msg(MERROR, "rpc_server_receive",
12413                       "recv_event_server() returned %d, abort", n_received);
12414                goto error;
12415             }
12416 
12417             /* send event to buffer */
12418             pbh = (INT *) _net_recv_buffer;
12419             pevent = (EVENT_HEADER *) (pbh + 1);
12420 
12421             status =
12422                 bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER),
12423                               SYNC);
12424             if (status != BM_SUCCESS)
12425                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d",
12426                       status);
12427 
12428             /* repeat for maximum 0.5 sec */
12429          } while (ss_millitime() - start_time < 500 && remaining);
12430       }
12431    }
12432 
12433    return RPC_SUCCESS;
12434 
12435  error:
12436 
12437    strcpy(str, _server_acception[index].host_name);
12438    if (strchr(str, '.'))
12439       *strchr(str, '.') = 0;
12440    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
12441           _server_acception[index].prog_name, str);
12442 
12443  exit:
12444 
12445    /* disconnect from experiment as MIDAS server */
12446    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12447       HNDLE hDB, hKey;
12448 
12449       cm_get_experiment_database(&hDB, &hKey);
12450 
12451       /* only disconnect from experiment if previously connected.
12452          Necessary for pure RPC servers (RPC_SRVR) */
12453       if (hDB) {
12454 #ifdef LOCAL_ROUTINES
12455          ss_alarm(0, cm_watchdog);
12456 #endif
12457 
12458          cm_delete_client_info(hDB, 0);
12459 
12460          bm_close_all_buffers();
12461          db_close_all_databases();
12462 
12463          rpc_deregister_functions();
12464 
12465          cm_set_experiment_database(0, 0);
12466 
12467          _msg_buffer = 0;
12468       }
12469    }
12470 
12471    /* close server connection */
12472    if (_server_acception[index].recv_sock)
12473       closesocket(_server_acception[index].recv_sock);
12474    if (_server_acception[index].send_sock)
12475       closesocket(_server_acception[index].send_sock);
12476    if (_server_acception[index].event_sock)
12477       closesocket(_server_acception[index].event_sock);
12478 
12479    /* free TCP cache */
12480    M_FREE(_server_acception[index].net_buffer);
12481    _server_acception[index].net_buffer = NULL;
12482 
12483    /* mark this entry as invalid */
12484    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12485 
12486    /* signal caller a shutdonw */
12487    if (status == RPC_SHUTDOWN)
12488       return status;
12489 
12490    /* don't abort if other than main connection is broken */
12491    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12492       return SS_SUCCESS;
12493 
12494    return status;
12495 }
12496 
12497 
12498 /********************************************************************/
12499 INT rpc_server_shutdown(void)
12500 /********************************************************************\
12501 
12502   Routine: rpc_server_shutdown
12503 
12504   Purpose: Shutdown RPC server, abort all connections
12505 
12506   Input:
12507     none
12508 
12509   Output:
12510     none
12511 
12512   Function value:
12513     RPC_SUCCESS             Successful completion
12514 
12515 \********************************************************************/
12516 {
12517    INT i;
12518    struct linger ling;
12519 
12520    /* close all open connections */
12521    for (i = 0; i < MAX_RPC_CONNECTION; i++)
12522       if (_server_acception[i].recv_sock != 0) {
12523          /* lingering needed for PCTCP */
12524          ling.l_onoff = 1;
12525          ling.l_linger = 0;
12526          setsockopt(_server_acception[i].recv_sock,
12527                     SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12528          closesocket(_server_acception[i].recv_sock);
12529 
12530          if (_server_acception[i].send_sock) {
12531             setsockopt(_server_acception[i].send_sock,
12532                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12533             closesocket(_server_acception[i].send_sock);
12534          }
12535 
12536          if (_server_acception[i].event_sock) {
12537             setsockopt(_server_acception[i].event_sock,
12538                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12539             closesocket(_server_acception[i].event_sock);
12540          }
12541 
12542          _server_acception[i].recv_sock = 0;
12543          _server_acception[i].send_sock = 0;
12544          _server_acception[i].event_sock = 0;
12545       }
12546 
12547    if (_lsock) {
12548       closesocket(_lsock);
12549       _lsock = 0;
12550       _server_registered = FALSE;
12551    }
12552 
12553    /* free suspend structures */
12554    ss_suspend_exit();
12555 
12556    return RPC_SUCCESS;
12557 }
12558 
12559 
12560 /********************************************************************/
12561 INT rpc_check_channels(void)
12562 /********************************************************************\
12563 
12564   Routine: rpc_check_channels
12565 
12566   Purpose: Check open rpc channels by sending watchdog messages
12567 
12568   Input:
12569     none
12570 
12571   Output:
12572     none
12573 
12574   Function value:
12575     RPC_SUCCESS             Channel is still alive
12576     RPC_NET_ERROR           Connection is broken
12577 
12578 \********************************************************************/
12579 {
12580    INT status, index, i, convert_flags;
12581    NET_COMMAND nc;
12582    fd_set readfds;
12583    struct timeval timeout;
12584 
12585    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
12586       if (_server_acception[index].recv_sock &&
12587           _server_acception[index].tid == ss_gettid() &&
12588           _server_acception[index].watchdog_timeout &&
12589           (ss_millitime() - _server_acception[index].last_activity >
12590            (DWORD) _server_acception[index].watchdog_timeout)) {
12591 /* printf("Send watchdog message to %s on %s\n",
12592                 _server_acception[index].prog_name,
12593                 _server_acception[index].host_name); */
12594 
12595          /* send a watchdog message */
12596          nc.header.routine_id = MSG_WATCHDOG;
12597          nc.header.param_size = 0;
12598 
12599          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12600          if (convert_flags) {
12601             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING,
12602                                convert_flags);
12603             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING,
12604                                convert_flags);
12605          }
12606 
12607          /* send the header to the client */
12608          i = send_tcp(_server_acception[index].send_sock,
12609                       (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12610 
12611          if (i < 0)
12612             goto exit;
12613 
12614          /* make some timeout checking */
12615          FD_ZERO(&readfds);
12616          FD_SET(_server_acception[index].send_sock, &readfds);
12617          FD_SET(_server_acception[index].recv_sock, &readfds);
12618 
12619          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
12620          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
12621 
12622          do {
12623             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12624 
12625             /* if an alarm signal was cought, restart select with reduced timeout */
12626             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12627                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12628 
12629          } while (status == -1);        /* dont return if an alarm signal was cought */
12630 
12631          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
12632              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
12633             goto exit;
12634 
12635          /* receive result on send socket */
12636          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
12637             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
12638             if (i <= 0)
12639                goto exit;
12640          }
12641       }
12642    }
12643 
12644    return RPC_SUCCESS;
12645 
12646  exit:
12647 
12648    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12649           _server_acception[index].host_name,
12650           _server_acception[index].prog_name,
12651           _server_acception[index].watchdog_timeout / 1000);
12652 
12653    /* disconnect from experiment */
12654    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12655       cm_disconnect_experiment();
12656 
12657    /* close server connection */
12658    if (_server_acception[index].recv_sock)
12659       closesocket(_server_acception[index].recv_sock);
12660    if (_server_acception[index].send_sock)
12661       closesocket(_server_acception[index].send_sock);
12662    if (_server_acception[index].event_sock)
12663       closesocket(_server_acception[index].event_sock);
12664 
12665    /* free TCP cache */
12666    M_FREE(_server_acception[index].net_buffer);
12667    _server_acception[index].net_buffer = NULL;
12668 
12669    /* mark this entry as invalid */
12670    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12671 
12672    return RPC_NET_ERROR;
12673 }
12674 
12675 /**dox***************************************************************/
12676 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12677 
12678 /**dox***************************************************************/
12679 /** @} */ /* end of rpcfunctionc */
12680 
12681 /**dox***************************************************************/
12682 /** @addtogroup bkfunctionc
12683  *  
12684  *  @{  */
12685 
12686 /********************************************************************\
12687 *                                                                    *
12688 *                 Bank functions                                     *
12689 *                                                                    *
12690 \********************************************************************/
12691 
12692 /********************************************************************/
12693 /**
12694 Initializes an event for Midas banks structure.
12695 Before banks can be created in an event, bk_init() has to be called first.
12696 @param event pointer to the area of event
12697 */
12698 void bk_init(void *event)
12699 {
12700    ((BANK_HEADER *) event)->data_size = 0;
12701    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12702 }
12703 
12704 /**dox***************************************************************/
12705 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12706 
12707 /********************************************************************/
12708 BOOL bk_is32(void *event)
12709 /********************************************************************\
12710 
12711   Routine: bk_is32
12712 
12713   Purpose: Return true if banks inside event are 32-bit banks
12714 
12715   Input:
12716     void   *event           pointer to the event
12717 
12718   Output:
12719     none
12720 
12721   Function value:
12722     none
12723 
12724 \********************************************************************/
12725 {
12726    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12727 }
12728 
12729 /**dox***************************************************************/
12730 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12731 
12732 /********************************************************************/
12733 /**
12734 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12735 Before banks can be created in an event, bk_init32() has to be called first.
12736 @param event pointer to the area of event
12737 @return void
12738 */
12739 void bk_init32(void *event)
12740 {
12741    ((BANK_HEADER *) event)->data_size = 0;
12742    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12743 }
12744 
12745 /********************************************************************/
12746 /**
12747 Returns the size of an event containing banks.
12748 The total size of an event is the value returned by bk_size() plus the size
12749 of the event header (sizeof(EVENT_HEADER)).
12750 @param event pointer to the area of event
12751 @return number of bytes contained in data area of event
12752 */
12753 INT bk_size(void *event)
12754 {
12755    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12756 }
12757 
12758 /********************************************************************/
12759 /**
12760 Create a Midas bank.
12761 The data pointer pdata must be used as an address to fill a
12762 bank. It is incremented with every value written to the bank and finally points
12763 to a location just after the last byte of the bank. It is then passed to
12764 the function bk_close() to finish the bank creation.
12765 \code
12766 INT *pdata;
12767 bk_init(pevent);
12768 bk_create(pevent, "ADC0", TID_INT, &pdata);
12769 *pdata++ = 123
12770 *pdata++ = 456
12771 bk_close(pevent, pdata);
12772 \endcode
12773 @param event pointer to the data area
12774 @param name of the bank, must be exactly 4 charaters
12775 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12776 midas.h
12777 @param pdata pointer to the data area of the newly created bank
12778 @return void
12779 */
12780 void bk_create(void *event, const char *name, WORD type, void *pdata)
12781 {
12782    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12783       BANK32 *pbk32;
12784 
12785       pbk32 =
12786           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12787                       ((BANK_HEADER *) event)->data_size);
12788       strncpy(pbk32->name, name, 4);
12789       pbk32->type = type;
12790       pbk32->data_size = 0;
12791       *((void **) pdata) = pbk32 + 1;
12792    } else {
12793       BANK *pbk;
12794 
12795       pbk =
12796           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12797                     ((BANK_HEADER *) event)->data_size);
12798       strncpy(pbk->name, name, 4);
12799       pbk->type = type;
12800       pbk->data_size = 0;
12801       *((void **) pdata) = pbk + 1;
12802    }
12803 }
12804 
12805 
12806 
12807 /**dox***************************************************************/
12808 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12809 
12810 /********************************************************************/
12811 int bk_delete(void *event, const char *name)
12812 /********************************************************************\
12813 
12814   Routine: bk_delete
12815 
12816   Purpose: Delete a MIDAS bank inside an event
12817 
12818   Input:
12819     void   *event           pointer to the event
12820     char   *name            Name of bank (exactly four letters)
12821 
12822   Function value:
12823     CM_SUCCESS              Bank has been deleted
12824     0                       Bank has not been found
12825 
12826 \********************************************************************/
12827 {
12828    BANK *pbk;
12829    BANK32 *pbk32;
12830    DWORD dname;
12831    int remaining;
12832 
12833    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12834       /* locate bank */
12835       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12836       strncpy((char *) &dname, name, 4);
12837       do {
12838          if (*((DWORD *) pbk32->name) == dname) {
12839             /* bank found, delete it */
12840             remaining =
12841                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12842                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk32 + 1) +
12843                                                      ALIGN8(pbk32->data_size));
12844 
12845             /* reduce total event size */
12846             ((BANK_HEADER *) event)->data_size -=
12847                 sizeof(BANK32) + ALIGN8(pbk32->data_size);
12848 
12849             /* copy remaining bytes */
12850             if (remaining > 0)
12851                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12852             return CM_SUCCESS;
12853          }
12854 
12855          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12856       } while ((DWORD) pbk32 - (DWORD) event <
12857                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12858    } else {
12859       /* locate bank */
12860       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12861       strncpy((char *) &dname, name, 4);
12862       do {
12863          if (*((DWORD *) pbk->name) == dname) {
12864             /* bank found, delete it */
12865             remaining =
12866                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12867                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk + 1) +
12868                                                      ALIGN8(pbk->data_size));
12869 
12870             /* reduce total event size */
12871             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12872 
12873             /* copy remaining bytes */
12874             if (remaining > 0)
12875                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12876             return CM_SUCCESS;
12877          }
12878 
12879          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12880       } while ((DWORD) pbk - (DWORD) event <
12881                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12882    }
12883 
12884    return 0;
12885 }
12886 
12887 /**dox***************************************************************/
12888 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12889 
12890 /********************************************************************/
12891 /**
12892 Close the Midas bank priviously created by bk_create().
12893 The data pointer pdata must be obtained by bk_create() and
12894 used as an address to fill a bank. It is incremented with every value written
12895 to the bank and finally points to a location just after the last byte of the
12896 bank. It is then passed to bk_close() to finish the bank creation
12897 @param event pointer to current composed event
12898 @param pdata  pointer to the data
12899 @return number of bytes contained in bank
12900 */
12901 INT bk_close(void *event, void *pdata)
12902 {
12903    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12904       BANK32 *pbk32;
12905 
12906       pbk32 =
12907           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12908                       ((BANK_HEADER *) event)->data_size);
12909       pbk32->data_size = (DWORD) (INT) pdata - (INT) (pbk32 + 1);
12910       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12911          printf("Warning: bank %c%c%c%c has zero size\n",
12912                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12913       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12914       return pbk32->data_size;
12915    } else {
12916       BANK *pbk;
12917 
12918       pbk =
12919           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12920                     ((BANK_HEADER *) event)->data_size);
12921       pbk->data_size = (WORD) (INT) pdata - (INT) (pbk + 1);
12922       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12923          printf("Warning: bank %c%c%c%c has zero size\n",
12924                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12925       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12926       return pbk->data_size;
12927    }
12928 }
12929 
12930 /********************************************************************/
12931 /**
12932 Extract the MIDAS bank name listing of an event.
12933 The bklist should be dimensioned with STRING_BANKLIST_MAX
12934 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12935 \code
12936 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12937 {
12938   INT    n_adc, nbanks;
12939   WORD   *pdata;
12940   char   banklist[STRING_BANKLIST_MAX];
12941 
12942   // Display # of banks and list of banks in the event
12943   nbanks = bk_list(pevent, banklist);
12944   printf("#banks:%d List:%s\n", nbanks, banklist);
12945 
12946   // look for ADC0 bank, return if not present
12947   n_adc = bk_locate(pevent, "ADC0", &pdata);
12948   ...
12949 }
12950 \endcode
12951 @param event pointer to current composed event
12952 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12953 @return number of bank found in this event.
12954 */
12955 INT bk_list(void *event, char *bklist)
12956 {                               /* Full event */
12957    INT nbk, size;
12958    BANK *pmbk = NULL;
12959    BANK32 *pmbk32 = NULL;
12960    char *pdata;
12961 
12962    /* compose bank list */
12963    bklist[0] = 0;
12964    nbk = 0;
12965    do {
12966       /* scan all banks for bank name only */
12967       if (bk_is32(event)) {
12968          size = bk_iterate32(event, &pmbk32, &pdata);
12969          if (pmbk32 == NULL)
12970             break;
12971       } else {
12972          size = bk_iterate(event, &pmbk, &pdata);
12973          if (pmbk == NULL)
12974             break;
12975       }
12976       nbk++;
12977 
12978       if (nbk > BANKLIST_MAX) {
12979          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12980          return (nbk - 1);
12981       }
12982       if (bk_is32(event))
12983          strncat(bklist, (char *) pmbk32->name, 4);
12984       else
12985          strncat(bklist, (char *) pmbk->name, 4);
12986    }
12987    while (1);
12988    return (nbk);
12989 }
12990 
12991 /********************************************************************/
12992 /**
12993 Locates a MIDAS bank of given name inside an event.
12994 @param event pointer to current composed event
12995 @param name bank name to look for
12996 @param pdata pointer to data area of bank, NULL if bank not found
12997 @return number of values inside the bank
12998 */
12999 INT bk_locate(void *event, const char *name, void *pdata)
13000 {
13001    BANK *pbk;
13002    BANK32 *pbk32;
13003    DWORD dname;
13004 
13005    if (bk_is32(event)) {
13006       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
13007       strncpy((char *) &dname, name, 4);
13008       do {
13009          if (*((DWORD *) pbk32->name) == dname) {
13010             *((void **) pdata) = pbk32 + 1;
13011             if (tid_size[pbk32->type & 0xFF] == 0)
13012                return pbk32->data_size;
13013             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
13014          }
13015          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13016       } while ((DWORD) pbk32 - (DWORD) event <
13017                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13018    } else {
13019       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13020       strncpy((char *) &dname, name, 4);
13021       do {
13022          if (*((DWORD *) pbk->name) == dname) {
13023             *((void **) pdata) = pbk + 1;
13024             if (tid_size[pbk->type & 0xFF] == 0)
13025                return pbk->data_size;
13026             return pbk->data_size / tid_size[pbk->type & 0xFF];
13027          }
13028          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13029       } while ((DWORD) pbk - (DWORD) event <
13030                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13031    }
13032 
13033    /* bank not found */
13034    *((void **) pdata) = NULL;
13035    return 0;
13036 }
13037 
13038 /********************************************************************/
13039 /**
13040 Finds a MIDAS bank of given name inside an event.
13041 @param pbkh pointer to current composed event
13042 @param name bank name to look for
13043 @param bklen number of elemtents in bank
13044 @param bktype bank type, one of TID_xxx
13045 @param pdata pointer to data area of bank, NULL if bank not found
13046 @return 1 if bank found, 0 otherwise
13047 */
13048 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype,
13049             void **pdata)
13050 {
13051    BANK *pbk;
13052    BANK32 *pbk32;
13053    DWORD dname;
13054 
13055    if (bk_is32(pbkh)) {
13056       pbk32 = (BANK32 *) (pbkh + 1);
13057       strncpy((char *) &dname, name, 4);
13058       do {
13059          if (*((DWORD *) pbk32->name) == dname) {
13060             *((void **) pdata) = pbk32 + 1;
13061             if (tid_size[pbk32->type & 0xFF] == 0)
13062                *bklen = pbk32->data_size;
13063             else
13064                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
13065 
13066             *bktype = pbk32->type;
13067             return 1;
13068          }
13069          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13070       } while ((DWORD) pbk32 - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
13071    } else {
13072       pbk = (BANK *) (pbkh + 1);
13073       strncpy((char *) &dname, name, 4);
13074       do {
13075          if (*((DWORD *) pbk->name) == dname) {
13076             *((void **) pdata) = pbk + 1;
13077             if (tid_size[pbk->type & 0xFF] == 0)
13078                *bklen = pbk->data_size;
13079             else
13080                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
13081 
13082             *bktype = pbk->type;
13083             return 1;
13084          }
13085          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13086       } while ((DWORD) pbk - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
13087    }
13088 
13089    /* bank not found */
13090    *((void **) pdata) = NULL;
13091    return 0;
13092 }
13093 
13094 /********************************************************************/
13095 /**
13096 Iterates through banks inside an event.
13097 The function can be used to enumerate all banks of an event.
13098 The returned pointer to the bank header has following structure:
13099 \code
13100 typedef struct {
13101 char   name[4];
13102 WORD   type;
13103 WORD   data_size;
13104 } BANK;
13105 \endcode
13106 where type is a TID_xxx value and data_size the size of the bank in bytes.
13107 \code
13108 BANK *pbk;
13109 INT  size;
13110 void *pdata;
13111 char name[5];
13112 pbk = NULL;
13113 do
13114 {
13115  size = bk_iterate(event, &pbk, &pdata);
13116  if (pbk == NULL)
13117   break;
13118  *((DWORD *)name) = *((DWORD *)(pbk)->name);
13119  name[4] = 0;
13120  printf("bank %s found\n", name);
13121 } while(TRUE);
13122 \endcode
13123 @param event Pointer to data area of event.
13124 @param pbk pointer to the bank header, must be NULL for the first call to
13125 this function.
13126 @param pdata Pointer to the bank header, must be NULL for the first
13127 call to this function
13128 @return Size of bank in bytes
13129 */
13130 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
13131 {
13132    if (*pbk == NULL)
13133       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13134    else
13135       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13136 
13137    *((void **) pdata) = (*pbk) + 1;
13138 
13139    if ((DWORD) * pbk - (DWORD) event >=
13140        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13141       *pbk = *((BANK **) pdata) = NULL;
13142       return 0;
13143    }
13144 
13145    return (*pbk)->data_size;
13146 }
13147 
13148 
13149 /**dox***************************************************************/
13150 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13151 
13152 /********************************************************************/
13153 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
13154 /********************************************************************\
13155 
13156   Routine: bk_iterate
13157 
13158   Purpose: Iterate through 32 bit MIDAS banks inside an event
13159 
13160   Input:
13161     void   *event           pointer to the event
13162     BANK   **pbk32          must be NULL for the first call to bk_iterate
13163 
13164   Output:
13165     BANK   **pbk            pointer to the bank header
13166     void   *pdata           pointer to data area of the bank
13167 
13168   Function value:
13169     INT    size of the bank in bytes
13170 
13171 \********************************************************************/
13172 {
13173    if (*pbk == NULL)
13174       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
13175    else
13176       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13177 
13178    *((void **) pdata) = (*pbk) + 1;
13179 
13180    if ((DWORD) * pbk - (DWORD) event >=
13181        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13182       *pbk = *((BANK32 **) pdata) = NULL;
13183       return 0;
13184    }
13185 
13186    return (*pbk)->data_size;
13187 }
13188 
13189 /**dox***************************************************************/
13190 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13191 
13192 /********************************************************************/
13193 /**
13194 Swaps bytes from little endian to big endian or vice versa for a whole event.
13195 
13196 An event contains a flag which is set by bk_init() to identify
13197 the endian format of an event. If force is FALSE, this flag is evaluated and
13198 the event is only swapped if it is in the "wrong" format for this system.
13199 An event can be swapped to the "wrong" format on purpose for example by a
13200 front-end which wants to produce events in a "right" format for a back-end
13201 analyzer which has different byte ordering.
13202 @param event pointer to data area of event
13203 @param force If TRUE, the event is always swapped, if FALSE, the event
13204 is only swapped if it is in the wrong format.
13205 @return 1==event has been swap, 0==event has not been swapped.
13206 */
13207 INT bk_swap(void *event, BOOL force)
13208 {
13209    BANK_HEADER *pbh;
13210    BANK *pbk;
13211    BANK32 *pbk32;
13212    void *pdata;
13213    WORD type;
13214    BOOL b32;
13215 
13216    pbh = (BANK_HEADER *) event;
13217 
13218    /* only swap if flags in high 16-bit */
13219    if (pbh->flags < 0x10000 && !force)
13220       return 0;
13221 
13222    /* swap bank header */
13223    DWORD_SWAP(&pbh->data_size);
13224    DWORD_SWAP(&pbh->flags);
13225 
13226    /* check for 32bit banks */
13227    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
13228 
13229    pbk = (BANK *) (pbh + 1);
13230    pbk32 = (BANK32 *) pbk;
13231 
13232    /* scan event */
13233    while ((PTYPE) pbk - (PTYPE) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
13234       /* swap bank header */
13235       if (b32) {
13236          DWORD_SWAP(&pbk32->type);
13237          DWORD_SWAP(&pbk32->data_size);
13238          pdata = pbk32 + 1;
13239          type = (WORD) pbk32->type;
13240       } else {
13241          WORD_SWAP(&pbk->type);
13242          WORD_SWAP(&pbk->data_size);
13243          pdata = pbk + 1;
13244          type = pbk->type;
13245       }
13246 
13247       /* pbk points to next bank */
13248       if (b32) {
13249          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13250          pbk = (BANK *) pbk32;
13251       } else {
13252          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13253          pbk32 = (BANK32 *) pbk;
13254       }
13255 
13256       switch (type) {
13257       case TID_WORD:
13258       case TID_SHORT:
13259          while ((PTYPE) pdata < (PTYPE) pbk) {
13260             WORD_SWAP(pdata);
13261             pdata = (void *) (((WORD *) pdata) + 1);
13262          }
13263          break;
13264 
13265       case TID_DWORD:
13266       case TID_INT:
13267       case TID_BOOL:
13268       case TID_FLOAT:
13269          while ((PTYPE) pdata < (PTYPE) pbk) {
13270             DWORD_SWAP(pdata);
13271             pdata = (void *) (((DWORD *) pdata) + 1);
13272          }
13273          break;
13274 
13275       case TID_DOUBLE:
13276          while ((PTYPE) pdata < (PTYPE) pbk) {
13277             QWORD_SWAP(pdata);
13278             pdata = (void *) (((double *) pdata) + 1);
13279          }
13280          break;
13281       }
13282    }
13283 
13284    return CM_SUCCESS;
13285 }
13286 
13287 /**dox***************************************************************/
13288 /** @} */ /* end of bkfunctionc */
13289 
13290 /**dox***************************************************************/
13291 /** @addtogroup hsfunctionc
13292  *  
13293  *  @{  */
13294 
13295 #if !defined(OS_VXWORKS)
13296 /********************************************************************\
13297 *                                                                    *
13298 *                 History functions                                  *
13299 *                                                                    *
13300 \********************************************************************/
13301 
13302 /**dox***************************************************************/
13303 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13304 
13305 static HISTORY *_history;
13306 static INT _history_entries = 0;
13307 static char _hs_path_name[MAX_STRING_LENGTH];
13308 
13309 /**dox***************************************************************/
13310 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13311 
13312 /********************************************************************/
13313 /**
13314 Sets the path for future history file accesses. Should
13315 be called before any other history function is called.
13316 @param path             Directory where history files reside
13317 @return HS_SUCCESS
13318 */
13319 INT hs_set_path(char *path)
13320 {
13321    /* set path locally and remotely */
13322    if (rpc_is_remote())
13323       rpc_call(RPC_HS_SET_PATH, path);
13324 
13325    strcpy(_hs_path_name, path);
13326 
13327    /* check for trailing directory seperator */
13328    if (strlen(_hs_path_name) > 0 &&
13329        _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
13330       strcat(_hs_path_name, DIR_SEPARATOR_STR);
13331 
13332    return HS_SUCCESS;
13333 }
13334 
13335 
13336 /********************************************************************/
13337 /**
13338 Open history file belonging to certain date. Internal use
13339            only.
13340 @param ltime          Date for which a history file should be opened.
13341 @param suffix         File name suffix like "hst", "idx", "idf"
13342 @param mode           R/W access mode
13343 @param fh             File handle
13344 @return HS_SUCCESS
13345 */
13346 INT hs_open_file(DWORD ltime, char *suffix, INT mode, int *fh)
13347 {
13348    struct tm *tms;
13349    char file_name[256];
13350 
13351    /* generate new file name YYMMDD.xxx */
13352 #if !defined(OS_VXWORKS)
13353 #if !defined(OS_VMS)
13354    tzset();
13355 #endif
13356 #endif
13357    tms = localtime((const time_t *) &ltime);
13358 
13359    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
13360            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
13361 
13362    /* open file, add O_BINARY flag for Windows NT */
13363    *fh = open(file_name, mode | O_BINARY, 0644);
13364 
13365    return HS_SUCCESS;
13366 }
13367 
13368 /**dox***************************************************************/
13369 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13370 
13371 /********************************************************************/
13372 INT hs_gen_index(DWORD ltime)
13373 /********************************************************************\
13374 
13375   Routine: hs_gen_index
13376 
13377   Purpose: Regenerate index files ("idx" and "idf" files) for a given
13378            history file ("hst"). Interal use only.
13379 
13380   Input:
13381     DWORD  ltime            Date for which a history file should
13382                             be analyzed.
13383 
13384   Output:
13385     none
13386 
13387   Function value:
13388     HS_SUCCESS              Successful completion
13389     HS_FILE_ERROR           Index files cannot be created
13390 
13391 \********************************************************************/
13392 {
13393    char event_name[NAME_LENGTH];
13394    int fh, fhd, fhi;
13395    INT n;
13396    HIST_RECORD rec;
13397    INDEX_RECORD irec;
13398    DEF_RECORD def_rec;
13399 
13400    printf("Recovering index files...\n");
13401 
13402    if (ltime == 0)
13403       ltime = time(NULL);
13404 
13405    /* open new index file */
13406    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
13407    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
13408 
13409    if (fhd < 0 || fhi < 0) {
13410       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
13411       return HS_FILE_ERROR;
13412    }
13413 
13414    /* open history file */
13415    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13416    if (fh < 0)
13417       return HS_FILE_ERROR;
13418    lseek(fh, 0, SEEK_SET);
13419 
13420    /* loop over file records in .hst file */
13421    do {
13422       n = read(fh, (char *) &rec, sizeof(rec));
13423       if (n < sizeof(rec))
13424          break;
13425 
13426       /* check if record type is definition */
13427       if (rec.record_type == RT_DEF) {
13428          /* read name */
13429          read(fh, event_name, sizeof(event_name));
13430 
13431          printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
13432 
13433          /* write definition index record */
13434          def_rec.event_id = rec.event_id;
13435          memcpy(def_rec.event_name, event_name, sizeof(event_name));
13436          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
13437          write(fhd, (char *) &def_rec, sizeof(def_rec));
13438 
13439          /* skip tags */
13440          lseek(fh, rec.data_size, SEEK_CUR);
13441       } else {
13442          /* write index record */
13443          irec.event_id = rec.event_id;
13444          irec.time = rec.time;
13445          irec.offset = TELL(fh) - sizeof(rec);
13446          write(fhi, (char *) &irec, sizeof(irec));
13447 
13448          /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
13449 
13450          /* skip data */
13451          lseek(fh, rec.data_size, SEEK_CUR);
13452       }
13453 
13454    } while (TRUE);
13455 
13456    close(fh);
13457    close(fhi);
13458    close(fhd);
13459 
13460    printf("...done.\n");
13461 
13462    return HS_SUCCESS;
13463 }
13464 
13465 
13466 /********************************************************************/
13467 INT hs_search_file(DWORD * ltime, INT direction)
13468 /********************************************************************\
13469 
13470   Routine: hs_search_file
13471 
13472   Purpose: Search an history file for a given date. If not found,
13473            look for files after date (direction==1) or before date
13474            (direction==-1) up to one year.
13475 
13476   Input:
13477     DWORD  *ltime           Date of history file
13478     INT    direction        Search direction
13479 
13480   Output:
13481     DWORD  *ltime           Date of history file found
13482 
13483   Function value:
13484     HS_SUCCESS              Successful completion
13485     HS_FILE_ERROR           No file found
13486 
13487 \********************************************************************/
13488 {
13489    DWORD lt;
13490    int fh, fhd, fhi;
13491    struct tm *tms;
13492 
13493    if (*ltime == 0)
13494       *ltime = time(NULL);
13495 
13496    lt = *ltime;
13497    do {
13498       /* try to open history file for date "lt" */
13499       hs_open_file(lt, "hst", O_RDONLY, &fh);
13500 
13501       /* if not found, look for next day */
13502       if (fh < 0)
13503          lt += direction * 3600 * 24;
13504 
13505       /* stop if more than a year before starting point or in the future */
13506    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 &&
13507             lt < (DWORD) time(NULL));
13508 
13509    if (fh < 0)
13510       return HS_FILE_ERROR;
13511 
13512    if (lt != *ltime) {
13513       /* if switched to new day, set start_time to 0:00 */
13514       tms = localtime((const time_t *) &lt);
13515       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13516       *ltime = mktime(tms);
13517    }
13518 
13519    /* check if index files are there */
13520    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
13521    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
13522 
13523    close(fh);
13524    close(fhd);
13525    close(fhi);
13526 
13527    /* generate them if not */
13528    if (fhd < 0 || fhi < 0)
13529       hs_gen_index(*ltime);
13530 
13531    return HS_SUCCESS;
13532 }
13533 
13534 
13535 /********************************************************************/
13536 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
13537 /********************************************************************\
13538 
13539   Routine: hs_define_evnet
13540 
13541   Purpose: Define a new event for which a history should be recorded.
13542            This routine must be called before any call to
13543            hs_write_event. It also should be called if the definition
13544            of the event has changed.
13545 
13546            The event definition is written directly to the history
13547            file. If the definition is identical to a previous
13548            definition, it is not written to the file.
13549 
13550 
13551   Input:
13552     DWORD  event_id         ID for this event. Must be unique.
13553     char   name             Name of this event
13554     TAG    tag              Tag list containing names and types of
13555                             variables in this event.
13556     DWORD  size             Size of tag array
13557 
13558   Output:
13559     <none>
13560 
13561   Function value:
13562     HS_SUCCESS              Successful completion
13563     HS_NO_MEMEORY           Out of memory
13564     HS_FILE_ERROR           Cannot open history file
13565 
13566 \********************************************************************/
13567 {
13568 /* History events are only written locally (?)
13569 
13570   if (rpc_is_remote())
13571     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
13572 */
13573    {
13574       HIST_RECORD rec, prev_rec;
13575       DEF_RECORD def_rec;
13576       char str[256], event_name[NAME_LENGTH], *buffer;
13577       int fh, fhi, fhd;
13578       INT i, n, len, index;
13579       struct tm *tmb;
13580 
13581       /* allocate new space for the new history descriptor */
13582       if (_history_entries == 0) {
13583          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
13584          memset(_history, 0, sizeof(HISTORY));
13585          if (_history == NULL)
13586             return HS_NO_MEMORY;
13587 
13588          _history_entries = 1;
13589          index = 0;
13590       } else {
13591          /* check if history already open */
13592          for (i = 0; i < _history_entries; i++)
13593             if (_history[i].event_id == event_id)
13594                break;
13595 
13596          /* if not found, create new one */
13597          if (i == _history_entries) {
13598             _history =
13599                 (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
13600             memset(&_history[_history_entries], 0, sizeof(HISTORY));
13601 
13602             _history_entries++;
13603             if (_history == NULL) {
13604                _history_entries--;
13605                return HS_NO_MEMORY;
13606             }
13607          }
13608          index = i;
13609       }
13610 
13611       /* assemble definition record header */
13612       rec.record_type = RT_DEF;
13613       rec.event_id = event_id;
13614       rec.time = time(NULL);
13615       rec.data_size = size;
13616       strncpy(event_name, name, NAME_LENGTH);
13617 
13618       /* pad tag names with zeos */
13619       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
13620          len = strlen(tag[i].name);
13621          memset(tag[i].name + len, 0, NAME_LENGTH - len);
13622       }
13623 
13624       /* if history structure not set up, do so now */
13625       if (!_history[index].hist_fh) {
13626          /* open history file */
13627          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13628          if (fh < 0)
13629             return HS_FILE_ERROR;
13630 
13631          /* open index files */
13632          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13633          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13634          lseek(fh, 0, SEEK_END);
13635          lseek(fhi, 0, SEEK_END);
13636          lseek(fhd, 0, SEEK_END);
13637 
13638          /* regenerate index if missing */
13639          if (TELL(fh) > 0 && TELL(fhd) == 0) {
13640             close(fh);
13641             close(fhi);
13642             close(fhd);
13643             hs_gen_index(rec.time);
13644             hs_open_file(rec.time, "hst", O_RDWR, &fh);
13645             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
13646             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
13647             lseek(fh, 0, SEEK_END);
13648             lseek(fhi, 0, SEEK_END);
13649             lseek(fhd, 0, SEEK_END);
13650          }
13651 
13652          tmb = localtime((const time_t *) &rec.time);
13653          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
13654 
13655          /* setup history structure */
13656          _history[index].hist_fh = fh;
13657          _history[index].index_fh = fhi;
13658          _history[index].def_fh = fhd;
13659          _history[index].def_offset = TELL(fh);
13660          _history[index].event_id = event_id;
13661          strcpy(_history[index].event_name, event_name);
13662          _history[index].base_time = mktime(tmb);
13663          _history[index].n_tag = size / sizeof(TAG);
13664          _history[index].tag = (TAG *) M_MALLOC(size);
13665          memcpy(_history[index].tag, tag, size);
13666 
13667          /* search previous definition */
13668          n = TELL(fhd) / sizeof(def_rec);
13669          def_rec.event_id = 0;
13670          for (i = n - 1; i >= 0; i--) {
13671             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13672             read(fhd, (char *) &def_rec, sizeof(def_rec));
13673             if (def_rec.event_id == event_id)
13674                break;
13675          }
13676          lseek(fhd, 0, SEEK_END);
13677 
13678          /* if definition found, compare it with new one */
13679          if (def_rec.event_id == event_id) {
13680             buffer = (char *) M_MALLOC(size);
13681             memset(buffer, 0, size);
13682 
13683             lseek(fh, def_rec.def_offset, SEEK_SET);
13684             read(fh, (char *) &prev_rec, sizeof(prev_rec));
13685             read(fh, str, NAME_LENGTH);
13686             read(fh, buffer, size);
13687             lseek(fh, 0, SEEK_END);
13688 
13689             if (prev_rec.data_size != size ||
13690                 strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13691                /* write definition to history file */
13692                write(fh, (char *) &rec, sizeof(rec));
13693                write(fh, event_name, NAME_LENGTH);
13694                write(fh, (char *) tag, size);
13695 
13696                /* write index record */
13697                def_rec.event_id = event_id;
13698                memcpy(def_rec.event_name, event_name, sizeof(event_name));
13699                def_rec.def_offset = _history[index].def_offset;
13700                write(fhd, (char *) &def_rec, sizeof(def_rec));
13701             } else
13702                /* definition identical, just remember old offset */
13703                _history[index].def_offset = def_rec.def_offset;
13704 
13705             M_FREE(buffer);
13706          } else {
13707             /* write definition to history file */
13708             write(fh, (char *) &rec, sizeof(rec));
13709             write(fh, event_name, NAME_LENGTH);
13710             write(fh, (char *) tag, size);
13711 
13712             /* write definition index record */
13713             def_rec.event_id = event_id;
13714             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13715             def_rec.def_offset = _history[index].def_offset;
13716             write(fhd, (char *) &def_rec, sizeof(def_rec));
13717          }
13718       } else {
13719          fh = _history[index].hist_fh;
13720          fhd = _history[index].def_fh;
13721 
13722          /* compare definition with previous definition */
13723          buffer = (char *) M_MALLOC(size);
13724          memset(buffer, 0, size);
13725 
13726          lseek(fh, _history[index].def_offset, SEEK_SET);
13727          read(fh, (char *) &prev_rec, sizeof(prev_rec));
13728          read(fh, str, NAME_LENGTH);
13729          read(fh, buffer, size);
13730 
13731          lseek(fh, 0, SEEK_END);
13732          lseek(fhd, 0, SEEK_END);
13733 
13734          if (prev_rec.data_size != size ||
13735              strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13736             /* save new definition offset */
13737             _history[index].def_offset = TELL(fh);
13738 
13739             /* write definition to history file */
13740             write(fh, (char *) &rec, sizeof(rec));
13741             write(fh, event_name, NAME_LENGTH);
13742             write(fh, (char *) tag, size);
13743 
13744             /* write index record */
13745             def_rec.event_id = event_id;
13746             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13747             def_rec.def_offset = _history[index].def_offset;
13748             write(fhd, (char *) &def_rec, sizeof(def_rec));
13749          }
13750 
13751          M_FREE(buffer);
13752       }
13753 
13754    }
13755 
13756    return HS_SUCCESS;
13757 }
13758 
13759 
13760 /********************************************************************/
13761 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13762 /********************************************************************\
13763 
13764   Routine: hs_write_event
13765 
13766   Purpose: Write an event to a history file.
13767 
13768   Input:
13769     DWORD  event_id         Event ID
13770     void   *data            Data buffer containing event
13771     DWORD  size             Data buffer size in bytes
13772 
13773   Output:
13774     none
13775                             future hs_write_event
13776 
13777   Function value:
13778     HS_SUCCESS              Successful completion
13779     HS_NO_MEMEORY           Out of memory
13780     HS_FILE_ERROR           Cannot write to history file
13781     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13782 
13783 \********************************************************************/
13784 {
13785 /* history events are only written locally (?)
13786 
13787   if (rpc_is_remote())
13788     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13789 */
13790    HIST_RECORD rec, drec;
13791    DEF_RECORD def_rec;
13792    INDEX_RECORD irec;
13793    int fh, fhi, fhd;
13794    INT index;
13795    struct tm tmb, tmr;
13796 
13797    /* find index to history structure */
13798    for (index = 0; index < _history_entries; index++)
13799       if (_history[index].event_id == event_id)
13800          break;
13801    if (index == _history_entries)
13802       return HS_UNDEFINED_EVENT;
13803 
13804    /* assemble record header */
13805    rec.record_type = RT_DATA;
13806    rec.event_id = _history[index].event_id;
13807    rec.time = time(NULL);
13808    rec.def_offset = _history[index].def_offset;
13809    rec.data_size = size;
13810 
13811    irec.event_id = _history[index].event_id;
13812    irec.time = rec.time;
13813 
13814    /* check if new day */
13815    memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13816    memcpy(&tmb, localtime((const time_t *) &_history[index].base_time), sizeof(tmb));
13817 
13818    if (tmr.tm_yday != tmb.tm_yday) {
13819       /* close current history file */
13820       close(_history[index].hist_fh);
13821       close(_history[index].def_fh);
13822       close(_history[index].index_fh);
13823 
13824       /* open new history file */
13825       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13826       if (fh < 0)
13827          return HS_FILE_ERROR;
13828 
13829       /* open new index file */
13830       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13831       if (fhi < 0)
13832          return HS_FILE_ERROR;
13833 
13834       /* open new definition index file */
13835       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13836       if (fhd < 0)
13837          return HS_FILE_ERROR;
13838 
13839       lseek(fh, 0, SEEK_END);
13840       lseek(fhi, 0, SEEK_END);
13841       lseek(fhd, 0, SEEK_END);
13842 
13843       /* remember new file handles */
13844       _history[index].hist_fh = fh;
13845       _history[index].index_fh = fhi;
13846       _history[index].def_fh = fhd;
13847 
13848       _history[index].def_offset = TELL(fh);
13849       rec.def_offset = _history[index].def_offset;
13850 
13851       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13852       _history[index].base_time = mktime(&tmr);
13853 
13854       /* write definition from _history structure */
13855       drec.record_type = RT_DEF;
13856       drec.event_id = _history[index].event_id;
13857       drec.time = rec.time;
13858       drec.data_size = _history[index].n_tag * sizeof(TAG);
13859 
13860       write(fh, (char *) &drec, sizeof(drec));
13861       write(fh, _history[index].event_name, NAME_LENGTH);
13862       write(fh, (char *) _history[index].tag, drec.data_size);
13863 
13864       /* write definition index record */
13865       def_rec.event_id = _history[index].event_id;
13866       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13867       def_rec.def_offset = _history[index].def_offset;
13868       write(fhd, (char *) &def_rec, sizeof(def_rec));
13869    }
13870 
13871    /* got to end of file */
13872    lseek(_history[index].hist_fh, 0, SEEK_END);
13873    irec.offset = TELL(_history[index].hist_fh);
13874 
13875    /* write record header */
13876    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13877 
13878    /* write data */
13879    write(_history[index].hist_fh, (char *) data, size);
13880 
13881    /* write index record */
13882    lseek(_history[index].index_fh, 0, SEEK_END);
13883    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13884       return HS_FILE_ERROR;
13885 
13886    return HS_SUCCESS;
13887 }
13888 
13889 
13890 /********************************************************************/
13891 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size,
13892                    INT event_id[], DWORD * id_size)
13893 /********************************************************************\
13894 
13895   Routine: hs_enum_events
13896 
13897   Purpose: Enumerate events for a given date
13898 
13899   Input:
13900     DWORD  ltime            Date at which events should be enumerated
13901 
13902   Output:
13903     char   *event_name      Array containing event names
13904     DWORD  *name_size       Size of name array
13905     char   *event_id        Array containing event IDs
13906     DWORD  *id_size         Size of ID array
13907 
13908   Function value:
13909     HS_SUCCESS              Successful completion
13910     HS_NO_MEMEORY           Out of memory
13911     HS_FILE_ERROR           Cannot open history file
13912 
13913 \********************************************************************/
13914 {
13915    int fh, fhd;
13916    INT status, i, j, n;
13917    DEF_RECORD def_rec;
13918 
13919    if (rpc_is_remote())
13920       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id,
13921                       id_size);
13922 
13923    /* search latest history file */
13924    status = hs_search_file(&ltime, -1);
13925    if (status != HS_SUCCESS) {
13926       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13927       return HS_FILE_ERROR;
13928    }
13929 
13930    /* open history and definition files */
13931    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13932    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13933    if (fh < 0 || fhd < 0) {
13934       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13935       return HS_FILE_ERROR;
13936    }
13937    lseek(fhd, 0, SEEK_SET);
13938 
13939    /* loop over definition index file */
13940    n = 0;
13941    do {
13942       /* read event definition */
13943       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13944       if (j < (int) sizeof(def_rec))
13945          break;
13946 
13947       /* look for existing entry for this event id */
13948       for (i = 0; i < n; i++)
13949          if (event_id[i] == (INT) def_rec.event_id) {
13950             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13951             break;
13952          }
13953 
13954       /* new entry found */
13955       if (i == n) {
13956          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13957             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13958             close(fh);
13959             close(fhd);
13960             return HS_NO_MEMORY;
13961          }
13962 
13963          /* copy definition record */
13964          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13965          event_id[i] = def_rec.event_id;
13966          n++;
13967       }
13968    } while (TRUE);
13969 
13970    close(fh);
13971    close(fhd);
13972    *name_size = n * NAME_LENGTH;
13973    *id_size = n * sizeof(INT);
13974 
13975    return HS_SUCCESS;
13976 }
13977 
13978 
13979 /********************************************************************/
13980 INT hs_count_events(DWORD ltime, DWORD * count)
13981 /********************************************************************\
13982 
13983   Routine: hs_count_events
13984 
13985   Purpose: Count number of different events for a given date
13986 
13987   Input:
13988     DWORD  ltime            Date at which events should be counted
13989 
13990   Output:
13991     DWORD  *count           Number of different events found
13992 
13993   Function value:
13994     HS_SUCCESS              Successful completion
13995     HS_FILE_ERROR           Cannot open history file
13996 
13997 \********************************************************************/
13998 {
13999    int fh, fhd;
14000    INT status, i, j, n;
14001    DWORD *id;
14002    DEF_RECORD def_rec;
14003 
14004    if (rpc_is_remote())
14005       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
14006 
14007    /* search latest history file */
14008    status = hs_search_file(&ltime, -1);
14009    if (status != HS_SUCCESS) {
14010       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
14011       return HS_FILE_ERROR;
14012    }
14013 
14014    /* open history and definition files */
14015    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14016    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14017    if (fh < 0 || fhd < 0) {
14018       cm_msg(MERROR, "hs_count_events", "cannot open index files");
14019       return HS_FILE_ERROR;
14020    }
14021 
14022    /* allocate event id array */
14023    lseek(fhd, 0, SEEK_END);
14024    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
14025    lseek(fhd, 0, SEEK_SET);
14026 
14027    /* loop over index file */
14028    n = 0;
14029    do {
14030       /* read definition index record */
14031       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
14032       if (j < (int) sizeof(def_rec))
14033          break;
14034 
14035       /* look for existing entries */
14036       for (i = 0; i < n; i++)
14037          if (id[i] == def_rec.event_id)
14038             break;
14039 
14040       /* new entry found */
14041       if (i == n) {
14042          id[i] = def_rec.event_id;
14043          n++;
14044       }
14045    } while (TRUE);
14046 
14047 
14048    M_FREE(id);
14049    close(fh);
14050    close(fhd);
14051    *count = n;
14052 
14053    return HS_SUCCESS;
14054 }
14055 
14056 
14057 /********************************************************************/
14058 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
14059 /********************************************************************\
14060 
14061   Routine: hs_get_event_id
14062 
14063   Purpose: Return event ID for a given name. If event cannot be found
14064            in current definition file, go back in time until found
14065 
14066   Input:
14067     DWORD  ltime            Date at which event ID should be looked for
14068 
14069   Output:
14070     DWORD  *id              Event ID
14071 
14072   Function value:
14073     HS_SUCCESS              Successful completion
14074     HS_FILE_ERROR           Cannot open history file
14075     HS_UNDEFINED_EVENT      Event "name" not found
14076 
14077 \********************************************************************/
14078 {
14079    int fh, fhd;
14080    INT status, i;
14081    DWORD lt;
14082    DEF_RECORD def_rec;
14083 
14084    if (rpc_is_remote())
14085       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
14086 
14087    /* search latest history file */
14088    if (ltime == 0)
14089       ltime = time(NULL);
14090 
14091    lt = ltime;
14092 
14093    do {
14094       status = hs_search_file(&lt, -1);
14095       if (status != HS_SUCCESS) {
14096          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
14097          return HS_FILE_ERROR;
14098       }
14099 
14100       /* open history and definition files */
14101       hs_open_file(lt, "hst", O_RDONLY, &fh);
14102       hs_open_file(lt, "idf", O_RDONLY, &fhd);
14103       if (fh < 0 || fhd < 0) {
14104          cm_msg(MERROR, "hs_count_events", "cannot open index files");
14105          return HS_FILE_ERROR;
14106       }
14107 
14108       /* loop over index file */
14109       *id = 0;
14110       do {
14111          /* read definition index record */
14112          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
14113          if (i < (int) sizeof(def_rec))
14114             break;
14115 
14116          if (strcmp(name, def_rec.event_name) == 0) {
14117             *id = def_rec.event_id;
14118             close(fh);
14119             close(fhd);
14120             return HS_SUCCESS;
14121          }
14122       } while (TRUE);
14123 
14124       close(fh);
14125       close(fhd);
14126 
14127       /* not found -> go back one day */
14128       lt -= 3600 * 24;
14129 
14130    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
14131 
14132    return HS_UNDEFINED_EVENT;
14133 }
14134 
14135 
14136 /********************************************************************/
14137 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
14138 /********************************************************************\
14139 
14140   Routine: hs_count_vars
14141 
14142   Purpose: Count number of variables for a given date and event id
14143 
14144   Input:
14145     DWORD  ltime            Date at which tags should be counted
14146 
14147   Output:
14148     DWORD  *count           Number of tags
14149 
14150   Function value:
14151     HS_SUCCESS              Successful completion
14152     HS_FILE_ERROR           Cannot open history file
14153 
14154 \********************************************************************/
14155 {
14156    int fh, fhd;
14157    INT i, n, status;
14158    DEF_RECORD def_rec;
14159    HIST_RECORD rec;
14160 
14161    if (rpc_is_remote())
14162       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
14163 
14164    /* search latest history file */
14165    status = hs_search_file(&ltime, -1);
14166    if (status != HS_SUCCESS) {
14167       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
14168       return HS_FILE_ERROR;
14169    }
14170 
14171    /* open history and definition files */
14172    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14173    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14174    if (fh < 0 || fhd < 0) {
14175       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
14176       return HS_FILE_ERROR;
14177    }
14178 
14179    /* search last definition */
14180    lseek(fhd, 0, SEEK_END);
14181    n = TELL(fhd) / sizeof(def_rec);
14182    def_rec.event_id = 0;
14183    for (i = n - 1; i >= 0; i--) {
14184       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14185       read(fhd, (char *) &def_rec, sizeof(def_rec));
14186       if (def_rec.event_id == event_id)
14187          break;
14188    }
14189    if (def_rec.event_id != event_id) {
14190       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
14191       return HS_FILE_ERROR;
14192    }
14193 
14194    /* read definition */
14195    lseek(fh, def_rec.def_offset, SEEK_SET);
14196    read(fh, (char *) &rec, sizeof(rec));
14197    *count = rec.data_size / sizeof(TAG);
14198 
14199    close(fh);
14200    close(fhd);
14201 
14202    return HS_SUCCESS;
14203 }
14204 
14205 
14206 /********************************************************************/
14207 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD *size,
14208                  DWORD *var_n, DWORD *n_size)
14209 /********************************************************************\
14210 
14211   Routine: hs_enum_vars
14212 
14213   Purpose: Enumerate variable tags for a given date and event id
14214 
14215   Input:
14216     DWORD  ltime            Date at which tags should be enumerated
14217     DWORD  event_id         Event ID
14218 
14219   Output:
14220     char   *var_name        Array containing variable names
14221     DWORD  *size            Size of name array
14222     DWORD  *var_n           Array size of variable
14223     DWORD  *n_size          Size of n array
14224 
14225   Function value:
14226     HS_SUCCESS              Successful completion
14227     HS_NO_MEMEORY           Out of memory
14228     HS_FILE_ERROR           Cannot open history file
14229 
14230 \********************************************************************/
14231 {
14232    char str[256];
14233    int fh, fhd;
14234    INT i, n, status;
14235    DEF_RECORD def_rec;
14236    HIST_RECORD rec;
14237    TAG *tag;
14238 
14239    if (rpc_is_remote())
14240       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
14241 
14242    /* search latest history file */
14243    status = hs_search_file(&ltime, -1);
14244    if (status != HS_SUCCESS) {
14245       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
14246       return HS_FILE_ERROR;
14247    }
14248 
14249    /* open history and definition files */
14250    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14251    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14252    if (fh < 0 || fhd < 0) {
14253       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
14254       return HS_FILE_ERROR;
14255    }
14256 
14257    /* search last definition */
14258    lseek(fhd, 0, SEEK_END);
14259    n = TELL(fhd) / sizeof(def_rec);
14260    def_rec.event_id = 0;
14261    for (i = n - 1; i >= 0; i--) {
14262       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14263       read(fhd, (char *) &def_rec, sizeof(def_rec));
14264       if (def_rec.event_id == event_id)
14265          break;
14266    }
14267    if (def_rec.event_id != event_id) {
14268       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
14269       return HS_FILE_ERROR;
14270    }
14271 
14272    /* read definition header */
14273    lseek(fh, def_rec.def_offset, SEEK_SET);
14274    read(fh, (char *) &rec, sizeof(rec));
14275    read(fh, str, NAME_LENGTH);
14276 
14277    /* read event definition */
14278    n = rec.data_size / sizeof(TAG);
14279    tag = (TAG *) M_MALLOC(rec.data_size);
14280    read(fh, (char *) tag, rec.data_size);
14281 
14282    if (n * NAME_LENGTH > (INT) *size || n * sizeof(DWORD) > *n_size) {
14283       
14284       /* store partial definition */
14285       for (i = 0; i < (INT) *size / NAME_LENGTH; i++) {
14286          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14287          var_n[i] = tag[i].n_data;
14288       }
14289 
14290       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
14291       M_FREE(tag);
14292       close(fh);
14293       close(fhd);
14294       return HS_NO_MEMORY;
14295    }
14296 
14297    /* store full definition */
14298    for (i = 0; i < n; i++) {
14299       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14300       var_n[i] = tag[i].n_data;
14301    }
14302    *size = n * NAME_LENGTH;
14303    *n_size = n * sizeof(DWORD);
14304 
14305    M_FREE(tag);
14306    close(fh);
14307    close(fhd);
14308 
14309    return HS_SUCCESS;
14310 }
14311 
14312 
14313 /********************************************************************/
14314 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
14315 /********************************************************************\
14316 
14317   Routine: hs_get_var
14318 
14319   Purpose: Get definition for certain variable
14320 
14321   Input:
14322     DWORD  ltime            Date at which variable definition should
14323                             be returned
14324     DWORD  event_id         Event ID
14325     char   *var_name        Name of variable
14326 
14327   Output:
14328     INT    *type            Type of variable
14329     INT    *n_data          Number of items in variable
14330 
14331   Function value:
14332     HS_SUCCESS              Successful completion
14333     HS_NO_MEMEORY           Out of memory
14334     HS_FILE_ERROR           Cannot open history file
14335 
14336 \********************************************************************/
14337 {
14338    char str[256];
14339    int fh, fhd;
14340    INT i, n, status;
14341    DEF_RECORD def_rec;
14342    HIST_RECORD rec;
14343    TAG *tag;
14344 
14345    if (rpc_is_remote())
14346       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
14347 
14348    /* search latest history file */
14349    status = hs_search_file(&ltime, -1);
14350    if (status != HS_SUCCESS) {
14351       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
14352       return HS_FILE_ERROR;
14353    }
14354 
14355    /* open history and definition files */
14356    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14357    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14358    if (fh < 0 || fhd < 0) {
14359       cm_msg(MERROR, "hs_get_var", "cannot open index files");
14360       return HS_FILE_ERROR;
14361    }
14362 
14363    /* search last definition */
14364    lseek(fhd, 0, SEEK_END);
14365    n = TELL(fhd) / sizeof(def_rec);
14366    def_rec.event_id = 0;
14367    for (i = n - 1; i >= 0; i--) {
14368       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14369       read(fhd, (char *) &def_rec, sizeof(def_rec));
14370       if (def_rec.event_id == event_id)
14371          break;
14372    }
14373    if (def_rec.event_id != event_id) {
14374       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
14375       return HS_FILE_ERROR;
14376    }
14377 
14378    /* read definition header */
14379    lseek(fh, def_rec.def_offset, SEEK_SET);
14380    read(fh, (char *) &rec, sizeof(rec));
14381    read(fh, str, NAME_LENGTH);
14382 
14383    /* read event definition */
14384    n = rec.data_size / sizeof(TAG);
14385    tag = (TAG *) M_MALLOC(rec.data_size);
14386    read(fh, (char *) tag, rec.data_size);
14387 
14388    /* search variable */
14389    for (i = 0; i < n; i++)
14390       if (strcmp(tag[i].name, var_name) == 0)
14391          break;
14392 
14393    close(fh);
14394    close(fhd);
14395 
14396    if (i < n) {
14397       *type = tag[i].type;
14398       *n_data = tag[i].n_data;
14399    } else {
14400       *type = *n_data = 0;
14401       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
14402       M_FREE(tag);
14403       return HS_UNDEFINED_VAR;
14404    }
14405 
14406    M_FREE(tag);
14407    return HS_SUCCESS;
14408 }
14409 
14410 
14411 /********************************************************************/
14412 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
14413             DWORD interval, char *tag_name, DWORD var_index,
14414             DWORD * time_buffer, DWORD * tbsize,
14415             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
14416 /********************************************************************\
14417 
14418   Routine: hs_read
14419 
14420   Purpose: Read history for a variable at a certain time interval
14421 
14422   Input:
14423     DWORD  event_id         Event ID
14424     DWORD  start_time       Starting Date/Time
14425     DWORD  end_time         End Date/Time
14426     DWORD  interval         Minimum time in seconds between reported
14427                             events. Can be used to skip events
14428     char   *tag_name        Variable name inside event
14429     DWORD  var_index        Index if variable is array
14430 
14431   Output:
14432     DWORD  *time_buffer     Buffer containing times for each value
14433     DWORD  *tbsize          Size of time buffer
14434     void   *data_buffer     Buffer containing variable values
14435     DWORD  *dbsize          Data buffer size
14436     DWORD  *type            Type of variable (one of TID_xxx)
14437     DWORD  *n               Number of time/value pairs found
14438                             in specified interval and placed into
14439                             time_buffer and data_buffer
14440 
14441 
14442   Function value:
14443     HS_SUCCESS              Successful completion
14444     HS_NO_MEMEORY           Out of memory
14445     HS_FILE_ERROR           Cannot open history file
14446     HS_WRONG_INDEX          var_index exceeds array size of variable
14447     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
14448     HS_TRUNCATED            Buffer too small, data has been truncated
14449 
14450 \********************************************************************/
14451 {
14452    DWORD prev_time, last_irec_time;
14453    int fh, fhd, fhi, cp = 0;
14454    INT i, delta, index = 0, status, cache_size;
14455    INDEX_RECORD irec, *pirec;
14456    HIST_RECORD rec, drec;
14457    INT old_def_offset, var_size = 0, var_offset = 0;
14458    TAG *tag;
14459    char str[NAME_LENGTH];
14460    struct tm *tms;
14461    char *cache;
14462 
14463    if (rpc_is_remote())
14464       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
14465                       tag_name, var_index, time_buffer, tbsize, data_buffer,
14466                       dbsize, type, n);
14467 
14468    /* if not time given, use present to one hour in past */
14469    if (start_time == 0)
14470       start_time = time(NULL) - 3600;
14471    if (end_time == 0)
14472       end_time = time(NULL);
14473 
14474    /* search history file for start_time */
14475    status = hs_search_file(&start_time, 1);
14476    if (status != HS_SUCCESS) {
14477       cm_msg(MERROR, "hs_read", "cannot find recent history file");
14478       *tbsize = *dbsize = *n = 0;
14479       return HS_FILE_ERROR;
14480    }
14481 
14482    /* open history and definition files */
14483    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14484    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14485    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14486    if (fh < 0 || fhd < 0 || fhi < 0) {
14487       cm_msg(MERROR, "hs_read", "cannot open index files");
14488       *tbsize = *dbsize = *n = 0;
14489       return HS_FILE_ERROR;
14490    }
14491 
14492    /* try to read index file into cache */
14493    lseek(fhi, 0, SEEK_END);
14494    cache_size = TELL(fhi);
14495 
14496    if (cache_size > 0) {
14497       cache = (char *) M_MALLOC(cache_size);
14498       if (cache) {
14499          lseek(fhi, 0, SEEK_SET);
14500          i = read(fhi, cache, cache_size);
14501          if (i < cache_size) {
14502             M_FREE(cache);
14503             close(fh);
14504             close(fhd);
14505             close(fhi);
14506             return HS_FILE_ERROR;
14507          }
14508       }
14509 
14510       /* search record closest to start time */
14511       if (cache == NULL) {
14512          lseek(fhi, 0, SEEK_END);
14513          delta = (TELL(fhi) / sizeof(irec)) / 2;
14514          lseek(fhi, delta * sizeof(irec), SEEK_SET);
14515          do {
14516             delta = (int) (abs(delta) / 2.0 + 0.5);
14517             read(fhi, (char *) &irec, sizeof(irec));
14518             if (irec.time > start_time)
14519                delta = -delta;
14520 
14521             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14522          } while (abs(delta) > 1 && irec.time != start_time);
14523          read(fhi, (char *) &irec, sizeof(irec));
14524          if (irec.time > start_time)
14525             delta = -abs(delta);
14526 
14527          i = TELL(fhi) + (delta - 1) * sizeof(irec);
14528          if (i <= 0)
14529             lseek(fhi, 0, SEEK_SET);
14530          else
14531             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14532          read(fhi, (char *) &irec, sizeof(irec));
14533       } else {
14534          delta = (cache_size / sizeof(irec)) / 2;
14535          cp = delta * sizeof(irec);
14536          do {
14537             delta = (int) (abs(delta) / 2.0 + 0.5);
14538             pirec = (INDEX_RECORD *) (cache + cp);
14539             if (pirec->time > start_time)
14540                delta = -delta;
14541 
14542             cp = cp + delta * sizeof(irec);
14543          } while (abs(delta) > 1 && pirec->time != start_time);
14544          pirec = (INDEX_RECORD *) (cache + cp);
14545          if (pirec->time > start_time)
14546             delta = -abs(delta);
14547 
14548          if (cp <= delta * (int) sizeof(irec))
14549             cp = 0;
14550          else
14551             cp = cp + delta * sizeof(irec);
14552 
14553          if (cp >= cache_size)
14554             cp = cache_size - sizeof(irec);
14555          if (cp < 0)
14556             cp = 0;
14557 
14558          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
14559          cp += sizeof(irec);
14560       }
14561    } else {                     /* file size > 0 */
14562 
14563       cache = NULL;
14564       irec.time = start_time;
14565    }
14566 
14567    /* read records, skip wrong IDs */
14568    old_def_offset = -1;
14569    *n = 0;
14570    prev_time = 0;
14571    last_irec_time = 0;
14572    do {
14573       if (irec.time < last_irec_time) {
14574          cm_msg(MERROR, "hs_read",
14575                 "corrupted history data: time does not increase: %d -> %d",
14576                 last_irec_time, irec.time);
14577          *tbsize = *dbsize = *n = 0;
14578          return HS_FILE_ERROR;
14579       }
14580       last_irec_time = irec.time;
14581       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14582          /* check if record time more than "interval" seconds after previous time */
14583          if (irec.time >= prev_time + interval) {
14584             prev_time = irec.time;
14585             lseek(fh, irec.offset, SEEK_SET);
14586             read(fh, (char *) &rec, sizeof(rec));
14587 
14588             /* if definition changed, read new definition */
14589             if ((INT) rec.def_offset != old_def_offset) {
14590                lseek(fh, rec.def_offset, SEEK_SET);
14591                read(fh, (char *) &drec, sizeof(drec));
14592                read(fh, str, NAME_LENGTH);
14593 
14594                tag = (TAG *) M_MALLOC(drec.data_size);
14595                if (tag == NULL) {
14596                   *n = *tbsize = *dbsize = 0;
14597                   if (cache)
14598                      M_FREE(cache);
14599                   close(fh);
14600                   close(fhd);
14601                   close(fhi);
14602                   return HS_NO_MEMORY;
14603                }
14604                read(fh, (char *) tag, drec.data_size);
14605 
14606                /* find index of tag_name in new definition */
14607                index = -1;
14608                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
14609                   if (equal_ustring(tag[i].name, tag_name)) {
14610                      index = i;
14611                      break;
14612                   }
14613 
14614                /*
14615                   if ((DWORD) i == drec.data_size/sizeof(TAG))
14616                   {
14617                   *n = *tbsize = *dbsize = 0;
14618                   if (cache)
14619                   M_FREE(cache);
14620 
14621                   return HS_UNDEFINED_VAR;
14622                   }
14623                 */
14624 
14625                if (index >= 0 && var_index >= tag[i].n_data) {
14626                   *n = *tbsize = *dbsize = 0;
14627                   if (cache)
14628                      M_FREE(cache);
14629                   M_FREE(tag);
14630                   close(fh);
14631                   close(fhd);
14632                   close(fhi);
14633                   return HS_WRONG_INDEX;
14634                }
14635 
14636                /* calculate offset for variable */
14637                if (index >= 0) {
14638                   *type = tag[i].type;
14639 
14640                   /* loop over all previous variables */
14641                   for (i = 0, var_offset = 0; i < index; i++)
14642                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
14643 
14644                   /* strings have size n_data */
14645                   if (tag[index].type == TID_STRING)
14646                      var_size = tag[i].n_data;
14647                   else
14648                      var_size = rpc_tid_size(tag[index].type);
14649 
14650                   var_offset += var_size * var_index;
14651                }
14652 
14653                M_FREE(tag);
14654                old_def_offset = rec.def_offset;
14655                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14656             }
14657 
14658             if (index >= 0) {
14659                /* check if data fits in buffers */
14660                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
14661                   *dbsize = (*n) * var_size;
14662                   *tbsize = (*n) * sizeof(DWORD);
14663                   if (cache)
14664                      M_FREE(cache);
14665                   close(fh);
14666                   close(fhd);
14667                   close(fhi);
14668                   return HS_TRUNCATED;
14669                }
14670 
14671                /* copy time from header */
14672                time_buffer[*n] = irec.time;
14673 
14674                /* copy data from record */
14675                lseek(fh, var_offset, SEEK_CUR);
14676                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
14677 
14678                /* increment counter */
14679                (*n)++;
14680             }
14681          }
14682       }
14683 
14684       /* read next index record */
14685       if (cache) {
14686          if (cp >= cache_size) {
14687             i = -1;
14688             M_FREE(cache);
14689             cache = NULL;
14690          } else
14691             i = sizeof(irec);
14692 
14693          if (cp < cache_size) {
14694             memcpy(&irec, cache + cp, sizeof(irec));
14695             cp += sizeof(irec);
14696          }
14697       } else
14698          i = read(fhi, (char *) &irec, sizeof(irec));
14699 
14700       /* end of file: search next history file */
14701       if (i <= 0) {
14702          close(fh);
14703          close(fhd);
14704          close(fhi);
14705 
14706          /* advance one day */
14707          tms = localtime((const time_t *) &last_irec_time);
14708          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14709          last_irec_time = mktime(tms);
14710 
14711          last_irec_time += 3600 * 24;
14712 
14713          if (last_irec_time > end_time)
14714             break;
14715 
14716          /* search next file */
14717          status = hs_search_file(&last_irec_time, 1);
14718          if (status != HS_SUCCESS)
14719             break;
14720 
14721          /* open history and definition files */
14722          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14723          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14724          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14725          if (fh < 0 || fhd < 0 || fhi < 0) {
14726             cm_msg(MERROR, "hs_read", "cannot open index files");
14727             break;
14728          }
14729 
14730          /* try to read index file into cache */
14731          lseek(fhi, 0, SEEK_END);
14732          cache_size = TELL(fhi);
14733          lseek(fhi, 0, SEEK_SET);
14734          cache = (char *) M_MALLOC(cache_size);
14735          if (cache) {
14736             i = read(fhi, cache, cache_size);
14737             if (i < cache_size)
14738                break;
14739             /* read first record */
14740             cp = 0;
14741             memcpy(&irec, cache, sizeof(irec));
14742          } else {
14743             /* read first record */
14744             i = read(fhi, (char *) &irec, sizeof(irec));
14745             if (i <= 0)
14746                break;
14747          }
14748 
14749          /* old definition becomes invalid */
14750          old_def_offset = -1;
14751       }
14752    } while (irec.time < end_time);
14753 
14754    if (cache)
14755       M_FREE(cache);
14756    close(fh);
14757    close(fhd);
14758    close(fhi);
14759 
14760    *dbsize = *n * var_size;
14761    *tbsize = *n * sizeof(DWORD);
14762 
14763    return HS_SUCCESS;
14764 }
14765 
14766 
14767 /********************************************************************/
14768 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time,
14769             DWORD interval, BOOL binary_time)
14770 /********************************************************************\
14771 
14772   Routine: hs_dump
14773 
14774   Purpose: Display history for a given event at stdout. The output
14775            can be redirected to be read by Excel for example.
14776 
14777   Input:
14778     DWORD  event_id         Event ID
14779     DWORD  start_time       Starting Date/Time
14780     DWORD  end_time         End Date/Time
14781     DWORD  interval         Minimum time in seconds between reported
14782                             events. Can be used to skip events
14783     BOOL   binary_time      Display DWORD time stamp
14784   Output:
14785     <screen output>
14786 
14787   Function value:
14788     HS_SUCCESS              Successful completion
14789     HS_FILE_ERROR           Cannot open history file
14790 
14791 \********************************************************************/
14792 {
14793    DWORD prev_time, last_irec_time;
14794    int fh, fhd, fhi;
14795    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14796    INDEX_RECORD irec;
14797    HIST_RECORD rec, drec;
14798    INT old_def_offset, offset;
14799    TAG *tag = NULL, *old_tag = NULL;
14800    char str[NAME_LENGTH], data_buffer[10000];
14801    struct tm *tms;
14802 
14803    /* if not time given, use present to one hour in past */
14804    if (start_time == 0)
14805       start_time = time(NULL) - 3600;
14806    if (end_time == 0)
14807       end_time = time(NULL);
14808 
14809    /* search history file for start_time */
14810    status = hs_search_file(&start_time, 1);
14811    if (status != HS_SUCCESS) {
14812       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14813       return HS_FILE_ERROR;
14814    }
14815 
14816    /* open history and definition files */
14817    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14818    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14819    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14820    if (fh < 0 || fhd < 0 || fhi < 0) {
14821       cm_msg(MERROR, "hs_dump", "cannot open index files");
14822       return HS_FILE_ERROR;
14823    }
14824 
14825    /* search record closest to start time */
14826    lseek(fhi, 0, SEEK_END);
14827    delta = (TELL(fhi) / sizeof(irec)) / 2;
14828    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14829    do {
14830       delta = (int) (abs(delta) / 2.0 + 0.5);
14831       read(fhi, (char *) &irec, sizeof(irec));
14832       if (irec.time > start_time)
14833          delta = -delta;
14834 
14835       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14836    } while (abs(delta) > 1 && irec.time != start_time);
14837    read(fhi, (char *) &irec, sizeof(irec));
14838    if (irec.time > start_time)
14839       delta = -abs(delta);
14840 
14841    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14842    if (i <= 0)
14843       lseek(fhi, 0, SEEK_SET);
14844    else
14845       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14846    read(fhi, (char *) &irec, sizeof(irec));
14847 
14848    /* read records, skip wrong IDs */
14849    old_def_offset = -1;
14850    prev_time = 0;
14851    last_irec_time = 0;
14852    do {
14853       if (irec.time < last_irec_time) {
14854          cm_msg(MERROR, "hs_dump",
14855                 "corrupted history data: time does not increase: %d -> %d",
14856                 last_irec_time, irec.time);
14857          return HS_FILE_ERROR;
14858       }
14859       last_irec_time = irec.time;
14860       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14861          if (irec.time >= prev_time + interval) {
14862             prev_time = irec.time;
14863             lseek(fh, irec.offset, SEEK_SET);
14864             read(fh, (char *) &rec, sizeof(rec));
14865 
14866             /* if definition changed, read new definition */
14867             if ((INT) rec.def_offset != old_def_offset) {
14868                lseek(fh, rec.def_offset, SEEK_SET);
14869                read(fh, (char *) &drec, sizeof(drec));
14870                read(fh, str, NAME_LENGTH);
14871 
14872                if (tag == NULL)
14873                   tag = (TAG *) M_MALLOC(drec.data_size);
14874                else
14875                   tag = (TAG *) realloc(tag, drec.data_size);
14876                if (tag == NULL)
14877                   return HS_NO_MEMORY;
14878                read(fh, (char *) tag, drec.data_size);
14879                n_tag = drec.data_size / sizeof(TAG);
14880 
14881                /* print tag names if definition has changed */
14882                if (old_tag == NULL || old_n_tag != n_tag ||
14883                    memcmp(old_tag, tag, drec.data_size) != 0) {
14884                   printf("Date\t");
14885                   for (i = 0; i < n_tag; i++) {
14886                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14887                         printf("%s\t", tag[i].name);
14888                      else
14889                         for (j = 0; j < (INT) tag[i].n_data; j++)
14890                            printf("%s%d\t", tag[i].name, j);
14891                   }
14892                   printf("\n");
14893 
14894                   if (old_tag == NULL)
14895                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14896                   else
14897                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14898                   memcpy(old_tag, tag, drec.data_size);
14899                   old_n_tag = n_tag;
14900                }
14901 
14902                old_def_offset = rec.def_offset;
14903                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14904             }
14905 
14906             /* print time from header */
14907             if (binary_time)
14908                printf("%li ", irec.time);
14909             else {
14910                sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14911                str[20] = '\t';
14912                printf(str);
14913             }
14914 
14915             /* read data */
14916             read(fh, data_buffer, rec.data_size);
14917 
14918             /* interprete data from tag definition */
14919             offset = 0;
14920             for (i = 0; i < n_tag; i++) {
14921                /* strings have a length of n_data */
14922                if (tag[i].type == TID_STRING) {
14923                   printf("%s\t", data_buffer + offset);
14924                   offset += tag[i].n_data;
14925                } else if (tag[i].n_data == 1) {
14926                   /* non-array data */
14927                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14928                              tag[i].type);
14929                   printf("%s\t", str);
14930                   offset += rpc_tid_size(tag[i].type);
14931                } else
14932                   /* loop over array data */
14933                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14934                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14935                                 tag[i].type);
14936                      printf("%s\t", str);
14937                      offset += rpc_tid_size(tag[i].type);
14938                   }
14939             }
14940             printf("\n");
14941          }
14942       }
14943 
14944       /* read next index record */
14945       i = read(fhi, (char *) &irec, sizeof(irec));
14946 
14947       /* end of file: search next history file */
14948       if (i <= 0) {
14949          close(fh);
14950          close(fhd);
14951          close(fhi);
14952 
14953          /* advance one day */
14954          tms = localtime((const time_t *) &last_irec_time);
14955          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14956          last_irec_time = mktime(tms);
14957 
14958          last_irec_time += 3600 * 24;
14959          if (last_irec_time > end_time)
14960             break;
14961 
14962          /* search next file */
14963          status = hs_search_file(&last_irec_time, 1);
14964          if (status != HS_SUCCESS)
14965             break;
14966 
14967          /* open history and definition files */
14968          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14969          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14970          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14971          if (fh < 0 || fhd < 0 || fhi < 0) {
14972             cm_msg(MERROR, "hs_dump", "cannot open index files");
14973             break;
14974          }
14975 
14976          /* read first record */
14977          i = read(fhi, (char *) &irec, sizeof(irec));
14978          if (i <= 0)
14979             break;
14980 
14981          /* old definition becomes invalid */
14982          old_def_offset = -1;
14983       }
14984    } while (irec.time < end_time);
14985 
14986    M_FREE(tag);
14987    M_FREE(old_tag);
14988    close(fh);
14989    close(fhd);
14990    close(fhi);
14991 
14992    return HS_SUCCESS;
14993 }
14994 
14995 
14996 /********************************************************************/
14997 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14998 /********************************************************************\
14999 
15000   Routine: hs_fdump
15001 
15002   Purpose: Display history for a given history file
15003 
15004   Input:
15005     char   *file_name       Name of file to dump
15006     DWORD  event_id         Event ID
15007     BOOL   binary_time      Display DWORD time stamp
15008 
15009   Output:
15010     <screen output>
15011 
15012   Function value:
15013     HS_SUCCESS              Successful completion
15014     HS_FILE_ERROR           Cannot open history file
15015 
15016 \********************************************************************/
15017 {
15018    int fh;
15019    INT n;
15020    HIST_RECORD rec;
15021    char event_name[NAME_LENGTH];
15022    char str[80];
15023 
15024    /* open file, add O_BINARY flag for Windows NT */
15025    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
15026    if (fh < 0) {
15027       cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
15028       return HS_FILE_ERROR;
15029    }
15030 
15031    /* loop over file records in .hst file */
15032    do {
15033       n = read(fh, (char *) &rec, sizeof(rec));
15034       if (n < sizeof(rec))
15035          break;
15036 
15037       /* check if record type is definition */
15038       if (rec.record_type == RT_DEF) {
15039          /* read name */
15040          read(fh, event_name, sizeof(event_name));
15041 
15042          if (rec.event_id == id || id == 0)
15043             printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
15044 
15045          /* skip tags */
15046          lseek(fh, rec.data_size, SEEK_CUR);
15047       } else {
15048          /* print data record */
15049          if (binary_time)
15050             sprintf(str, "%li ", rec.time);
15051          else {
15052             strcpy(str, ctime((const time_t *) &rec.time) + 4);
15053             str[15] = 0;
15054          }
15055          if (rec.event_id == id || id == 0)
15056             printf("ID %ld, %s, size %ld\n", rec.event_id, str, rec.data_size);
15057 
15058          /* skip data */
15059          lseek(fh, rec.data_size, SEEK_CUR);
15060       }
15061 
15062    } while (TRUE);
15063 
15064    close(fh);
15065 
15066    return HS_SUCCESS;
15067 }
15068 #endif                          /* OS_VXWORKS hs section */
15069 
15070 /**dox***************************************************************/
15071 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15072 
15073 /**dox***************************************************************/
15074 /** @} */ /* end of hsfunctionc */
15075 
15076 /**dox***************************************************************/
15077 /** @addtogroup elfunctionc
15078  *  
15079  *  @{  */
15080 
15081 /**dox***************************************************************/
15082 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15083 
15084 /********************************************************************\
15085 *                                                                    *
15086 *               Electronic logbook functions                         *
15087 *                                                                    *
15088 \********************************************************************/
15089 
15090 /********************************************************************/
15091 void el_decode(char *message, char *key, char *result, int size)
15092 {
15093    char *rstart = result;
15094    char *pc;
15095 
15096    if (result == NULL)
15097       return;
15098 
15099    *result = 0;
15100 
15101    if (strstr(message, key)) {
15102       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
15103          *result++ = *pc++;
15104       *result = 0;
15105    }
15106 
15107    assert((int)strlen(rstart) < size);
15108 }
15109 
15110 /**dox***************************************************************/
15111 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15112 
15113 /********************************************************************/
15114 /**
15115 Submit an ELog entry.
15116 @param run  Run Number.
15117 @param author Message author.
15118 @param type Message type.
15119 @param system Message system.
15120 @param subject Subject.
15121 @param text Message text.
15122 @param reply_to In reply to this message.
15123 @param encoding Text encoding, either HTML or plain.
15124 @param afilename1   File name of attachment.
15125 @param buffer1      File contents.
15126 @param buffer_size1 Size of buffer in bytes.
15127 @param afilename2   File name of attachment.
15128 @param buffer2      File contents.
15129 @param buffer_size2 Size of buffer in bytes.
15130 @param afilename3   File name of attachment.
15131 @param buffer3      File contents.
15132 @param buffer_size3 Size of buffer in bytes.
15133 @param tag          If given, edit existing message.
15134 @param tag_size     Maximum size of tag.
15135 @return EL_SUCCESS
15136 */
15137 INT el_submit(int run, char *author, char *type, char *system, char *subject,
15138               char *text, char *reply_to, char *encoding,
15139               char *afilename1, char *buffer1, INT buffer_size1,
15140               char *afilename2, char *buffer2, INT buffer_size2,
15141               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
15142 {
15143    if (rpc_is_remote())
15144       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
15145                       text, reply_to, encoding,
15146                       afilename1, buffer1, buffer_size1,
15147                       afilename2, buffer2, buffer_size2,
15148                       afilename3, buffer3, buffer_size3, tag, tag_size);
15149 
15150 #ifdef LOCAL_ROUTINES
15151    {
15152       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset =
15153           0, tail_size = 0;
15154       struct tm *tms = NULL;
15155       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
15156           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
15157       HNDLE hDB;
15158       time_t now;
15159       char message[10000], *p, *buffer = NULL;
15160       BOOL bedit;
15161 
15162       cm_get_experiment_database(&hDB, NULL);
15163 
15164       bedit = (tag[0] != 0);
15165 
15166       /* request semaphore */
15167       cm_get_experiment_mutex(NULL, &mutex);
15168       ss_mutex_wait_for(mutex, 0);
15169 
15170       /* get run number from ODB if not given */
15171       if (run > 0)
15172          run_number = run;
15173       else {
15174          /* get run number */
15175          size = sizeof(run_number);
15176          status =
15177              db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
15178                           TRUE);
15179          assert(status == SUCCESS);
15180       }
15181 
15182       if (run_number < 0) {
15183          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d",
15184                 run_number);
15185          abort();
15186       }
15187 
15188       for (index = 0; index < 3; index++) {
15189          /* generate filename for attachment */
15190          afile_name[index][0] = file_name[0] = 0;
15191 
15192          if (index == 0) {
15193             strcpy(afilename, afilename1);
15194             buffer = buffer1;
15195             buffer_size = buffer_size1;
15196          } else if (index == 1) {
15197             strcpy(afilename, afilename2);
15198             buffer = buffer2;
15199             buffer_size = buffer_size2;
15200          } else if (index == 2) {
15201             strcpy(afilename, afilename3);
15202             buffer = buffer3;
15203             buffer_size = buffer_size3;
15204          }
15205 
15206          if (afilename[0]) {
15207             strcpy(file_name, afilename);
15208             p = file_name;
15209             while (strchr(p, ':'))
15210                p = strchr(p, ':') + 1;
15211             while (strchr(p, '\\'))
15212                p = strchr(p, '\\') + 1; /* NT */
15213             while (strchr(p, '/'))
15214                p = strchr(p, '/') + 1;  /* Unix */
15215             while (strchr(p, ']'))
15216                p = strchr(p, ']') + 1;  /* VMS */
15217 
15218             /* assemble ELog filename */
15219             if (p[0]) {
15220                dir[0] = 0;
15221                if (hDB > 0) {
15222                   size = sizeof(dir);
15223                   memset(dir, 0, size);
15224                   status =
15225                       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
15226                                    FALSE);
15227                   if (status != DB_SUCCESS)
15228                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING,
15229                                   TRUE);
15230 
15231                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15232                      strcat(dir, DIR_SEPARATOR_STR);
15233                }
15234 #if !defined(OS_VXWORKS)
15235 #if !defined(OS_VMS)
15236                tzset();
15237 #endif
15238 #endif
15239 
15240                time((time_t *) & now);
15241                tms = localtime((const time_t *) &now);
15242 
15243                strcpy(str, p);
15244                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
15245                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
15246                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
15247                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
15248                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
15249                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
15250 
15251                /* save attachment */
15252                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15253                if (fh < 0) {
15254                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"",
15255                          file_name);
15256                } else {
15257                   write(fh, buffer, buffer_size);
15258                   close(fh);
15259                }
15260             }
15261          }
15262       }
15263 
15264       /* generate new file name YYMMDD.log in data directory */
15265       cm_get_experiment_database(&hDB, NULL);
15266 
15267       size = sizeof(dir);
15268       memset(dir, 0, size);
15269       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15270       if (status != DB_SUCCESS)
15271          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15272 
15273       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15274          strcat(dir, DIR_SEPARATOR_STR);
15275 
15276 #if !defined(OS_VXWORKS)
15277 #if !defined(OS_VMS)
15278       tzset();
15279 #endif
15280 #endif
15281 
15282       if (bedit) {
15283          /* edit existing message */
15284          strcpy(str, tag);
15285          if (strchr(str, '.')) {
15286             offset = atoi(strchr(str, '.') + 1);
15287             *strchr(str, '.') = 0;
15288          }
15289          sprintf(file_name, "%s%s.log", dir, str);
15290          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15291          if (fh < 0) {
15292             ss_mutex_release(mutex);
15293             return EL_FILE_ERROR;
15294          }
15295          lseek(fh, offset, SEEK_SET);
15296          read(fh, str, 16);
15297          size = atoi(str + 9);
15298          read(fh, message, size);
15299 
15300          el_decode(message, "Date: ", date, sizeof(date));
15301          el_decode(message, "Thread: ", thread, sizeof(thread));
15302          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
15303 
15304          /* buffer tail of logfile */
15305          lseek(fh, 0, SEEK_END);
15306          tail_size = TELL(fh) - (offset + size);
15307 
15308          if (tail_size > 0) {
15309             buffer = (char *) M_MALLOC(tail_size);
15310             if (buffer == NULL) {
15311                close(fh);
15312                ss_mutex_release(mutex);
15313                return EL_FILE_ERROR;
15314             }
15315 
15316             lseek(fh, offset + size, SEEK_SET);
15317             n = read(fh, buffer, tail_size);
15318          }
15319          lseek(fh, offset, SEEK_SET);
15320       } else {
15321          /* create new message */
15322          time((time_t *) & now);
15323          tms = localtime((const time_t *) &now);
15324 
15325          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15326                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15327 
15328          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15329          if (fh < 0) {
15330             ss_mutex_release(mutex);
15331             return EL_FILE_ERROR;
15332          }
15333 
15334          strcpy(date, ctime(&now));
15335          date[24] = 0;
15336 
15337          if (reply_to[0])
15338             sprintf(thread, "%16s %16s", reply_to, "0");
15339          else
15340             sprintf(thread, "%16s %16s", "0", "0");
15341 
15342          lseek(fh, 0, SEEK_END);
15343       }
15344 
15345       /* compose message */
15346 
15347       sprintf(message, "Date: %s\n", date);
15348       sprintf(message + strlen(message), "Thread: %s\n", thread);
15349       sprintf(message + strlen(message), "Run: %d\n", run_number);
15350       sprintf(message + strlen(message), "Author: %s\n", author);
15351       sprintf(message + strlen(message), "Type: %s\n", type);
15352       sprintf(message + strlen(message), "System: %s\n", system);
15353       sprintf(message + strlen(message), "Subject: %s\n", subject);
15354 
15355       /* keep original attachment if edit and no new attachment */
15356       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 &&
15357           afile_name[2][0] == 0)
15358          sprintf(message + strlen(message), "Attachment: %s", attachment);
15359       else {
15360          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
15361          if (afile_name[1][0])
15362             sprintf(message + strlen(message), ",%s", afile_name[1]);
15363          if (afile_name[2][0])
15364             sprintf(message + strlen(message), ",%s", afile_name[2]);
15365       }
15366       sprintf(message + strlen(message), "\n");
15367 
15368       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
15369       sprintf(message + strlen(message), "========================================\n");
15370       strcat(message, text);
15371 
15372       assert(strlen(message) < sizeof(message)); /* bomb out on array overrun. */
15373 
15374       size = 0;
15375       sprintf(start_str, "$Start$: %6d\n", size);
15376       sprintf(end_str, "$End$:   %6d\n\f", size);
15377 
15378       size = strlen(message) + strlen(start_str) + strlen(end_str);
15379 
15380       if (tag != NULL && !bedit)
15381          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1,
15382                  tms->tm_mday, (int) TELL(fh));
15383 
15384       /* size has to fit in 6 digits */
15385       assert(size < 999999);
15386 
15387       sprintf(start_str, "$Start$: %6d\n", size);
15388       sprintf(end_str, "$End$:   %6d\n\f", size);
15389 
15390       write(fh, start_str, strlen(start_str));
15391       write(fh, message, strlen(message));
15392       write(fh, end_str, strlen(end_str));
15393 
15394       if (bedit) {
15395          if (tail_size > 0) {
15396             n = write(fh, buffer, tail_size);
15397             M_FREE(buffer);
15398          }
15399 
15400          /* truncate file here */
15401 #ifdef OS_WINNT
15402          chsize(fh, TELL(fh));
15403 #else
15404          ftruncate(fh, TELL(fh));
15405 #endif
15406       }
15407 
15408       close(fh);
15409 
15410       /* if reply, mark original message */
15411       if (reply_to[0] && !bedit) {
15412          strcpy(last, reply_to);
15413          do {
15414             status = el_search_message(last, &fh, FALSE);
15415             if (status == EL_SUCCESS) {
15416                /* position to next thread location */
15417                lseek(fh, 72, SEEK_CUR);
15418                memset(str, 0, sizeof(str));
15419                read(fh, str, 16);
15420                lseek(fh, -16, SEEK_CUR);
15421 
15422                /* if no reply yet, set it */
15423                if (atoi(str) == 0) {
15424                   sprintf(str, "%16s", tag);
15425                   write(fh, str, 16);
15426                   close(fh);
15427                   break;
15428                } else {
15429                   /* if reply set, find last one in chain */
15430                   strcpy(last, strtok(str, " "));
15431                   close(fh);
15432                }
15433             } else
15434                /* stop on error */
15435                break;
15436 
15437          } while (TRUE);
15438       }
15439 
15440       /* release elog mutex */
15441       ss_mutex_release(mutex);
15442    }
15443 #endif                          /* LOCAL_ROUTINES */
15444 
15445    return EL_SUCCESS;
15446 }
15447 
15448 /**dox***************************************************************/
15449 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15450 
15451 /********************************************************************/
15452 INT el_search_message(char *tag, int *fh, BOOL walk)
15453 {
15454    int i, size, offset, direction, last, status;
15455    struct tm *tms, ltms;
15456    DWORD lt, ltime, lact;
15457    char str[256], file_name[256], dir[256];
15458    HNDLE hDB;
15459 
15460 #if !defined(OS_VXWORKS)
15461 #if !defined(OS_VMS)
15462    tzset();
15463 #endif
15464 #endif
15465 
15466    /* open file */
15467    cm_get_experiment_database(&hDB, NULL);
15468 
15469    size = sizeof(dir);
15470    memset(dir, 0, size);
15471    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15472    if (status != DB_SUCCESS)
15473       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15474 
15475    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15476       strcat(dir, DIR_SEPARATOR_STR);
15477 
15478    /* check tag for direction */
15479    direction = 0;
15480    if (strpbrk(tag, "+-")) {
15481       direction = atoi(strpbrk(tag, "+-"));
15482       *strpbrk(tag, "+-") = 0;
15483    }
15484 
15485    /* if tag is given, open file directly */
15486    if (tag[0]) {
15487       /* extract time structure from tag */
15488       tms = &ltms;
15489       memset(tms, 0, sizeof(struct tm));
15490       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
15491       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
15492       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
15493       tms->tm_hour = 12;
15494 
15495       if (tms->tm_year < 90)
15496          tms->tm_year += 100;
15497       ltime = lt = mktime(tms);
15498 
15499       strcpy(str, tag);
15500       if (strchr(str, '.')) {
15501          offset = atoi(strchr(str, '.') + 1);
15502          *strchr(str, '.') = 0;
15503       } else
15504          return EL_FILE_ERROR;
15505 
15506       do {
15507          tms = localtime((const time_t *) &ltime);
15508 
15509          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15510                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15511          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15512 
15513          if (*fh < 0) {
15514             if (!walk)
15515                return EL_FILE_ERROR;
15516 
15517             if (direction == -1)
15518                ltime -= 3600 * 24;      /* one day back */
15519             else
15520                ltime += 3600 * 24;      /* go forward one day */
15521 
15522             /* set new tag */
15523             tms = localtime((const time_t *) &ltime);
15524             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1,
15525                     tms->tm_mday);
15526          }
15527 
15528          /* in forward direction, stop today */
15529          if (direction != -1 && ltime > (DWORD) time(NULL) + 3600 * 24)
15530             break;
15531 
15532          /* in backward direction, go back 10 years */
15533          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
15534             break;
15535 
15536       } while (*fh < 0);
15537 
15538       if (*fh < 0)
15539          return EL_FILE_ERROR;
15540 
15541       lseek(*fh, offset, SEEK_SET);
15542 
15543       /* check if start of message */
15544       i = read(*fh, str, 15);
15545       if (i <= 0) {
15546          close(*fh);
15547          return EL_FILE_ERROR;
15548       }
15549 
15550       if (strncmp(str, "$Start$: ", 9) != 0) {
15551          close(*fh);
15552          return EL_FILE_ERROR;
15553       }
15554 
15555       lseek(*fh, offset, SEEK_SET);
15556    }
15557 
15558    /* open most recent file if no tag given */
15559    if (tag[0] == 0) {
15560       time((long *) &lt);
15561       ltime = lt;
15562       do {
15563          tms = localtime((const time_t *) &ltime);
15564 
15565          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15566                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15567          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15568 
15569          if (*fh < 0)
15570             ltime -= 3600 * 24; /* one day back */
15571 
15572       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
15573 
15574       if (*fh < 0)
15575          return EL_FILE_ERROR;
15576 
15577       /* remember tag */
15578       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15579 
15580       lseek(*fh, 0, SEEK_END);
15581 
15582       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
15583    }
15584 
15585 
15586    if (direction == -1) {
15587       /* seek previous message */
15588 
15589       if (TELL(*fh) == 0) {
15590          /* go back one day */
15591          close(*fh);
15592 
15593          lt = ltime;
15594          do {
15595             lt -= 3600 * 24;
15596             tms = localtime((const time_t *) &lt);
15597             sprintf(str, "%02d%02d%02d.0",
15598                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15599 
15600             status = el_search_message(str, fh, FALSE);
15601 
15602          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
15603 
15604          if (status != EL_SUCCESS)
15605             return EL_FIRST_MSG;
15606 
15607          /* adjust tag */
15608          strcpy(tag, str);
15609 
15610          /* go to end of current file */
15611          lseek(*fh, 0, SEEK_END);
15612       }
15613 
15614       /* read previous message size */
15615       lseek(*fh, -17, SEEK_CUR);
15616       i = read(*fh, str, 17);
15617       if (i <= 0) {
15618          close(*fh);
15619          return EL_FILE_ERROR;
15620       }
15621 
15622       if (strncmp(str, "$End$: ", 7) != 0) {
15623          close(*fh);
15624          return EL_FILE_ERROR;
15625       }
15626         
15627       /* make sure the input string to atoi() is zero-terminated:
15628        * $End$:      355garbage
15629        * 01234567890123456789 */
15630       str[15] = 0;
15631 
15632       size = atoi(str + 7);
15633       assert(size > 15);
15634 
15635       lseek(*fh, -size, SEEK_CUR);
15636 
15637       /* adjust tag */
15638       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15639    }
15640 
15641    if (direction == 1) {
15642       /* seek next message */
15643 
15644       /* read current message size */
15645       last = TELL(*fh);
15646 
15647       i = read(*fh, str, 15);
15648       if (i <= 0) {
15649          close(*fh);
15650          return EL_FILE_ERROR;
15651       }
15652       lseek(*fh, -15, SEEK_CUR);
15653 
15654       if (strncmp(str, "$Start$: ", 9) != 0) {
15655          close(*fh);
15656          return EL_FILE_ERROR;
15657       }
15658 
15659       /* make sure the input string to atoi() is zero-terminated
15660        * $Start$:    606garbage
15661        * 01234567890123456789 */
15662       str[15] = 0;
15663 
15664       size = atoi(str+9);
15665       assert(size > 15);
15666 
15667       lseek(*fh, size, SEEK_CUR);
15668 
15669       /* if EOF, goto next day */
15670       i = read(*fh, str, 15);
15671       if (i < 15) {
15672          close(*fh);
15673          time((long *) &lact);
15674 
15675          lt = ltime;
15676          do {
15677             lt += 3600 * 24;
15678             tms = localtime((const time_t *) &lt);
15679             sprintf(str, "%02d%02d%02d.0",
15680                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15681 
15682             status = el_search_message(str, fh, FALSE);
15683 
15684          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
15685 
15686          if (status != EL_SUCCESS)
15687             return EL_LAST_MSG;
15688 
15689          /* adjust tag */
15690          strcpy(tag, str);
15691 
15692          /* go to beginning of current file */
15693          lseek(*fh, 0, SEEK_SET);
15694       } else
15695          lseek(*fh, -15, SEEK_CUR);
15696 
15697       /* adjust tag */
15698       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15699    }
15700 
15701    return EL_SUCCESS;
15702 }
15703 
15704 
15705 /********************************************************************/
15706 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
15707                 char *system, char *subject, char *text, int *textsize,
15708                 char *orig_tag, char *reply_tag,
15709                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
15710 /********************************************************************\
15711 
15712   Routine: el_retrieve
15713 
15714   Purpose: Retrieve an ELog entry by its message tab
15715 
15716   Input:
15717     char   *tag             tag in the form YYMMDD.offset
15718     int    *size            Size of text buffer
15719 
15720   Output:
15721     char   *tag             tag of retrieved message
15722     char   *date            Date/time of message recording
15723     int    *run             Run number
15724     char   *author          Message author
15725     char   *type            Message type
15726     char   *system          Message system
15727     char   *subject         Subject
15728     char   *text            Message text
15729     char   *orig_tag        Original message if this one is a reply
15730     char   *reply_tag       Reply for current message
15731     char   *attachment1/2/3 File attachment
15732     char   *encoding        Encoding of message
15733     int    *size            Actual message text size
15734 
15735   Function value:
15736     EL_SUCCESS              Successful completion
15737     EL_LAST_MSG             Last message in log
15738 
15739 \********************************************************************/
15740 {
15741    int size, fh = 0, offset, search_status, rd;
15742    char str[256], *p;
15743    char message[10000], thread[256], attachment_all[256];
15744 
15745    if (tag[0]) {
15746       search_status = el_search_message(tag, &fh, TRUE);
15747       if (search_status != EL_SUCCESS)
15748          return search_status;
15749    } else {
15750       /* open most recent message */
15751       strcpy(tag, "-1");
15752       search_status = el_search_message(tag, &fh, TRUE);
15753       if (search_status != EL_SUCCESS)
15754          return search_status;
15755    }
15756 
15757    /* extract message size */
15758    offset = TELL(fh);
15759    rd = read(fh, str, 15);
15760    assert(rd == 15);
15761    
15762    /* make sure the input string is zero-terminated before we call atoi() */
15763    str[15] = 0;
15764 
15765    /* get size */
15766    size = atoi(str+9);
15767    
15768    assert(strncmp(str,"$Start$:",8) == 0);
15769    assert(size > 15);
15770    assert(size < sizeof(message));
15771    
15772    memset(message, 0, sizeof(message));
15773 
15774    rd = read(fh, message, size);
15775    assert(rd > 0);
15776    assert((rd+15 == size)||(rd == size));
15777 
15778    close(fh);
15779 
15780    /* decode message */
15781    if (strstr(message, "Run: ") && run)
15782       *run = atoi(strstr(message, "Run: ") + 5);
15783 
15784    el_decode(message, "Date: ",     date,     80); /* size from show_elog_submit_query() */
15785    el_decode(message, "Thread: ", thread, sizeof(thread));
15786    el_decode(message, "Author: ",   author,   80); /* size from show_elog_submit_query() */
15787    el_decode(message, "Type: ",     type,     80); /* size from show_elog_submit_query() */
15788    el_decode(message, "System: ",   system,   80); /* size from show_elog_submit_query() */
15789    el_decode(message, "Subject: ",  subject, 256); /* size from show_elog_submit_query() */
15790    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15791    el_decode(message, "Encoding: ", encoding, 80); /* size from show_elog_submit_query() */
15792 
15793    /* break apart attachements */
15794    if (attachment1 && attachment2 && attachment3) {
15795       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15796       p = strtok(attachment_all, ",");
15797       if (p != NULL) {
15798          strcpy(attachment1, p);
15799          p = strtok(NULL, ",");
15800          if (p != NULL) {
15801             strcpy(attachment2, p);
15802             p = strtok(NULL, ",");
15803             if (p != NULL)
15804                strcpy(attachment3, p);
15805          }
15806       }
15807 
15808       assert(strlen(attachment1) < 256); /* size from show_elog_submit_query() */
15809       assert(strlen(attachment2) < 256); /* size from show_elog_submit_query() */
15810       assert(strlen(attachment3) < 256); /* size from show_elog_submit_query() */
15811    }
15812 
15813    /* conver thread in reply-to and reply-from */
15814    if (orig_tag != NULL && reply_tag != NULL) {
15815       p = strtok(thread, " \r");
15816       if (p != NULL)
15817          strcpy(orig_tag, p);
15818       else
15819          strcpy(orig_tag, "");
15820       p = strtok(NULL, " \r");
15821       if (p != NULL)
15822          strcpy(reply_tag, p);
15823       else
15824          strcpy(reply_tag, "");
15825       if (atoi(orig_tag) == 0)
15826          orig_tag[0] = 0;
15827       if (atoi(reply_tag) == 0)
15828          reply_tag[0] = 0;
15829    }
15830 
15831    p = strstr(message, "========================================\n");
15832 
15833    if (text != NULL) {
15834       if (p != NULL) {
15835          p += 41;
15836          if ((int) strlen(p) >= *textsize) {
15837             strncpy(text, p, *textsize - 1);
15838             text[*textsize - 1] = 0;
15839             return EL_TRUNCATED;
15840          } else {
15841             strcpy(text, p);
15842 
15843             /* strip end tag */
15844             if (strstr(text, "$End$"))
15845                *strstr(text, "$End$") = 0;
15846 
15847             *textsize = strlen(text);
15848          }
15849       } else {
15850          text[0] = 0;
15851          *textsize = 0;
15852       }
15853    }
15854 
15855    if (search_status == EL_LAST_MSG)
15856       return EL_LAST_MSG;
15857 
15858    return EL_SUCCESS;
15859 }
15860 
15861 
15862 /********************************************************************/
15863 INT el_search_run(int run, char *return_tag)
15864 /********************************************************************\
15865 
15866   Routine: el_search_run
15867 
15868   Purpose: Find first message belonging to a specific run
15869 
15870   Input:
15871     int    run              Run number
15872 
15873   Output:
15874     char   *tag             tag of retrieved message
15875 
15876   Function value:
15877     EL_SUCCESS              Successful completion
15878     EL_LAST_MSG             Last message in log
15879 
15880 \********************************************************************/
15881 {
15882    int actual_run, fh, status;
15883    char tag[256];
15884 
15885    tag[0] = return_tag[0] = 0;
15886 
15887    do {
15888       /* open first message in file */
15889       strcat(tag, "-1");
15890       status = el_search_message(tag, &fh, TRUE);
15891       if (status == EL_FIRST_MSG)
15892          break;
15893       if (status != EL_SUCCESS)
15894          return status;
15895       close(fh);
15896 
15897       if (strchr(tag, '.') != NULL)
15898          strcpy(strchr(tag, '.'), ".0");
15899 
15900       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15901                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15902    } while (actual_run >= run);
15903 
15904    while (actual_run < run) {
15905       strcat(tag, "+1");
15906       status = el_search_message(tag, &fh, TRUE);
15907       if (status == EL_LAST_MSG)
15908          break;
15909       if (status != EL_SUCCESS)
15910          return status;
15911       close(fh);
15912 
15913       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15914                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15915    }
15916 
15917    strcpy(return_tag, tag);
15918 
15919    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15920       return status;
15921 
15922    return EL_SUCCESS;
15923 }
15924 
15925 
15926 /********************************************************************/
15927 INT el_delete_message(char *tag)
15928 /********************************************************************\
15929 
15930   Routine: el_submit
15931 
15932   Purpose: Submit an ELog entry
15933 
15934   Input:
15935     char   *tag             Message tage
15936 
15937   Output:
15938     <none>
15939 
15940   Function value:
15941     EL_SUCCESS              Successful completion
15942 
15943 \********************************************************************/
15944 {
15945 #ifdef LOCAL_ROUTINES
15946    INT n, size, fh, mutex, offset = 0, tail_size, status;
15947    char dir[256], str[256], file_name[256];
15948    HNDLE hDB;
15949    char *buffer = NULL;
15950 
15951    cm_get_experiment_database(&hDB, NULL);
15952 
15953    /* request semaphore */
15954    cm_get_experiment_mutex(NULL, &mutex);
15955    ss_mutex_wait_for(mutex, 0);
15956 
15957    /* generate file name YYMMDD.log in data directory */
15958    cm_get_experiment_database(&hDB, NULL);
15959 
15960    size = sizeof(dir);
15961    memset(dir, 0, size);
15962    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15963    if (status != DB_SUCCESS)
15964       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15965 
15966    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15967       strcat(dir, DIR_SEPARATOR_STR);
15968 
15969    strcpy(str, tag);
15970    if (strchr(str, '.')) {
15971       offset = atoi(strchr(str, '.') + 1);
15972       *strchr(str, '.') = 0;
15973    }
15974    sprintf(file_name, "%s%s.log", dir, str);
15975    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15976    if (fh < 0) {
15977       ss_mutex_release(mutex);
15978       return EL_FILE_ERROR;
15979    }
15980    lseek(fh, offset, SEEK_SET);
15981    read(fh, str, 16);
15982    size = atoi(str + 9);
15983 
15984    /* buffer tail of logfile */
15985    lseek(fh, 0, SEEK_END);
15986    tail_size = TELL(fh) - (offset + size);
15987 
15988    if (tail_size > 0) {
15989       buffer = (char *) M_MALLOC(tail_size);
15990       if (buffer == NULL) {
15991          close(fh);
15992          ss_mutex_release(mutex);
15993          return EL_FILE_ERROR;
15994       }
15995 
15996       lseek(fh, offset + size, SEEK_SET);
15997       n = read(fh, buffer, tail_size);
15998    }
15999    lseek(fh, offset, SEEK_SET);
16000 
16001    if (tail_size > 0) {
16002       n = write(fh, buffer, tail_size);
16003       M_FREE(buffer);
16004    }
16005 
16006    /* truncate file here */
16007 #ifdef OS_WINNT
16008    chsize(fh, TELL(fh));
16009 #else
16010    ftruncate(fh, TELL(fh));
16011 #endif
16012 
16013    /* if file length gets zero, delete file */
16014    tail_size = lseek(fh, 0, SEEK_END);
16015    close(fh);
16016 
16017    if (tail_size == 0)
16018       remove(file_name);
16019 
16020    /* release elog mutex */
16021    ss_mutex_release(mutex);
16022 #endif                          /* LOCAL_ROUTINES */
16023 
16024    return EL_SUCCESS;
16025 }
16026 
16027 /**dox***************************************************************/
16028 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16029 
16030 /**dox***************************************************************/
16031 /** @} */ /* end of elfunctionc */
16032 
16033 /**dox***************************************************************/
16034 /** @addtogroup alfunctionc
16035  *  
16036  *  @{  */
16037 
16038 /**dox***************************************************************/
16039 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16040 
16041 /********************************************************************\
16042 *                                                                    *
16043 *                     Alarm functions                                *
16044 *                                                                    *
16045 \********************************************************************/
16046 
16047 /********************************************************************/
16048 BOOL al_evaluate_condition(char *condition, char *value)
16049 {
16050    HNDLE hDB, hkey;
16051    int i, j, index, size;
16052    KEY key;
16053    double value1, value2;
16054    char str[256], op[3], function[80];
16055    char data[10000];
16056    DWORD time;
16057 
16058    strcpy(str, condition);
16059    op[1] = op[2] = 0;
16060    value1 = value2 = 0;
16061    index = 0;
16062 
16063    /* find value and operator */
16064    for (i = strlen(str) - 1; i > 0; i--)
16065       if (strchr("<>=!", str[i]) != NULL)
16066          break;
16067    op[0] = str[i];
16068    value2 = atof(str + i + 1);
16069    str[i] = 0;
16070 
16071    if (i > 0 && strchr("<>=!", str[i - 1])) {
16072       op[1] = op[0];
16073       op[0] = str[--i];
16074       str[i] = 0;
16075    }
16076 
16077    i--;
16078    while (i > 0 && str[i] == ' ')
16079       i--;
16080    str[i + 1] = 0;
16081 
16082    /* check if function */
16083    function[0] = 0;
16084    if (str[i] == ')') {
16085       str[i--] = 0;
16086       if (strchr(str, '(')) {
16087          *strchr(str, '(') = 0;
16088          strcpy(function, str);
16089          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
16090             str[j] = str[i];
16091          str[j] = 0;
16092          i = j - 1;
16093       }
16094    }
16095 
16096    /* find key */
16097    if (str[i] == ']') {
16098       str[i--] = 0;
16099       while (i > 0 && isdigit(str[i]))
16100          i--;
16101       index = atoi(str + i + 1);
16102       str[i] = 0;
16103    }
16104 
16105    cm_get_experiment_database(&hDB, NULL);
16106    db_find_key(hDB, 0, str, &hkey);
16107    if (!hkey) {
16108       cm_msg(MERROR, "al_evaluate_condition",
16109              "Cannot find key %s to evaluate alarm condition", str);
16110       if (value)
16111          strcpy(value, "unknown");
16112       return FALSE;
16113    }
16114 
16115    if (equal_ustring(function, "access")) {
16116       /* check key access time */
16117       db_get_key_time(hDB, hkey, &time);
16118       sprintf(str, "%ld", time);
16119       value1 = atof(str);
16120    } else {
16121       /* get key data and convert to double */
16122       db_get_key(hDB, hkey, &key);
16123       size = sizeof(data);
16124       db_get_data(hDB, hkey, data, &size, key.type);
16125       db_sprintf(str, data, size, index, key.type);
16126       value1 = atof(str);
16127    }
16128 
16129    /* return value */
16130    if (value)
16131       strcpy(value, str);
16132 
16133    /* now do logical operation */
16134    if (strcmp(op, "=") == 0)
16135       return value1 == value2;
16136    if (strcmp(op, "==") == 0)
16137       return value1 == value2;
16138    if (strcmp(op, "!=") == 0)
16139       return value1 != value2;
16140    if (strcmp(op, "<") == 0)
16141       return value1 < value2;
16142    if (strcmp(op, ">") == 0)
16143       return value1 > value2;
16144    if (strcmp(op, "<=") == 0)
16145       return value1 <= value2;
16146    if (strcmp(op, ">=") == 0)
16147       return value1 >= value2;
16148 
16149    return FALSE;
16150 }
16151 
16152 /**dox***************************************************************/
16153 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16154 
16155 /********************************************************************/
16156 /**
16157 Trigger a certain alarm.
16158 \code  ...
16159   lazy.alarm[0] = 0;
16160   size = sizeof(lazy.alarm);
16161   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
16162 
16163   // trigger alarm if defined
16164   if (lazy.alarm[0])
16165     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
16166   ...
16167 \endcode
16168 @param alarm_name Alarm name, defined in /alarms/alarms
16169 @param alarm_message Optional message which goes with alarm
16170 @param default_class If alarm is not yet defined under
16171                     /alarms/alarms/<alarm_name>, a new one
16172                     is created and this default class is used.
16173 @param cond_str String displayed in alarm condition
16174 @param type Alarm type, one of AT_xxx
16175 @return AL_SUCCESS, AL_INVALID_NAME
16176 */
16177 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class,
16178                      char *cond_str, INT type)
16179 {
16180    if (rpc_is_remote())
16181       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message,
16182                       default_class, cond_str, type);
16183 
16184 #ifdef LOCAL_ROUTINES
16185    {
16186       int status, size;
16187       HNDLE hDB, hkeyalarm;
16188       char str[256];
16189       ALARM alarm;
16190       BOOL flag;
16191       ALARM_ODB_STR(alarm_odb_str);
16192 
16193       cm_get_experiment_database(&hDB, NULL);
16194 
16195       /* check online mode */
16196       flag = TRUE;
16197       size = sizeof(flag);
16198       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
16199       if (!flag)
16200          return AL_SUCCESS;
16201 
16202       /* find alarm */
16203       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16204       db_find_key(hDB, 0, str, &hkeyalarm);
16205       if (!hkeyalarm) {
16206          /* alarm must be an internal analyzer alarm, so create a default alarm */
16207          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
16208          db_find_key(hDB, 0, str, &hkeyalarm);
16209          if (!hkeyalarm) {
16210             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
16211             return AL_ERROR_ODB;
16212          }
16213 
16214          if (default_class && default_class[0])
16215             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
16216          status = TRUE;
16217          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
16218       }
16219 
16220       /* set parameters for internal alarms */
16221       if (type != AT_EVALUATED && type != AT_PERIODIC) {
16222          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
16223          strcpy(str, cond_str);
16224          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
16225       }
16226 
16227       size = sizeof(alarm);
16228       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16229       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16230          /* make sure alarm record has right structure */
16231          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
16232 
16233          size = sizeof(alarm);
16234          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16235          if (status != DB_SUCCESS) {
16236             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
16237             return AL_ERROR_ODB;
16238          }
16239       }
16240 
16241       /* if internal alarm, check if active and check interval */
16242       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
16243          /* check global alarm flag */
16244          flag = TRUE;
16245          size = sizeof(flag);
16246          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL,
16247                       TRUE);
16248          if (!flag)
16249             return AL_SUCCESS;
16250 
16251          if (!alarm.active)
16252             return AL_SUCCESS;
16253 
16254          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
16255             return AL_SUCCESS;
16256 
16257          /* now the alarm will be triggered, so save time */
16258          alarm.checked_last = ss_time();
16259       }
16260 
16261       /* write back alarm message for internal alarms */
16262       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
16263          strncpy(alarm.alarm_message, alarm_message, 79);
16264          alarm.alarm_message[79] = 0;
16265       }
16266 
16267       /* now trigger alarm class defined in this alarm */
16268       if (alarm.alarm_class[0])
16269          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
16270 
16271       /* signal alarm being triggered */
16272       cm_asctime(str, sizeof(str));
16273 
16274       if (!alarm.triggered)
16275          strcpy(alarm.time_triggered_first, str);
16276 
16277       alarm.triggered++;
16278       strcpy(alarm.time_triggered_last, str);
16279 
16280       alarm.checked_last = ss_time();
16281 
16282       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16283       if (status != DB_SUCCESS) {
16284          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
16285          return AL_ERROR_ODB;
16286       }
16287 
16288    }
16289 #endif                          /* LOCAL_ROUTINES */
16290 
16291    return AL_SUCCESS;
16292 }
16293 
16294 /**dox***************************************************************/
16295 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16296 
16297 /********************************************************************/
16298 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
16299 /********************************************************************\
16300 
16301   Routine: al_trigger_class
16302 
16303   Purpose: Trigger a certain alarm class
16304 
16305   Input:
16306     char   *alarm_class     Alarm class, must be defined in
16307                             /alarms/classes
16308     char   *alarm_message   Optional message which goes with alarm
16309     BOOL   first            TRUE if alarm is triggered first time
16310                             (used for elog)
16311 
16312   Output:
16313 
16314   Function value:
16315     AL_INVALID_NAME         Alarm class not defined
16316     AL_SUCCESS              Successful completion
16317 
16318 \********************************************************************/
16319 {
16320    int status, size, state;
16321    HNDLE hDB, hkeyclass;
16322    char str[256], command[256], tag[32];
16323    ALARM_CLASS ac;
16324 
16325    cm_get_experiment_database(&hDB, NULL);
16326 
16327    /* get alarm class */
16328    sprintf(str, "/Alarms/Classes/%s", alarm_class);
16329    db_find_key(hDB, 0, str, &hkeyclass);
16330    if (!hkeyclass) {
16331       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
16332       return AL_INVALID_NAME;
16333    }
16334 
16335    size = sizeof(ac);
16336    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16337    if (status != DB_SUCCESS) {
16338       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
16339       return AL_ERROR_ODB;
16340    }
16341 
16342    /* write system message */
16343    if (ac.write_system_message &&
16344        (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
16345       sprintf(str, "%s: %s", alarm_class, alarm_message);
16346       cm_msg(MTALK, "al_trigger_class", str);
16347       ac.system_message_last = ss_time();
16348    }
16349 
16350    /* write elog message on first trigger */
16351    if (ac.write_elog_message && first)
16352       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
16353                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
16354 
16355    /* execute command */
16356    if (ac.execute_command[0] &&
16357        ac.execute_interval > 0 &&
16358        (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
16359       sprintf(str, "%s: %s", alarm_class, alarm_message);
16360       sprintf(command, ac.execute_command, str);
16361       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
16362       ss_system(command);
16363       ac.execute_last = ss_time();
16364    }
16365 
16366    /* stop run */
16367    if (ac.stop_run) {
16368       state = STATE_STOPPED;
16369       size = sizeof(state);
16370       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
16371       if (state != STATE_STOPPED)
16372          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
16373    }
16374 
16375    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16376    if (status != DB_SUCCESS) {
16377       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
16378       return AL_ERROR_ODB;
16379    }
16380 
16381    return AL_SUCCESS;
16382 }
16383 
16384 
16385 /********************************************************************/
16386 INT al_reset_alarm(char *alarm_name)
16387 /********************************************************************\
16388 
16389   Routine: al_reset_alarm
16390 
16391   Purpose: Reset (acknowledge) alarm
16392 
16393   Input:
16394     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
16395                             If NULL reset all alarms
16396 
16397   Output:
16398     <none>
16399 
16400   Function value:
16401     AL_INVALID_NAME         Alarm name not defined
16402     AL_RESET                Alarm was triggered and reset
16403     AL_SUCCESS              Successful completion
16404 
16405 \********************************************************************/
16406 {
16407    int status, size, i;
16408    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
16409    KEY key;
16410    char str[256];
16411    ALARM alarm;
16412    ALARM_CLASS ac;
16413 
16414    cm_get_experiment_database(&hDB, NULL);
16415 
16416    if (alarm_name == NULL) {
16417       /* reset all alarms */
16418       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
16419       if (hkeyalarm) {
16420          for (i = 0;; i++) {
16421             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
16422 
16423             if (!hsubkey)
16424                break;
16425 
16426             db_get_key(hDB, hsubkey, &key);
16427             al_reset_alarm(key.name);
16428          }
16429       }
16430       return AL_SUCCESS;
16431    }
16432 
16433    /* find alarm and alarm class */
16434    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16435    db_find_key(hDB, 0, str, &hkeyalarm);
16436    if (!hkeyalarm) {
16437       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
16438       return AL_INVALID_NAME;
16439    }
16440 
16441    size = sizeof(alarm);
16442    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16443    if (status != DB_SUCCESS) {
16444       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
16445       return AL_ERROR_ODB;
16446    }
16447 
16448    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
16449    db_find_key(hDB, 0, str, &hkeyclass);
16450    if (!hkeyclass) {
16451       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB",
16452              alarm.alarm_class);
16453       return AL_INVALID_NAME;
16454    }
16455 
16456    size = sizeof(ac);
16457    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16458    if (status != DB_SUCCESS) {
16459       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
16460       return AL_ERROR_ODB;
16461    }
16462 
16463    if (alarm.triggered) {
16464       alarm.triggered = 0;
16465       alarm.time_triggered_first[0] = 0;
16466       alarm.time_triggered_last[0] = 0;
16467       alarm.checked_last = 0;
16468 
16469       ac.system_message_last = 0;
16470       ac.execute_last = 0;
16471 
16472       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16473       if (status != DB_SUCCESS) {
16474          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
16475          return AL_ERROR_ODB;
16476       }
16477       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16478       if (status != DB_SUCCESS) {
16479          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
16480          return AL_ERROR_ODB;
16481       }
16482       return AL_RESET;
16483    }
16484 
16485    return AL_SUCCESS;
16486 }
16487 
16488 
16489 /********************************************************************/
16490 INT al_check()
16491 /********************************************************************\
16492 
16493   Routine: al_scan
16494 
16495   Purpose: Scan ODB alarams and programs
16496 
16497   Input:
16498 
16499   Output:
16500 
16501   Function value:
16502     AL_SUCCESS              Successful completion
16503 
16504 \********************************************************************/
16505 {
16506    if (rpc_is_remote())
16507       return rpc_call(RPC_AL_CHECK);
16508 
16509 #ifdef LOCAL_ROUTINES
16510    {
16511       INT i, status, size, mutex;
16512       HNDLE hDB, hkeyroot, hkey;
16513       KEY key;
16514       ALARM alarm;
16515       char str[256], value[256];
16516       DWORD now;
16517       PROGRAM_INFO program_info;
16518       BOOL flag;
16519 
16520       ALARM_CLASS_STR(alarm_class_str);
16521       ALARM_ODB_STR(alarm_odb_str);
16522       ALARM_PERIODIC_STR(alarm_periodic_str);
16523 
16524       cm_get_experiment_database(&hDB, NULL);
16525 
16526       if (hDB == 0)
16527          return AL_SUCCESS;     /* called from server not yet connected */
16528 
16529       /* check online mode */
16530       flag = TRUE;
16531       size = sizeof(flag);
16532       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
16533       if (!flag)
16534          return AL_SUCCESS;
16535 
16536       /* check global alarm flag */
16537       flag = TRUE;
16538       size = sizeof(flag);
16539       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
16540       if (!flag)
16541          return AL_SUCCESS;
16542 
16543       /* request semaphore */
16544       cm_get_experiment_mutex(&mutex, NULL);
16545       status = ss_mutex_wait_for(mutex, 100);
16546       if (status != SS_SUCCESS)
16547          return SUCCESS;        /* someone else is doing alarm business */
16548 
16549       /* check ODB alarms */
16550       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16551       if (!hkeyroot) {
16552          /* create default ODB alarm */
16553          status =
16554              db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
16555          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16556          if (!hkeyroot) {
16557             ss_mutex_release(mutex);
16558             return SUCCESS;
16559          }
16560 
16561          status =
16562              db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic",
16563                               strcomb(alarm_periodic_str));
16564          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16565          if (!hkeyroot) {
16566             ss_mutex_release(mutex);
16567             return SUCCESS;
16568          }
16569 
16570          /* create default alarm classes */
16571          status =
16572              db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
16573          status =
16574              db_create_record(hDB, 0, "/Alarms/Classes/Warning",
16575                               strcomb(alarm_class_str));
16576          if (status != DB_SUCCESS) {
16577             ss_mutex_release(mutex);
16578             return SUCCESS;
16579          }
16580       }
16581 
16582       for (i = 0;; i++) {
16583          status = db_enum_key(hDB, hkeyroot, i, &hkey);
16584          if (status == DB_NO_MORE_SUBKEYS)
16585             break;
16586 
16587          db_get_key(hDB, hkey, &key);
16588 
16589          size = sizeof(alarm);
16590          status = db_get_record(hDB, hkey, &alarm, &size, 0);
16591          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16592             /* make sure alarm record has right structure */
16593             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
16594             size = sizeof(alarm);
16595             status = db_get_record(hDB, hkey, &alarm, &size, 0);
16596             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16597                cm_msg(MERROR, "al_check", "Cannot get alarm record");
16598                continue;
16599             }
16600          }
16601 
16602          /* check periodic alarm only when active */
16603          if (alarm.active &&
16604              alarm.type == AT_PERIODIC &&
16605              alarm.check_interval > 0 &&
16606              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16607             /* if checked_last has not been set, set it to current time */
16608             if (alarm.checked_last == 0) {
16609                alarm.checked_last = ss_time();
16610                db_set_record(hDB, hkey, &alarm, size, 0);
16611             } else
16612                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "",
16613                                 AT_PERIODIC);
16614          }
16615 
16616          /* check alarm only when active and not internal */
16617          if (alarm.active &&
16618              alarm.type == AT_EVALUATED &&
16619              alarm.check_interval > 0 &&
16620              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16621             /* if condition is true, trigger alarm */
16622             if (al_evaluate_condition(alarm.condition, value)) {
16623                sprintf(str, alarm.alarm_message, value);
16624                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
16625             } else {
16626                alarm.checked_last = ss_time();
16627                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
16628                if (status != DB_SUCCESS) {
16629                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
16630                   continue;
16631                }
16632             }
16633          }
16634       }
16635 
16636       /* check /programs alarms */
16637       db_find_key(hDB, 0, "/Programs", &hkeyroot);
16638       if (hkeyroot) {
16639          for (i = 0;; i++) {
16640             status = db_enum_key(hDB, hkeyroot, i, &hkey);
16641             if (status == DB_NO_MORE_SUBKEYS)
16642                break;
16643 
16644             db_get_key(hDB, hkey, &key);
16645 
16646             /* don't check "execute on xxx" */
16647             if (key.type != TID_KEY)
16648                continue;
16649 
16650             size = sizeof(program_info);
16651             status = db_get_record(hDB, hkey, &program_info, &size, 0);
16652             if (status != DB_SUCCESS) {
16653                cm_msg(MERROR, "al_check", "Cannot get program info record");
16654                continue;
16655             }
16656 
16657             now = ss_time();
16658 
16659             rpc_get_name(str);
16660             str[strlen(key.name)] = 0;
16661             if (!equal_ustring(str, key.name) &&
16662                 cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
16663                if (program_info.first_failed == 0)
16664                   program_info.first_failed = now;
16665 
16666                /* fire alarm when not running for more than what specified in check interval */
16667                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
16668                   /* if not running and alarm calss defined, trigger alarm */
16669                   if (program_info.alarm_class[0]) {
16670                      sprintf(str, "Program %s is not running", key.name);
16671                      al_trigger_alarm(key.name, str, program_info.alarm_class,
16672                                       "Program not running", AT_PROGRAM);
16673                   }
16674 
16675                   /* auto restart program */
16676                   if (program_info.auto_restart && program_info.start_command[0]) {
16677                      ss_system(program_info.start_command);
16678                      program_info.first_failed = 0;
16679                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
16680                   }
16681                }
16682             } else
16683                program_info.first_failed = 0;
16684 
16685             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
16686          }
16687       }
16688 
16689       ss_mutex_release(mutex);
16690    }
16691 #endif                          /* LOCAL_COUTINES */
16692 
16693    return SUCCESS;
16694 }
16695 
16696 /**dox***************************************************************/
16697 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16698 
16699 /** @} */ /* end of alfunctionc */
16700 
16701 /***** sKIP eb_xxx **************************************************/
16702 /**dox***************************************************************/
16703 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16704 /***** sKIP eb_xxx **************************************************/
16705 
16706 #if !defined(OS_VXWORKS)
16707 /********************************************************************\
16708 *                                                                    *
16709 *                 Event buffer functions                             *
16710 *                                                                    *
16711 \********************************************************************/
16712 
16713 /* PAA several modification in the eb_xxx()
16714    also new function eb_buffer_full()
16715 */
16716 static char *_event_ring_buffer = NULL;
16717 static INT _eb_size;
16718 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16719 
16720 /********************************************************************/
16721 INT eb_create_buffer(INT size)
16722 /********************************************************************\
16723 
16724   Routine: eb_create_buffer
16725 
16726   Purpose: Create an event buffer. Has to be called initially before
16727            any other eb_xxx function
16728 
16729   Input:
16730     INT    size             Size in bytes
16731 
16732   Output:
16733     none
16734 
16735   Function value:
16736     CM_SUCCESS              Successful completion
16737     BM_NO_MEMEORY           Out of memory
16738 
16739 \********************************************************************/
16740 {
16741    _event_ring_buffer = (char *) M_MALLOC(size);
16742    if (_event_ring_buffer == NULL)
16743       return BM_NO_MEMORY;
16744 
16745    memset(_event_ring_buffer, 0, size);
16746    _eb_size = size;
16747 
16748    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16749 
16750    _send_sock = rpc_get_event_sock();
16751 
16752    return CM_SUCCESS;
16753 }
16754 
16755 /********************************************************************/
16756 INT eb_free_buffer()
16757 /********************************************************************\
16758 
16759   Routine: eb_free_buffer
16760 
16761   Purpose: Free memory allocated voa eb_create_buffer
16762 
16763   Input:
16764     none
16765 
16766   Output:
16767     none
16768 
16769   Function value:
16770     CM_SUCCESS              Successful completion
16771 
16772 \********************************************************************/
16773 {
16774    if (_event_ring_buffer)
16775       M_FREE(_event_ring_buffer);
16776 
16777    _eb_size = 0;
16778    return CM_SUCCESS;
16779 }
16780 
16781 
16782 /********************************************************************/
16783 INT eb_free_space(void)
16784 /********************************************************************\
16785 
16786   Routine: eb_free_space
16787 
16788   Purpose: Compute and return usable free space in the event buffer
16789 
16790   Input:
16791     none
16792 
16793   Output:
16794     none
16795 
16796   Function value:
16797     INT    Number of usable free bytes in the event buffer
16798 
16799 \********************************************************************/
16800 {
16801    INT free;
16802 
16803    if (_event_ring_buffer == NULL) {
16804       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16805       return -1;
16806    }
16807 
16808    if (_eb_write_pointer >= _eb_read_pointer) {
16809       free = _eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer);
16810    } else if (_eb_write_pointer >= _event_ring_buffer) {
16811       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16812    } else if (_eb_end_pointer == _event_ring_buffer) {
16813       _eb_write_pointer = _event_ring_buffer;
16814       free = _eb_size;
16815    } else if (_eb_read_pointer == _event_ring_buffer) {
16816       free = 0;
16817    } else {
16818       _eb_write_pointer = _event_ring_buffer;
16819       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16820    }
16821 
16822    return free;
16823 }
16824 
16825 
16826 /********************************************************************/
16827 DWORD eb_get_level()
16828 /********************************************************************\
16829 
16830   Routine: eb_get_level
16831 
16832   Purpose: Return filling level of event buffer in percent
16833 
16834   Input:
16835     none
16836 
16837   Output:
16838     none
16839 
16840   Function value:
16841     DWORD level              0..99
16842 
16843 \********************************************************************/
16844 {
16845    INT size;
16846 
16847    size = _eb_size - eb_free_space();
16848 
16849    return (100 * size) / _eb_size;
16850 }
16851 
16852 
16853 /********************************************************************/
16854 BOOL eb_buffer_full(void)
16855 /********************************************************************\
16856 
16857   Routine: eb_buffer_full
16858 
16859   Purpose: Test if there is sufficient space in the event buffer
16860     for another event
16861 
16862   Input:
16863     none
16864 
16865   Output:
16866     none
16867 
16868   Function value:
16869     BOOL  Is there enough space for another event in the event buffer
16870 
16871 \********************************************************************/
16872 {
16873    INT free;
16874 
16875    free = eb_free_space();
16876 
16877    /* if max. event won't fit, return zero */
16878    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16879 }
16880 
16881 
16882 /********************************************************************/
16883 EVENT_HEADER *eb_get_pointer()
16884 /********************************************************************\
16885 
16886   Routine: eb_get_pointer
16887 
16888   Purpose: Get pointer to next free location in event buffer
16889 
16890   Input:
16891     none
16892 
16893   Output:
16894     none
16895 
16896   Function value:
16897     EVENT_HEADER *            Pointer to free location
16898 
16899 \********************************************************************/
16900 {
16901    /* if max. event won't fit, return zero */
16902    if (eb_buffer_full()) {
16903 #ifdef OS_VXWORKS
16904       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16905              _eb_read_pointer - _event_ring_buffer,
16906              _eb_write_pointer - _event_ring_buffer,
16907              _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16908 #endif
16909       return NULL;
16910    }
16911 
16912    /* leave space for buffer handle */
16913    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16914 }
16915 
16916 
16917 /********************************************************************/
16918 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16919 /********************************************************************\
16920 
16921   Routine: eb_increment_pointer
16922 
16923   Purpose: Increment write pointer of event buffer after an event
16924            has been copied into the buffer (at an address previously
16925            obtained via eb_get_pointer)
16926 
16927   Input:
16928     INT buffer_handle         Buffer handle event should be sent to
16929     INT event_size            Event size in bytes including header
16930 
16931   Output:
16932     none
16933 
16934   Function value:
16935     CM_SUCCESS                Successful completion
16936 
16937 \********************************************************************/
16938 {
16939    INT aligned_event_size;
16940 
16941    /* if not connected remotely, use bm_send_event */
16942    if (_send_sock == 0)
16943       return bm_send_event(buffer_handle,
16944                            _eb_write_pointer + sizeof(INT), event_size, SYNC);
16945 
16946    aligned_event_size = ALIGN8(event_size);
16947 
16948    /* copy buffer handle */
16949    *((INT *) _eb_write_pointer) = buffer_handle;
16950    _eb_write_pointer += sizeof(INT) + aligned_event_size;
16951 
16952    if (_eb_write_pointer > _eb_end_pointer)
16953       _eb_end_pointer = _eb_write_pointer;
16954 
16955    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16956       cm_msg(MERROR, "eb_increment_pointer",
16957              "event size (%d) exeeds maximum event size (%d)", event_size,
16958              MAX_EVENT_SIZE);
16959 
16960    if (_eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer) <
16961        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16962       _eb_write_pointer = _event_ring_buffer;
16963 
16964       /* avoid rp==wp */
16965       if (_eb_read_pointer == _event_ring_buffer)
16966          _eb_write_pointer--;
16967    }
16968 
16969    return CM_SUCCESS;
16970 }
16971 
16972 
16973 /********************************************************************/
16974 INT eb_send_events(BOOL send_all)
16975 /********************************************************************\
16976 
16977   Routine: eb_send_events
16978 
16979   Purpose: Send events from the event buffer to the server
16980 
16981   Input:
16982     BOOL send_all             If FALSE, only send events if buffer
16983                               contains more than _opt_tcp_size bytes
16984 
16985   Output:
16986     none
16987 
16988   Function value:
16989     CM_SUCCESS                Successful completion
16990 
16991 \********************************************************************/
16992 {
16993    char *eb_wp, *eb_ep;
16994    INT size, i;
16995 
16996    /* write pointers are volatile, so make copy */
16997    eb_ep = _eb_end_pointer;
16998    eb_wp = _eb_write_pointer;
16999 
17000    if (eb_wp == _eb_read_pointer)
17001       return CM_SUCCESS;
17002    if (eb_wp > _eb_read_pointer) {
17003       size = (PTYPE) eb_wp - (PTYPE) _eb_read_pointer;
17004 
17005       /* don't send if less than optimal TCP buffer size available */
17006       if (size < (INT) _opt_tcp_size && !send_all)
17007          return CM_SUCCESS;
17008    } else {
17009       /* send last piece of event buffer */
17010       size = (PTYPE) eb_ep - (PTYPE) _eb_read_pointer;
17011    }
17012 
17013    while (size > _opt_tcp_size) {
17014       /* send buffer */
17015       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
17016       if (i < 0) {
17017          printf("send_tcp() returned %d\n", i);
17018          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
17019          return RPC_NET_ERROR;
17020       }
17021 
17022       _eb_read_pointer += _opt_tcp_size;
17023       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
17024          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
17025 
17026       size -= _opt_tcp_size;
17027    }
17028 
17029    if (send_all || eb_wp < _eb_read_pointer) {
17030       /* send buffer */
17031       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
17032       if (i < 0) {
17033          printf("send_tcp() returned %d\n", i);
17034          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
17035          return RPC_NET_ERROR;
17036       }
17037 
17038       _eb_read_pointer += size;
17039       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
17040          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
17041    }
17042 
17043    /* Check for case where eb_wp = eb_ring_buffer - 1 */
17044    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
17045       return CM_SUCCESS;
17046    }
17047 
17048    if (eb_wp != _eb_read_pointer)
17049       return BM_MORE_EVENTS;
17050 
17051    return CM_SUCCESS;
17052 }
17053 
17054 #endif                          /* OS_VXWORKS  eb section */
17055 
17056 /**dox***************************************************************/
17057 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17058 
17059 /**dox***************************************************************/
17060 /** @addtogroup dmfunctionc
17061  *  
17062  *  @{  */
17063 
17064 /**dox***************************************************************/
17065 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17066 
17067 /********************************************************************\
17068 *                                                                    *
17069 *                 Dual memory buffer functions                       *
17070 *                                                                    *
17071 * Provide a dual memory buffer scheme for handling front-end         *
17072 * event. This code as been requested for allowing contemporary       *
17073 * task handling a)acquisition, b)network transfer if possible.       *
17074 * The pre-compiler switch will determine the mode of operation.      *
17075 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
17076 * a seperate task taking care of the dm_area_send                    *
17077 *                                                                    *
17078 * "*" : visible functions                                            *
17079 * dm_buffer_create():     *Setup the dual memory buffer              *
17080 *                          Setup semaphore                           *
17081 *                          Spawn second thread                       *
17082 * dm_buffer_release():    *Release memory allocation for dm          *
17083 *                          Force a kill of 2nd thread                *
17084 *                          Remove semaphore                          *
17085 * dm_area_full():         *Check for both area being full            *
17086 *                          None blocking, may be used for interrupt  *
17087 *                          disable.                                  *
17088 * dm_pointer_get()     :  *Check memory space and return pointer     *
17089 *                          Blocking function with timeout if no more *
17090 *                          space for next event. If error will abort.*
17091 * dm_pointer_increment(): *Move pointer to next free location        *
17092 *                          None blocking. performs bm_send_event if  *
17093 *                          local connection.                         *
17094 * dm_area_send():         *Transfer FULL buffer(s)                   *
17095 *                          None blocking function.                   *
17096 *                          if DUAL_THREAD: Give sem_send semaphore   *
17097 *                          else transfer FULL buffer                 *
17098 * dm_area_flush():        *Transfer all remaining events from dm     *
17099 *                          Blocking function with timeout            *
17100 *                          if DUAL_THREAD: Give sem_flush semaphore. *
17101 * dm_task():               Secondary thread handling DUAL_THREAD     *
17102 *                          mechanism. Serves 2 requests:             *
17103 *                          dm_send:  Transfer FULL buffer only.      *
17104 *                          dm_flush: Transfer ALL buffers.           *
17105 * dm_area_switch():        internal, used by dm_pointer_get()        *
17106 * dm_active_full():        internal: check space in current buffer   *
17107 * dm_buffer_send():        internal: send data for given area        *
17108 * dm_buffer_time_get():    interal: return the time stamp of the     *
17109 *                          last switch                               *
17110 \********************************************************************/
17111 
17112 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
17113 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
17114 #define DM_KILL        12       /* Kill request for 2nd thread */
17115 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
17116 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
17117 
17118 typedef struct {
17119    char *pt;                    /* top pointer    memory buffer          */
17120    char *pw;                    /* write pointer  memory buffer          */
17121    char *pe;                    /* end   pointer  memory buffer          */
17122    char *pb;                    /* bottom pointer memory buffer          */
17123    BOOL full;                   /* TRUE if memory buffer is full         */
17124    DWORD serial;                /* full buffer serial# for evt order     */
17125 } DMEM_AREA;
17126 
17127 typedef struct {
17128    DMEM_AREA *pa;               /* active memory buffer */
17129    DMEM_AREA area1;             /* mem buffer area 1 */
17130    DMEM_AREA area2;             /* mem buffer area 2 */
17131    DWORD serial;                /* overall buffer serial# for evt order     */
17132    INT action;                  /* for multi thread configuration */
17133    DWORD last_active;           /* switch time stamp */
17134    HNDLE sem_send;              /* semaphore for dm_task */
17135    HNDLE sem_flush;             /* semaphore for dm_task */
17136 } DMEM_BUFFER;
17137 
17138 DMEM_BUFFER dm;
17139 INT dm_user_max_event_size;
17140 
17141 /**dox***************************************************************/
17142 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17143 
17144 /********************************************************************/
17145 /**
17146 Setup a dual memory buffer. Has to be called initially before
17147            any other dm_xxx function
17148 @param size             Size in bytes
17149 @param user_max_event_size max event size
17150 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
17151 */
17152 INT dm_buffer_create(INT size, INT user_max_event_size)
17153 {
17154 
17155    dm.area1.pt = (char *) M_MALLOC(size);
17156    if (dm.area1.pt == NULL)
17157       return (BM_NO_MEMORY);
17158    dm.area2.pt = (char *) M_MALLOC(size);
17159    if (dm.area2.pt == NULL)
17160       return (BM_NO_MEMORY);
17161 
17162    /* check user event size against the system MAX_EVENT_SIZE */
17163    if (user_max_event_size > MAX_EVENT_SIZE) {
17164       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
17165       return BM_MEMSIZE_MISMATCH;
17166    }
17167    dm_user_max_event_size = user_max_event_size;
17168 
17169    memset(dm.area1.pt, 0, size);
17170    memset(dm.area2.pt, 0, size);
17171 
17172    /* initialize pointers */
17173    dm.area1.pb = dm.area1.pt + size - 1024;
17174    dm.area1.pw = dm.area1.pe = dm.area1.pt;
17175    dm.area2.pb = dm.area2.pt + size - 1024;
17176    dm.area2.pw = dm.area2.pe = dm.area2.pt;
17177 
17178   /*-PAA-*/
17179 #ifdef DM_DEBUG
17180    printf(" in dm_buffer_create ---------------------------------\n");
17181    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
17182    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
17183 #endif
17184 
17185    /* activate first area */
17186    dm.pa = &dm.area1;
17187 
17188    /* Default not full */
17189    dm.area1.full = dm.area2.full = FALSE;
17190 
17191    /* Reset serial buffer number with proper starting sequence */
17192    dm.area1.serial = dm.area2.serial = 0;
17193    /* ensure proper serial on next increment */
17194    dm.serial = 1;
17195 
17196    /* set active buffer time stamp */
17197    dm.last_active = ss_millitime();
17198 
17199    /* get socket for event sending */
17200    _send_sock = rpc_get_event_sock();
17201 
17202 #ifdef DM_DUAL_THREAD
17203    {
17204       INT status;
17205       VX_TASK_SPAWN starg;
17206 
17207       /* create semaphore */
17208       status = ss_mutex_create("send", &dm.sem_send);
17209       if (status != SS_CREATED && status != SS_SUCCESS) {
17210          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
17211          return status;
17212       }
17213       status = ss_mutex_create("flush", &dm.sem_flush);
17214       if (status != SS_CREATED && status != SS_SUCCESS) {
17215          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
17216          return status;
17217       }
17218       /* spawn dm_task */
17219       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
17220 
17221 #ifdef OS_VXWORKS
17222       /* Fill up the necessary arguments */
17223       strcpy(starg.name, "areaSend");
17224       starg.priority = 120;
17225       starg.stackSize = 20000;
17226 #endif
17227 
17228       if ((status = ss_thread_create(dm_task, (void *) &starg))
17229           != SS_SUCCESS) {
17230          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
17231          return status;
17232       }
17233 #ifdef OS_WINNT
17234       /* necessary for true MUTEX (NT) */
17235       ss_mutex_wait_for(dm.sem_send, 0);
17236 #endif
17237    }
17238 #endif                          /* DM_DUAL_THREAD */
17239 
17240    return CM_SUCCESS;
17241 }
17242 
17243 /**dox***************************************************************/
17244 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17245 
17246 /********************************************************************/
17247 INT dm_buffer_release(void)
17248 /********************************************************************\
17249   Routine: dm_buffer_release
17250 
17251   Purpose: Release dual memory buffers
17252   Input:
17253     none
17254   Output:
17255     none
17256   Function value:
17257     CM_SUCCESS              Successful completion
17258 \********************************************************************/
17259 {
17260    if (dm.area1.pt) {
17261       free(dm.area1.pt);
17262       dm.area1.pt = NULL;
17263    }
17264    if (dm.area2.pt) {
17265       free(dm.area2.pt);
17266       dm.area2.pt = NULL;
17267    }
17268    dm.serial = 0;
17269    dm.area1.full = dm.area2.full = TRUE;
17270    dm.area1.serial = dm.area2.serial = 0;
17271 
17272 #ifdef DM_DUAL_THREAD
17273    /* kill spawned dm_task */
17274    dm.action = DM_KILL;
17275    ss_mutex_release(dm.sem_send);
17276    ss_mutex_release(dm.sem_flush);
17277 
17278    /* release semaphore */
17279    ss_mutex_delete(dm.sem_send, 0);
17280    ss_mutex_delete(dm.sem_flush, 0);
17281 #endif
17282 
17283    return CM_SUCCESS;
17284 }
17285 
17286 /********************************************************************/
17287 INLINE DMEM_AREA *dm_area_switch(void)
17288 /********************************************************************\
17289   Routine: dm_area_switch
17290 
17291   Purpose: set active area to the other empty area or NULL if both
17292            area are full. May have to check the serial consistancy...
17293   Input:
17294     none
17295   Output:
17296     none
17297   Function value:
17298     DMEM_AREA *            Pointer to active area or both full
17299 \********************************************************************/
17300 {
17301    volatile BOOL full1, full2;
17302 
17303    full1 = dm.area1.full;
17304    full2 = dm.area2.full;
17305 
17306    if (!full1 && !full2) {
17307       if (dm.area1.serial <= dm.area2.serial)
17308          return (&(dm.area1));
17309       else
17310          return (&(dm.area2));
17311    }
17312 
17313    if (!full1) {
17314       return (&(dm.area1));
17315    } else if (!full2) {
17316       return (&(dm.area2));
17317    }
17318    return (NULL);
17319 }
17320 
17321 /********************************************************************/
17322 INLINE BOOL dm_area_full(void)
17323 /********************************************************************\
17324   Routine: dm_area_full
17325 
17326   Purpose: Test if both area are full in order to block interrupt
17327   Input:
17328     none
17329   Output:
17330     none
17331   Function value:
17332     BOOL         TRUE if not enough space for another event
17333 \********************************************************************/
17334 {
17335    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
17336       return TRUE;
17337    return FALSE;
17338 }
17339 
17340 /********************************************************************/
17341 INLINE BOOL dm_active_full(void)
17342 /********************************************************************\
17343   Routine: dm_active_full
17344 
17345   Purpose: Test if there is sufficient space in either event buffer
17346            for another event.
17347   Input:
17348     none
17349   Output:
17350     none
17351   Function value:
17352     BOOL         TRUE if not enough space for another event
17353 \********************************************************************/
17354 {
17355    /* catch both full areas, waiting for transfer */
17356    if (dm.pa == NULL)
17357       return TRUE;
17358    /* Check the space in the active buffer only
17359       as I don't switch buffer here */
17360    if (dm.pa->full)
17361       return TRUE;
17362    return (((PTYPE) dm.pa->pb - (PTYPE) dm.pa->pw) < (INT)
17363            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
17364 }
17365 
17366 /********************************************************************/
17367 DWORD dm_buffer_time_get(void)
17368 /********************************************************************\
17369   Routine: dm_buffer_time_get
17370 
17371   Purpose: return the time from the last buffer switch.
17372 
17373   Input:
17374     none
17375   Output:
17376     none
17377   Function value:
17378     DWORD        time stamp
17379 
17380 \********************************************************************/
17381 {
17382    return (dm.last_active);
17383 }
17384 
17385 
17386 /********************************************************************/
17387 EVENT_HEADER *dm_pointer_get(void)
17388 /********************************************************************\
17389   Routine: dm_pointer_get
17390 
17391   Purpose: Get pointer to next free location in event buffer.
17392            after 10sec tries, it times out return NULL indicating a
17393            serious problem, i.e. abort.
17394   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
17395   Input:
17396     none
17397   Output:
17398     DM_BUFFER * dm    local valid dm to work on
17399   Function value:
17400     EVENT_HEADER *    Pointer to free location
17401     NULL              cannot after several attempt get free space => abort
17402 \********************************************************************/
17403 {
17404    int timeout, status;
17405 
17406    /* Is there still space in the active area ? */
17407    if (!dm_active_full())
17408       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17409 
17410    /* no more space => switch area */
17411 
17412    /* Tag current area with global dm.serial for order consistency */
17413    dm.pa->serial = dm.serial++;
17414 
17415    /* set active buffer time stamp */
17416    dm.last_active = ss_millitime();
17417 
17418    /* mark current area full */
17419    dm.pa->full = TRUE;
17420 
17421    /* Trigger/do data transfer (Now/don't wait) */
17422    if ((status = dm_area_send()) == RPC_NET_ERROR) {
17423       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
17424       return NULL;
17425    }
17426 
17427    /* wait switch completion (max 10 sec) */
17428    timeout = ss_millitime();    /* with timeout */
17429    while ((ss_millitime() - timeout) < 10000) {
17430       dm.pa = dm_area_switch();
17431       if (dm.pa != NULL)
17432          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17433       ss_sleep(200);
17434 #ifdef DM_DEBUG
17435       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
17436              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
17437              dm.area2.serial, dm.serial);
17438 #endif
17439    }
17440 
17441    /* Time running out abort */
17442    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
17443    return NULL;
17444 }
17445 
17446 
17447 /********************************************************************/
17448 int dm_pointer_increment(INT buffer_handle, INT event_size)
17449 /********************************************************************\
17450   Routine: dm_pointer_increment
17451 
17452   Purpose: Increment write pointer of event buffer after an event
17453            has been copied into the buffer (at an address previously
17454            obtained via dm_pointer_get)
17455   Input:
17456     INT buffer_handle         Buffer handle event should be sent to
17457     INT event_size            Event size in bytes including header
17458   Output:
17459     none
17460   Function value:
17461     CM_SUCCESS                Successful completion
17462     status                    from bm_send_event for local connection
17463 \********************************************************************/
17464 {
17465    INT aligned_event_size;
17466 
17467    /* if not connected remotely, use bm_send_event */
17468    if (_send_sock == 0) {
17469       *((INT *) dm.pa->pw) = buffer_handle;
17470       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
17471    }
17472    aligned_event_size = ALIGN8(event_size);
17473 
17474    *((INT *) dm.pa->pw) = buffer_handle;
17475 
17476    /* adjust write pointer */
17477    dm.pa->pw += sizeof(INT) + aligned_event_size;
17478 
17479    /* adjust end pointer */
17480    dm.pa->pe = dm.pa->pw;
17481 
17482    return CM_SUCCESS;
17483 }
17484 
17485 /********************************************************************/
17486 INLINE INT dm_buffer_send(DMEM_AREA * larea)
17487 /********************************************************************\
17488   Routine: dm_buffer_send
17489 
17490   Purpose: Ship data to the cache in fact!
17491            Basically the same do loop is done in the send_tcp.
17492            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
17493            Introduced for bringing tcp option to user code.
17494   Input:
17495     DMEM_AREA * larea   The area to work with.
17496   Output:
17497     none
17498   Function value:
17499     CM_SUCCESS       Successful completion
17500     DM_ACTIVE_NULL   Both area were/are full
17501     RPC_NET_ERROR    send error
17502 \********************************************************************/
17503 {
17504    INT tot_size, nwrite;
17505    char *lpt;
17506 
17507    /* if not connected remotely, use bm_send_event */
17508    if (_send_sock == 0)
17509       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
17510 
17511    /* alias */
17512    lpt = larea->pt;
17513 
17514    /* Get overall buffer size */
17515    tot_size = (PTYPE) larea->pe - (PTYPE) lpt;
17516 
17517    /* shortcut for speed */
17518    if (tot_size == 0)
17519       return CM_SUCCESS;
17520 
17521 #ifdef DM_DEBUG
17522    printf("lpt:%p size:%i ", lpt, tot_size);
17523 #endif
17524    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
17525 #ifdef DM_DEBUG
17526    printf("nwrite:%i  errno:%i\n", nwrite, errno);
17527 #endif
17528    if (nwrite < 0)
17529       return RPC_NET_ERROR;
17530 
17531    /* reset area */
17532    larea->pw = larea->pe = larea->pt;
17533    larea->full = FALSE;
17534    return CM_SUCCESS;
17535 }
17536 
17537 /********************************************************************/
17538 INT dm_area_send(void)
17539 /********************************************************************\
17540   Routine: dm_area_send
17541 
17542   Purpose: Empty the FULL area only in proper event order
17543            Meant to be use either in mfe.c scheduler on every event
17544 
17545   Dual memory scheme:
17546    DM_DUAL_THREAD : Trigger sem_send
17547    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
17548                     if dm.pa is NULL (were both full) and now both are empty
17549 
17550   Input:
17551     none
17552   Output:
17553     none
17554   Function value:
17555     CM_SUCCESS                Successful completion
17556     RPC_NET_ERROR             send error
17557 \********************************************************************/
17558 {
17559 #ifdef DM_DUAL_THREAD
17560    INT status;
17561 
17562    /* force a DM_SEND if possible. Don't wait for completion */
17563    dm.action = DM_SEND;
17564    ss_mutex_release(dm.sem_send);
17565 #ifdef OS_WINNT
17566    /* necessary for true MUTEX (NT) */
17567    status = ss_mutex_wait_for(dm.sem_send, 1);
17568    if (status == SS_NO_MUTEX) {
17569       printf(" timeout while waiting for sem_send\n");
17570       return RPC_NET_ERROR;
17571    }
17572 #endif
17573 
17574    return CM_SUCCESS;
17575 #else
17576    /* ---------- NOT IN DUAL THREAD ----------- */
17577    INT status = 0;
17578 
17579    /* if no DUAL thread everything is local then */
17580    /* select the full area */
17581    if (dm.area1.full && dm.area2.full)
17582       if (dm.area1.serial <= dm.area2.serial)
17583          status = dm_buffer_send(&dm.area1);
17584       else
17585          status = dm_buffer_send(&dm.area2);
17586    else if (dm.area1.full)
17587       status = dm_buffer_send(&dm.area1);
17588    else if (dm.area2.full)
17589       status = dm_buffer_send(&dm.area2);
17590    if (status != CM_SUCCESS)
17591       return status;            /* catch transfer error too */
17592 
17593    if (dm.pa == NULL) {
17594       printf(" sync send dm.pa:%p full 1%li 2%li\n", dm.pa, dm.area1.full, dm.area2.full);
17595       dm.pa = &dm.area1;
17596    }
17597    return CM_SUCCESS;
17598 #endif
17599 }
17600 
17601 /********************************************************************/
17602 INT dm_task(void *pointer)
17603 /********************************************************************\
17604   Routine: dm_task
17605 
17606   Purpose: async send events doing a double purpose:
17607   a) send full buffer if found (DM_SEND) set by dm_active_full
17608   b) flush full areas (DM_FLUSH) set by dm_area_flush
17609   Input:
17610   none
17611   Output:
17612   none
17613   Function value:
17614   none
17615   \********************************************************************/
17616 {
17617 #ifdef DM_DUAL_THREAD
17618    INT status, timeout;
17619 
17620    printf("Semaphores initialization ... in areaSend ");
17621    /* Check or Wait for semaphore to be setup */
17622    timeout = ss_millitime();
17623    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
17624       ss_sleep(200);
17625    if (dm.sem_send == 0)
17626       goto kill;
17627 
17628 #ifdef OS_WINNT
17629    /* necessary for true MUTEX (NT) get semaphore */
17630    ss_mutex_wait_for(dm.sem_flush, 0);
17631 #endif
17632 
17633    /* Main FOREVER LOOP */
17634    printf("task areaSend ready...\n");
17635    while (1) {
17636       if (!dm_area_full()) {
17637          /* wait semaphore here ........ 0 == forever */
17638          ss_mutex_wait_for(dm.sem_send, 0);
17639 #ifdef OS_WINNT
17640          /* necessary for true MUTEX (NT) give semaphore */
17641          ss_mutex_release(dm.sem_send);
17642 #endif
17643       }
17644       if (dm.action == DM_SEND) {
17645 #ifdef DM_DEBUG
17646          printf("Send %i %i ", dm.area1.full, dm.area2.full);
17647 #endif
17648          /* DM_SEND : Empty the oldest buffer only. */
17649          if (dm.area1.full && dm.area2.full) {
17650             if (dm.area1.serial <= dm.area2.serial)
17651                status = dm_buffer_send(&dm.area1);
17652             else
17653                status = dm_buffer_send(&dm.area2);
17654          } else if (dm.area1.full)
17655             status = dm_buffer_send(&dm.area1);
17656          else if (dm.area2.full)
17657             status = dm_buffer_send(&dm.area2);
17658 
17659          if (status != CM_SUCCESS) {
17660             cm_msg(MERROR, "dm_task", "network error %i", status);
17661             goto kill;
17662          }
17663       } /* if DM_SEND */
17664       else if (dm.action == DM_FLUSH) {
17665          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
17666             events) Empty both area in order independently of being full or not */
17667          if (dm.area1.serial <= dm.area2.serial) {
17668             status = dm_buffer_send(&dm.area1);
17669             if (status != CM_SUCCESS)
17670                goto error;
17671             status = dm_buffer_send(&dm.area2);
17672             if (status != CM_SUCCESS)
17673                goto error;
17674          } else {
17675             status = dm_buffer_send(&dm.area2);
17676             if (status != CM_SUCCESS)
17677                goto error;
17678             status = dm_buffer_send(&dm.area1);
17679             if (status != CM_SUCCESS)
17680                goto error;
17681          }
17682          /* reset counter */
17683          dm.area1.serial = 0;
17684          dm.area2.serial = dm.serial = 1;
17685 #ifdef DM_DEBUG
17686          printf("dm.action: Flushing ...\n");
17687 #endif
17688          /* reset area to #1 */
17689          dm.pa = &dm.area1;
17690 
17691          /* release user */
17692          ss_mutex_release(dm.sem_flush);
17693 #ifdef OS_WINNT
17694          /* necessary for true MUTEX (NT) get semaphore back */
17695          ss_mutex_wait_for(dm.sem_flush, 0);
17696 #endif
17697       }
17698       /* if FLUSH */
17699       if (dm.action == DM_KILL)
17700          goto kill;
17701 
17702    }                            /* FOREVER (go back wainting for semaphore) */
17703 
17704    /* kill spawn now */
17705  error:
17706    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
17707  kill:
17708    ss_mutex_release(dm.sem_flush);
17709 #ifdef OS_WINNT
17710    ss_mutex_wait_for(dm.sem_flush, 1);
17711 #endif
17712    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
17713    exit;
17714    return 1;
17715 #else
17716    printf("DM_DUAL_THREAD not defined\n");
17717    return 0;
17718 #endif
17719 }
17720 
17721 /********************************************************************/
17722 INT dm_area_flush(void)
17723 /********************************************************************\
17724   Routine: dm_area_flush
17725 
17726   Purpose: Flush all the events in the areas.
17727            Used in mfe for BOR events, periodic events and
17728            if rate to low in main loop once a second. The standard
17729            data transfer should be done/triggered by dm_area_send (sync/async)
17730            in dm_pointer_get().
17731   Input:
17732     none
17733   Output:
17734     none
17735   Function value:
17736     CM_SUCCESS       Successful completion
17737     RPC_NET_ERROR    send error
17738 \********************************************************************/
17739 {
17740    INT status;
17741 #ifdef DM_DUAL_THREAD
17742    /* request FULL flush */
17743    dm.action = DM_FLUSH;
17744    ss_mutex_release(dm.sem_send);
17745 #ifdef OS_WINNT
17746    /* necessary for true MUTEX (NT) get semaphore back */
17747    ss_mutex_wait_for(dm.sem_send, 0);
17748 #endif
17749 
17750    /* important to wait for completion before continue with timeout
17751       timeout specified milliseconds */
17752    status = ss_mutex_wait_for(dm.sem_flush, 10000);
17753 #ifdef DM_DEBUG
17754    printf("dm_area_flush after waiting %i\n", status);
17755 #endif
17756 #ifdef OS_WINNT
17757    ss_mutex_release(dm.sem_flush);      /* give it back now */
17758 #endif
17759 
17760    return status;
17761 #else
17762    /* full flush done here */
17763    /* select in order both area independently of being full or not */
17764    if (dm.area1.serial <= dm.area2.serial) {
17765       status = dm_buffer_send(&dm.area1);
17766       if (status != CM_SUCCESS)
17767          return status;
17768       status = dm_buffer_send(&dm.area2);
17769       if (status != CM_SUCCESS)
17770          return status;
17771    } else {
17772       status = dm_buffer_send(&dm.area2);
17773       if (status != CM_SUCCESS)
17774          return status;
17775       status = dm_buffer_send(&dm.area1);
17776       if (status != CM_SUCCESS)
17777          return status;
17778    }
17779    /* reset serial counter */
17780    dm.area1.serial = dm.area2.serial = 0;
17781    dm.last_active = ss_millitime();
17782    return CM_SUCCESS;
17783 #endif
17784 }
17785 
17786 /********************************************************************/
17787 
17788 /**dox***************************************************************/
17789 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17790 
17791 /**dox***************************************************************/
17792 /** @} */ /* end of dmfunctionc */
17793 
17794 /**dox***************************************************************/
17795 /** @} */ /* 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 - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk