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.229  2004/12/17 08:16:06  midas
00010   Fixed compiler warnings
00011 
00012   Revision 1.228  2004/12/14 09:07:18  olchansk
00013   Add 5 minute timeouts to locking in bm_lock_buffer(), el_submit, el_delete_message()
00014 
00015   Revision 1.227  2004/11/26 20:42:52  pierre
00016   fix comment for VxWorks
00017 
00018   Revision 1.226  2004/11/10 05:40:23  pierre
00019   correct transition loop check on client side
00020 
00021   Revision 1.225  2004/11/10 01:05:46  pierre
00022   fix transition problem
00023 
00024   Revision 1.224  2004/10/07 22:04:17  pierre
00025   Doxygen correction
00026 
00027   Revision 1.223  2004/10/06 22:02:25  midas
00028   Implemented multiple requests for a transition
00029 
00030   Revision 1.221  2004/10/04 17:55:24  midas
00031   Fixed bug in cm_msg() with incorrect buffer size for 'message file'
00032 
00033   Revision 1.220  2004/10/04 15:35:25  midas
00034   Removed error triggered by bm_close_all_buffers()
00035 
00036   Revision 1.219  2004/10/01 23:35:53  midas
00037   Removed PRE/POST transitions and implemented sequence order of transitions
00038 
00039   Revision 1.218  2004/09/30 19:58:11  midas
00040   Added more debugging info to cm_transition
00041 
00042   Revision 1.217  2004/09/30 18:21:28  midas
00043   Changed debug printout for transitions
00044 
00045   Revision 1.216  2004/09/28 20:21:05  midas
00046   Fixed missing LF in debug output
00047 
00048   Revision 1.215  2004/09/28 20:05:59  midas
00049   Revised debug logging for mserver
00050 
00051   Revision 1.214  2004/09/28 18:30:24  midas
00052   Changed rpc_debug_print arguments
00053 
00054   Revision 1.213  2004/09/28 17:13:21  midas
00055   Added startup debug code for mserver
00056 
00057   Revision 1.212  2004/09/17 01:00:55  midas
00058   Lock ODB during startup of programs
00059 
00060   Revision 1.211  2004/07/15 14:59:35  midas
00061   Fixed compiler warning
00062 
00063   Revision 1.210  2004/07/15 14:58:12  midas
00064   Fixed severe bug in bm_check_buffers which crashed the logger during run stop
00065 
00066   Revision 1.209  2004/07/12 12:23:03  midas
00067   Fixed small bug in hs_enum_vars()
00068 
00069   Revision 1.208  2004/07/12 11:32:13  midas
00070   Fixed types in cm_msg()
00071 
00072   Revision 1.207  2004/05/07 19:40:11  midas
00073   Replaced min/max by MIN/MAX macros
00074 
00075   Revision 1.206  2004/05/03 11:30:37  midas
00076   Implemented cm_query_transition()
00077 
00078   Revision 1.205  2004/04/30 07:26:38  midas
00079   Fixed compiler warning
00080 
00081   Revision 1.204  2004/03/31 17:35:40  olchansk
00082   Fix the infamous problem with "last NN days broken" in Elog
00083   catch more memory overruns in the elog code
00084 
00085   Revision 1.203  2004/03/19 09:58:22  midas
00086   Changed client inactivity time check in cm_watchdog
00087 
00088   Revision 1.202  2004/01/17 05:35:53  olchansk
00089   replace #define ALIGN() with ALIGN8() to dodge namespace pollution under macosx
00090   hide strlcpy() & co #ifdef HAVE_STRLCPY (macosx already has strlcpy())
00091   correct inconsistent prototype of dbg_malloc() and dbg_calloc()
00092 
00093   Revision 1.201  2004/01/13 00:52:18  pierre
00094   fix dox comment for vxworks
00095 
00096   Revision 1.200  2004/01/08 08:40:10  midas
00097   Implemented standard indentation
00098 
00099   Revision 1.199  2004/01/08 06:44:00  pierre
00100   Doxygen the file
00101 
00102   Revision 1.198  2003/11/24 08:22:46  midas
00103   Changed timeouts from INT to DWORD, added ignore_timeout to cm_cleanup, adde '-f' flag to ODBEdit 'cleanup'
00104 
00105   Revision 1.197  2003/11/20 11:29:44  midas
00106   Implemented db_check_record and use it in most places instead of db_create_record
00107 
00108   Revision 1.196  2003/11/01 01:27:58  olchansk
00109   abort if cannot read /runinfo/run number
00110 
00111   Revision 1.195  2003/10/30 12:03:11  midas
00112   Removed tabs
00113 
00114   Revision 1.194  2003/10/13 00:07:40  olchansk
00115   refuse run number zero and abort on corrupted run numbers
00116 
00117   Revision 1.193  2003/10/12 22:56:33  olchansk
00118   when submitting new Elog message, add the message text to the outgoing email.
00119   add traps for some array overruns (see http://midas.triumf.ca/forum/Development%20Area/12)
00120 
00121   Revision 1.192  2003/09/04 11:47:48  midas
00122   Fixed problem with hKey in cm_transition
00123 
00124   Revision 1.191  2003/05/09 07:40:05  midas
00125   Added extra parameter to cm_get_environment
00126 
00127   Revision 1.190  2003/05/08 19:36:32  midas
00128   Changed size_t into INT
00129 
00130   Revision 1.189  2003/05/02 09:03:01  midas
00131   Fixed buffer overflows by strlcpy()
00132 
00133   Revision 1.188  2003/04/25 13:54:04  midas
00134   Fixed compiler warnings
00135 
00136   Revision 1.187  2003/04/22 12:01:29  midas
00137   Added graceful shutdown of odbedit->frontend connection
00138 
00139   Revision 1.186  2003/04/22 10:09:06  midas
00140   Added RPC_NODELAY option
00141 
00142   Revision 1.185  2003/04/16 19:34:42  pierre
00143   mv stdio.h, ctype.h into midasinc.h
00144 
00145   Revision 1.184  2003/04/15 08:37:04  midas
00146   Removed error message in rpc_client_connect on broken connection, since this is normal if the frontend for example is restarted
00147 
00148   Revision 1.183  2003/04/14 11:01:04  midas
00149   Added bk_find(), added htonl(INADDR_ANY) for bind()
00150 
00151   Revision 1.182  2003/04/09 13:42:47  midas
00152   Made file compile under C++
00153 
00154   Revision 1.181  2003/03/28 07:59:50  midas
00155   Removed old code
00156 
00157   Revision 1.180  2003/03/27 19:40:27  olchansk
00158   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.
00159 
00160   Revision 1.179  2003/03/22 07:06:47  olchansk
00161   prevent infinite loop in hs_read() and hs_dump() when reading broken history files.
00162 
00163   Revision 1.178  2003/01/14 12:19:23  midas
00164   Removed unnecessary code
00165 
00166   Revision 1.177  2003/01/14 08:14:32  midas
00167   Removed unnecessary bind()
00168 
00169   Revision 1.176  2003/01/13 17:07:01  midas
00170   Fixed problem with missing history in recent files
00171 
00172   Revision 1.175  2002/11/27 12:54:49  midas
00173   Removed unnecessary bind()
00174 
00175   Revision 1.174  2002/10/21 00:14:38  olchansk
00176   add missing error reporting
00177 
00178   Revision 1.173  2002/10/15 19:14:06  olchansk
00179   disallow recursive rpc_server_disconnect() (->rpc_call->rpc_server_disconnect)
00180 
00181   Revision 1.172  2002/10/04 09:05:39  midas
00182   Set timeout to 0 in cm_enable_watchdog(FALSE)
00183 
00184   Revision 1.171  2002/09/23 18:13:49  pierre
00185   correct cm_cleanup() rpc_call arg list
00186 
00187   Revision 1.170  2002/09/23 09:50:23  midas
00188   Fixed problem with odbedit 'cleanup' command
00189 
00190   Revision 1.169  2002/09/18 16:39:16  pierre
00191   add bk_list()
00192 
00193   Revision 1.168  2002/09/17 08:58:43  midas
00194   Fix for watchdog timeout after tape operations
00195 
00196   Revision 1.167  2002/09/13 07:32:47  midas
00197   Added client name to cm_cleanup()
00198 
00199   Revision 1.166  2002/09/12 10:42:20  midas
00200   Added note to cm_cleanup()
00201 
00202   Revision 1.165  2002/09/09 17:57:14  pierre
00203   #if !defined(OS_VXWORKS) for eb_ & hs_ section
00204 
00205   Revision 1.164  2002/06/25 19:39:48  pierre
00206   doc++ functions  strstr
00207 
00208   Revision 1.163  2002/06/25 19:00:36  pierre
00209   doc++ functions
00210 
00211   Revision 1.162  2002/05/29 18:49:37  midas
00212   Fixed bug with 'shutdown all' in odbedit
00213 
00214   Revision 1.161  2002/05/29 07:25:13  midas
00215   Fixed bug with shutting down programs
00216 
00217   Revision 1.160  2002/05/28 12:47:46  midas
00218   Shut down client connection in FTCP mode
00219 
00220   Revision 1.159  2002/05/27 14:29:10  midas
00221   Improved rpc timeout error reports
00222 
00223   Revision 1.158  2002/05/22 06:07:01  midas
00224   Call bm_defragment_event for both EVENTID_FRAG1 and EVENTID_FRAG
00225 
00226   Revision 1.157  2002/05/22 05:43:32  midas
00227   Added extra variables to hs_enum_vars for mhist to display array size
00228 
00229   Revision 1.156  2002/05/22 05:26:38  midas
00230   Fixed problem with empty history files
00231 
00232   Revision 1.155  2002/05/16 18:01:13  midas
00233   Added subdir creation in logger and improved program restart scheme
00234 
00235   Revision 1.154  2002/05/15 23:43:39  midas
00236   Added bm_defragment_event()
00237 
00238   Revision 1.153  2002/05/14 04:24:53  midas
00239   Fixed bug on nonexisting message file
00240 
00241   Revision 1.152  2002/05/11 01:22:48  midas
00242   Improved malloc/free debugging
00243 
00244   Revision 1.151  2002/05/10 16:49:37  midas
00245   Moved 'execute on start' before TR_PRESTART
00246 
00247   Revision 1.150  2002/05/10 01:41:19  midas
00248   Added optional debug output to cm_transition
00249 
00250   Revision 1.149  2002/05/10 00:17:05  midas
00251   Run start abort causes logger to delete old data file on next run start
00252 
00253   Revision 1.148  2002/05/08 22:15:24  pierre
00254   add db_get_value arg doc
00255 
00256   Revision 1.147  2002/05/08 19:54:40  midas
00257   Added extra parameter to function db_get_value()
00258 
00259   Revision 1.146  2002/05/07 22:27:56  midas
00260   Fixed bug that history files did not get closed on hs_read
00261 
00262   Revision 1.145  2002/03/13 08:38:00  midas
00263   Added periodic alarms
00264 
00265   Revision 1.144  2002/02/02 11:33:45  midas
00266   Fixed bug in hs_read with small history files
00267 
00268   Revision 1.143  2002/01/30 13:03:34  midas
00269   Fixed small bug in history function
00270 
00271   Revision 1.142  2001/12/12 18:27:03  pierre
00272   add doc++, fix comments
00273 
00274   Revision 1.141  2001/12/12 17:46:21  pierre
00275   1.8.3-2 doc++ comments
00276 
00277   Revision 1.140  2001/12/05 11:29:51  midas
00278   Changed creation of "/Loger/xxx dir"
00279 
00280   Revision 1.139  2001/11/20 14:42:15  midas
00281   Added "/logger/history dir" and "/logger/elog dir"
00282 
00283   Revision 1.138  2001/10/25 22:18:48  pierre
00284   added doc++ comments
00285 
00286   Revision 1.137  2001/10/05 22:35:46  pierre
00287   - change doc \_ to _
00288   - Change MALLOC to M_MALLOC, FREE to M_FREE macros.
00289 
00290   Revision 1.136  2001/08/07 13:07:01  midas
00291   Return error if subprocess creation in rpc_server_accept fails
00292 
00293   Revision 1.135  2001/08/07 08:07:09  midas
00294   Fixed bug in el_retrieve with attachment decoding
00295 
00296   Revision 1.134  2001/06/27 11:55:50  midas
00297   Fixed compiler warnings (came from IRIX)
00298 
00299   Revision 1.133  2001/06/15 08:49:56  midas
00300   Fixed bug when query gave no result if only messages from today are present
00301 
00302   Revision 1.132  2001/05/22 09:27:13  midas
00303   Fixed bug when searching old messages
00304 
00305   Revision 1.131  2001/04/10 01:17:44  midas
00306   Fixed bug in cm_msg_retrieve which screwed up message display in mhttpd
00307 
00308   Revision 1.130  2001/02/19 11:29:05  midas
00309   Set run stop time in ODB before run is stopped in order to have the proper
00310   value in the runxxx.odb file
00311 
00312   Revision 1.129  2001/01/30 08:28:13  midas
00313   Use va_arg(..., double) for float numbers
00314 
00315   Revision 1.128  2000/12/06 02:58:24  midas
00316   Put extended error information for bind() call
00317 
00318   Revision 1.127  2000/11/14 12:19:24  midas
00319   Fixed bug in cm_msg_retrieve, added improved "more" feature in message display
00320 
00321   Revision 1.126  2000/11/14 08:17:04  midas
00322   Added number of messages for cm_msg_retrieve and in odbedit "old" command
00323 
00324   Revision 1.125  2000/11/06 14:19:19  midas
00325   Don't return from hs_read if variable not found (could be present later...)
00326 
00327   Revision 1.124  2000/10/21 12:26:12  midas
00328   Fixed bug with cache pointer in hs_read
00329 
00330   Revision 1.123  2000/09/29 13:31:12  midas
00331   ODBEdit cleanup now deletes open record with no client attached to
00332 
00333   Revision 1.122  2000/08/21 14:18:39  midas
00334   bk_close returns bank size
00335 
00336   Revision 1.121  2000/08/21 07:05:48  midas
00337   Added cm_msg_log1(...,facility) to be compatible with older programs
00338 
00339   Revision 1.120  2000/08/11 12:16:44  midas
00340   Fixed bug with "facility" being NULL
00341 
00342   Revision 1.119  2000/08/11 11:43:51  midas
00343   Added cm_msg1 to produce messages which go to a differnt logging file
00344 
00345   Revision 1.118  2000/08/10 08:04:56  midas
00346   Create default /runinfo structure in cm_connect_experiment
00347 
00348   Revision 1.117  2000/05/16 10:38:17  midas
00349   - Set MIDAS_DIR as the default /logger/data dir on cm_connect_experiment
00350   - Remove elog file if all messages are deleted
00351 
00352   Revision 1.116  2000/05/09 09:06:12  midas
00353   Added MIDAS_EXPTAB environment variable and hashmark comments in exptab
00354 
00355   Revision 1.115  2000/05/08 14:29:38  midas
00356   Added delete option in ELog
00357 
00358   Revision 1.114  2000/05/05 14:20:05  midas
00359   Do online mode check in al_trigger_alarm
00360 
00361   Revision 1.113  2000/04/26 20:27:06  pierre
00362   -Added doc++ comments on some functions.
00363 
00364   Revision 1.112  2000/04/25 11:55:42  midas
00365   Adjusted tabs for history functions
00366 
00367   Revision 1.111  2000/04/17 16:28:21  pierre
00368   - Added arg "BOOL binary_time" to hs_dump(), hs_fdump() for mhist -b
00369 
00370   Revision 1.110  2000/03/29 09:14:47  midas
00371   Fixed bug with original message tagging having the wrong offset
00372 
00373   Revision 1.109  2000/03/17 13:00:06  midas
00374   Frontends use default timeout fo 60 sec.
00375 
00376   Revision 1.108  2000/03/17 10:55:15  midas
00377   Don't trigger internal alarms if alarm system is off
00378 
00379   Revision 1.107  2000/03/04 00:42:29  midas
00380   Delete elog & alarm mutexes correctly
00381 
00382   Revision 1.106  2000/03/03 22:46:07  midas
00383   Remove elog and alarm mutex on exit
00384 
00385   Revision 1.105  2000/03/03 01:45:13  midas
00386   Added web password for mhttpd, added webpasswd command in odbedit
00387 
00388   Revision 1.104  2000/03/01 23:06:19  midas
00389   bk_xxx functions now don't use global variable _pbk
00390 
00391   Revision 1.103  2000/02/29 21:59:05  midas
00392   Fixec bug with order of actions in cm_transition
00393 
00394   Revision 1.102  2000/02/29 02:10:26  midas
00395   Added cm_is_ctrlc_pressed and cm_ack_ctrlc_pressed
00396 
00397   Revision 1.101  2000/02/25 22:49:29  midas
00398   Increased timeouts
00399 
00400   Revision 1.100  2000/02/25 22:19:09  midas
00401   Improved Ctrl-C handling
00402 
00403   Revision 1.99  2000/02/24 23:58:29  midas
00404   Fixed problem with _requested_transition being update by hotlink too late
00405 
00406   Revision 1.98  2000/02/24 22:29:25  midas
00407   Added deferred transitions
00408 
00409   Revision 1.97  2000/02/23 21:07:44  midas
00410   Changed spaces and tabulators
00411 
00412   Revision 1.96  2000/02/15 11:07:51  midas
00413   Changed GET_xxx to bit flags
00414 
00415   Revision 1.95  2000/02/09 08:03:52  midas
00416   Fixed bracket indention
00417 
00418   Revision 1.94  1999/12/08 16:10:43  midas
00419   Fixed another watchdog bug causing remote clients to crash
00420 
00421   Revision 1.93  1999/12/08 11:44:25  midas
00422   Fixed bug with watchdog timeout
00423 
00424   Revision 1.92  1999/12/08 10:00:41  midas
00425   Changed error string to single line
00426 
00427   Revision 1.91  1999/12/08 00:25:20  pierre
00428   - add cm_get_path in cm_msg_retrieve for midas.log location.
00429   - mod dm_buffer_create for arg "max user event size".
00430   - fix dm_area_flush.
00431   - fix other compilation warnings for OSF/1
00432 
00433   Revision 1.90  1999/11/26 08:31:58  midas
00434   midas.log is now places in the same directory as the .SHM files in case
00435   there is no data dir in the ODB
00436 
00437   Revision 1.89  1999/11/25 13:29:55  midas
00438   Fixed bug in cm_msg_retrieve
00439 
00440   Revision 1.88  1999/11/23 15:52:40  midas
00441   If an event is larger than the buffer read or write cache, it bypasses the
00442   cache.
00443 
00444   Revision 1.87  1999/11/19 09:49:58  midas
00445   Fixed bug with wrong default watchdog timeout in cm_connect_experiment1
00446 
00447   Revision 1.86  1999/11/12 10:04:59  midas
00448   Fixed bug with WATCHDOG_INTERVAL
00449 
00450   Revision 1.85  1999/11/10 15:05:16  midas
00451   Did some additional database locking
00452 
00453   Revision 1.84  1999/11/10 13:56:12  midas
00454   Alarm record only gets created when old one mismatches
00455 
00456   Revision 1.83  1999/11/10 10:39:11  midas
00457   Changed initialization of alarms
00458 
00459   Revision 1.82  1999/11/10 08:30:44  midas
00460   Fixed bug when editing the last elog message
00461 
00462   Revision 1.81  1999/11/09 14:44:08  midas
00463   Changed ODB locking in cm_cleanup
00464 
00465   Revision 1.80  1999/11/09 13:17:25  midas
00466   Added secure ODB feature
00467 
00468   Revision 1.79  1999/11/08 13:56:09  midas
00469   Added different alarm types
00470 
00471   Revision 1.78  1999/10/27 15:13:56  midas
00472   Added "access(<key>)" in alarm system
00473 
00474   Revision 1.77  1999/10/27 13:37:57  midas
00475   Added event size check in bm_send_event
00476 
00477   Revision 1.76  1999/10/18 15:52:12  midas
00478   Use "alarm count" to declare programs dead if inactive for 5 minutes
00479 
00480   Revision 1.75  1999/10/18 14:41:51  midas
00481   Use /programs/<name>/Watchdog timeout in all programs as timeout value. The
00482   default value can be submitted by calling cm_connect_experiment1(..., timeout)
00483 
00484   Revision 1.74  1999/10/13 08:03:28  midas
00485   Fixed bug displaying executed message as %d
00486 
00487   Revision 1.73  1999/10/11 14:14:03  midas
00488   Use ss_system in certain places
00489 
00490   Revision 1.72  1999/10/11 13:01:22  midas
00491   Produce system message when executing an alarm script
00492 
00493   Revision 1.71  1999/10/08 22:15:03  midas
00494   Added ftruncate for LINUX
00495 
00496   Revision 1.70  1999/10/08 22:00:30  midas
00497   Finished editing of elog messages
00498 
00499   Revision 1.69  1999/10/08 15:07:06  midas
00500   Program check creates new internal alarm when triggered
00501 
00502   Revision 1.68  1999/10/08 13:21:20  midas
00503   Alarm system disabled when running offline
00504 
00505   Revision 1.67  1999/10/07 13:50:49  midas
00506   Fixed bug with date in el_submit
00507 
00508   Revision 1.66  1999/10/07 13:31:18  midas
00509   Fixed truncated date in el_submit, cut off @host in author search
00510 
00511   Revision 1.65  1999/10/06 06:56:02  midas
00512   Include weekday in elog
00513 
00514   Revision 1.64  1999/10/05 13:16:10  midas
00515   Added global alarm flag "/alarms/alarm system active"
00516 
00517   Revision 1.63  1999/10/04 11:54:14  midas
00518   Submit full alarm string to execute command
00519 
00520   Revision 1.62  1999/09/30 22:59:06  pierre
00521   - fix bk_close for BK32
00522 
00523   Revision 1.61  1999/09/29 19:23:33  pierre
00524   - Fix bk_iterate,swap,locate for bank32
00525 
00526   Revision 1.60  1999/09/27 13:49:04  midas
00527   Added bUnique parameter to cm_shutdown
00528 
00529   Revision 1.59  1999/09/27 12:54:08  midas
00530   Finished alarm system
00531 
00532   Revision 1.58  1999/09/27 08:56:53  midas
00533   Fixed bug with missing run number in elog
00534 
00535   Revision 1.57  1999/09/23 14:00:48  midas
00536   Used capital names for mutexes
00537 
00538   Revision 1.56  1999/09/23 12:45:49  midas
00539   Added 32 bit banks
00540 
00541   Revision 1.55  1999/09/22 08:57:08  midas
00542   Implemented auto start and auto stop in /programs
00543 
00544   Revision 1.54  1999/09/21 14:57:39  midas
00545   Added "execute on start/stop" under /programs
00546 
00547   Revision 1.53  1999/09/21 14:15:04  midas
00548   Replaces cm_execute by system()
00549 
00550   Revision 1.52  1999/09/21 13:48:04  midas
00551   Added programs check in al_check
00552 
00553   Revision 1.51  1999/09/17 15:59:03  midas
00554   Added internal alarms
00555 
00556   Revision 1.50  1999/09/17 15:06:48  midas
00557   Moved al_check into cm_yield() and rpc_server_thread
00558 
00559   Revision 1.49  1999/09/17 11:50:53  midas
00560   Added al_xxx functions
00561 
00562   Revision 1.48  1999/09/17 11:48:06  midas
00563   Alarm system half finished
00564 
00565   Revision 1.47  1999/09/15 13:33:34  midas
00566   Added remote el_submit functionality
00567 
00568   Revision 1.46  1999/09/14 15:15:45  midas
00569   Moved el_xxx funtions into midas.c
00570 
00571   Revision 1.45  1999/09/13 11:08:24  midas
00572   Check NULL as experiment in cm_connect_experiment
00573 
00574   Revision 1.44  1999/09/10 06:11:15  midas
00575   Used %100 for year in tms structure
00576 
00577   Revision 1.43  1999/08/03 14:41:09  midas
00578   Lock buffer in bm_skip_event
00579 
00580   Revision 1.42  1999/08/03 11:15:07  midas
00581   Added bm_skip_event
00582 
00583   Revision 1.41  1999/07/21 09:22:01  midas
00584   Added Ctrl-C handler to cm_connect_experiment and cm_yield
00585 
00586   Revision 1.40  1999/06/28 12:01:21  midas
00587   Added hs_fdump
00588 
00589   Revision 1.39  1999/06/25 12:01:54  midas
00590   Added bk_delete function
00591 
00592   Revision 1.38  1999/06/23 09:36:24  midas
00593   - Fixed "too many connections" bug
00594   - incorporated PAAs dm_xxx changes
00595 
00596   Revision 1.37  1999/05/05 12:02:33  midas
00597   Added and modified history functions, added db_set_num_values
00598 
00599   Revision 1.36  1999/04/30 14:22:01  midas
00600   Send buffer name via bm_notify_client to java application
00601 
00602   Revision 1.35  1999/04/30 13:19:54  midas
00603   Changed inter-process communication (ss_resume, bm_notify_clients, etc)
00604   to strings so that server process can receive it's own watchdog produced
00605   messages (pass buffer name insteas buffer handle)
00606 
00607   Revision 1.34  1999/04/30 10:58:58  midas
00608   Added -D debug to screen for mserver
00609 
00610   Revision 1.33  1999/04/29 10:48:02  midas
00611   Implemented "/System/Client Notify" key
00612 
00613   Revision 1.32  1999/04/28 15:27:28  midas
00614   Made hs_read working for Java
00615 
00616   Revision 1.31  1999/04/27 15:16:14  midas
00617   Increased ASCII_BUFFER_SIZE to 64500
00618 
00619   Revision 1.30  1999/04/27 11:11:26  midas
00620   Added rpc_register_client
00621 
00622   Revision 1.29  1999/04/23 11:42:52  midas
00623   Made db_get_data_index working for Java
00624 
00625   Revision 1.28  1999/04/19 07:47:00  midas
00626   Added cm_msg_retrieve
00627 
00628   Revision 1.27  1999/04/16 15:13:28  midas
00629   bm_notify_client notifies ASCII client (Java) always
00630 
00631   Revision 1.26  1999/04/15 15:43:06  midas
00632   Added functionality for bm_receive_event in ASCII mode
00633 
00634   Revision 1.25  1999/04/15 09:58:42  midas
00635   Switched if (rpc_list[i].id == 0) statements
00636 
00637   Revision 1.24  1999/04/13 12:20:43  midas
00638   Added db_get_data1 (for Java)
00639 
00640   Revision 1.23  1999/04/08 15:26:05  midas
00641   Worked on rpc_execute_ascii
00642 
00643   Revision 1.22  1999/03/23 10:37:39  midas
00644   Fixed bug in cm_set_watchdog_params which causes mtape report ODB errors
00645 
00646   Revision 1.21  1999/02/12 10:55:03  midas
00647   Accepted PAA's modification in cm_set_watchdog_params()
00648 
00649   Revision 1.20  1999/02/11 13:14:46  midas
00650   Basic ASCII protocol implemented in server
00651 
00652   Revision 1.19  1999/02/09 14:38:23  midas
00653   Added debug logging facility
00654 
00655   Revision 1.18  1999/02/06 00:17:12  pierre
00656   - Fix local watchdog timeout in cm_set_watchdog_params()
00657   - Touch dm_xxx functions for OS_WINNT
00658 
00659   Revision 1.17  1999/02/02 07:42:22  midas
00660   Only print warning about zero length bank in bk_close if bank has type TID_STRUCT
00661 
00662   Revision 1.16  1999/02/01 15:41:23  midas
00663   Added warning for zero length bank in bk_close
00664 
00665   Revision 1.15  1999/02/01 13:03:49  midas
00666   Added /system/clients/xxx/link timeout to show current TCP timeout value
00667 
00668   Revision 1.14  1999/01/22 09:31:16  midas
00669   Fixed again status return from ss_mutex_create in bm_open_buffer
00670 
00671   Revision 1.13  1999/01/21 23:09:17  pierre
00672   - Incorporate dm_semaphore_...() functionality into ss_mutex_...()
00673   - Remove dm_semaphore_...(), adjust dm_...() accordingly.
00674   - Incorporate taskSpawn into ss_thread_create (system.c).
00675   - Adjust status value returnd from ss_mutex_create().
00676 
00677   Revision 1.12  1999/01/20 08:55:44  midas
00678   - Renames ss_xxx_mutex to ss_mutex_xxx
00679   - Added timout flag to ss_mutex_wait_for
00680 
00681   Revision 1.11  1999/01/19 19:58:56  pierre
00682   - Fix compiler warning in dm_buffer_send
00683 
00684   Revision 1.10  1999/01/18 17:50:35  pierre
00685   - Added dm_...() functions for Dual Memory buffer handling.
00686 
00687   Revision 1.9  1998/12/11 17:00:02  midas
00688   Fixed a few typos
00689 
00690   Revision 1.8  1998/10/28 12:05:57  midas
00691   Fixed minor compiler warning
00692 
00693   Revision 1.7  1998/10/28 12:01:30  midas
00694   Added version number to run start notification
00695 
00696   Revision 1.6  1998/10/27 10:53:48  midas
00697   - Added run start notification
00698   - Added ss_shell() for NT
00699 
00700   Revision 1.5  1998/10/23 14:21:50  midas
00701   - Modified version scheme from 1.06 to 1.6.0
00702   - cm_get_version() now returns versino as string
00703 
00704   Revision 1.4  1998/10/13 07:34:42  midas
00705   Reopened database in case of wrong password
00706 
00707   Revision 1.3  1998/10/12 12:19:02  midas
00708   Added Log tag in header
00709 
00710   Revision 1.2  1998/10/12 11:59:10  midas
00711   Added Log tag in header
00712 
00713 \********************************************************************/
00714 
00715 #include "midas.h"
00716 #include "msystem.h"
00717 #include <assert.h>
00718 
00719 /**dox***************************************************************/
00720 /** @file midas.c
00721 The main core C-code for Midas.
00722 */
00723 
00724 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00725  */
00726 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00727  */
00728 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00729  */
00730 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00731  */
00732 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00733  */
00734 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00735  */
00736 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00737  */
00738 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00739  */
00740 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00741  */
00742 
00743 /**dox***************************************************************/
00744 /** @addtogroup midasincludecode
00745  *  
00746  *  @{  */
00747 
00748 /**dox***************************************************************/
00749 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00750 
00751 /********************************************************************/
00752 /* data type sizes */
00753 INT tid_size[] = {
00754    0,                           /* tid == 0 not defined                               */
00755    1,                           /* TID_BYTE      unsigned byte         0       255    */
00756    1,                           /* TID_SBYTE     signed byte         -128      127    */
00757    1,                           /* TID_CHAR      single character      0       255    */
00758    2,                           /* TID_WORD      two bytes             0      65535   */
00759    2,                           /* TID_SHORT     signed word        -32768    32767   */
00760    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00761    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00762    4,                           /* TID_BOOL      four bytes bool       0        1     */
00763    4,                           /* TID_FLOAT     4 Byte float format                  */
00764    8,                           /* TID_DOUBLE    8 Byte float format                  */
00765    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00766    0,                           /* TID_STRING    zero terminated string               */
00767    0,                           /* TID_ARRAY     variable length array of unkown type */
00768    0,                           /* TID_STRUCT    C structure                          */
00769    0,                           /* TID_KEY       key in online database               */
00770    0                            /* TID_LINK      link in online database              */
00771 };
00772 
00773 /* data type names */
00774 char *tid_name[] = {
00775    "NULL",
00776    "BYTE",
00777    "SBYTE",
00778    "CHAR",
00779    "WORD",
00780    "SHORT",
00781    "DWORD",
00782    "INT",
00783    "BOOL",
00784    "FLOAT",
00785    "DOUBLE",
00786    "BITFIELD",
00787    "STRING",
00788    "ARRAY",
00789    "STRUCT",
00790    "KEY",
00791    "LINK"
00792 };
00793 
00794 struct {
00795    int transition;
00796    char name[32];
00797 } trans_name[] = {
00798    {
00799    TR_START, "START",}, {
00800    TR_STOP, "STOP",}, {
00801    TR_PAUSE, "PAUSE",}, {
00802    TR_RESUME, "RESUME",}, {
00803 TR_DEFERRED, "DEFERRED",},};
00804 
00805 /* Globals */
00806 #ifdef OS_MSDOS
00807 extern unsigned _stklen = 60000U;
00808 #endif
00809 
00810 extern DATABASE *_database;
00811 extern INT _database_entries;
00812 
00813 static BUFFER *_buffer;
00814 static INT _buffer_entries = 0;
00815 
00816 static INT _msg_buffer = 0;
00817 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00818 
00819 static REQUEST_LIST *_request_list;
00820 static INT _request_list_entries = 0;
00821 
00822 static EVENT_HEADER *_event_buffer;
00823 static INT _event_buffer_size = 0;
00824 
00825 static char *_net_recv_buffer;
00826 static INT _net_recv_buffer_size = 0;
00827 
00828 static char *_net_send_buffer;
00829 static INT _net_send_buffer_size = 0;
00830 
00831 static char *_tcp_buffer = NULL;
00832 static INT _tcp_wp = 0;
00833 static INT _tcp_rp = 0;
00834 
00835 static INT _send_sock;
00836 
00837 static void (*_debug_print) (char *) = NULL;
00838 static INT _debug_mode = 0;
00839 
00840 static INT _watchdog_last_called = 0;
00841 
00842 /* table for transition functions */
00843 
00844 typedef struct {
00845    INT transition;
00846    INT sequence_number;
00847     INT(*func) (INT, char *);
00848 } TRANS_TABLE;
00849 
00850 #define MAX_TRANSITIONS 20
00851 
00852 TRANS_TABLE _trans_table[MAX_TRANSITIONS];
00853 
00854 TRANS_TABLE _deferred_trans_table[] = {
00855    {TR_START},
00856    {TR_STOP},
00857    {TR_PAUSE},
00858    {TR_RESUME},
00859    {0}
00860 };
00861 
00862 static BOOL _server_registered = FALSE;
00863 
00864 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00865 
00866 void cm_ctrlc_handler(int sig);
00867 
00868 typedef struct {
00869    INT code;
00870    char *string;
00871 } ERROR_TABLE;
00872 
00873 ERROR_TABLE _error_table[] = {
00874    {CM_WRONG_PASSWORD, "Wrong password"},
00875    {CM_UNDEF_EXP, "Experiment not defined"},
00876    {CM_UNDEF_ENVIRON,
00877     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00878    {RPC_NET_ERROR, "Cannot connect to remote host"},
00879    {0, NULL}
00880 };
00881 
00882 
00883 typedef struct {
00884    void *adr;
00885    int size;
00886    char file[80];
00887    int line;
00888 } DBG_MEM_LOC;
00889 
00890 DBG_MEM_LOC *_mem_loc = NULL;
00891 INT _n_mem = 0;
00892 
00893 void *dbg_malloc(unsigned int size, char *file, int line)
00894 {
00895    FILE *f;
00896    void *adr;
00897    int i;
00898 
00899    adr = malloc(size);
00900 
00901    /* search for deleted entry */
00902    for (i = 0; i < _n_mem; i++)
00903       if (_mem_loc[i].adr == NULL)
00904          break;
00905 
00906    if (i == _n_mem) {
00907       _n_mem++;
00908       if (!_mem_loc)
00909          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00910       else
00911          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00912    }
00913 
00914    _mem_loc[i].adr = adr;
00915    _mem_loc[i].size = size;
00916    strcpy(_mem_loc[i].file, file);
00917    _mem_loc[i].line = line;
00918 
00919    f = fopen("mem.txt", "w");
00920    for (i = 0; i < _n_mem; i++)
00921       if (_mem_loc[i].adr)
00922          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00923                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00924    fclose(f);
00925 
00926    return adr;
00927 }
00928 
00929 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00930 {
00931    void *adr;
00932 
00933    adr = dbg_malloc(size * count, file, line);
00934    if (adr)
00935       memset(adr, 0, size * count);
00936 
00937    return adr;
00938 }
00939 
00940 void dbg_free(void *adr, char *file, int line)
00941 {
00942    FILE *f;
00943    int i;
00944 
00945    free(adr);
00946 
00947    for (i = 0; i < _n_mem; i++)
00948       if (_mem_loc[i].adr == adr)
00949          break;
00950 
00951    if (i < _n_mem)
00952       _mem_loc[i].adr = NULL;
00953 
00954    f = fopen("mem.txt", "w");
00955    for (i = 0; i < _n_mem; i++)
00956       if (_mem_loc[i].adr)
00957          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00958                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00959    fclose(f);
00960 
00961 }
00962 
00963 #ifndef HAVE_STRLCPY
00964 /*---- strlcpy and strlcat to avoid buffer overflow ----------------*/
00965 
00966 /*
00967  * Copy src to string dst of size siz.  At most siz-1 characters
00968  * will be copied.  Always NUL terminates (unless size == 0).
00969  * Returns strlen(src); if retval >= siz, truncation occurred.
00970  */
00971 INT strlcpy(char *dst, const char *src, INT size)
00972 {
00973    char *d = dst;
00974    const char *s = src;
00975    INT n = size;
00976 
00977    /* Copy as many bytes as will fit */
00978    if (n != 0 && --n != 0) {
00979       do {
00980          if ((*d++ = *s++) == 0)
00981             break;
00982       } while (--n != 0);
00983    }
00984 
00985    /* Not enough room in dst, add NUL and traverse rest of src */
00986    if (n == 0) {
00987       if (size != 0)
00988          *d = '\0';             /* NUL-terminate dst */
00989       while (*s++);
00990    }
00991 
00992    return (s - src - 1);        /* count does not include NUL */
00993 }
00994 
00995 /*
00996  * Appends src to string dst of size siz (unlike strncat, siz is the
00997  * full size of dst, not space left).  At most siz-1 characters
00998  * will be copied.  Always NUL terminates (unless size <= strlen(dst)).
00999  * Returns strlen(src) + MIN(size, strlen(initial dst)).
01000  * If retval >= size, truncation occurred.
01001  */
01002 INT strlcat(char *dst, const char *src, INT size)
01003 {
01004    char *d = dst;
01005    const char *s = src;
01006    INT n = size;
01007    INT dlen;
01008 
01009    /* Find the end of dst and adjust bytes left but don't go past end */
01010    while (n-- != 0 && *d != '\0')
01011       d++;
01012    dlen = d - dst;
01013    n = size - dlen;
01014 
01015    if (n == 0)
01016       return (dlen + strlen(s));
01017    while (*s != '\0') {
01018       if (n != 1) {
01019          *d++ = *s;
01020          n--;
01021       }
01022       s++;
01023    }
01024    *d = '\0';
01025 
01026    return (dlen + (s - src));   /* count does not include NUL */
01027 }
01028 #endif
01029 
01030 /********************************************************************\
01031 *                                                                    *
01032 *              Common message functions                              *
01033 *                                                                    *
01034 \********************************************************************/
01035 
01036 static int (*_message_print) (const char *) = puts;
01037 static INT _message_mask_system = MT_ALL;
01038 static INT _message_mask_user = MT_ALL;
01039 
01040 
01041 /**dox***************************************************************/
01042 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01043 
01044 /**dox***************************************************************/
01045 /** @addtogroup msgfunctionc
01046  *  
01047  *  @{  */
01048 
01049 /********************************************************************/
01050 /**
01051 Convert error code to string. Used after cm_connect_experiment to print
01052 error string in command line programs or windows programs.
01053 @param code Error code as defined in midas.h
01054 @param string Error string
01055 @return CM_SUCCESS
01056 */
01057 INT cm_get_error(INT code, char *string)
01058 {
01059    INT i;
01060 
01061    for (i = 0; _error_table[i].code; i++)
01062       if (_error_table[i].code == code) {
01063          strcpy(string, _error_table[i].string);
01064          return CM_SUCCESS;
01065       }
01066 
01067    sprintf(string, "Unexpected error #%d", code);
01068    return CM_SUCCESS;
01069 }
01070 
01071 /********************************************************************/
01072 /** 
01073 Set message masks. When a message is generated by calling cm_msg(),
01074 it can got to two destinatinons. First a user defined callback routine
01075 and second to the "SYSMSG" buffer.
01076 
01077 A user defined callback receives all messages which satisfy the user_mask.
01078 
01079 \code
01080 int message_print(const char *msg)
01081 {
01082   char str[160];
01083 
01084   memset(str, ' ', 159);
01085   str[159] = 0;
01086   if (msg[0] == '[')
01087     msg = strchr(msg, ']')+2;
01088   memcpy(str, msg, strlen(msg));
01089   ss_printf(0, 20, str);
01090   return 0;
01091 }
01092 ...
01093   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
01094 ...
01095 \endcode
01096 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
01097 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
01098 @param func Function which receives all printout. By setting "puts",
01099        messages are just printed to the screen.
01100 @return CM_SUCCESS
01101 */
01102 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
01103 {
01104    _message_mask_system = system_mask;
01105    _message_mask_user = user_mask;
01106    _message_print = func;
01107 
01108    return BM_SUCCESS;
01109 }
01110 
01111 /********************************************************************/
01112 /**
01113 Write message to logging file. Called by cm_msg.
01114 @attention May burn your fingers
01115 @param message_type      Message type
01116 @param message          Message string
01117 @return CM_SUCCESS
01118 */
01119 INT cm_msg_log(INT message_type, const char *message)
01120 {
01121    char dir[256];
01122    char filename[256];
01123    char path[256];
01124    char str[256];
01125    FILE *f;
01126    INT status, size;
01127    HNDLE hDB, hKey;
01128 
01129    if (rpc_is_remote())
01130       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
01131 
01132    if (message_type != MT_DEBUG) {
01133       cm_get_experiment_database(&hDB, NULL);
01134 
01135       if (hDB) {
01136          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01137          if (status == DB_SUCCESS) {
01138             size = sizeof(dir);
01139             memset(dir, 0, size);
01140             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01141             if (dir[0] != 0)
01142                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01143                   strcat(dir, DIR_SEPARATOR_STR);
01144 
01145             strcpy(filename, "midas.log");
01146             size = sizeof(filename);
01147             db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01148                          TRUE);
01149 
01150             strcpy(path, dir);
01151             strcat(path, filename);
01152          } else {
01153             cm_get_path(dir);
01154             if (dir[0] != 0)
01155                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01156                   strcat(dir, DIR_SEPARATOR_STR);
01157 
01158             strcpy(path, dir);
01159             strcat(path, "midas.log");
01160          }
01161       } else
01162          strcpy(path, "midas.log");
01163 
01164       f = fopen(path, "a");
01165       if (f == NULL) {
01166          printf("Cannot open message log file %s\n", path);
01167       } else {
01168          strcpy(str, ss_asctime());
01169          fprintf(f, str);
01170          fprintf(f, " %s\n", message);
01171 
01172          fclose(f);
01173       }
01174    }
01175 
01176    return CM_SUCCESS;
01177 }
01178 
01179 /********************************************************************/
01180 /**
01181 Write message to logging file. Called by cm_msg().
01182 @internal 
01183 @param message_type      Message type
01184 @param message          Message string
01185 @param facility         Message facility, filename in which messages will be written
01186 @return CM_SUCCESS
01187 */
01188 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
01189 /********************************************************************\
01190 
01191   Routine: cm_msg_log1
01192 
01193   Purpose: Write message to logging file. Called by cm_msg.
01194            Internal use only
01195 
01196   Input:
01197     INT    message_type      Message type
01198     char   *message          Message string
01199     char   *
01200 
01201   Output:
01202     none
01203 
01204   Function value:
01205     CM_SUCCESS
01206 
01207 \********************************************************************/
01208 {
01209    char dir[256];
01210    char filename[256];
01211    char path[256];
01212    char str[256];
01213    FILE *f;
01214    INT status, size;
01215    HNDLE hDB, hKey;
01216 
01217 
01218    if (rpc_is_remote())
01219       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
01220 
01221    if (message_type != MT_DEBUG) {
01222       cm_get_experiment_database(&hDB, NULL);
01223 
01224       if (hDB) {
01225          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01226          if (status == DB_SUCCESS) {
01227             size = sizeof(dir);
01228             memset(dir, 0, size);
01229             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01230             if (dir[0] != 0)
01231                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01232                   strcat(dir, DIR_SEPARATOR_STR);
01233 
01234             if (facility[0]) {
01235                strcpy(filename, facility);
01236                strcat(filename, ".log");
01237             } else {
01238                strcpy(filename, "midas.log");
01239                size = sizeof(filename);
01240                db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01241                             TRUE);
01242             }
01243 
01244             strcpy(path, dir);
01245             strcat(path, filename);
01246          } else {
01247             cm_get_path(dir);
01248             if (dir[0] != 0)
01249                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01250                   strcat(dir, DIR_SEPARATOR_STR);
01251 
01252             strcpy(path, dir);
01253             if (facility[0]) {
01254                strcat(path, facility);
01255                strcat(path, ".log");
01256             } else
01257                strcat(path, "midas.log");
01258          }
01259       } else {
01260          if (facility[0]) {
01261             strcpy(path, facility);
01262             strcat(path, ".log");
01263          } else
01264             strcpy(path, "midas.log");
01265       }
01266 
01267       f = fopen(path, "a");
01268       if (f == NULL) {
01269          printf("Cannot open message log file %s\n", path);
01270       } else {
01271          strcpy(str, ss_asctime());
01272          fprintf(f, str);
01273          fprintf(f, " %s\n", message);
01274          fclose(f);
01275       }
01276    }
01277 
01278    return CM_SUCCESS;
01279 }
01280 
01281 /********************************************************************/
01282 /** 
01283 This routine can be called whenever an internal error occurs
01284 or an informative message is produced. Different message
01285 types can be enabled or disabled by setting the type bits
01286 via cm_set_msg_print().
01287 @attention Do not add the "\n" escape carriage control at the end of the
01288 formated line as it is already added by the client on the receiving side.
01289 \code
01290    ...
01291    cm_msg(MINFO, "my program", "This is a information message only);
01292    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
01293    cm_msg(MTALK, "my_program", My program is Done!");
01294    ...
01295 \endcode
01296 @param message_type (See @ref midas_macro).
01297 @param filename Name of source file where error occured
01298 @param line Line number where error occured
01299 @param routine Routine name.
01300 @param format message to printout, ... Parameters like for printf()
01301 @return CM_SUCCESS
01302 */
01303 INT cm_msg(INT message_type, char *filename, INT line,
01304            const char *routine, const char *format, ...)
01305 {
01306    va_list argptr;
01307    char event[1000], str[256], local_message[256], send_message[256], *pc;
01308    EVENT_HEADER *pevent;
01309    INT status;
01310    static BOOL in_routine = FALSE;
01311 
01312    /* avoid recursive calls */
01313    if (in_routine)
01314       return 0;
01315 
01316    in_routine = TRUE;
01317 
01318    /* strip path */
01319    pc = filename + strlen(filename);
01320    while (*pc != '\\' && *pc != '/' && pc != filename)
01321       pc--;
01322    if (pc != filename)
01323       pc++;
01324 
01325    /* print client name into string */
01326    if (message_type == MT_USER)
01327       sprintf(send_message, "[%s] ", routine);
01328    else {
01329       rpc_get_name(str);
01330       if (str[0])
01331          sprintf(send_message, "[%s] ", str);
01332       else
01333          send_message[0] = 0;
01334    }
01335 
01336    local_message[0] = 0;
01337 
01338    /* preceed error messages with file and line info */
01339    if (message_type == MT_ERROR) {
01340       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01341       strcat(send_message, str);
01342       strcat(local_message, str);
01343    }
01344 
01345    /* print argument list into message */
01346    va_start(argptr, format);
01347    vsprintf(str, (char *) format, argptr);
01348    va_end(argptr);
01349    strcat(send_message, str);
01350    strcat(local_message, str);
01351 
01352    /* call user function if set via cm_set_msg_print */
01353    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01354       _message_print(local_message);
01355 
01356    /* return if system mask is not set */
01357    if ((message_type & _message_mask_system) == 0) {
01358       in_routine = FALSE;
01359       return CM_SUCCESS;
01360    }
01361 
01362    /* copy message to event */
01363    pevent = (EVENT_HEADER *) event;
01364    strcpy(event + sizeof(EVENT_HEADER), send_message);
01365 
01366    /* send event if not of type MLOG */
01367    if (message_type != MT_LOG) {
01368       /* if no message buffer already opened, do so now */
01369       if (_msg_buffer == 0) {
01370          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01371          if (status != BM_SUCCESS && status != BM_CREATED) {
01372             in_routine = FALSE;
01373             return status;
01374          }
01375       }
01376 
01377       /* setup the event header and send the message */
01378       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01379                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01380       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01381    }
01382 
01383    /* log message */
01384    cm_msg_log(message_type, send_message);
01385 
01386    in_routine = FALSE;
01387 
01388    return CM_SUCCESS;
01389 }
01390 
01391 /********************************************************************/
01392 /**
01393 This routine is similar to @ref cm_msg().
01394 It differs from cm_msg() only by the logging destination being a file
01395 given through the argument list i.e:\b facility
01396 @internal
01397 @attention Do not add the "\n" escape carriage control at the end of the
01398 formated line as it is already added by the client on the receiving side.
01399 The first arg in the following example uses the predefined
01400 macro MINFO which handles automatically the first 3 arguments of the function
01401 (see @ref midas_macro).
01402 \code   ...
01403    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
01404    ...
01405 //----- File my_log_file.log
01406 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
01407 \endcode
01408 @param message_type See @ref midas_macro.
01409 @param filename Name of source file where error occured
01410 @param line Line number where error occured
01411 @param facility Logging file name
01412 @param routine Routine name
01413 @param format message to printout, ... Parameters like for printf()
01414 @return CM_SUCCESS
01415 */
01416 INT cm_msg1(INT message_type, char *filename, INT line,
01417             const char *facility, const char *routine, const char *format, ...)
01418 {
01419    va_list argptr;
01420    char event[1000], str[256], local_message[256], send_message[256], *pc;
01421    EVENT_HEADER *pevent;
01422    INT status;
01423    static BOOL in_routine = FALSE;
01424 
01425    /* avoid recursive calles */
01426    if (in_routine)
01427       return 0;
01428 
01429    in_routine = TRUE;
01430 
01431    /* strip path */
01432    pc = filename + strlen(filename);
01433    while (*pc != '\\' && *pc != '/' && pc != filename)
01434       pc--;
01435    if (pc != filename)
01436       pc++;
01437 
01438    /* print client name into string */
01439    if (message_type == MT_USER)
01440       sprintf(send_message, "[%s] ", routine);
01441    else {
01442       rpc_get_name(str);
01443       if (str[0])
01444          sprintf(send_message, "[%s] ", str);
01445       else
01446          send_message[0] = 0;
01447    }
01448 
01449    local_message[0] = 0;
01450 
01451    /* preceed error messages with file and line info */
01452    if (message_type == MT_ERROR) {
01453       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01454       strcat(send_message, str);
01455       strcat(local_message, str);
01456    }
01457 
01458    /* print argument list into message */
01459    va_start(argptr, format);
01460    vsprintf(str, (char *) format, argptr);
01461    va_end(argptr);
01462 
01463    if (facility)
01464       sprintf(local_message + strlen(local_message), "{%s} ", facility);
01465 
01466    strcat(send_message, str);
01467    strcat(local_message, str);
01468 
01469    /* call user function if set via cm_set_msg_print */
01470    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01471       _message_print(local_message);
01472 
01473    /* return if system mask is not set */
01474    if ((message_type & _message_mask_system) == 0) {
01475       in_routine = FALSE;
01476       return CM_SUCCESS;
01477    }
01478 
01479    /* copy message to event */
01480    pevent = (EVENT_HEADER *) event;
01481    strcpy(event + sizeof(EVENT_HEADER), send_message);
01482 
01483    /* send event if not of type MLOG */
01484    if (message_type != MT_LOG) {
01485       /* if no message buffer already opened, do so now */
01486       if (_msg_buffer == 0) {
01487          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01488          if (status != BM_SUCCESS && status != BM_CREATED) {
01489             in_routine = FALSE;
01490             return status;
01491          }
01492       }
01493 
01494       /* setup the event header and send the message */
01495       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01496                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01497       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01498    }
01499 
01500    /* log message */
01501    cm_msg_log1(message_type, send_message, facility);
01502 
01503    in_routine = FALSE;
01504 
01505    return CM_SUCCESS;
01506 }
01507 
01508 /********************************************************************/
01509 /** 
01510 Register a dispatch function for receiving system messages.
01511 - example code from mlxspeaker.c
01512 \code
01513 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
01514 {
01515   char str[256], *pc, *sp;
01516   // print message
01517   printf("%s\n", (char *)(message));
01518 
01519   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
01520                  ,header->event_id
01521                  ,header->trigger_mask
01522                  ,header->serial_number
01523                  ,header->data_size);
01524   pc = strchr((char *)(message),']')+2;
01525   ...
01526   // skip none talking message
01527   if (header->trigger_mask == MT_TALK ||
01528       header->trigger_mask == MT_USER)
01529    ...
01530 }
01531 
01532 int main(int argc, char *argv[])
01533 {
01534   ...
01535   // now connect to server
01536   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
01537   if (status != CM_SUCCESS)
01538     return 1;
01539   // Register callback for messages
01540   cm_msg_register(receive_message);
01541   ...
01542 }
01543 \endcode
01544 @param func Dispatch function.
01545 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
01546 */
01547 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
01548 {
01549    INT status, id;
01550 
01551    /* if no message buffer already opened, do so now */
01552    if (_msg_buffer == 0) {
01553       status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01554       if (status != BM_SUCCESS && status != BM_CREATED)
01555          return status;
01556    }
01557 
01558    _msg_dispatch = func;
01559 
01560    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
01561 
01562    return status;
01563 }
01564 
01565 /********************************************************************/
01566 /**
01567 Retrieve old messages from log file 
01568 @param  n_message        Number of messages to retrieve
01569 @param  message          buf_size bytes of messages, separated
01570                          by \n characters. The returned number
01571                          of bytes is normally smaller than the
01572                          initial buf_size, since only full
01573                          lines are returned.
01574 @param *buf_size         Size of message buffer to fill
01575 @return CM_SUCCESS
01576 */
01577 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
01578 {
01579    char dir[256];
01580    char filename[256];
01581    char path[256], *p;
01582    FILE *f;
01583    INT status, size, offset, i;
01584    HNDLE hDB, hKey;
01585 
01586 
01587    if (rpc_is_remote())
01588       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
01589 
01590    cm_get_experiment_database(&hDB, NULL);
01591 
01592    if (hDB) {
01593       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01594       if (status == DB_SUCCESS) {
01595          size = sizeof(dir);
01596          memset(dir, 0, size);
01597          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01598          if (dir[0] != 0)
01599             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01600                strcat(dir, DIR_SEPARATOR_STR);
01601 
01602          strcpy(filename, "midas.log");
01603          size = sizeof(filename);
01604          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
01605 
01606          strcpy(path, dir);
01607          strcat(path, filename);
01608       } else {
01609          cm_get_path(dir);
01610          if (dir[0] != 0)
01611             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01612                strcat(dir, DIR_SEPARATOR_STR);
01613 
01614          strcpy(path, dir);
01615          strcat(path, "midas.log");
01616       }
01617    } else
01618       strcpy(path, "midas.log");
01619 
01620    f = fopen(path, "rb");
01621    if (f == NULL) {
01622       sprintf(message, "Cannot open message log file %s\n", path);
01623       *buf_size = strlen(message);
01624       return CM_DB_ERROR;
01625    } else {
01626       /* position buf_size bytes before the EOF */
01627       fseek(f, -(*buf_size - 1), SEEK_END);
01628       offset = ftell(f);
01629       if (offset != 0) {
01630          /* go to end of line */
01631          fgets(message, *buf_size - 1, f);
01632          offset = ftell(f) - offset;
01633          *buf_size -= offset;
01634       }
01635 
01636       memset(message, 0, *buf_size);
01637       fread(message, 1, *buf_size - 1, f);
01638       message[*buf_size - 1] = 0;
01639       fclose(f);
01640 
01641       p = message + (*buf_size - 2);
01642 
01643       /* goto end of buffer */
01644       while (p != message && *p == 0)
01645          p--;
01646 
01647       /* strip line break */
01648       while (p != message && (*p == '\n' || *p == '\r'))
01649          *(p--) = 0;
01650 
01651       /* trim buffer so that last n_messages remain */
01652       for (i = 0; i < n_message; i++) {
01653          while (p != message && *p != '\n')
01654             p--;
01655 
01656          while (p != message && (*p == '\n' || *p == '\r'))
01657             p--;
01658       }
01659       if (p != message) {
01660          p++;
01661          while (*p == '\n' || *p == '\r')
01662             p++;
01663       }
01664 
01665       *buf_size = (*buf_size - 1) - ((PTYPE) p - (PTYPE) message);
01666 
01667       memmove(message, p, *buf_size);
01668       message[*buf_size] = 0;
01669    }
01670 
01671    return CM_SUCCESS;
01672 }
01673 
01674 /**dox***************************************************************/
01675                    /** @} *//* end of msgfunctionc */
01676 
01677 /**dox***************************************************************/
01678 /** @addtogroup cmfunctionc
01679  *  
01680  *  @{  */
01681 
01682 /********************************************************************/
01683 /**
01684 Get time from MIDAS server and set local time.
01685 @param    seconds         Time in seconds
01686 @return CM_SUCCESS
01687 */
01688 INT cm_synchronize(DWORD * seconds)
01689 {
01690    INT sec, status;
01691 
01692    /* if connected to server, get time from there */
01693    if (rpc_is_remote()) {
01694       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01695 
01696       /* set local time */
01697       if (status == CM_SUCCESS)
01698          ss_settime(sec);
01699    }
01700 
01701    /* return time to caller */
01702    if (seconds != NULL) {
01703       *seconds = ss_time();
01704    }
01705 
01706    return CM_SUCCESS;
01707 }
01708 
01709 /********************************************************************/
01710 /**
01711 Get time from MIDAS server and set local time.
01712 @param    str            return time string
01713 @param    buf_size       Maximum size of str
01714 @return   CM_SUCCESS
01715 */
01716 INT cm_asctime(char *str, INT buf_size)
01717 {
01718    /* if connected to server, get time from there */
01719    if (rpc_is_remote())
01720       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01721 
01722    /* return local time */
01723    strcpy(str, ss_asctime());
01724 
01725    return CM_SUCCESS;
01726 }
01727 
01728 /********************************************************************/
01729 /**
01730 Get time from ss_time on server.
01731 @param    time string
01732 @return   CM_SUCCESS
01733 */
01734 INT cm_time(DWORD * time)
01735 {
01736    /* if connected to server, get time from there */
01737    if (rpc_is_remote())
01738       return rpc_call(RPC_CM_TIME, time);
01739 
01740    /* return local time */
01741    *time = ss_time();
01742 
01743    return CM_SUCCESS;
01744 }
01745 
01746 /**dox***************************************************************/
01747                    /** @} *//* end of cmfunctionc */
01748 
01749 /********************************************************************\
01750 *                                                                    *
01751 *           cm_xxx  -  Common Functions to buffer & database         *
01752 *                                                                    *
01753 \********************************************************************/
01754 
01755 /* Globals */
01756 
01757 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01758 static HNDLE _hDB = 0;          /* Database handle */
01759 static char _client_name[NAME_LENGTH];
01760 static char _path_name[MAX_STRING_LENGTH];
01761 static INT _call_watchdog = TRUE;
01762 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01763 INT _mutex_alarm, _mutex_elog;
01764 
01765 /**dox***************************************************************/
01766 /** @addtogroup cmfunctionc
01767  *  
01768  *  @{  */
01769 
01770 /**
01771 Return version number of current MIDAS library as a string
01772 @return version number * 100
01773 */
01774 char *cm_get_version()
01775 {
01776    return MIDAS_VERSION;
01777 }
01778 
01779 /********************************************************************/
01780 /**
01781 Set path to actual experiment. This function gets called
01782 by cm_connect_experiment if the connection is established
01783 to a local experiment (not through the TCP/IP server).
01784 The path is then used for all shared memory routines.
01785 @param  path             Pathname
01786 @return CM_SUCCESS
01787 */
01788 INT cm_set_path(char *path)
01789 {
01790    strcpy(_path_name, path);
01791 
01792    /* check for trailing directory seperator */
01793    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01794       strcat(_path_name, DIR_SEPARATOR_STR);
01795 
01796    return CM_SUCCESS;
01797 }
01798 
01799 /********************************************************************/
01800 /**
01801 Return the path name previously set with cm_set_path.
01802 @param  path             Pathname
01803 @return CM_SUCCESS
01804 */
01805 INT cm_get_path(char *path)
01806 {
01807    strcpy(path, _path_name);
01808 
01809    return CM_SUCCESS;
01810 }
01811 
01812 /**dox***************************************************************/
01813                    /** @} *//* end of cmfunctionc */
01814 
01815 /**dox***************************************************************/
01816 /** @addtogroup cmfunctionc
01817  *  
01818  *  @{  */
01819 
01820 /**dox***************************************************************/
01821 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01822 
01823 typedef struct {
01824    char name[NAME_LENGTH];
01825    char directory[MAX_STRING_LENGTH];
01826    char user[NAME_LENGTH];
01827 } experiment_table;
01828 
01829 static experiment_table exptab[MAX_EXPERIMENT];
01830 
01831 /**dox***************************************************************/
01832 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01833 
01834 /**
01835 Scan the "exptab" file for MIDAS experiment names and save them
01836 for later use by rpc_server_accept(). The file is first searched
01837 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01838 @return CM_SUCCESS<br>
01839         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01840 */
01841 INT cm_scan_experiments(void)
01842 {
01843    INT i;
01844    FILE *f;
01845    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01846 
01847    for (i = 0; i < MAX_EXPERIMENT; i++)
01848       exptab[i].name[0] = 0;
01849 
01850    /* MIDAS_DIR overrides exptab */
01851    if (getenv("MIDAS_DIR")) {
01852       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01853 
01854       strcpy(exptab[0].name, "Default");
01855       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01856       exptab[0].user[0] = 0;
01857 
01858       return CM_SUCCESS;
01859    }
01860 
01861    /* default directory for different OSes */
01862 #if defined (OS_WINNT)
01863    if (getenv("SystemRoot"))
01864       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01865    else if (getenv("windir"))
01866       strlcpy(str, getenv("windir"), sizeof(str));
01867    else
01868       strcpy(str, "");
01869 
01870    strcpy(alt_str, str);
01871    strcat(str, "\\system32\\exptab");
01872    strcat(alt_str, "\\system\\exptab");
01873 #elif defined (OS_UNIX)
01874    strcpy(str, "/etc/exptab");
01875    strcpy(alt_str, "/exptab");
01876 #else
01877    strcpy(str, "exptab");
01878    strcpy(alt_str, "exptab");
01879 #endif
01880 
01881    /* MIDAS_EXPTAB overrides default directory */
01882    if (getenv("MIDAS_EXPTAB")) {
01883       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01884       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01885    }
01886 
01887    /* read list of available experiments */
01888    f = fopen(str, "r");
01889    if (f == NULL) {
01890       f = fopen(alt_str, "r");
01891       if (f == NULL)
01892          return CM_UNDEF_ENVIRON;
01893    }
01894 
01895    i = 0;
01896    if (f != NULL) {
01897       do {
01898          str[0] = 0;
01899          if (fgets(str, 100, f) == NULL)
01900             break;
01901          if (str[0] && str[0] != '#') {
01902             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01903 
01904             /* check for trailing directory separator */
01905             pdir = exptab[i].directory;
01906             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01907                strcat(pdir, DIR_SEPARATOR_STR);
01908 
01909             i++;
01910          }
01911       } while (!feof(f));
01912       fclose(f);
01913    }
01914 
01915    /*
01916       for (j=0 ; j<i ; j++)
01917       {
01918       sprintf(str, "Scanned experiment %s", exptab[j].name);
01919       cm_msg(MINFO, str);
01920       }
01921     */
01922 
01923    return CM_SUCCESS;
01924 }
01925 
01926 /********************************************************************/
01927 /**
01928 Delete client info from database
01929 @param hDB               Database handle
01930 @param pid               PID of entry to delete, zero for this process.
01931 @return CM_SUCCESS
01932 */
01933 INT cm_delete_client_info(HNDLE hDB, INT pid)
01934 {
01935 #ifdef LOCAL_ROUTINES
01936 
01937    /* only do it if local */
01938    if (!rpc_is_remote()) {
01939       INT status;
01940       HNDLE hKey;
01941       char str[256];
01942 
01943       if (!pid)
01944          pid = ss_gettid();
01945 
01946       /* don't delete info from a closed database */
01947       if (_database_entries == 0)
01948          return CM_SUCCESS;
01949 
01950       /* make operation atomic by locking database */
01951       db_lock_database(hDB);
01952 
01953       sprintf(str, "System/Clients/%0d", pid);
01954       status = db_find_key1(hDB, 0, str, &hKey);
01955       if (status != DB_SUCCESS) {
01956          db_unlock_database(hDB);
01957          return status;
01958       }
01959 
01960       /* unlock client entry and delete it without locking DB */
01961       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01962       db_delete_key1(hDB, hKey, 1, TRUE);
01963 
01964       db_unlock_database(hDB);
01965 
01966       /* touch notify key to inform others */
01967       status = 0;
01968       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01969    }
01970 #endif                          /*LOCAL_ROUTINES */
01971 
01972    return CM_SUCCESS;
01973 }
01974 
01975 /********************************************************************/
01976 /**
01977 Check if a client with a /system/client/xxx entry has
01978 a valid entry in the ODB client table. If not, remove
01979 that client from the /system/client tree. 
01980 @param   hDB               Handle to online database
01981 @param   hKeyClient        Handle to client key
01982 @return  CM_SUCCESS, CM_NO_CLIENT
01983 */
01984 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01985 {
01986 #ifdef LOCAL_ROUTINES
01987 
01988    KEY key;
01989    DATABASE_HEADER *pheader;
01990    DATABASE_CLIENT *pclient;
01991    INT i, client_pid, status;
01992    char name[NAME_LENGTH];
01993 
01994    db_get_key(hDB, hKeyClient, &key);
01995    client_pid = atoi(key.name);
01996 
01997    i = sizeof(name);
01998    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01999 
02000    db_lock_database(hDB);
02001    if (_database[hDB - 1].attached) {
02002       pheader = _database[hDB - 1].database_header;
02003       pclient = pheader->client;
02004 
02005       /* loop through clients */
02006       for (i = 0; i < pheader->max_client_index; i++, pclient++)
02007          if (pclient->tid == client_pid)
02008             break;
02009 
02010       if (i == pheader->max_client_index) {
02011          /* client not found : delete ODB stucture */
02012          db_unlock_database(hDB);
02013 
02014          status = cm_delete_client_info(hDB, client_pid);
02015          if (status != CM_SUCCESS)
02016             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
02017          else
02018             cm_msg(MINFO, "cm_check_clinet",
02019                    "Deleted /System/Clients/%d entry for client %s\n", client_pid, name);
02020 
02021          return CM_NO_CLIENT;
02022       }
02023    }
02024 
02025    db_unlock_database(hDB);
02026 
02027 #endif                          /*LOCAL_ROUTINES */
02028 
02029    return CM_SUCCESS;
02030 }
02031 
02032 /********************************************************************/
02033 /**
02034 Set client information in online database and return handle 
02035 @param  hDB              Handle to online database  
02036 @param  hKeyClient       returned key
02037 @param  host_name        server name 
02038 @param  client_name      Name of this program as it will be seen
02039                          by other clients.
02040 @param  hw_type          Type of byte order
02041 @param  password         MIDAS password  
02042 @param  watchdog_timeout Default watchdog timeout, can be overwritten
02043                          by ODB setting /programs/<name>/Watchdog timeout
02044 @return   CM_SUCCESS
02045 */
02046 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
02047                        char *client_name, INT hw_type, char *password,
02048                        DWORD watchdog_timeout)
02049 {
02050    if (rpc_is_remote())
02051       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
02052                       host_name, client_name, hw_type, password, watchdog_timeout);
02053 
02054 #ifdef LOCAL_ROUTINES
02055    {
02056       INT status, pid, data, i, index, size;
02057       HNDLE hKey, hSubkey;
02058       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
02059       BOOL call_watchdog, allow;
02060       PROGRAM_INFO_STR(program_info_str);
02061 
02062       /* check security if password is presend */
02063       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
02064       if (hKey) {
02065          /* get password */
02066          size = sizeof(pwd);
02067          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
02068 
02069          /* first check allowed hosts list */
02070          allow = FALSE;
02071          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
02072          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
02073             allow = TRUE;
02074 
02075          /* check allowed programs list */
02076          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
02077          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
02078             allow = TRUE;
02079 
02080          /* now check password */
02081          if (!allow &&
02082              strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
02083             if (password[0])
02084                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s",
02085                       host_name);
02086             db_close_all_databases();
02087             bm_close_all_buffers();
02088             _msg_buffer = 0;
02089             return CM_WRONG_PASSWORD;
02090          }
02091       }
02092 
02093       /* make following operation atomic by locking database */
02094       db_lock_database(hDB);
02095 
02096       /* check if entry with this pid exists already */
02097       pid = ss_gettid();
02098 
02099       sprintf(str, "System/Clients/%0d", pid);
02100       status = db_find_key(hDB, 0, str, &hKey);
02101       if (status == DB_SUCCESS) {
02102          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02103          db_delete_key(hDB, hKey, TRUE);
02104       }
02105 
02106       if (strlen(client_name) >= NAME_LENGTH)
02107          client_name[NAME_LENGTH] = 0;
02108 
02109       strcpy(name, client_name);
02110       strcpy(orig_name, client_name);
02111 
02112       /* check if client name already exists */
02113       status = db_find_key(hDB, 0, "System/Clients", &hKey);
02114 
02115       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
02116          for (i = 0;; i++) {
02117             status = db_enum_key(hDB, hKey, i, &hSubkey);
02118             if (status == DB_NO_MORE_SUBKEYS)
02119                break;
02120 
02121             if (status == DB_SUCCESS) {
02122                size = sizeof(str);
02123                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
02124             }
02125 
02126             /* check if client is living */
02127             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
02128                continue;
02129 
02130             if (equal_ustring(str, name)) {
02131                sprintf(name, "%s%d", client_name, index);
02132                break;
02133             }
02134          }
02135       }
02136 
02137       /* set name */
02138       sprintf(str, "System/Clients/%0d/Name", pid);
02139       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
02140       if (status != DB_SUCCESS) {
02141          db_unlock_database(hDB);
02142          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
02143          return status;
02144       }
02145 
02146       /* copy new client name */
02147       strcpy(client_name, name);
02148       db_set_client_name(hDB, client_name);
02149 
02150       /* set also as rpc name */
02151       rpc_set_name(client_name);
02152 
02153       /* use /system/clients/PID as root */
02154       sprintf(str, "System/Clients/%0d", pid);
02155       db_find_key(hDB, 0, str, &hKey);
02156 
02157       /* set host name */
02158       status =
02159           db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
02160       if (status != DB_SUCCESS) {
02161          db_unlock_database(hDB);
02162          return status;
02163       }
02164 
02165       /* set computer id */
02166       status = db_set_value(hDB, hKey, "Hardware type", &hw_type,
02167                             sizeof(hw_type), 1, TID_INT);
02168       if (status != DB_SUCCESS) {
02169          db_unlock_database(hDB);
02170          return status;
02171       }
02172 
02173       /* set server port */
02174       data = 0;
02175       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
02176       if (status != DB_SUCCESS) {
02177          db_unlock_database(hDB);
02178          return status;
02179       }
02180 
02181       /* lock client entry */
02182       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02183 
02184       /* get (set) default watchdog timeout */
02185       size = sizeof(watchdog_timeout);
02186       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
02187       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02188 
02189       /* define /programs entry */
02190       sprintf(str, "/Programs/%s", orig_name);
02191       db_create_record(hDB, 0, str, strcomb(program_info_str));
02192 
02193       /* save handle for ODB and client */
02194       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
02195       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
02196 
02197       /* save watchdog timeout */
02198       cm_get_watchdog_params(&call_watchdog, NULL);
02199       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02200       if (call_watchdog)
02201          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02202 
02203       /* end of atomic operations */
02204       db_unlock_database(hDB);
02205 
02206       /* touch notify key to inform others */
02207       data = 0;
02208       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
02209 
02210       *hKeyClient = hKey;
02211    }
02212 #endif                          /* LOCAL_ROUTINES */
02213 
02214    return CM_SUCCESS;
02215 }
02216 
02217 /********************************************************************/
02218 /**
02219 Get info about the current client 
02220 @param  *client_name       Client name.  
02221 @return   CM_SUCCESS, CM_UNDEF_EXP  
02222 */
02223 INT cm_get_client_info(char *client_name)
02224 {
02225    INT status, length;
02226    HNDLE hDB, hKey;
02227 
02228    /* get root key of client */
02229    cm_get_experiment_database(&hDB, &hKey);
02230    if (!hDB) {
02231       client_name[0] = 0;
02232       return CM_UNDEF_EXP;
02233    }
02234 
02235    status = db_find_key(hDB, hKey, "Name", &hKey);
02236    if (status != DB_SUCCESS)
02237       return status;
02238 
02239    length = NAME_LENGTH;
02240    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
02241    if (status != DB_SUCCESS)
02242       return status;
02243 
02244    return CM_SUCCESS;
02245 }
02246 
02247 /********************************************************************/
02248 /**
02249 Returns MIDAS environment variables. 
02250 @attention This function can be used to evaluate the standard MIDAS
02251            environment variables before connecting to an experiment
02252            (see @ref Environment_variables).
02253            The usual way is that the host name and experiment name are first derived
02254            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
02255            They can then be superseded by command line parameters with -h and -e flags.
02256 \code
02257 #include <stdio.h>
02258 #include <midas.h>
02259 main(int argc, char *argv[])
02260 {
02261   INT  status, i;
02262   char host_name[256],exp_name[32];
02263 
02264   // get default values from environment
02265   cm_get_environment(host_name, exp_name);
02266 
02267   // parse command line parameters
02268   for (i=1 ; i<argc ; i++)
02269     {
02270     if (argv[i][0] == '-')
02271       {
02272       if (i+1 >= argc || argv[i+1][0] == '-')
02273         goto usage;
02274       if (argv[i][1] == 'e')
02275         strcpy(exp_name, argv[++i]);
02276       else if (argv[i][1] == 'h')
02277         strcpy(host_name, argv[++i]);
02278       else
02279         {
02280 usage:
02281         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02282         return 1;
02283         }
02284       }
02285     }
02286   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02287   if (status != CM_SUCCESS)
02288     return 1;
02289     ...do anyting...
02290   cm_disconnect_experiment();
02291 }
02292 \endcode
02293 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
02294 @param host_name_size     string length
02295 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
02296 @param exp_name_size      string length
02297 @return CM_SUCCESS
02298 */
02299 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name,
02300                        int exp_name_size)
02301 {
02302    host_name[0] = exp_name[0] = 0;
02303 
02304    if (getenv("MIDAS_SERVER_HOST"))
02305       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
02306 
02307    if (getenv("MIDAS_EXPT_NAME"))
02308       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
02309 
02310    return CM_SUCCESS;
02311 }
02312 
02313 
02314 /**dox***************************************************************/
02315 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02316 
02317 /********************************************************************/
02318 void cm_check_connect(void)
02319 {
02320    if (_hKeyClient)
02321       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
02322 }
02323 
02324 /**dox***************************************************************/
02325 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02326 
02327 /********************************************************************/
02328 /**
02329 This function connects to an existing MIDAS experiment.
02330 This must be the first call in a MIDAS application.
02331 It opens three TCP connection to the remote host (one for RPC calls,
02332 one to send events and one for hot-link notifications from the remote host)
02333 and writes client information into the ODB under /System/Clients.
02334 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
02335 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
02336 experiment name (see @ref Environment_variables).
02337 For that purpose, the function cm_get_environment()
02338 should be called prior to cm_connect_experiment(). If command line
02339 parameters -h and -e are used, the evaluation should be done between
02340 cm_get_environment() and cm_connect_experiment(). The function
02341 cm_disconnect_experiment() must be called before a MIDAS application exits.
02342 \code
02343 #include <stdio.h>
02344 #include <midas.h>
02345 main(int argc, char *argv[])
02346 {
02347   INT  status, i;
02348   char host_name[256],exp_name[32];
02349 
02350   // get default values from environment
02351   cm_get_environment(host_name, exp_name);
02352 
02353   // parse command line parameters
02354   for (i=1 ; i<argc ; i++)
02355     {
02356     if (argv[i][0] == '-')
02357       {
02358       if (i+1 >= argc || argv[i+1][0] == '-')
02359         goto usage;
02360       if (argv[i][1] == 'e')
02361         strcpy(exp_name, argv[++i]);
02362       else if (argv[i][1] == 'h')
02363         strcpy(host_name, argv[++i]);
02364       else
02365         {
02366 usage:
02367         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02368         return 1;
02369         }
02370       }
02371     }
02372   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02373   if (status != CM_SUCCESS)
02374     return 1;
02375   ...do operations...
02376   cm_disconnect_experiment();
02377 }
02378 \endcode
02379 @param host_name Specifies host to connect to. Must be a valid IP host name.
02380   The string can be empty ("") if to connect to the local computer.
02381 @param exp_name Specifies the experiment to connect to.
02382   If this string is empty, the number of defined experiments in exptab is checked.
02383   If only one experiment is defined, the function automatically connects to this
02384   one. If more than one experiment is defined, a list is presented and the user
02385   can interactively select one experiment.
02386 @param client_name Client name of the calling program as it can be seen by
02387   others (like the scl command in ODBEdit).
02388 @param func Callback function to read in a password if security has
02389   been enabled. In all command line applications this function is NULL which
02390   invokes an internal ss_gets() function to read in a password.
02391   In windows environments (MS Windows, X Windows) a function can be supplied to
02392   open a dialog box and read in the password. The argument of this function must
02393   be the returned password.
02394 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
02395 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
02396 */
02397 INT cm_connect_experiment(char *host_name, char *exp_name,
02398                           char *client_name, void (*func) (char *))
02399 {
02400    INT status;
02401    char str[256];
02402 
02403    status = cm_connect_experiment1(host_name, exp_name, client_name,
02404                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
02405    if (status != CM_SUCCESS) {
02406       cm_get_error(status, str);
02407       puts(str);
02408    }
02409 
02410    return status;
02411 }
02412 
02413 /********************************************************************/
02414 /**
02415 Connect to a MIDAS experiment (to the online database) on
02416            a specific host.
02417 @internal
02418 */
02419 INT cm_connect_experiment1(char *host_name, char *exp_name,
02420                            char *client_name, void (*func) (char *),
02421                            INT odb_size, DWORD watchdog_timeout)
02422 {
02423    INT status, i, mutex_elog, mutex_alarm, size;
02424    char local_host_name[HOST_NAME_LENGTH];
02425    char client_name1[NAME_LENGTH];
02426    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
02427    HNDLE hDB, hKeyClient;
02428    BOOL call_watchdog;
02429    RUNINFO_STR(runinfo_str);
02430 
02431    if (_hKeyClient)
02432       cm_disconnect_experiment();
02433 
02434    rpc_set_name(client_name);
02435 
02436    /* check for local host */
02437    if (equal_ustring(host_name, "local"))
02438       host_name[0] = 0;
02439 
02440 #ifdef OS_WINNT
02441    {
02442       WSADATA WSAData;
02443 
02444       /* Start windows sockets */
02445       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02446          return RPC_NET_ERROR;
02447    }
02448 #endif
02449 
02450    /* search for experiment name in exptab */
02451    if (exp_name == NULL)
02452       exp_name = "";
02453 
02454    strcpy(exp_name1, exp_name);
02455    if (exp_name1[0] == 0) {
02456       status = cm_select_experiment(host_name, exp_name1);
02457       if (status != CM_SUCCESS)
02458          return status;
02459    }
02460 
02461    /* connect to MIDAS server */
02462    if (host_name[0]) {
02463       status = rpc_server_connect(host_name, exp_name1);
02464       if (status != RPC_SUCCESS)
02465          return status;
02466 
02467       /* register MIDAS library functions */
02468       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
02469       if (status != RPC_SUCCESS)
02470          return status;
02471    } else {
02472       /* lookup path for *SHM files and save it */
02473       status = cm_scan_experiments();
02474       if (status != CM_SUCCESS)
02475          return status;
02476 
02477       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
02478          if (equal_ustring(exp_name1, exptab[i].name))
02479             break;
02480 
02481       /* return if experiment not defined */
02482       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
02483          /* message should be displayed by application
02484             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
02485             cm_msg(MERROR, str);
02486           */
02487          return CM_UNDEF_EXP;
02488       }
02489 
02490       cm_set_path(exptab[i].directory);
02491 
02492       /* create alarm and elog mutexes */
02493       status = ss_mutex_create("ALARM", &mutex_alarm);
02494       if (status != SS_CREATED && status != SS_SUCCESS) {
02495          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
02496          return status;
02497       }
02498       status = ss_mutex_create("ELOG", &mutex_elog);
02499       if (status != SS_CREATED && status != SS_SUCCESS) {
02500          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
02501          return status;
02502       }
02503       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
02504    }
02505 
02506    /* open ODB */
02507    if (odb_size == 0)
02508       odb_size = DEFAULT_ODB_SIZE;
02509 
02510    status = db_open_database("ODB", odb_size, &hDB, client_name);
02511    if (status != DB_SUCCESS && status != DB_CREATED) {
02512       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02513       return status;
02514    }
02515 
02516    /* now setup client info */
02517    gethostname(local_host_name, sizeof(local_host_name));
02518 
02519    /* check watchdog timeout */
02520    if (watchdog_timeout == 0)
02521       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
02522 
02523    strcpy(client_name1, client_name);
02524    password[0] = 0;
02525    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02526                                client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02527                                password, watchdog_timeout);
02528 
02529    if (status == CM_WRONG_PASSWORD) {
02530       if (func == NULL)
02531          strcpy(str, ss_getpass("Password: "));
02532       else
02533          func(str);
02534 
02535       /* re-open database */
02536       status = db_open_database("ODB", odb_size, &hDB, client_name);
02537       if (status != DB_SUCCESS && status != DB_CREATED) {
02538          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02539          return status;
02540       }
02541 
02542       strcpy(password, ss_crypt(str, "mi"));
02543       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02544                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02545                                   password, watchdog_timeout);
02546       if (status != CM_SUCCESS) {
02547          /* disconnect */
02548          if (rpc_is_remote())
02549             rpc_server_disconnect();
02550 
02551          return status;
02552       }
02553    }
02554 
02555    cm_set_experiment_database(hDB, hKeyClient);
02556 
02557    /* set experiment name in ODB */
02558    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
02559 
02560    /* set data dir in ODB */
02561    cm_get_path(str);
02562    size = sizeof(str);
02563    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
02564 
02565    /* check /runinfo structure */
02566    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), FALSE);
02567    if (status == DB_STRUCT_MISMATCH) {
02568       cm_msg(MERROR, "cm_connect_experiment1",
02569              "Aborting on mismatching /Runinfo structure");
02570       cm_disconnect_experiment();
02571       abort();
02572    }
02573 
02574    /* register server to be able to be called by other clients */
02575    status = cm_register_server();
02576    if (status != CM_SUCCESS)
02577       return status;
02578 
02579    /* set watchdog timeout */
02580    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
02581    size = sizeof(watchdog_timeout);
02582    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
02583    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02584    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02585 
02586    /* send startup notification */
02587    if (strchr(local_host_name, '.'))
02588       *strchr(local_host_name, '.') = 0;
02589 
02590    /* startup message is not displayed */
02591    _message_print = NULL;
02592 
02593    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started",
02594           client_name, local_host_name);
02595 
02596    /* enable system and user messages to stdout as default */
02597    cm_set_msg_print(MT_ALL, MT_ALL, puts);
02598 
02599    /* call cm_check_connect when exiting */
02600    atexit((void (*)(void)) cm_check_connect);
02601 
02602    /* register ctrl-c handler */
02603    ss_ctrlc_handler(cm_ctrlc_handler);
02604 
02605    return CM_SUCCESS;
02606 }
02607 
02608 /********************************************************************/
02609 /** 
02610 Connect to a MIDAS server and return all defined
02611            experiments in *exp_name[MAX_EXPERIMENTS]
02612 @param  host_name         Internet host name.
02613 @param  exp_name          list of experiment names
02614 @return CM_SUCCESS, RPC_NET_ERROR
02615 */
02616 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
02617 {
02618    INT i, status;
02619    struct sockaddr_in bind_addr;
02620    INT sock;
02621    char str[MAX_EXPERIMENT * NAME_LENGTH];
02622    struct hostent *phe;
02623 
02624    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
02625       status = cm_scan_experiments();
02626       if (status != CM_SUCCESS)
02627          return status;
02628 
02629       for (i = 0; i < MAX_EXPERIMENT; i++)
02630          strcpy(exp_name[i], exptab[i].name);
02631 
02632       return CM_SUCCESS;
02633    }
02634 #ifdef OS_WINNT
02635    {
02636       WSADATA WSAData;
02637 
02638       /* Start windows sockets */
02639       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02640          return RPC_NET_ERROR;
02641    }
02642 #endif
02643 
02644    /* create a new socket for connecting to remote server */
02645    sock = socket(AF_INET, SOCK_STREAM, 0);
02646    if (sock == -1) {
02647       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02648       return RPC_NET_ERROR;
02649    }
02650 
02651    /* connect to remote node */
02652    memset(&bind_addr, 0, sizeof(bind_addr));
02653    bind_addr.sin_family = AF_INET;
02654    bind_addr.sin_addr.s_addr = 0;
02655    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
02656 
02657 #ifdef OS_VXWORKS
02658    {
02659       INT host_addr;
02660 
02661       host_addr = hostGetByName(host_name);
02662       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02663    }
02664 #else
02665    phe = gethostbyname(host_name);
02666    if (phe == NULL) {
02667       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
02668       return RPC_NET_ERROR;
02669    }
02670    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02671 #endif
02672 
02673 #ifdef OS_UNIX
02674    do {
02675       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02676 
02677       /* don't return if an alarm signal was cought */
02678    } while (status == -1 && errno == EINTR);
02679 #else
02680    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02681 #endif
02682 
02683    if (status != 0) {
02684 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02685       return RPC_NET_ERROR;
02686    }
02687 
02688    /* request experiment list */
02689    send(sock, "I", 2, 0);
02690 
02691    for (i = 0; i < MAX_EXPERIMENT; i++) {
02692       exp_name[i][0] = 0;
02693       status = recv_string(sock, str, sizeof(str), 1000);
02694 
02695       if (status < 0)
02696          return RPC_NET_ERROR;
02697 
02698       if (status == 0)
02699          break;
02700 
02701       strcpy(exp_name[i], str);
02702    }
02703 
02704    exp_name[i][0] = 0;
02705    closesocket(sock);
02706 
02707    return CM_SUCCESS;
02708 }
02709 
02710 /********************************************************************/
02711 /**
02712 Connect to a MIDAS server and select an experiment
02713            from the experiments available on this server
02714 @internal
02715 @param  host_name         Internet host name.
02716 @param  exp_name          list of experiment names
02717 @return CM_SUCCESS, RPC_NET_ERROR
02718 */
02719 INT cm_select_experiment(char *host_name, char *exp_name)
02720 {
02721    INT status, i;
02722    char expts[MAX_EXPERIMENT][NAME_LENGTH];
02723    char str[32];
02724 
02725    /* retrieve list of experiments and make selection */
02726    status = cm_list_experiments(host_name, expts);
02727    if (status != CM_SUCCESS)
02728       return status;
02729 
02730    if (expts[1][0]) {
02731       if (host_name[0])
02732          printf("Available experiments on server %s:\n", host_name);
02733       else
02734          printf("Available experiments on local computer:\n");
02735 
02736       for (i = 0; expts[i][0]; i++)
02737          printf("%d : %s\n", i, expts[i]);
02738       printf("Select number: ");
02739       ss_gets(str, 32);
02740       i = atoi(str);
02741       strcpy(exp_name, expts[i]);
02742    } else
02743       strcpy(exp_name, expts[0]);
02744 
02745    return CM_SUCCESS;
02746 }
02747 
02748 /********************************************************************/
02749 /**
02750 Connect to a MIDAS client of the current experiment
02751 @internal
02752 @param  client_name       Name of client to connect to. This name
02753                             is set by the other client via the
02754                             cm_connect_experiment call.
02755 @param  hConn            Connection handle
02756 @return CM_SUCCESS, CM_NO_CLIENT
02757 */
02758 INT cm_connect_client(char *client_name, HNDLE * hConn)
02759 {
02760    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02761    INT status, i, length, port;
02762    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02763 
02764    /* find client entry in ODB */
02765    cm_get_experiment_database(&hDB, &hKey);
02766 
02767    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02768    if (status != DB_SUCCESS)
02769       return status;
02770 
02771    i = 0;
02772    do {
02773       /* search for client with specific name */
02774       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02775       if (status == DB_NO_MORE_SUBKEYS)
02776          return CM_NO_CLIENT;
02777 
02778       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02779       if (status != DB_SUCCESS)
02780          return status;
02781 
02782       length = NAME_LENGTH;
02783       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02784       if (status != DB_SUCCESS)
02785          return status;
02786 
02787       if (equal_ustring(name, client_name)) {
02788          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02789          if (status != DB_SUCCESS)
02790             return status;
02791 
02792          length = sizeof(INT);
02793          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02794          if (status != DB_SUCCESS)
02795             return status;
02796 
02797          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02798          if (status != DB_SUCCESS)
02799             return status;
02800 
02801          length = sizeof(host_name);
02802          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02803          if (status != DB_SUCCESS)
02804             return status;
02805 
02806          /* client found -> connect to its server port */
02807          return rpc_client_connect(host_name, port, client_name, hConn);
02808       }
02809 
02810 
02811    } while (TRUE);
02812 }
02813 
02814 /********************************************************************/
02815 /**
02816 Disconnect from a MIDAS client 
02817 @param   hConn             Connection handle obtained via
02818                              cm_connect_client()
02819 @param   bShutdown         If TRUE, disconnect from client and
02820                              shut it down (exit the client program)
02821                              by sending a RPC_SHUTDOWN message
02822 @return   see rpc_client_disconnect()
02823 */
02824 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02825 {
02826    return rpc_client_disconnect(hConn, bShutdown);
02827 }
02828 
02829 /********************************************************************/
02830 /**
02831 Disconnect from a MIDAS experiment.
02832 @attention Should be the last call to a MIDAS library function in an
02833 application before it exits. This function removes the client information
02834 from the ODB, disconnects all TCP connections and frees all internal
02835 allocated memory. See cm_connect_experiment() for example.
02836 @return CM_SUCCESS
02837 */
02838 INT cm_disconnect_experiment(void)
02839 {
02840    HNDLE hDB, hKey;
02841    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02842 
02843    /* send shutdown notification */
02844    rpc_get_name(client_name);
02845    gethostname(local_host_name, sizeof(local_host_name));
02846    if (strchr(local_host_name, '.'))
02847       *strchr(local_host_name, '.') = 0;
02848 
02849    /* disconnect message not displayed */
02850    _message_print = NULL;
02851 
02852    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped",
02853           client_name, local_host_name);
02854 
02855    if (rpc_is_remote()) {
02856       /* close open records */
02857       db_close_all_records();
02858 
02859       rpc_client_disconnect(-1, FALSE);
02860       rpc_server_disconnect();
02861    } else {
02862       rpc_client_disconnect(-1, FALSE);
02863 
02864 #ifdef LOCAL_ROUTINES
02865       ss_alarm(0, cm_watchdog);
02866       _watchdog_last_called = 0;
02867 #endif                          /* LOCAL_ROUTINES */
02868 
02869       /* delete client info */
02870       cm_get_experiment_database(&hDB, &hKey);
02871 
02872       if (hDB)
02873          cm_delete_client_info(hDB, 0);
02874 
02875       bm_close_all_buffers();
02876       db_close_all_databases();
02877    }
02878 
02879    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02880       rpc_server_shutdown();
02881 
02882    /* free RPC list */
02883    rpc_deregister_functions();
02884 
02885    cm_set_experiment_database(0, 0);
02886 
02887    _msg_buffer = 0;
02888 
02889    /* free memory buffers */
02890    if (_event_buffer_size > 0) {
02891       M_FREE(_event_buffer);
02892       _event_buffer_size = 0;
02893    }
02894 
02895    if (_net_recv_buffer_size > 0) {
02896       M_FREE(_net_recv_buffer);
02897       _net_recv_buffer_size = 0;
02898    }
02899 
02900    if (_net_send_buffer_size > 0) {
02901       M_FREE(_net_send_buffer);
02902       _net_send_buffer_size = 0;
02903    }
02904 
02905    if (_tcp_buffer != NULL) {
02906       M_FREE(_tcp_buffer);
02907       _tcp_buffer = NULL;
02908    }
02909 
02910    return CM_SUCCESS;
02911 }
02912 
02913 /********************************************************************/
02914 /**
02915 Set the handle to the ODB for the currently connected experiment
02916 @param hDB              Database handle
02917 @param hKeyClient       Key handle of client structure
02918 @return CM_SUCCESS
02919 */
02920 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02921 {
02922    _hDB = hDB;
02923    _hKeyClient = hKeyClient;
02924 
02925    return CM_SUCCESS;
02926 }
02927 
02928 
02929 
02930 /**dox***************************************************************/
02931 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02932 
02933 /********************************************************************/
02934 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02935 /********************************************************************\
02936 
02937   Routine: cm_set_experiment_mutex
02938 
02939   Purpose: Set the handle to the experiment wide mutexes
02940 
02941   Input:
02942     INT    mutex_alarm      Alarm mutex
02943     INT    mutex_elog       Elog mutex
02944 
02945   Output:
02946     none
02947 
02948   Function value:
02949     CM_SUCCESS              Successful completion
02950 
02951 \********************************************************************/
02952 {
02953    _mutex_alarm = mutex_alarm;
02954    _mutex_elog = mutex_elog;
02955 
02956    return CM_SUCCESS;
02957 }
02958 
02959 /**dox***************************************************************/
02960 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02961 
02962 /********************************************************************/
02963 /** 
02964 Get the handle to the ODB from the currently connected experiment.
02965 
02966 @attention This function returns the handle of the online database (ODB) which
02967 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02968 to access the client information in the ODB. If the client key handle is not needed,
02969 the parameter can be NULL.
02970 \code
02971 HNDLE hDB, hkeyclient;
02972  char  name[32];
02973  int   size;
02974  db_get_experiment_database(&hdb, &hkeyclient);
02975  size = sizeof(name);
02976  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02977  printf("My name is %s\n", name);
02978 \endcode
02979 @param hDB Database handle.
02980 @param hKeyClient Handle for key where search starts, zero for root.
02981 @return CM_SUCCESS
02982 */
02983 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02984 {
02985    if (_hDB) {
02986       if (hDB != NULL)
02987          *hDB = _hDB;
02988       if (hKeyClient != NULL)
02989          *hKeyClient = _hKeyClient;
02990    } else {
02991       if (hDB != NULL)
02992          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02993       if (hKeyClient != NULL)
02994          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02995    }
02996 
02997    return CM_SUCCESS;
02998 }
02999 
03000 /**dox***************************************************************/
03001 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03002 
03003 /********************************************************************/
03004 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
03005 /********************************************************************\
03006 
03007   Routine: cm_get_experiment_mutex
03008 
03009   Purpose: Get the handle to the experiment wide mutexes
03010 
03011   Input:
03012     none
03013 
03014   Output:
03015     INT    mutex_alarm      Alarm mutex
03016     INT    mutex_elog       Elog mutex
03017 
03018   Function value:
03019     CM_SUCCESS              Successful completion
03020 
03021 \********************************************************************/
03022 {
03023    if (mutex_alarm)
03024       *mutex_alarm = _mutex_alarm;
03025    if (mutex_elog)
03026       *mutex_elog = _mutex_elog;
03027 
03028    return CM_SUCCESS;
03029 }
03030 
03031 /**dox***************************************************************/
03032 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03033 
03034 /********************************************************************/
03035 /**
03036 Sets the internal watchdog flags and the own timeout.
03037 If call_watchdog is TRUE, the cm_watchdog routine is called
03038 periodically from the system to show other clients that
03039 this application is "alive". On UNIX systems, the
03040 alarm() timer is used which is then not available for
03041 user purposes.
03042 
03043 The timeout specifies the time, after which the calling
03044 application should be considered "dead" by other clients.
03045 Normally, the cm_watchdog() routines is called periodically.
03046 If a client crashes, this does not occur any more. Then
03047 other clients can detect this and clear all buffer and
03048 database entries of this application so they are not
03049 blocked any more. If this application should not checked
03050 by others, the timeout can be specified as zero.
03051 It might be useful for debugging purposes to do so,
03052 because if a debugger comes to a breakpoint and stops
03053 the application, the periodic call of cm_watchdog
03054 is disabled and the client looks like dead.
03055 
03056 If the timeout is not zero, but the watchdog is not
03057 called (call_watchdog == FALSE), the user must ensure
03058 to call cm_watchdog periodically with a period of
03059 WATCHDOG_INTERVAL milliseconds or less.
03060 
03061 An application which calles system routines which block
03062 the alarm signal for some time, might increase the
03063 timeout to the maximum expected blocking time before
03064 issuing the calls. One example is the logger doing
03065 Exabyte tape IO, which can take up to one minute.
03066 @param    call_watchdog   Call the cm_watchdog routine periodically
03067 @param    timeout         Timeout for this application in ms
03068 @return   CM_SUCCESS
03069 */
03070 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
03071 {
03072    INT i;
03073 
03074    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
03075    _watchdog_timeout = timeout;
03076 
03077    if (rpc_is_remote())
03078       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
03079 
03080 #ifdef LOCAL_ROUTINES
03081 
03082    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
03083       HNDLE hDB, hKey;
03084 
03085       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
03086 
03087       /* write timeout value to client enty in ODB */
03088       cm_get_experiment_database(&hDB, &hKey);
03089 
03090       if (hDB) {
03091          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03092          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
03093          db_set_mode(hDB, hKey, MODE_READ, TRUE);
03094       }
03095    } else {
03096       _call_watchdog = call_watchdog;
03097       _watchdog_timeout = timeout;
03098 
03099       /* set watchdog flag of all open buffers */
03100       for (i = _buffer_entries; i > 0; i--) {
03101          BUFFER_CLIENT *pclient;
03102          BUFFER_HEADER *pheader;
03103          INT index;
03104 
03105          index = _buffer[i - 1].client_index;
03106          pheader = _buffer[i - 1].buffer_header;
03107          pclient = &pheader->client[index];
03108 
03109          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03110              _buffer[i - 1].index != rpc_get_server_acception())
03111             continue;
03112 
03113          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03114              _buffer[i - 1].index != ss_gettid())
03115             continue;
03116 
03117          if (!_buffer[i - 1].attached)
03118             continue;
03119 
03120          /* clear entry from client structure in buffer header */
03121          pclient->watchdog_timeout = timeout;
03122 
03123          /* show activity */
03124          pclient->last_activity = ss_millitime();
03125       }
03126 
03127       /* set watchdog flag of alll open databases */
03128       for (i = _database_entries; i > 0; i--) {
03129          DATABASE_HEADER *pheader;
03130          DATABASE_CLIENT *pclient;
03131          INT index;
03132 
03133          db_lock_database(i);
03134          index = _database[i - 1].client_index;
03135          pheader = _database[i - 1].database_header;
03136          pclient = &pheader->client[index];
03137 
03138          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03139              _database[i - 1].index != rpc_get_server_acception()) {
03140             db_unlock_database(i);
03141             continue;
03142          }
03143 
03144          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03145              _database[i - 1].index != ss_gettid()) {
03146             db_unlock_database(i);
03147             continue;
03148          }
03149 
03150          if (!_database[i - 1].attached) {
03151             db_unlock_database(i);
03152             continue;
03153          }
03154 
03155          /* clear entry from client structure in buffer header */
03156          pclient->watchdog_timeout = timeout;
03157 
03158          /* show activity */
03159          pclient->last_activity = ss_millitime();
03160 
03161          db_unlock_database(i);
03162       }
03163 
03164       if (call_watchdog)
03165          /* restart watchdog */
03166          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
03167       else
03168          /* kill current timer */
03169          ss_alarm(0, cm_watchdog);
03170    }
03171 
03172 #endif                          /* LOCAL_ROUTINES */
03173 
03174    return CM_SUCCESS;
03175 }
03176 
03177 /********************************************************************/
03178 /**
03179 Return the current watchdog parameters
03180 @param call_watchdog   Call the cm_watchdog routine periodically
03181 @param timeout         Timeout for this application in seconds
03182 @return   CM_SUCCESS
03183 */
03184 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
03185 {
03186    if (call_watchdog)
03187       *call_watchdog = _call_watchdog;
03188    if (timeout)
03189       *timeout = _watchdog_timeout;
03190 
03191    return CM_SUCCESS;
03192 }
03193 
03194 /********************************************************************/
03195 /**
03196 Return watchdog information about specific client
03197 @param    hDB              ODB handle
03198 @param    client_name     ODB client name
03199 @param    timeout         Timeout for this application in seconds
03200 @param    last            Last time watchdog was called in msec
03201 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
03202 */
03203 
03204 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
03205 {
03206    if (rpc_is_remote())
03207       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
03208 
03209 #ifdef LOCAL_ROUTINES
03210    {
03211       DATABASE_HEADER *pheader;
03212       DATABASE_CLIENT *pclient;
03213       INT i;
03214 
03215       if (hDB > _database_entries || hDB <= 0) {
03216          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03217          return DB_INVALID_HANDLE;
03218       }
03219 
03220       if (!_database[hDB - 1].attached) {
03221          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03222          return DB_INVALID_HANDLE;
03223       }
03224 
03225       /* lock database */
03226       db_lock_database(hDB);
03227 
03228       pheader = _database[hDB - 1].database_header;
03229       pclient = pheader->client;
03230 
03231       /* find client */
03232       for (i = 0; i < pheader->max_client_index; i++, pclient++)
03233          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
03234             *timeout = pclient->watchdog_timeout;
03235             *last = ss_millitime() - pclient->last_activity;
03236             db_unlock_database(hDB);
03237             return CM_SUCCESS;
03238          }
03239 
03240       *timeout = *last = 0;
03241 
03242       db_unlock_database(hDB);
03243 
03244       return CM_NO_CLIENT;
03245    }
03246 #else                           /* LOCAL_ROUTINES */
03247    return CM_SUCCESS;
03248 #endif                          /* LOCAL_ROUTINES */
03249 }
03250 
03251 
03252 /**dox***************************************************************/
03253 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03254 
03255 /********************************************************************/
03256 INT cm_register_server(void)
03257 /********************************************************************\
03258 
03259   Routine: cm_register_server
03260 
03261   Purpose: Register a server which can be called from other clients
03262            of a specific experiment.
03263 
03264   Input:
03265     none
03266 
03267   Output:
03268     none
03269 
03270   Function value:
03271     CM_SUCCESS              Successful completion
03272 
03273 \********************************************************************/
03274 {
03275    INT status, port;
03276    HNDLE hDB, hKey;
03277 
03278    if (!_server_registered) {
03279       port = 0;
03280       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
03281       if (status != RPC_SUCCESS)
03282          return status;
03283       _server_registered = TRUE;
03284 
03285       /* register MIDAS library functions */
03286       rpc_register_functions(rpc_get_internal_list(1), NULL);
03287 
03288       /* store port number in ODB */
03289       cm_get_experiment_database(&hDB, &hKey);
03290 
03291       status = db_find_key(hDB, hKey, "Server Port", &hKey);
03292       if (status != DB_SUCCESS)
03293          return status;
03294 
03295       /* unlock database */
03296       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03297 
03298       /* set value */
03299       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
03300       if (status != DB_SUCCESS)
03301          return status;
03302 
03303       /* lock database */
03304       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03305    }
03306 
03307    return CM_SUCCESS;
03308 }
03309 
03310 /**dox***************************************************************/
03311 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03312 
03313 /********************************************************************/
03314 /**
03315 Registers a callback function for run transitions.
03316 This function internally registers the transition callback
03317 function and publishes its request for transition notification by writing
03318 a transition request to /System/Clients/<pid>/Transition XXX.
03319 Other clients making a transition scan the transition requests of all clients
03320 and call their transition callbacks via RPC.
03321 
03322 Clients can register for transitions (Start/Stop/Pause/Resume) in a given
03323 sequence. All sequence numbers given in the registration are sorted on 
03324 a transition and the clients are contacted in ascending order. By default,
03325 all programs register with a sequence number of 500. The logger however
03326 uses 200 for start, so that it can open files before the other clients
03327 are contacted, and 800 for stop, so that the files get closed when all
03328 other clients have gone already through the stop trantition.
03329 
03330 The callback function returns CM_SUCCESS if it can perform the transition or
03331 a value larger than one in case of error. An error string can be copied
03332 into the error variable.
03333 @attention The callback function will be called on transitions from inside the
03334     cm_yield() function which therefore must be contained in the main program loop.
03335 \code
03336 INT start(INT run_number, char *error)
03337 {
03338   if (<not ok>)
03339     {
03340     strcpy(error, "Cannot start because ...");
03341     return 2;
03342     }
03343   printf("Starting run %d\n", run_number);
03344   return CM_SUCCESS;
03345 }
03346 main()
03347 {
03348   ...
03349   cm_register_transition(TR_START, start, 500);
03350   do
03351     {
03352     status = cm_yield(1000);
03353     } while (status != RPC_SHUTDOWN &&
03354              status != SS_ABORT);
03355   ...
03356 }
03357 \endcode
03358 @param transition Transition to register for (see @ref state_transition)
03359 @param func Callback function.
03360 @param sequence_number Sequence number for that transition (1..1000)
03361 @return CM_SUCCESS
03362 */
03363 INT cm_register_transition(INT transition, INT(*func) (INT, char *), INT sequence_number)
03364 {
03365    INT status, i;
03366    HNDLE hDB, hKey, hKeyTrans;
03367    KEY key;
03368    char str[256];
03369 
03370    /* check for valid transition */
03371    if (transition != TR_START && transition != TR_STOP &&
03372        transition != TR_PAUSE && transition != TR_RESUME) {
03373       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03374       return CM_INVALID_TRANSITION;
03375    }
03376 
03377    cm_get_experiment_database(&hDB, &hKey);
03378 
03379    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
03380 
03381    /* find empty slot */
03382    for (i = 0; i < MAX_TRANSITIONS; i++)
03383       if (!_trans_table[i].transition)
03384          break;
03385 
03386    if (i == MAX_TRANSITIONS) {
03387       cm_msg(MERROR, "cm_register_transition",
03388              "To many transition registrations. Please increase MAX_TRANSITIONS and recompile");
03389       return CM_TOO_MANY_REQUESTS;
03390    }
03391 
03392    _trans_table[i].transition = transition;
03393    _trans_table[i].func = func;
03394    _trans_table[i].sequence_number = sequence_number;
03395 
03396    for (i = 0; i < 13; i++)
03397       if (trans_name[i].transition == transition)
03398          break;
03399 
03400    sprintf(str, "Transition %s", trans_name[i].name);
03401 
03402    /* unlock database */
03403    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03404 
03405    /* set value */
03406    status = db_find_key(hDB, hKey, str, &hKeyTrans);
03407    if (!hKeyTrans) {
03408       status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
03409       if (status != DB_SUCCESS)
03410          return status;
03411    } else {
03412       status = db_get_key(hDB, hKeyTrans, &key);
03413       if (status != DB_SUCCESS)
03414          return status;
03415       status =
03416           db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values,
03417                             TID_INT);
03418       if (status != DB_SUCCESS)
03419          return status;
03420    }
03421 
03422    /* re-lock database */
03423    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03424 
03425    return CM_SUCCESS;
03426 }
03427 
03428 /********************************************************************/
03429 /**
03430 Change the transition sequence for the calling program.
03431 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03432 @param sequence_number New sequence number, should be between 1 and 1000
03433 @return     CM_SUCCESS
03434 */
03435 INT cm_set_transition_sequence(INT transition, INT sequence_number)
03436 {
03437    INT status, i;
03438    HNDLE hDB, hKey;
03439    char str[256];
03440 
03441    /* check for valid transition */
03442    if (transition != TR_START && transition != TR_STOP &&
03443        transition != TR_PAUSE && transition != TR_RESUME) {
03444       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03445       return CM_INVALID_TRANSITION;
03446    }
03447 
03448    cm_get_experiment_database(&hDB, &hKey);
03449 
03450    /* Find the transition type from the list */
03451    for (i = 0; i < 13; i++)
03452       if (trans_name[i].transition == transition)
03453          break;
03454    sprintf(str, "Transition %s", trans_name[i].name);
03455 
03456    /* Change local sequence number for this transition type */
03457    for (i = 0; i < MAX_TRANSITIONS; i++)
03458       if (_trans_table[i].transition == transition) {
03459          _trans_table[i].sequence_number = sequence_number;
03460          break;
03461       }
03462 
03463    /* unlock database */
03464    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03465 
03466    /* set value */
03467    status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT);
03468    if (status != DB_SUCCESS)
03469       return status;
03470 
03471    /* re-lock database */
03472    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03473 
03474    return CM_SUCCESS;
03475 
03476 }
03477 
03478 /**dox***************************************************************/
03479 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03480 
03481 static INT _requested_transition;
03482 static DWORD _deferred_transition_mask;
03483 
03484 /**dox***************************************************************/
03485 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03486 
03487 /********************************************************************/
03488 /**
03489 Register a deferred transition handler. If a client is
03490 registered as a deferred transition handler, it may defer
03491 a requested transition by returning FALSE until a certain
03492 condition (like a motor reaches its end position) is
03493 reached.
03494 @param transition      One of TR_xxx
03495 @param (*func)         Function which gets called whenever
03496                        a transition is requested. If it returns
03497                        FALSE, the transition is not performed.
03498 @return CM_SUCCESS,    <error> Error from ODB access
03499 */
03500 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
03501 {
03502    INT status, i, size;
03503    char tr_key_name[256];
03504    HNDLE hDB, hKey;
03505 
03506    cm_get_experiment_database(&hDB, &hKey);
03507 
03508    for (i = 0; _deferred_trans_table[i].transition; i++)
03509       if (_deferred_trans_table[i].transition == transition)
03510          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
03511 
03512    /* set new transition mask */
03513    _deferred_transition_mask |= transition;
03514 
03515    for (i = 0; i < 13; i++)
03516       if (trans_name[i].transition == transition)
03517          break;
03518 
03519    sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
03520 
03521    /* unlock database */
03522    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03523 
03524    /* set value */
03525    i = 0;
03526    status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT);
03527    if (status != DB_SUCCESS)
03528       return status;
03529 
03530    /* re-lock database */
03531    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03532 
03533    /* hot link requested transition */
03534    size = sizeof(_requested_transition);
03535    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size,
03536                 TID_INT, TRUE);
03537    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
03538    status =
03539        db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL,
03540                       NULL);
03541    if (status != DB_SUCCESS) {
03542       cm_msg(MERROR, "cm_register_deferred_transition",
03543              "Cannot hotlink /Runinfo/Requested Transition");
03544       return status;
03545    }
03546 
03547    return CM_SUCCESS;
03548 }
03549 
03550 /********************************************************************/
03551 /**
03552 Check for any deferred transition. If a deferred transition
03553 handler has been registered via the
03554 cm_register_deferred_transition function, this routine
03555 should be called regularly. It checks if a transition
03556 request is pending. If so, it calld the registered handler
03557 if the transition should be done and then actually does
03558 the transition.
03559 @return     CM_SUCCESS, <error>  Error from cm_transition()
03560 */
03561 INT cm_check_deferred_transition()
03562 {
03563    INT i, status;
03564    char str[256];
03565    static BOOL first;
03566 
03567    if (_requested_transition == 0)
03568       first = TRUE;
03569 
03570    if (_requested_transition & _deferred_transition_mask) {
03571       for (i = 0; _deferred_trans_table[i].transition; i++)
03572          if (_deferred_trans_table[i].transition == _requested_transition)
03573             break;
03574 
03575       if (_deferred_trans_table[i].transition == _requested_transition) {
03576          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition,
03577                                                                    first)) {
03578             status =
03579                 cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str),
03580                               SYNC, FALSE);
03581             if (status != CM_SUCCESS)
03582                cm_msg(MERROR, "cm_check_deferred_transition",
03583                       "Cannot perform deferred transition: %s", str);
03584 
03585             /* bypass hotlink and set _requested_transition directly to zero */
03586             _requested_transition = 0;
03587 
03588             return status;
03589          }
03590          first = FALSE;
03591       }
03592    }
03593 
03594    return SUCCESS;
03595 }
03596 
03597 
03598 /**dox***************************************************************/
03599 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03600 
03601 /********************************************************************/
03602 
03603 /**dox***************************************************************/
03604 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03605 
03606 typedef struct {
03607    int sequence_number;
03608    char host_name[HOST_NAME_LENGTH];
03609    char client_name[NAME_LENGTH];
03610    int port;
03611 } TR_CLIENT;
03612 
03613 int tr_compare(const void *arg1, const void *arg2)
03614 {
03615    return ((TR_CLIENT *) arg1)->sequence_number - ((TR_CLIENT *) arg2)->sequence_number;
03616 }
03617 
03618 /********************************************************************/
03619 /**
03620 Performs a run transition (Start/Stop/Pause/Resume).
03621 
03622 Synchronous/Asynchronous flag.
03623 If set to ASYNC, the transition is done
03624 asynchronously, meaning that clients are connected and told to execute their
03625 callback routine, but no result is awaited. The return value is
03626 specified by the transition callback function on the remote clients. If all callbacks
03627 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03628 perform the transition, the return value of this callback is returned from
03629 cm_transition().
03630 The async_flag is usually FALSE so that transition callbacks can block a
03631 run transition in case of problems and return an error string. The only exception are
03632 situations where a run transition is performed automatically by a program which
03633 cannot block in a transition. For example the logger can cause a run stop when a
03634 disk is nearly full but it cannot block in the cm_transition() function since it
03635 has its own run stop callback which must flush buffers and close disk files and
03636 tapes.
03637 \code
03638 ...
03639     i = 1;
03640     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03641 
03642       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03643       if (status != CM_SUCCESS)
03644       {
03645         // in case of error
03646         printf("Error: %s\n", str);
03647       }
03648     ...
03649 \endcode
03650 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03651 @param run_number New run number. If zero, use current run number plus one.
03652 @param perror returned error string.
03653 @param strsize Size of error string.
03654 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03655 @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
03656 @return CM_SUCCESS, <error> error code from remote client
03657 */
03658 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize,
03659                   INT async_flag, INT debug_flag)
03660 {
03661    INT i, j, status, index, size, sequence_number, port, state, old_timeout, n_tr_clients;
03662    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hConn, hKeyTrans;
03663    DWORD seconds;
03664    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH],
03665        str[256], error[256], tr_key_name[256];
03666    KEY key;
03667    BOOL deferred;
03668    PROGRAM_INFO program_info;
03669    TR_CLIENT *tr_client;
03670 
03671    deferred = (transition & TR_DEFERRED) > 0;
03672    transition &= ~TR_DEFERRED;
03673 
03674    /* check for valid transition */
03675    if (transition != TR_START && transition != TR_STOP &&
03676        transition != TR_PAUSE && transition != TR_RESUME) {
03677       cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
03678       return CM_INVALID_TRANSITION;
03679    }
03680 
03681    /* get key of local client */
03682    cm_get_experiment_database(&hDB, &hKeylocal);
03683 
03684    if (perror != NULL)
03685       strcpy(perror, "Success");
03686 
03687    /* if no run number is given, get it from DB */
03688    if (run_number == 0) {
03689       size = sizeof(run_number);
03690       status =
03691           db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03692       assert(status == SUCCESS);
03693    }
03694 
03695    if (run_number <= 0) {
03696       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d",
03697              run_number);
03698       abort();
03699    }
03700 
03701    /* Set new run number in ODB */
03702    if (transition == TR_START) {
03703       if (debug_flag == 1)
03704          printf("Setting run number %d in ODB\n", run_number);
03705       if (debug_flag == 2)
03706          cm_msg(MDEBUG, "cm_transition", "cm_transition: Setting run number %d in ODB",
03707                 run_number);
03708 
03709       status = db_set_value(hDB, 0, "Runinfo/Run number",
03710                             &run_number, sizeof(run_number), 1, TID_INT);
03711       assert(status == SUCCESS);
03712       if (status != DB_SUCCESS)
03713          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03714    }
03715 
03716    if (deferred) {
03717       /* remove transition request */
03718       i = 0;
03719       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03720    } else {
03721       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03722       if (status != DB_SUCCESS) {
03723          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03724          return status;
03725       }
03726 
03727       /* check if deferred transition already in progress */
03728       size = sizeof(INT);
03729       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03730       if (i) {
03731          if (perror)
03732             sprintf(perror, "Deferred transition already in progress");
03733 
03734          return CM_TRANSITION_IN_PROGRESS;
03735       }
03736 
03737       for (i = 0; i < 13; i++)
03738          if (trans_name[i].transition == transition)
03739             break;
03740 
03741       sprintf(tr_key_name, "Transition %s DEFERRED", trans_name[i].name);
03742 
03743       /* search database for clients with deferred transition request */
03744       for (i = 0, status = 0;; i++) {
03745          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03746          if (status == DB_NO_MORE_SUBKEYS)
03747             break;
03748 
03749          if (status == DB_SUCCESS) {
03750             size = sizeof(sequence_number);
03751             status = db_get_value(hDB, hSubkey, tr_key_name,
03752                                   &sequence_number, &size, TID_INT, FALSE);
03753 
03754             /* if registered for deferred transition, set flag in ODB and return */
03755             if (status == DB_SUCCESS) {
03756                size = NAME_LENGTH;
03757                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03758                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition,
03759                             sizeof(int), 1, TID_INT);
03760 
03761                if (debug_flag == 1)
03762                   printf("---- Transition %s deferred by client \"%s\" ----\n",
03763                          trans_name[i].name, str);
03764                if (debug_flag == 2)
03765                   cm_msg(MDEBUG, "cm_transition",
03766                          "cm_transition: ---- Transition %s deferred by client \"%s\" ----",
03767                          trans_name[i].name, str);
03768 
03769                if (perror)
03770                   sprintf(perror, "Transition deferred by client \"%s\"", str);
03771 
03772                return CM_DEFERRED_TRANSITION;
03773             }
03774          }
03775       }
03776    }
03777 
03778    /* execute programs on start */
03779    if (transition == TR_START) {
03780       str[0] = 0;
03781       size = sizeof(str);
03782       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING,
03783                    TRUE);
03784       if (str[0])
03785          ss_system(str);
03786 
03787       db_find_key(hDB, 0, "/Programs", &hRootKey);
03788       if (hRootKey) {
03789          for (i = 0;; i++) {
03790             status = db_enum_key(hDB, hRootKey, i, &hKey);
03791             if (status == DB_NO_MORE_SUBKEYS)
03792                break;
03793 
03794             db_get_key(hDB, hKey, &key);
03795 
03796             /* don't check "execute on xxx" */
03797             if (key.type != TID_KEY)
03798                continue;
03799 
03800             size = sizeof(program_info);
03801             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03802             if (status != DB_SUCCESS) {
03803                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03804                continue;
03805             }
03806 
03807             if (program_info.auto_start && program_info.start_command[0])
03808                ss_system(program_info.start_command);
03809          }
03810       }
03811    }
03812 
03813    /* set new start time in database */
03814    if (transition == TR_START) {
03815       /* ASCII format */
03816       cm_asctime(str, sizeof(str));
03817       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03818 
03819       /* reset stop time */
03820       seconds = 0;
03821       db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03822                    &seconds, sizeof(seconds), 1, TID_DWORD);
03823 
03824       /* Seconds since 1.1.1970 */
03825       cm_time(&seconds);
03826       db_set_value(hDB, 0, "Runinfo/Start Time binary",
03827                    &seconds, sizeof(seconds), 1, TID_DWORD);
03828    }
03829 
03830    /* set stop time in database */
03831    if (transition == TR_STOP) {
03832       size = sizeof(state);
03833       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03834       if (status != DB_SUCCESS)
03835          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03836 
03837       if (state != STATE_STOPPED) {
03838          /* stop time binary */
03839          cm_time(&seconds);
03840          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03841                                &seconds, sizeof(seconds), 1, TID_DWORD);
03842          if (status != DB_SUCCESS)
03843             cm_msg(MERROR, "cm_transition",
03844                    "cannot set \"Runinfo/Stop Time binary\" in database");
03845 
03846          /* stop time ascii */
03847          cm_asctime(str, sizeof(str));
03848          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03849          if (status != DB_SUCCESS)
03850             cm_msg(MERROR, "cm_transition",
03851                    "cannot set \"Runinfo/Stop Time\" in database");
03852       }
03853    }
03854 
03855    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03856    if (status != DB_SUCCESS) {
03857       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03858       return status;
03859    }
03860 
03861    for (i = 0; i < 13; i++)
03862       if (trans_name[i].transition == transition)
03863          break;
03864 
03865    if (debug_flag == 1)
03866       printf("---- Transition %s started ----\n", trans_name[i].name);
03867    if (debug_flag == 2)
03868       cm_msg(MDEBUG, "cm_transition", "cm_transition: ---- Transition %s started ----",
03869              trans_name[i].name);
03870 
03871    sprintf(tr_key_name, "Transition %s", trans_name[i].name);
03872 
03873    /* search database for clients which registered for transition */
03874    n_tr_clients = 0;
03875    tr_client = NULL;
03876 
03877    for (i = 0, status = 0;; i++) {
03878       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03879       if (status == DB_NO_MORE_SUBKEYS)
03880          break;
03881 
03882       if (status == DB_SUCCESS) {
03883          status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
03884 
03885          if (status == DB_SUCCESS) {
03886 
03887             db_get_key(hDB, hKeyTrans, &key);
03888 
03889             for (j = 0; j < key.num_values; j++) {
03890                size = sizeof(sequence_number);
03891                status =
03892                    db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT);
03893                assert(status == DB_SUCCESS);
03894 
03895                if (tr_client == NULL)
03896                   tr_client = (TR_CLIENT *) malloc(sizeof(TR_CLIENT));
03897                else
03898                   tr_client =
03899                       (TR_CLIENT *) realloc(tr_client,
03900                                             sizeof(TR_CLIENT) * (n_tr_clients + 1));
03901                assert(tr_client);
03902 
03903                tr_client[n_tr_clients].sequence_number = sequence_number;
03904 
03905                if (hSubkey == hKeylocal) {
03906                   /* remember own client */
03907                   tr_client[n_tr_clients].port = 0;
03908                } else {
03909                   /* get client info */
03910                   size = sizeof(client_name);
03911                   db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING,
03912                                TRUE);
03913                   strcpy(tr_client[n_tr_clients].client_name, client_name);
03914 
03915                   size = sizeof(port);
03916                   db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03917                   tr_client[n_tr_clients].port = port;
03918 
03919                   size = sizeof(host_name);
03920                   db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03921                   strcpy(tr_client[n_tr_clients].host_name, host_name);
03922                }
03923 
03924                n_tr_clients++;
03925             }
03926          }
03927       }
03928    }
03929 
03930    /* sort clients according to sequence number */
03931    if (n_tr_clients > 1)
03932       qsort(tr_client, n_tr_clients, sizeof(TR_CLIENT), tr_compare);
03933 
03934    /* contact ordered clients for transition */
03935    for (index = 0; index < n_tr_clients; index++) {
03936       /* erase error string */
03937       error[0] = 0;
03938 
03939       if (debug_flag == 1)
03940          printf("\n==== Found client \"%s\" with sequence number %d\n",
03941                 tr_client[index].client_name, tr_client[index].sequence_number);
03942       if (debug_flag == 2)
03943          cm_msg(MDEBUG, "cm_transition",
03944                 "cm_transition: ==== Found client \"%s\" with sequence number %d",
03945                 tr_client[index].client_name, tr_client[index].sequence_number);
03946 
03947       /* if own client call transition callback directly */
03948       if (tr_client[index].port == 0) {
03949          for (i = 0; _trans_table[i].transition; i++)
03950             if (_trans_table[i].transition == transition)
03951                break;
03952 
03953          /* call registerd function */
03954          if (_trans_table[i].transition == transition && _trans_table[i].func) {
03955             if (debug_flag == 1)
03956                printf("Calling local transition callback\n");
03957             if (debug_flag == 2)
03958                cm_msg(MDEBUG, "cm_transition",
03959                       "cm_transition: Calling local transition callback");
03960 
03961             status = _trans_table[i].func(run_number, error);
03962 
03963             if (debug_flag == 1)
03964                printf("Local transition callback finished\n");
03965             if (debug_flag == 2)
03966                cm_msg(MDEBUG, "cm_transition",
03967                       "cm_transition: Local transition callback finished");
03968          } else
03969             status = CM_SUCCESS;
03970 
03971          if (perror != NULL)
03972             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03973                    strlen(error) + 1 : strsize);
03974 
03975          if (status != CM_SUCCESS) {
03976             free(tr_client);
03977             return status;
03978          }
03979 
03980       } else {
03981 
03982          /* contact client if transition mask set */
03983          if (debug_flag == 1)
03984             printf("Connecting to 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: Connecting to client \"%s\" on host %s...",
03989                    tr_client[index].client_name, tr_client[index].host_name);
03990 
03991          /* client found -> connect to its server port */
03992          status = rpc_client_connect(tr_client[index].host_name, tr_client[index].port,
03993                                      tr_client[index].client_name, &hConn);
03994          if (status != RPC_SUCCESS) {
03995             cm_msg(MERROR, "cm_transition",
03996                    "cannot connect to client \"%s\" on host %s, port %d",
03997                    tr_client[index].client_name, tr_client[index].host_name,
03998                    tr_client[index].port);
03999             continue;
04000          }
04001 
04002          if (debug_flag == 1)
04003             printf("Connection established to client \"%s\" on host %s\n",
04004                    tr_client[index].client_name, tr_client[index].host_name);
04005          if (debug_flag == 2)
04006             cm_msg(MDEBUG, "cm_transition",
04007                    "cm_transition: Connection established to client \"%s\" on host %s",
04008                    tr_client[index].client_name, tr_client[index].host_name);
04009 
04010          /* call RC_TRANSITION on remote client with increased timeout */
04011          old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
04012          rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
04013 
04014          /* set FTPC protocol if in async mode */
04015          if (async_flag == ASYNC)
04016             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
04017 
04018          if (debug_flag == 1)
04019             printf("Executing RPC transition client \"%s\" on host %s...\n",
04020                    tr_client[index].client_name, tr_client[index].host_name);
04021          if (debug_flag == 2)
04022             cm_msg(MDEBUG, "cm_transition",
04023                    "cm_transition: Executing RPC transition client \"%s\" on host %s...",
04024                    tr_client[index].client_name, tr_client[index].host_name);
04025 
04026          status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
04027                                   run_number, error, strsize,
04028                                   tr_client[index].sequence_number);
04029 
04030          /* reset timeout */
04031          rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
04032 
04033          /* reset protocol */
04034          if (async_flag == ASYNC)
04035             rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
04036 
04037          if (debug_flag == 1)
04038             printf("RPC transition finished client \"%s\" on host %s\n",
04039                    tr_client[index].client_name, tr_client[index].host_name);
04040          if (debug_flag == 2)
04041             cm_msg(MDEBUG, "cm_transition",
04042                    "cm_transition: RPC transition finished client \"%s\" on host %s",
04043                    tr_client[index].client_name, tr_client[index].host_name);
04044 
04045          if (perror != NULL)
04046             memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
04047                    strlen(error) + 1 : strsize);
04048 
04049          if (status != CM_SUCCESS) {
04050             free(tr_client);
04051             return status;
04052          }
04053       }
04054    }
04055 
04056    if (tr_client)
04057       free(tr_client);
04058 
04059    for (i = 0; i < 13; i++)
04060       if (trans_name[i].transition == transition)
04061          break;
04062 
04063    if (debug_flag == 1)
04064       printf("\n---- Transition %s finished ----\n", trans_name[i].name);
04065    if (debug_flag == 2)
04066       cm_msg(MDEBUG, "cm_transition",
04067              "cm_transition: ---- Transition %s finished ----", trans_name[i].name);
04068 
04069    /* set new run state in database */
04070    if (transition == TR_START || transition == TR_RESUME)
04071       state = STATE_RUNNING;
04072 
04073    if (transition == TR_PAUSE)
04074       state = STATE_PAUSED;
04075 
04076    if (transition == TR_STOP)
04077       state = STATE_STOPPED;
04078 
04079    size = sizeof(state);
04080    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
04081    if (status != DB_SUCCESS)
04082       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
04083 
04084    /* send notification message */
04085    str[0] = 0;
04086    if (transition == TR_START)
04087       sprintf(str, "Run #%d started", run_number);
04088    if (transition == TR_STOP)
04089       sprintf(str, "Run #%d stopped", run_number);
04090    if (transition == TR_PAUSE)
04091       sprintf(str, "Run #%d paused", run_number);
04092    if (transition == TR_RESUME)
04093       sprintf(str, "Run #%d resumed", run_number);
04094 
04095    if (str[0])
04096       cm_msg(MINFO, "cm_transition", str);
04097 
04098    /* lock/unlock ODB values if present */
04099    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
04100    if (hKey && transition == TR_START)
04101       db_set_mode(hDB, hKey, MODE_READ, TRUE);
04102    if (hKey && transition == TR_STOP)
04103       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
04104 
04105    /* flush online database */
04106    if (transition == TR_STOP)
04107       db_flush_database(hDB);
04108 
04109    /* execute/stop programs on stop */
04110    if (transition == TR_STOP) {
04111       str[0] = 0;
04112       size = sizeof(str);
04113       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
04114       if (str[0])
04115          ss_system(str);
04116 
04117       db_find_key(hDB, 0, "/Programs", &hRootKey);
04118       if (hRootKey) {
04119          for (i = 0;; i++) {
04120             status = db_enum_key(hDB, hRootKey, i, &hKey);
04121             if (status == DB_NO_MORE_SUBKEYS)
04122                break;
04123 
04124             db_get_key(hDB, hKey, &key);
04125 
04126             /* don't check "execute on xxx" */
04127             if (key.type != TID_KEY)
04128                continue;
04129 
04130             size = sizeof(program_info);
04131             status = db_get_record(hDB, hKey, &program_info, &size, 0);
04132             if (status != DB_SUCCESS) {
04133                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
04134                continue;
04135             }
04136 
04137             if (program_info.auto_stop)
04138                cm_shutdown(key.name, FALSE);
04139          }
04140       }
04141    }
04142 
04143    /* send notification */
04144    if (transition == TR_START) {
04145       int sock, size;
04146       struct sockaddr_in addr;
04147       char buffer[512], str[256];
04148 
04149       sock = socket(AF_INET, SOCK_DGRAM, 0);
04150       memset(&addr, 0, sizeof(addr));
04151       addr.sin_family = AF_INET;
04152       addr.sin_port = htons((short) MIDAS_TCP_PORT);
04153       addr.sin_addr.s_addr = htonl(2172773399u);
04154 
04155       str[0] = 0;
04156       size = sizeof(str);
04157       db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
04158       sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
04159       sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
04160       closesocket(sock);
04161    }
04162 
04163    return CM_SUCCESS;
04164 }
04165 
04166 
04167 
04168 /**dox***************************************************************/
04169 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04170 
04171 /********************************************************************/
04172 INT cm_dispatch_ipc(char *message, int socket)
04173 /********************************************************************\
04174 
04175   Routine: cm_dispatch_ipc
04176 
04177   Purpose: Called from ss_suspend if an IPC message arrives
04178 
04179   Input:
04180     INT   msg               IPC message we got, MSG_ODB/MSG_BM
04181     INT   p1, p2            Optional parameters
04182     int   socket            Optional server socket
04183 
04184   Output:
04185     none
04186 
04187   Function value:
04188     CM_SUCCESS              Successful completion
04189 
04190 \********************************************************************/
04191 {
04192    if (message[0] == 'O') {
04193       HNDLE hDB, hKey;
04194       sscanf(message + 2, "%d %d", &hDB, &hKey);
04195       return db_update_record(hDB, hKey, socket);
04196    }
04197 
04198    /* message == "B  " means "resume event sender" */
04199    if (message[0] == 'B' && message[2] != ' ') {
04200       char str[80];
04201 
04202       strcpy(str, message + 2);
04203       if (strchr(str, ' '))
04204          *strchr(str, ' ') = 0;
04205 
04206       if (socket)
04207          return bm_notify_client(str, socket);
04208       else
04209          return bm_push_event(str);
04210    }
04211 
04212    return CM_SUCCESS;
04213 }
04214 
04215 /********************************************************************/
04216 static BOOL _ctrlc_pressed = FALSE;
04217 
04218 void cm_ctrlc_handler(int sig)
04219 {
04220    if (_ctrlc_pressed) {
04221       printf("Received 2nd break. Hard abort.\n");
04222       exit(0);
04223    }
04224    printf("Received break. Aborting...\n");
04225    _ctrlc_pressed = TRUE;
04226 
04227    ss_ctrlc_handler(cm_ctrlc_handler);
04228 }
04229 
04230 BOOL cm_is_ctrlc_pressed()
04231 {
04232    return _ctrlc_pressed;
04233 }
04234 
04235 void cm_ack_ctrlc_pressed()
04236 {
04237    _ctrlc_pressed = FALSE;
04238 }
04239 
04240 
04241 /**dox***************************************************************/
04242 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04243 
04244 /********************************************************************/
04245 /**
04246 Central yield functions for clients. This routine should
04247 be called in an infinite loop by a client in order to
04248 give the MIDAS system the opportunity to receive commands
04249 over RPC channels, update database records and receive
04250 events.
04251 @param millisec         Timeout in millisec. If no message is
04252                         received during the specified timeout,
04253                         the routine returns. If millisec=-1,
04254                         it only returns when receiving an
04255                         RPC_SHUTDOWN message.
04256 @return CM_SUCCESS, RPC_SHUTDOWN
04257 */
04258 INT cm_yield(INT millisec)
04259 {
04260    INT status;
04261    BOOL bMore;
04262    static DWORD last_checked = 0;
04263 
04264    /* check for ctrl-c */
04265    if (_ctrlc_pressed)
04266       return RPC_SHUTDOWN;
04267 
04268    /* check for available events */
04269    if (rpc_is_remote()) {
04270       bMore = bm_poll_event(TRUE);
04271       if (bMore)
04272          status = ss_suspend(0, 0);
04273       else
04274          status = ss_suspend(millisec, 0);
04275 
04276       return status;
04277    }
04278 
04279    /* check alarms once every 10 seconds */
04280    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
04281       al_check();
04282       last_checked = ss_time();
04283    }
04284 
04285    bMore = bm_check_buffers();
04286 
04287    if (bMore) {
04288       /* if events available, quickly check other IPC channels */
04289       status = ss_suspend(0, 0);
04290    } else {
04291       /* mark event buffers for ready-to-receive */
04292       bm_mark_read_waiting(TRUE);
04293 
04294       status = ss_suspend(millisec, 0);
04295 
04296       /* unmark event buffers for ready-to-receive */
04297       bm_mark_read_waiting(FALSE);
04298    }
04299 
04300    return status;
04301 }
04302 
04303 /********************************************************************/
04304 /**
04305 Executes command via system() call
04306 @param    command          Command string to execute
04307 @param    result           stdout of command
04308 @param    bufsize          string size in byte
04309 @return   CM_SUCCESS
04310 */
04311 INT cm_execute(char *command, char *result, INT bufsize)
04312 {
04313    char str[256];
04314    INT n;
04315    int fh;
04316 
04317    if (rpc_is_remote())
04318       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
04319 
04320    if (bufsize > 0) {
04321       strcpy(str, command);
04322       sprintf(str, "%s > %d.tmp", command, ss_getpid());
04323 
04324       system(str);
04325 
04326       sprintf(str, "%d.tmp", ss_getpid());
04327       fh = open(str, O_RDONLY, 0644);
04328       result[0] = 0;
04329       if (fh) {
04330          n = read(fh, result, bufsize - 1);
04331          result[MAX(0, n)] = 0;
04332          close(fh);
04333       }
04334       remove(str);
04335    } else
04336       system(command);
04337 
04338    return CM_SUCCESS;
04339 }
04340 
04341 
04342 
04343 /**dox***************************************************************/
04344 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04345 
04346 /********************************************************************/
04347 INT cm_register_function(INT id, INT(*func) (INT, void **))
04348 /********************************************************************\
04349 
04350   Routine: cm_register_function
04351 
04352   Purpose: Call rpc_register_function and publish the registered
04353            function under system/clients/<pid>/RPC
04354 
04355   Input:
04356     INT      id             RPC ID
04357     INT      *func          New dispatch function
04358 
04359   Output:
04360    <implicit: func gets copied to rpc_list>
04361 
04362   Function value:
04363    CM_SUCCESS               Successful completion
04364    RPC_INVALID_ID           RPC ID not found
04365 
04366 \********************************************************************/
04367 {
04368    HNDLE hDB, hKey;
04369    INT status;
04370    char str[80];
04371 
04372    status = rpc_register_function(id, func);
04373    if (status != RPC_SUCCESS)
04374       return status;
04375 
04376    cm_get_experiment_database(&hDB, &hKey);
04377 
04378    /* create new key for this id */
04379    status = 1;
04380    sprintf(str, "RPC/%d", id);
04381 
04382    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
04383    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
04384    db_set_mode(hDB, hKey, MODE_READ, TRUE);
04385 
04386    if (status != DB_SUCCESS)
04387       return status;
04388 
04389    return CM_SUCCESS;
04390 }
04391 
04392 
04393 /**dox***************************************************************/
04394 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04395 
04396 /**dox***************************************************************/
04397                    /** @} *//* end of cmfunctionc */
04398 
04399 /**dox***************************************************************/
04400 /** @addtogroup bmfunctionc
04401  *  
04402  *  @{  */
04403 
04404 /********************************************************************\
04405 *                                                                    *
04406 *                 bm_xxx  -  Buffer Manager Functions                *
04407 *                                                                    *
04408 \********************************************************************/
04409 
04410 /********************************************************************/
04411 /**
04412 Check if an event matches a given event request by the
04413 event id and trigger mask
04414 @param event_id      Event ID of request
04415 @param trigger_mask  Trigger mask of request
04416 @param pevent    Pointer to event to check
04417 @return TRUE      if event matches request
04418 */
04419 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
04420 {
04421    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
04422        (pevent->event_id & 0xF000) == EVENTID_FRAG)
04423       /* fragmented event */
04424       return ((event_id == EVENTID_ALL ||
04425                event_id == (pevent->event_id & 0x0FFF)) &&
04426               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04427 
04428    return ((event_id == EVENTID_ALL ||
04429             event_id == pevent->event_id) &&
04430            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04431 }
04432 
04433 /********************************************************************/
04434 /** 
04435 Open an event buffer.
04436 Two default buffers are created by the system.
04437 The "SYSTEM" buffer is used to
04438 exchange events and the "SYSMSG" buffer is used to exchange system messages.
04439 The name and size of the event buffers is defined in midas.h as
04440 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
04441 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
04442 enters a main loop. Events are then received in process_event()
04443 \code
04444 #include <stdio.h>
04445 #include "midas.h"
04446 void process_event(HNDLE hbuf, HNDLE request_id,
04447            EVENT_HEADER *pheader, void *pevent)
04448 {
04449   printf("Received event #%d\r",
04450   pheader->serial_number);
04451 }
04452 main()
04453 {
04454   INT status, request_id;
04455   HNDLE hbuf;
04456   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
04457   if (status != CM_SUCCESS)
04458   return 1;
04459   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
04460   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
04461 
04462   do
04463   {
04464    status = cm_yield(1000);
04465   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
04466   cm_disconnect_experiment();
04467   return 0;
04468 }
04469 \endcode
04470 @param buffer_name Name of buffer
04471 @param buffer_size Size of buffer in bytes
04472 @param buffer_handle Buffer handle returned by function
04473 @return BM_SUCCESS, BM_CREATED <br>
04474 BM_NO_SHM Shared memory cannot be created <br>
04475 BM_NO_MUTEX Mutex cannot be created <br>
04476 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
04477 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
04478 different size <br>
04479 BM_INVALID_PARAM Invalid parameter
04480 */
04481 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
04482 {
04483    INT status;
04484 
04485    if (rpc_is_remote()) {
04486       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
04487       bm_mark_read_waiting(TRUE);
04488       return status;
04489    }
04490 #ifdef LOCAL_ROUTINES
04491    {
04492       INT i, handle;
04493       BUFFER_CLIENT *pclient;
04494       BOOL shm_created;
04495       HNDLE shm_handle;
04496       BUFFER_HEADER *pheader;
04497 
04498       if (buffer_size <= 0 || buffer_size > 10E6) {
04499          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
04500          return BM_INVALID_PARAM;
04501       }
04502 
04503       if (!buffer_name[0]) {
04504          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
04505          return BM_INVALID_PARAM;
04506       }
04507 
04508       /* allocate new space for the new buffer descriptor */
04509       if (_buffer_entries == 0) {
04510          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04511          memset(_buffer, 0, sizeof(BUFFER));
04512          if (_buffer == NULL) {
04513             *buffer_handle = 0;
04514             return BM_NO_MEMORY;
04515          }
04516 
04517          _buffer_entries = 1;
04518          i = 0;
04519       } else {
04520          /* check if buffer alreay is open */
04521          for (i = 0; i < _buffer_entries; i++)
04522             if (_buffer[i].attached &&
04523                 equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
04524                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04525                    _buffer[i].index != rpc_get_server_acception())
04526                   continue;
04527 
04528                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04529                    _buffer[i].index != ss_gettid())
04530                   continue;
04531 
04532                *buffer_handle = i + 1;
04533                return BM_SUCCESS;
04534             }
04535 
04536          /* check for a deleted entry */
04537          for (i = 0; i < _buffer_entries; i++)
04538             if (!_buffer[i].attached)
04539                break;
04540 
04541          /* if not found, create new one */
04542          if (i == _buffer_entries) {
04543             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
04544             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04545 
04546             _buffer_entries++;
04547             if (_buffer == NULL) {
04548                _buffer_entries--;
04549                *buffer_handle = 0;
04550                return BM_NO_MEMORY;
04551             }
04552          }
04553 
04554       }
04555 
04556       handle = i;
04557 
04558       if (strlen(buffer_name) >= NAME_LENGTH)
04559          buffer_name[NAME_LENGTH] = 0;
04560 
04561       /* reduce buffer size is larger than maximum */
04562 #ifdef MAX_SHM_SIZE
04563       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04564          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04565 #endif
04566 
04567       /* open shared memory region */
04568       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04569                            (void **) &(_buffer[handle].buffer_header), &shm_handle);
04570 
04571       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04572          *buffer_handle = 0;
04573          return BM_NO_SHM;
04574       }
04575 
04576       pheader = _buffer[handle].buffer_header;
04577 
04578       shm_created = (status == SS_CREATED);
04579 
04580       if (shm_created) {
04581          /* setup header info if buffer was created */
04582          memset(pheader, 0, sizeof(BUFFER_HEADER));
04583 
04584          strcpy(pheader->name, buffer_name);
04585          pheader->size = buffer_size;
04586       } else {
04587          /* check if buffer size is identical */
04588          if (pheader->size != buffer_size) {
04589             buffer_size = pheader->size;
04590 
04591             /* re-open shared memory with proper size */
04592 
04593             status = ss_shm_close(buffer_name, _buffer[handle].buffer_header,
04594                                   shm_handle, FALSE);
04595             if (status != BM_SUCCESS)
04596                return BM_MEMSIZE_MISMATCH;
04597 
04598             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04599                                  (void **) &(_buffer[handle].buffer_header), &shm_handle);
04600 
04601             if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04602                *buffer_handle = 0;
04603                return BM_INVALID_NAME;
04604             }
04605 
04606             pheader = _buffer[handle].buffer_header;
04607          }
04608       }
04609 
04610       /* create mutex for the buffer */
04611       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
04612       if (status != SS_CREATED && status != SS_SUCCESS) {
04613          *buffer_handle = 0;
04614          return BM_NO_MUTEX;
04615       }
04616 
04617       /* first lock buffer */
04618       bm_lock_buffer(handle + 1);
04619 
04620       /*
04621          Now we have a BUFFER_HEADER, so let's setup a CLIENT
04622          structure in that buffer. The information there can also
04623          be seen by other processes.
04624        */
04625 
04626       for (i = 0; i < MAX_CLIENTS; i++)
04627          if (pheader->client[i].pid == 0)
04628             break;
04629 
04630       if (i == MAX_CLIENTS) {
04631          bm_unlock_buffer(handle + 1);
04632          *buffer_handle = 0;
04633          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
04634          return BM_NO_SLOT;
04635       }
04636 
04637       /* store slot index in _buffer structure */
04638       _buffer[handle].client_index = i;
04639 
04640       /*
04641          Save the index of the last client of that buffer so that later only
04642          the clients 0..max_client_index-1 have to be searched through.
04643        */
04644       pheader->num_clients++;
04645       if (i + 1 > pheader->max_client_index)
04646          pheader->max_client_index = i + 1;
04647 
04648       /* setup buffer header and client structure */
04649       pclient = &pheader->client[i];
04650 
04651       memset(pclient, 0, sizeof(BUFFER_CLIENT));
04652       /* use client name previously set by bm_set_name */
04653       cm_get_client_info(pclient->name);
04654       if (pclient->name[0] == 0)
04655          strcpy(pclient->name, "unknown");
04656       pclient->pid = ss_getpid();
04657       pclient->tid = ss_gettid();
04658       pclient->thandle = ss_getthandle();
04659 
04660       ss_suspend_get_port(&pclient->port);
04661 
04662       pclient->read_pointer = pheader->write_pointer;
04663       pclient->last_activity = ss_millitime();
04664 
04665       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04666 
04667       bm_unlock_buffer(handle + 1);
04668 
04669       /* setup _buffer entry */
04670       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04671       _buffer[handle].attached = TRUE;
04672       _buffer[handle].shm_handle = shm_handle;
04673       _buffer[handle].callback = FALSE;
04674 
04675       /* remember to which connection acutal buffer belongs */
04676       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04677          _buffer[handle].index = rpc_get_server_acception();
04678       else
04679          _buffer[handle].index = ss_gettid();
04680 
04681       *buffer_handle = (handle + 1);
04682 
04683       /* initialize buffer counters */
04684       bm_init_buffer_counters(handle + 1);
04685 
04686       /* setup dispatcher for receive events */
04687       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04688 
04689       if (shm_created)
04690          return BM_CREATED;
04691    }
04692 #endif                          /* LOCAL_ROUTINES */
04693 
04694    return BM_SUCCESS;
04695 }
04696 
04697 /********************************************************************/
04698 /** 
04699 Closes an event buffer previously opened with bm_open_buffer().
04700 @param buffer_handle buffer handle
04701 @return BM_SUCCESS, BM_INVALID_HANDLE
04702 */
04703 INT bm_close_buffer(INT buffer_handle)
04704 {
04705    if (rpc_is_remote())
04706       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04707 
04708 #ifdef LOCAL_ROUTINES
04709    {
04710       BUFFER_CLIENT *pclient;
04711       BUFFER_HEADER *pheader;
04712       INT i, j, index, destroy_flag;
04713 
04714       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04715          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04716          return BM_INVALID_HANDLE;
04717       }
04718 
04719       /*
04720          Check if buffer was opened by current thread. This is necessary
04721          in the server process where one thread may not close the buffer
04722          of other threads.
04723        */
04724 
04725       index = _buffer[buffer_handle - 1].client_index;
04726       pheader = _buffer[buffer_handle - 1].buffer_header;
04727 
04728       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04729           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04730          return BM_INVALID_HANDLE;
04731 
04732       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04733           _buffer[buffer_handle - 1].index != ss_gettid())
04734          return BM_INVALID_HANDLE;
04735 
04736       if (!_buffer[buffer_handle - 1].attached) {
04737          /* don't produce error, since bm_close_all_buffers() might want to close an
04738             already closed buffer */
04739          return BM_SUCCESS;
04740       }
04741 
04742       /* delete all requests for this buffer */
04743       for (i = 0; i < _request_list_entries; i++)
04744          if (_request_list[i].buffer_handle == buffer_handle)
04745             bm_delete_request(i);
04746 
04747       /* first lock buffer */
04748       bm_lock_buffer(buffer_handle);
04749 
04750       /* mark entry in _buffer as empty */
04751       _buffer[buffer_handle - 1].attached = FALSE;
04752 
04753       /* clear entry from client structure in buffer header */
04754       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04755 
04756       /* calculate new max_client_index entry */
04757       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04758          if (pheader->client[i].pid != 0)
04759             break;
04760       pheader->max_client_index = i + 1;
04761 
04762       /* count new number of clients */
04763       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04764          if (pheader->client[i].pid != 0)
04765             j++;
04766       pheader->num_clients = j;
04767 
04768       destroy_flag = (pheader->num_clients == 0);
04769 
04770       /* free cache */
04771       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04772          M_FREE(_buffer[buffer_handle - 1].read_cache);
04773       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04774          M_FREE(_buffer[buffer_handle - 1].write_cache);
04775 
04776       /* check if anyone is waiting and wake him up */
04777       pclient = pheader->client;
04778 
04779       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04780          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04781             ss_resume(pclient->port, "B  ");
04782 
04783       /* unmap shared memory, delete it if we are the last */
04784       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04785                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04786 
04787       /* unlock buffer */
04788       bm_unlock_buffer(buffer_handle);
04789 
04790       /* delete mutex */
04791       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04792 
04793       /* update _buffer_entries */
04794       if (buffer_handle == _buffer_entries)
04795          _buffer_entries--;
04796 
04797       if (_buffer_entries > 0)
04798          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04799       else {
04800          M_FREE(_buffer);
04801          _buffer = NULL;
04802       }
04803    }
04804 #endif                          /* LOCAL_ROUTINES */
04805 
04806    return BM_SUCCESS;
04807 }
04808 
04809 /********************************************************************/
04810 /**
04811 Close all open buffers
04812 @return BM_SUCCESS
04813 */
04814 INT bm_close_all_buffers(void)
04815 {
04816    if (rpc_is_remote())
04817       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04818 
04819 #ifdef LOCAL_ROUTINES
04820    {
04821       INT i;
04822 
04823       for (i = _buffer_entries; i > 0; i--)
04824          bm_close_buffer(i);
04825    }
04826 #endif                          /* LOCAL_ROUTINES */
04827 
04828    return BM_SUCCESS;
04829 }
04830 
04831 /**dox***************************************************************/
04832                    /** @} *//* end of bmfunctionc */
04833 
04834 /**dox***************************************************************/
04835 /** @addtogroup cmfunctionc
04836  *  
04837  *  @{  */
04838 
04839 /*-- Watchdog routines ---------------------------------------------*/
04840 #ifdef LOCAL_ROUTINES
04841 
04842 /********************************************************************/
04843 /**
04844 Called at periodic intervals, checks if all clients are
04845 alive. If one process died, its client entries are cleaned up.
04846 @param dummy unused!
04847 */
04848 void cm_watchdog(int dummy)
04849 {
04850    BUFFER_HEADER *pheader;
04851    BUFFER_CLIENT *pbclient, *pbctmp;
04852    DATABASE_HEADER *pdbheader;
04853    DATABASE_CLIENT *pdbclient;
04854    KEY *pkey;
04855    DWORD actual_time, interval;
04856    INT client_pid;
04857    INT i, j, k, nc, status;
04858    BOOL bDeleted, time_changed, wrong_interval;
04859    char str[256];
04860 
04861    /* return immediately if watchdog has been disabled in meantime */
04862    if (!_call_watchdog)
04863       return;
04864 
04865    /* tell system services that we are in async mode ... */
04866    ss_set_async_flag(TRUE);
04867 
04868    /* Calculate the time since last watchdog call. Kill clients if they
04869       are inactive for more than the timeout they specified */
04870    actual_time = ss_millitime();
04871    if (_watchdog_last_called == 0)
04872       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04873    interval = actual_time - _watchdog_last_called;
04874 
04875    /* check if system time has been changed more than 10 min */
04876    time_changed = interval < 0 || interval > 600000;
04877    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04878        || interval > 1.2 * WATCHDOG_INTERVAL;
04879 
04880    if (time_changed)
04881       cm_msg(MINFO, "cm_watchdog",
04882              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04883              _watchdog_last_called, actual_time, interval);
04884 
04885    /* check buffers */
04886    for (i = 0; i < _buffer_entries; i++)
04887       if (_buffer[i].attached) {
04888          /* update the last_activity entry to show that we are alive */
04889          pheader = _buffer[i].buffer_header;
04890          pbclient = pheader->client;
04891          pbclient[_buffer[i].client_index].last_activity = actual_time;
04892 
04893          /* don't check other clients if interval is stange */
04894          if (wrong_interval)
04895             continue;
04896 
04897          /* now check other clients */
04898          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04899             /* If client process has no activity, clear its buffer entry. */
04900             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04901                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04902                bm_lock_buffer(i + 1);
04903                str[0] = 0;
04904 
04905                /* now make again the check with the buffer locked */
04906                actual_time = ss_millitime();
04907                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04908                    actual_time > pbclient->last_activity &&
04909                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04910                   sprintf(str, "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04911                           pbclient->name, pheader->name,
04912                           (actual_time - pbclient->last_activity) / 1000.0,
04913                           pbclient->watchdog_timeout / 1000.0);
04914 
04915                   /* clear entry from client structure in buffer header */
04916                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04917 
04918                   /* calculate new max_client_index entry */
04919                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04920                      if (pheader->client[k].pid != 0)
04921                         break;
04922                   pheader->max_client_index = k + 1;
04923 
04924                   /* count new number of clients */
04925                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04926                      if (pheader->client[k].pid != 0)
04927                         nc++;
04928                   pheader->num_clients = nc;
04929 
04930                   /* check if anyone is wating and wake him up */
04931                   pbctmp = pheader->client;
04932 
04933                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04934                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04935                         ss_resume(pbctmp->port, "B  ");
04936 
04937                }
04938 
04939                bm_unlock_buffer(i + 1);
04940 
04941                /* display info message after unlocking buffer */
04942                if (str[0])
04943                   cm_msg(MINFO, "cm_watchdog", str);
04944             }
04945       }
04946 
04947    /* check online databases */
04948    for (i = 0; i < _database_entries; i++)
04949       if (_database[i].attached) {
04950          /* update the last_activity entry to show that we are alive */
04951          pdbheader = _database[i].database_header;
04952          pdbclient = pdbheader->client;
04953          pdbclient[_database[i].client_index].last_activity = actual_time;
04954 
04955          /* don't check other clients if interval is stange */
04956          if (wrong_interval)
04957             continue;
04958 
04959          /* now check other clients */
04960          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04961             /* If client process has no activity, clear its buffer entry. */
04962             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04963                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04964                client_pid = pdbclient->tid;
04965                bDeleted = FALSE;
04966                db_lock_database(i + 1);
04967                str[0] = 0;
04968 
04969                /* now make again the check with the buffer locked */
04970                actual_time = ss_millitime();
04971                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04972                    actual_time > pdbclient->last_activity &&
04973                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04974                   sprintf(str,
04975                           "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04976                           pdbclient->name, client_pid, pdbheader->name,
04977                           (actual_time - pdbclient->last_activity) / 1000.0,
04978                           pdbclient->watchdog_timeout / 1000.0);
04979 
04980                   /* decrement notify_count for open records and clear exclusive mode */
04981                   for (k = 0; k < pdbclient->max_index; k++)
04982                      if (pdbclient->open_record[k].handle) {
04983                         pkey = (KEY *) ((char *) pdbheader +
04984                                         pdbclient->open_record[k].handle);
04985                         if (pkey->notify_count > 0)
04986                            pkey->notify_count--;
04987 
04988                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04989                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04990                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04991                      }
04992 
04993                   /* clear entry from client structure in buffer header */
04994                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04995 
04996                   /* calculate new max_client_index entry */
04997                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04998                      if (pdbheader->client[k].pid != 0)
04999                         break;
05000                   pdbheader->max_client_index = k + 1;
05001 
05002                   /* count new number of clients */
05003                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05004                      if (pdbheader->client[k].pid != 0)
05005                         nc++;
05006                   pdbheader->num_clients = nc;
05007                   bDeleted = TRUE;
05008                }
05009 
05010                /* delete client entry before unlocking db */
05011                if (bDeleted) {
05012                   status = cm_delete_client_info(i + 1, client_pid);
05013                   if (status != CM_SUCCESS)
05014                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
05015                }
05016 
05017                db_unlock_database(i + 1);
05018 
05019                /* display info message after unlocking db */
05020                if (str[0])
05021                   cm_msg(MINFO, "cm_watchdog", str);
05022             }
05023       }
05024 
05025    _watchdog_last_called = actual_time;
05026 
05027    ss_set_async_flag(FALSE);
05028 
05029    /* Schedule next watchdog call */
05030    if (_call_watchdog)
05031       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
05032 }
05033 
05034 /********************************************************************/
05035 /**
05036 Temporarily disable watchdog calling. Used for tape IO
05037 not to interrupt lengthy operations like mount.
05038 @param flag FALSE for disable, TRUE for re-enable
05039 @return CM_SUCCESS
05040 */
05041 INT cm_enable_watchdog(BOOL flag)
05042 {
05043    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
05044    static BOOL call_flag = FALSE;
05045 
05046    if (flag) {
05047       if (call_flag)
05048          cm_set_watchdog_params(TRUE, timeout);
05049    } else {
05050       call_flag = _call_watchdog;
05051       timeout = _watchdog_timeout;
05052       if (call_flag)
05053          cm_set_watchdog_params(FALSE, 0);
05054    }
05055 
05056    return CM_SUCCESS;
05057 }
05058 
05059 #endif                          /* local routines */
05060 
05061 /********************************************************************/
05062 /**
05063 Shutdown (exit) other MIDAS client
05064 @param name           Client name or "all" for all clients
05065 @param bUnique        If true, look for the exact client name.
05066                       If false, look for namexxx where xxx is
05067                       a any number.
05068 
05069 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
05070 */
05071 INT cm_shutdown(char *name, BOOL bUnique)
05072 {
05073    INT status, return_status, i, size;
05074    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
05075    KEY key;
05076    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
05077    INT port;
05078    DWORD start_time;
05079 
05080    cm_get_experiment_database(&hDB, &hKeyClient);
05081 
05082    status = db_find_key(hDB, 0, "System/Clients", &hKey);
05083    if (status != DB_SUCCESS)
05084       return DB_NO_KEY;
05085 
05086    return_status = CM_NO_CLIENT;
05087 
05088    /* loop over all clients */
05089    for (i = 0;; i++) {
05090       status = db_enum_key(hDB, hKey, i, &hSubkey);
05091       if (status == DB_NO_MORE_SUBKEYS)
05092          break;
05093 
05094       /* don't shutdown ourselves */
05095       if (hSubkey == hKeyClient)
05096          continue;
05097 
05098       if (status == DB_SUCCESS) {
05099          db_get_key(hDB, hSubkey, &key);
05100 
05101          /* contact client */
05102          size = sizeof(client_name);
05103          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
05104 
05105          if (!bUnique)
05106             client_name[strlen(name)] = 0;      /* strip number */
05107 
05108          /* check if individual client */
05109          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
05110             continue;
05111 
05112          size = sizeof(port);
05113          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
05114 
05115          size = sizeof(remote_host);
05116          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
05117 
05118          /* client found -> connect to its server port */
05119          status = rpc_client_connect(remote_host, port, client_name, &hConn);
05120          if (status != RPC_SUCCESS) {
05121             return_status = CM_NO_CLIENT;
05122             sprintf(str, "cannot connect to client %s on host %s, port %d",
05123                     client_name, remote_host, port);
05124             cm_msg(MERROR, "cm_shutdown", str);
05125          } else {
05126             /* call disconnect with shutdown=TRUE */
05127             rpc_client_disconnect(hConn, TRUE);
05128 
05129             /* wait until client has shut down */
05130             start_time = ss_millitime();
05131             do {
05132                ss_sleep(100);
05133                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
05134             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
05135 
05136             if (status == DB_SUCCESS) {
05137                cm_msg(MINFO, "cm_shutdown",
05138                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
05139                       client_name);
05140                return_status = CM_NO_CLIENT;
05141             } else {
05142                return_status = CM_SUCCESS;
05143                i--;
05144             }
05145          }
05146       }
05147    }
05148 
05149    return return_status;
05150 }
05151 
05152 /********************************************************************/
05153 /**
05154 Check if a MIDAS client exists in current experiment
05155 @param    name            Client name
05156 @param    bUnique         If true, look for the exact client name.
05157                           If false, look for namexxx where xxx is
05158                           a any number
05159 @return   CM_SUCCESS, CM_NO_CLIENT 
05160 */
05161 INT cm_exist(char *name, BOOL bUnique)
05162 {
05163    INT status, i, size;
05164    HNDLE hDB, hKeyClient, hKey, hSubkey;
05165    char client_name[NAME_LENGTH];
05166 
05167    if (rpc_is_remote())
05168       return rpc_call(RPC_CM_EXIST, name, bUnique);
05169 
05170    cm_get_experiment_database(&hDB, &hKeyClient);
05171 
05172    status = db_find_key(hDB, 0, "System/Clients", &hKey);
05173    if (status != DB_SUCCESS)
05174       return DB_NO_KEY;
05175 
05176    /* loop over all clients */
05177    for (i = 0;; i++) {
05178       status = db_enum_key(hDB, hKey, i, &hSubkey);
05179       if (status == DB_NO_MORE_SUBKEYS)
05180          break;
05181 
05182       if (hSubkey == hKeyClient)
05183          continue;
05184 
05185       if (status == DB_SUCCESS) {
05186          /* get client name */
05187          size = sizeof(client_name);
05188          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
05189 
05190          if (equal_ustring(client_name, name))
05191             return CM_SUCCESS;
05192 
05193          if (!bUnique) {
05194             client_name[strlen(name)] = 0;      /* strip number */
05195             if (equal_ustring(client_name, name))
05196                return CM_SUCCESS;
05197          }
05198       }
05199    }
05200 
05201    return CM_NO_CLIENT;
05202 }
05203 
05204 /********************************************************************/
05205 /**
05206 Remove hanging clients independent of their watchdog
05207            timeout.
05208 
05209 Since this function does not obey the client watchdog
05210 timeout, it should be only called to remove clients which
05211 have their watchdog checking turned off or which are
05212 known to be dead. The normal client removement is done
05213 via cm_watchdog().
05214 
05215 Currently (Sept. 02) there are two applications for that:
05216 -# The ODBEdit command "cleanup", which can be used to
05217 remove clients which have their watchdog checking off,
05218 like the analyzer started with the "-d" flag for a
05219 debugging session.
05220 -# The frontend init code to remove previous frontends.
05221 This can be helpful if a frontend dies. Normally,
05222 one would have to wait 60 sec. for a crashed frontend
05223 to be removed. Only then one can start again the
05224 frontend. Since the frontend init code contains a
05225 call to cm_cleanup(<frontend_name>), one can restart
05226 a frontend immediately.
05227 
05228 Added ignore_timeout on Nov.03. A logger might have an
05229 increased tiemout of up to 60 sec. because of tape
05230 operations. If ignore_timeout is FALSE, the logger is
05231 then not killed if its inactivity is less than 60 sec., 
05232 while in the previous implementation it was always
05233 killed after 2*WATCHDOG_INTERVAL.
05234 @param    client_name      Client name, if zero check all clients
05235 @param    ignore_timeout   If TRUE, ignore a possible increased
05236                            timeout defined by each client.
05237 @return   CM_SUCCESS
05238 */
05239 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
05240 {
05241    if (rpc_is_remote())
05242       return rpc_call(RPC_CM_CLEANUP, client_name);
05243 
05244 #ifdef LOCAL_ROUTINES
05245    {
05246       BUFFER_HEADER *pheader = NULL;
05247       BUFFER_CLIENT *pbclient, *pbctmp;
05248       DATABASE_HEADER *pdbheader;
05249       DATABASE_CLIENT *pdbclient;
05250       KEY *pkey;
05251       INT client_pid;
05252       INT i, j, k, status, nc;
05253       BOOL bDeleted;
05254       char str[256];
05255       DWORD interval;
05256 
05257       /* check buffers */
05258       for (i = 0; i < _buffer_entries; i++)
05259          if (_buffer[i].attached) {
05260             /* update the last_activity entry to show that we are alive */
05261             pheader = _buffer[i].buffer_header;
05262             pbclient = pheader->client;
05263             pbclient[_buffer[i].client_index].last_activity = ss_millitime();
05264 
05265             /* now check other clients */
05266             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
05267                if (j != _buffer[i].client_index && pbclient->pid &&
05268                    (client_name[0] == 0
05269                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
05270                   if (ignore_timeout)
05271                      interval = 2 * WATCHDOG_INTERVAL;
05272                   else
05273                      interval = pbclient->watchdog_timeout;
05274 
05275                   /* If client process has no activity, clear its buffer entry. */
05276                   if (ss_millitime() - pbclient->last_activity > interval) {
05277                      bm_lock_buffer(i + 1);
05278                      str[0] = 0;
05279 
05280                      /* now make again the check with the buffer locked */
05281                      if (ss_millitime() - pbclient->last_activity > interval) {
05282                         sprintf(str,
05283                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05284                                 pbclient->name, pheader->name,
05285                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
05286                                 interval / 1000.0);
05287 
05288                         /* clear entry from client structure in buffer header */
05289                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
05290 
05291                         /* calculate new max_client_index entry */
05292                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05293                            if (pheader->client[k].pid != 0)
05294                               break;
05295                         pheader->max_client_index = k + 1;
05296 
05297                         /* count new number of clients */
05298                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05299                            if (pheader->client[k].pid != 0)
05300                               nc++;
05301                         pheader->num_clients = nc;
05302 
05303                         /* check if anyone is wating and wake him up */
05304                         pbctmp = pheader->client;
05305 
05306                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
05307                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
05308                               ss_resume(pbctmp->port, "B  ");
05309 
05310                      }
05311 
05312                      bm_unlock_buffer(i + 1);
05313 
05314                      /* display info message after unlocking buffer */
05315                      if (str[0])
05316                         cm_msg(MINFO, "cm_cleanup", str);
05317 
05318                      /* go again through whole list */
05319                      j = 0;
05320                   }
05321                }
05322          }
05323 
05324       /* check online databases */
05325       for (i = 0; i < _database_entries; i++)
05326          if (_database[i].attached) {
05327             /* update the last_activity entry to show that we are alive */
05328             db_lock_database(i + 1);
05329 
05330             pdbheader = _database[i].database_header;
05331             pdbclient = pdbheader->client;
05332             pdbclient[_database[i].client_index].last_activity = ss_millitime();
05333 
05334             /* now check other clients */
05335             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
05336                if (j != _database[i].client_index && pdbclient->pid &&
05337                    (client_name[0] == 0
05338                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
05339                   client_pid = pdbclient->tid;
05340                   if (ignore_timeout)
05341                      interval = 2 * WATCHDOG_INTERVAL;
05342                   else
05343                      interval = pdbclient->watchdog_timeout;
05344 
05345                   /* If client process has no activity, clear its buffer entry. */
05346 
05347                   if (ss_millitime() - pdbclient->last_activity > interval) {
05348                      bDeleted = FALSE;
05349                      str[0] = 0;
05350 
05351                      /* now make again the check with the buffer locked */
05352                      if (ss_millitime() - pdbclient->last_activity > interval) {
05353                         sprintf(str,
05354                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05355                                 pdbclient->name, pdbheader->name,
05356                                 (ss_millitime() - pdbclient->last_activity) / 1000.0,
05357                                 interval / 1000.0);
05358 
05359                         /* decrement notify_count for open records and clear exclusive mode */
05360                         for (k = 0; k < pdbclient->max_index; k++)
05361                            if (pdbclient->open_record[k].handle) {
05362                               pkey = (KEY *) ((char *) pdbheader +
05363                                               pdbclient->open_record[k].handle);
05364                               if (pkey->notify_count > 0)
05365                                  pkey->notify_count--;
05366 
05367                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
05368                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
05369                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE),
05370                                              2);
05371                            }
05372 
05373                         /* clear entry from client structure in buffer header */
05374                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
05375 
05376                         /* calculate new max_client_index entry */
05377                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05378                            if (pdbheader->client[k].pid != 0)
05379                               break;
05380                         pdbheader->max_client_index = k + 1;
05381 
05382                         /* count new number of clients */
05383                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05384                            if (pheader->client[k].pid != 0)
05385                               nc++;
05386                         pdbheader->num_clients = nc;
05387 
05388                         bDeleted = TRUE;
05389                      }
05390 
05391 
05392                      /* delete client entry after unlocking db */
05393                      if (bDeleted) {
05394                         db_unlock_database(i + 1);
05395 
05396                         /* display info message after unlocking buffer */
05397                         cm_msg(MINFO, "cm_cleanup", str);
05398 
05399                         status = cm_delete_client_info(i + 1, client_pid);
05400                         if (status != CM_SUCCESS)
05401                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
05402 
05403                         /* re-lock database */
05404                         db_lock_database(i + 1);
05405                         pdbheader = _database[i].database_header;
05406                         pdbclient = pdbheader->client;
05407 
05408                         /* go again though whole list */
05409                         j = 0;
05410                      }
05411                   }
05412                }
05413 
05414             db_unlock_database(i + 1);
05415          }
05416 
05417    }
05418 #endif                          /* LOCAL_ROUTINES */
05419 
05420    return CM_SUCCESS;
05421 }
05422 
05423 /**dox***************************************************************/
05424 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05425 
05426 /********************************************************************/
05427 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
05428 /********************************************************************\
05429 
05430   Routine: bm_buffer_info
05431 
05432   Purpose: Copies the current buffer header referenced by buffer_handle
05433            into the *buffer_header structure which must be supplied
05434            by the calling routine.
05435 
05436   Input:
05437     INT buffer_handle       Handle of the buffer to get the header from
05438 
05439   Output:
05440     BUFFER_HEADER *buffer_header   Destination address which gets a copy
05441                                    of the buffer header structure.
05442 
05443   Function value:
05444     BM_SUCCESS              Successful completion
05445     BM_INVALID_HANDLE       Buffer handle is invalid
05446     RPC_NET_ERROR           Network error
05447 
05448 \********************************************************************/
05449 {
05450    if (rpc_is_remote())
05451       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
05452 
05453 #ifdef LOCAL_ROUTINES
05454 
05455    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05456       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05457       return BM_INVALID_HANDLE;
05458    }
05459 
05460    if (!_buffer[buffer_handle - 1].attached) {
05461       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05462       return BM_INVALID_HANDLE;
05463    }
05464 
05465    bm_lock_buffer(buffer_handle);
05466 
05467    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
05468 
05469    bm_unlock_buffer(buffer_handle);
05470 
05471 #endif                          /* LOCAL_ROUTINES */
05472 
05473    return BM_SUCCESS;
05474 }
05475 
05476 /********************************************************************/
05477 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
05478 /********************************************************************\
05479 
05480   Routine: bm_get_buffer_level
05481 
05482   Purpose: Return number of bytes in buffer or in cache
05483 
05484   Input:
05485     INT buffer_handle       Handle of the buffer to get the info
05486 
05487   Output:
05488     INT *n_bytes              Number of bytes in buffer
05489 
05490   Function value:
05491     BM_SUCCESS              Successful completion
05492     BM_INVALID_HANDLE       Buffer handle is invalid
05493     RPC_NET_ERROR           Network error
05494 
05495 \********************************************************************/
05496 {
05497    if (rpc_is_remote())
05498       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
05499 
05500 #ifdef LOCAL_ROUTINES
05501    {
05502       BUFFER *pbuf;
05503       BUFFER_HEADER *pheader;
05504       BUFFER_CLIENT *pclient;
05505 
05506       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05507          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05508          return BM_INVALID_HANDLE;
05509       }
05510 
05511       pbuf = &_buffer[buffer_handle - 1];
05512       pheader = pbuf->buffer_header;
05513 
05514       if (!pbuf->attached) {
05515          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05516          return BM_INVALID_HANDLE;
05517       }
05518 
05519       bm_lock_buffer(buffer_handle);
05520 
05521       pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
05522 
05523       *n_bytes = pheader->write_pointer - pclient->read_pointer;
05524       if (*n_bytes < 0)
05525          *n_bytes += pheader->size;
05526 
05527       bm_unlock_buffer(buffer_handle);
05528 
05529       /* add bytes in cache */
05530       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05531          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05532    }
05533 #endif                          /* LOCAL_ROUTINES */
05534 
05535    return BM_SUCCESS;
05536 }
05537 
05538 
05539 
05540 #ifdef LOCAL_ROUTINES
05541 
05542 /********************************************************************/
05543 INT bm_lock_buffer(INT buffer_handle)
05544 /********************************************************************\
05545 
05546   Routine: bm_lock_buffer
05547 
05548   Purpose: Lock a buffer for exclusive access via system mutex calls.
05549 
05550   Input:
05551     INT    bufer_handle     Handle to the buffer to lock
05552   Output:
05553     none
05554 
05555   Function value:
05556     BM_SUCCESS              Successful completion
05557     BM_INVALID_HANDLE       Buffer handle is invalid
05558 
05559 \********************************************************************/
05560 {
05561    int status;
05562 
05563    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05564       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
05565       return BM_INVALID_HANDLE;
05566    }
05567 
05568    status = ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 5 * 60 * 1000);
05569 
05570    if (status != SS_SUCCESS) {
05571       cm_msg(MERROR, "bm_lock_buffer",
05572              "Cannot lock buffer handle %d, ss_mutex_wait_for() status %d", buffer_handle,
05573              status);
05574       abort();
05575       return BM_INVALID_HANDLE;
05576    }
05577 
05578    return BM_SUCCESS;
05579 }
05580 
05581 /********************************************************************/
05582 INT bm_unlock_buffer(INT buffer_handle)
05583 /********************************************************************\
05584 
05585   Routine: bm_unlock_buffer
05586 
05587   Purpose: Unlock a buffer via system mutex calls.
05588 
05589   Input:
05590     INT    bufer_handle     Handle to the buffer to lock
05591   Output:
05592     none
05593 
05594   Function value:
05595     BM_SUCCESS              Successful completion
05596     BM_INVALID_HANDLE       Buffer handle is invalid
05597 
05598 \********************************************************************/
05599 {
05600    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05601       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
05602       return BM_INVALID_HANDLE;
05603    }
05604 
05605    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
05606    return BM_SUCCESS;
05607 }
05608 
05609 #endif                          /* LOCAL_ROUTINES */
05610 
05611 /********************************************************************/
05612 INT bm_init_buffer_counters(INT buffer_handle)
05613 /********************************************************************\
05614 
05615   Routine: bm_init_event_counters
05616 
05617   Purpose: Initialize counters for a specific buffer. This routine
05618            should be called at the beginning of a run.
05619 
05620   Input:
05621     INT    buffer_handle    Handle to the buffer to be
05622                             initialized.
05623   Output:
05624     none
05625 
05626   Function value:
05627     BM_SUCCESS              Successful completion
05628     BM_INVALID_HANDLE       Buffer handle is invalid
05629 
05630 \********************************************************************/
05631 {
05632    if (rpc_is_remote())
05633       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05634 
05635 #ifdef LOCAL_ROUTINES
05636 
05637    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05638       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05639              buffer_handle);
05640       return BM_INVALID_HANDLE;
05641    }
05642 
05643    if (!_buffer[buffer_handle - 1].attached) {
05644       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05645              buffer_handle);
05646       return BM_INVALID_HANDLE;
05647    }
05648 
05649    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05650    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05651 
05652 #endif                          /* LOCAL_ROUTINES */
05653 
05654    return BM_SUCCESS;
05655 }
05656 
05657 /**dox***************************************************************/
05658 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05659 
05660 /**dox***************************************************************/
05661                    /** @} *//* end of cmfunctionc */
05662 
05663 /**dox***************************************************************/
05664 /** @addtogroup bmfunctionc
05665  *  
05666  *  @{  */
05667 
05668 /********************************************************************/
05669 /**
05670 Modifies buffer cache size.
05671 Without a buffer cache, events are copied to/from the shared
05672 memory event by event.
05673 
05674 To protect processed from accessing the shared memory simultaneously,
05675 semaphores are used. Since semaphore operations are CPU consuming (typically
05676 50-100us) this can slow down the data transfer especially for small events.
05677 By using a cache the number of semaphore operations is reduced dramatically.
05678 Instead writing directly to the shared memory, the events are copied to a
05679 local cache buffer. When this buffer is full, it is copied to the shared
05680 memory in one operation. The same technique can be used when receiving events.
05681 
05682 The drawback of this method is that the events have to be copied twice, once to the
05683 cache and once from the cache to the shared memory. Therefore it can happen that the
05684 usage of a cache even slows down data throughput on a given environment (computer
05685 type, OS type, event size).
05686 The cache size has therefore be optimized manually to maximize data throughput.
05687 @param buffer_handle buffer handle obtained via bm_open_buffer()
05688 @param read_size cache size for reading events in bytes, zero for no cache
05689 @param write_size cache size for writing events in bytes, zero for no cache
05690 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05691 */
05692 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05693 /*------------------------------------------------------------------*/
05694 {
05695    if (rpc_is_remote())
05696       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
05697 
05698 #ifdef LOCAL_ROUTINES
05699    {
05700       BUFFER *pbuf;
05701 
05702       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05703          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05704          return BM_INVALID_HANDLE;
05705       }
05706 
05707       if (!_buffer[buffer_handle - 1].attached) {
05708          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05709          return BM_INVALID_HANDLE;
05710       }
05711 
05712       if (read_size < 0 || read_size > 1E6) {
05713          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05714          return BM_INVALID_PARAM;
05715       }
05716 
05717       if (write_size < 0 || write_size > 1E6) {
05718          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05719          return BM_INVALID_PARAM;
05720       }
05721 
05722       /* manage read cache */
05723       pbuf = &_buffer[buffer_handle - 1];
05724 
05725       if (pbuf->read_cache_size > 0)
05726          M_FREE(pbuf->read_cache);
05727 
05728       if (read_size > 0) {
05729          pbuf->read_cache = (char *) M_MALLOC(read_size);
05730          if (pbuf->read_cache == NULL) {
05731             cm_msg(MERROR, "bm_set_cache_size",
05732                    "not enough memory to allocate cache buffer");
05733             return BM_NO_MEMORY;
05734          }
05735       }
05736 
05737       pbuf->read_cache_size = read_size;
05738       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05739 
05740       /* manage write cache */
05741       if (pbuf->write_cache_size > 0)
05742          M_FREE(pbuf->write_cache);
05743 
05744       if (write_size > 0) {
05745          pbuf->write_cache = (char *) M_MALLOC(write_size);
05746          if (pbuf->write_cache == NULL) {
05747             cm_msg(MERROR, "bm_set_cache_size",
05748                    "not enough memory to allocate cache buffer");
05749             return BM_NO_MEMORY;
05750          }
05751       }
05752 
05753       pbuf->write_cache_size = write_size;
05754       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05755 
05756    }
05757 #endif                          /* LOCAL_ROUTINES */
05758 
05759    return BM_SUCCESS;
05760 }
05761 
05762 /********************************************************************/
05763 /**
05764 Compose a Midas event header.
05765 An event header can usually be set-up manually or
05766 through this routine. If the data size of the event is not known when
05767 the header is composed, it can be set later with event_header->data-size = <...>
05768 Following structure is created at the beginning of an event
05769 \code
05770 typedef struct {
05771  short int     event_id;
05772  short int     trigger_mask;
05773  DWORD         serial_number;
05774  DWORD         time_stamp;
05775  DWORD         data_size;
05776 } EVENT_HEADER;
05777 
05778 char event[1000];
05779  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05780  *(event+sizeof(EVENT_HEADER)) = <...>
05781 \endcode
05782 @param event_header pointer to the event header
05783 @param event_id event ID of the event
05784 @param trigger_mask trigger mask of the event
05785 @param size size if the data part of the event in bytes
05786 @param serial serial number
05787 @return BM_SUCCESS
05788 */
05789 INT bm_compose_event(EVENT_HEADER * event_header,
05790                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05791 {
05792    event_header->event_id = event_id;
05793    event_header->trigger_mask = trigger_mask;
05794    event_header->data_size = size;
05795    event_header->time_stamp = ss_time();
05796    event_header->serial_number = serial;
05797 
05798    return BM_SUCCESS;
05799 }
05800 
05801 
05802 /**dox***************************************************************/
05803 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05804 
05805 /********************************************************************/
05806 INT bm_add_event_request(INT buffer_handle, short int event_id,
05807                          short int trigger_mask,
05808                          INT sampling_type,
05809                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05810                          INT request_id)
05811 /********************************************************************\
05812 
05813   Routine:  bm_add_event_request
05814 
05815   Purpose:  Place a request for a specific event type in the client
05816             structure of the buffer refereced by buffer_handle.
05817 
05818   Input:
05819     INT          buffer_handle  Handle to the buffer where the re-
05820                                 quest should be placed in
05821 
05822     short int    event_id       Event ID      \
05823     short int    trigger_mask   Trigger mask  / Event specification
05824 
05825     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05826 
05827 
05828                  Note: to request all types of events, use
05829                    event_id = 0 (all others should be !=0 !)
05830                    trigger_mask = TRIGGER_ALL
05831                    sampling_typ = GET_ALL
05832 
05833 
05834     void         *func          Callback function
05835     INT          request_id     Request id (unique number assigned
05836                                 by bm_request_event)
05837 
05838   Output:
05839     none
05840 
05841   Function value:
05842     BM_SUCCESS              Successful completion
05843     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05844                             MIDAS.H should be increased.
05845     BM_INVALID_HANDLE       Buffer handle is invalid
05846     RPC_NET_ERROR           Network error
05847 
05848 \********************************************************************/
05849 {
05850    if (rpc_is_remote())
05851       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05852                       trigger_mask, sampling_type, (INT) func, request_id);
05853 
05854 #ifdef LOCAL_ROUTINES
05855    {
05856       INT i;
05857       BUFFER_CLIENT *pclient;
05858 
05859       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05860          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05861                 buffer_handle);
05862          return BM_INVALID_HANDLE;
05863       }
05864 
05865       if (!_buffer[buffer_handle - 1].attached) {
05866          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05867                 buffer_handle);
05868          return BM_INVALID_HANDLE;
05869       }
05870 
05871       /* avoid callback/non callback requests */
05872       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05873          cm_msg(MERROR, "bm_add_event_request",
05874                 "mixing callback/non callback requests not possible");
05875          return BM_INVALID_MIXING;
05876       }
05877 
05878       /* get a pointer to the proper client structure */
05879       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05880                   client[_buffer[buffer_handle - 1].client_index]);
05881 
05882       /* lock buffer */
05883       bm_lock_buffer(buffer_handle);
05884 
05885       /* look for a empty request entry */
05886       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05887          if (!pclient->event_request[i].valid)
05888             break;
05889 
05890       if (i == MAX_EVENT_REQUESTS) {
05891          bm_unlock_buffer(buffer_handle);
05892          return BM_NO_MEMORY;
05893       }
05894 
05895       /* setup event_request structure */
05896       pclient->event_request[i].id = request_id;
05897       pclient->event_request[i].valid = TRUE;
05898       pclient->event_request[i].event_id = event_id;
05899       pclient->event_request[i].trigger_mask = trigger_mask;
05900       pclient->event_request[i].sampling_type = sampling_type;
05901       pclient->event_request[i].dispatch = func;
05902 
05903       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05904 
05905       /* set callback flag in buffer structure */
05906       if (func != NULL)
05907          _buffer[buffer_handle - 1].callback = TRUE;
05908 
05909       /*
05910          Save the index of the last request in the list so that later only the
05911          requests 0..max_request_index-1 have to be searched through.
05912        */
05913 
05914       if (i + 1 > pclient->max_request_index)
05915          pclient->max_request_index = i + 1;
05916 
05917       bm_unlock_buffer(buffer_handle);
05918    }
05919 #endif                          /* LOCAL_ROUTINES */
05920 
05921    return BM_SUCCESS;
05922 }
05923 
05924 /**dox***************************************************************/
05925 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05926 
05927 /********************************************************************/
05928 /**
05929 Place an event request based on certain characteristics.
05930 Multiple event requests can be placed for each buffer, which
05931 are later identified by their request ID. They can contain different callback
05932 routines. Example see bm_open_buffer() and bm_receive_event()
05933 @param buffer_handle buffer handle obtained via bm_open_buffer()
05934 @param event_id event ID for requested events. Use EVENTID_ALL
05935 to receive events with any ID.
05936 @param trigger_mask trigger mask for requested events.
05937 The requested events must have at least one bit in its
05938 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05939 receive events with any trigger mask.
05940 @param sampling_type specifies how many events to receive.
05941 A value of GET_ALL receives all events which
05942 match the specified event ID and trigger mask. If the events are consumed slower
05943 than produced, the producer is automatically slowed down. A value of GET_SOME
05944 receives as much events as possible without slowing down the producer. GET_ALL is
05945 typically used by the logger, while GET_SOME is typically used by analyzers.
05946 @param request_id request ID returned by the function.
05947 This ID is passed to the callback routine and must
05948 be used in the bm_delete_request() routine.
05949 @param func allback routine which gets called when an event of the
05950 specified type is received.
05951 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05952 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05953 should be increased.
05954 */
05955 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05956                      short int trigger_mask,
05957                      INT sampling_type, HNDLE * request_id,
05958                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05959 {
05960    INT index, status;
05961 
05962    /* allocate new space for the local request list */
05963    if (_request_list_entries == 0) {
05964       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05965       memset(_request_list, 0, sizeof(REQUEST_LIST));
05966       if (_request_list == NULL) {
05967          cm_msg(MERROR, "bm_request_event",
05968                 "not enough memory to allocate request list buffer");
05969          return BM_NO_MEMORY;
05970       }
05971 
05972       _request_list_entries = 1;
05973       index = 0;
05974    } else {
05975       /* check for a deleted entry */
05976       for (index = 0; index < _request_list_entries; index++)
05977          if (!_request_list[index].buffer_handle)
05978             break;
05979 
05980       /* if not found, create new one */
05981       if (index == _request_list_entries) {
05982          _request_list = (REQUEST_LIST *) realloc(_request_list,
05983                                                   sizeof(REQUEST_LIST) *
05984                                                   (_request_list_entries + 1));
05985          if (_request_list == NULL) {
05986             cm_msg(MERROR, "bm_request_event",
05987                    "not enough memory to allocate request list buffer");
05988             return BM_NO_MEMORY;
05989          }
05990 
05991          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05992 
05993          _request_list_entries++;
05994       }
05995    }
05996 
05997    /* initialize request list */
05998    _request_list[index].buffer_handle = buffer_handle;
05999    _request_list[index].event_id = event_id;
06000    _request_list[index].trigger_mask = trigger_mask;
06001    _request_list[index].dispatcher = func;
06002 
06003    *request_id = index;
06004 
06005    /* add request in buffer structure */
06006    status = bm_add_event_request(buffer_handle, event_id, trigger_mask,
06007                                  sampling_type, func, index);
06008    if (status != BM_SUCCESS)
06009       return status;
06010 
06011    return BM_SUCCESS;
06012 }
06013 
06014 /********************************************************************/
06015 /**
06016 Delete a previously placed request for a specific event
06017 type in the client structure of the buffer refereced by buffer_handle.
06018 @param buffer_handle  Handle to the buffer where the re-
06019                                 quest should be placed in
06020 @param request_id     Request id returned by bm_request_event
06021 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
06022 */
06023 INT bm_remove_event_request(INT buffer_handle, INT request_id)
06024 {
06025    if (rpc_is_remote())
06026       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
06027 
06028 #ifdef LOCAL_ROUTINES
06029    {
06030       INT i, deleted;
06031       BUFFER_CLIENT *pclient;
06032 
06033       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06034          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
06035                 buffer_handle);
06036          return BM_INVALID_HANDLE;
06037       }
06038 
06039       if (!_buffer[buffer_handle - 1].attached) {
06040          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
06041                 buffer_handle);
06042          return BM_INVALID_HANDLE;
06043       }
06044 
06045       /* get a pointer to the proper client structure */
06046       pclient = &(_buffer[buffer_handle - 1].buffer_header->
06047                   client[_buffer[buffer_handle - 1].client_index]);
06048 
06049       /* lock buffer */
06050       bm_lock_buffer(buffer_handle);
06051 
06052       /* check all requests and set to zero if matching */
06053       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
06054          if (pclient->event_request[i].valid &&
06055              pclient->event_request[i].id == request_id) {
06056             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
06057             deleted++;
06058          }
06059 
06060       /* calculate new max_request_index entry */
06061       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
06062          if (pclient->event_request[i].valid)
06063             break;
06064 
06065       pclient->max_request_index = i + 1;
06066 
06067       /* caluclate new all_flag */
06068       pclient->all_flag = FALSE;
06069 
06070       for (i = 0; i < pclient->max_request_index; i++)
06071          if (pclient->event_request[i].valid &&
06072              (pclient->event_request[i].sampling_type & GET_ALL)) {
06073             pclient->all_flag = TRUE;
06074             break;
06075          }
06076 
06077       bm_unlock_buffer(buffer_handle);
06078 
06079       if (!deleted)
06080          return BM_NOT_FOUND;
06081    }
06082 #endif                          /* LOCAL_ROUTINES */
06083 
06084    return BM_SUCCESS;
06085 }
06086 
06087 /********************************************************************/
06088 /** 
06089 Deletes an event request previously done with bm_request_event().
06090 When an event request gets deleted, events of that requested type are
06091 not received any more. When a buffer is closed via bm_close_buffer(), all
06092 event requests from that buffer are deleted automatically
06093 @param request_id request identifier given by bm_request_event()
06094 @return BM_SUCCESS, BM_INVALID_HANDLE
06095 */
06096 INT bm_delete_request(INT request_id)
06097 {
06098    if (request_id < 0 || request_id >= _request_list_entries)
06099       return BM_INVALID_HANDLE;
06100 
06101    /* remove request entry from buffer */
06102    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
06103 
06104    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
06105 
06106    return BM_SUCCESS;
06107 }
06108 
06109 /********************************************************************/
06110 /** 
06111 Sends an event to a buffer.
06112 This function check if the buffer has enough space for the
06113 event, then copies the event to the buffer in shared memory.
06114 If clients have requests for the event, they are notified via an UDP packet.
06115 \code
06116 char event[1000];
06117 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06118 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06119 
06120 // set first byte of event
06121 *(event+sizeof(EVENT_HEADER)) = <...>
06122 #include <stdio.h>
06123 #include "midas.h"
06124 main()
06125 {
06126  INT status, i;
06127  HNDLE hbuf;
06128  char event[1000];
06129  status = cm_connect_experiment("", "Sample", "Producer", NULL);
06130  if (status != CM_SUCCESS)
06131  return 1;
06132  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06133 
06134  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
06135  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
06136 
06137  // set event data
06138  for (i=0 ; i<100 ; i++)
06139  *(event+sizeof(EVENT_HEADER)+i) = i;
06140  // send event
06141  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
06142  cm_disconnect_experiment();
06143  return 0;
06144 }
06145 \endcode
06146 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06147 @param source Address of event buffer
06148 @param buf_size Size of event including event header in bytes
06149 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06150 blocks if the buffer has not enough free space to receive the event.
06151 If TRUE, the function returns immediately with a
06152 value of BM_ASYNC_RETURN without writing the event to the buffer
06153 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
06154 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
06155 buffer has not enough space to receive event<br>
06156 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
06157 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
06158 recompile.
06159 */
06160 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
06161 {
06162    EVENT_HEADER *pevent;
06163 
06164    /* check if event size defined in header matches buf_size */
06165    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
06166                                         sizeof(EVENT_HEADER))) {
06167       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
06168              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
06169                                             sizeof(EVENT_HEADER)));
06170       return BM_INVALID_PARAM;
06171    }
06172 
06173    /* check for maximal event size */
06174    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
06175       cm_msg(MERROR, "bm_send_event",
06176              "event size (%d) larger than maximum event size (%d)",
06177              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
06178       return BM_NO_MEMORY;
06179    }
06180 
06181    if (rpc_is_remote())
06182       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
06183 
06184 #ifdef LOCAL_ROUTINES
06185    {
06186       BUFFER *pbuf;
06187       BUFFER_HEADER *pheader;
06188       BUFFER_CLIENT *pclient, *pc;
06189       EVENT_REQUEST *prequest;
06190       EVENT_HEADER *pevent_test;
06191       INT i, j, min_wp, size, total_size, status;
06192       INT increment;
06193       INT my_client_index;
06194       INT old_write_pointer;
06195       INT old_read_pointer, new_read_pointer;
06196       INT num_requests_client;
06197       char *pdata;
06198       BOOL blocking;
06199       INT n_blocking;
06200       INT request_id;
06201       char str[80];
06202 
06203       pbuf = &_buffer[buffer_handle - 1];
06204 
06205       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06206          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06207          return BM_INVALID_HANDLE;
06208       }
06209 
06210       if (!pbuf->attached) {
06211          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06212          return BM_INVALID_HANDLE;
06213       }
06214 
06215       pevent = (EVENT_HEADER *) source;
06216       total_size = buf_size;
06217 
06218       /* round up total_size to next DWORD boundary */
06219       total_size = ALIGN8(total_size);
06220 
06221       /* look if there is space in the cache */
06222       if (pbuf->write_cache_size) {
06223          status = BM_SUCCESS;
06224 
06225          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
06226             status = bm_flush_cache(buffer_handle, async_flag);
06227 
06228          if (status != BM_SUCCESS)
06229             return status;
06230 
06231          if (total_size < pbuf->write_cache_size) {
06232             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
06233 
06234             pbuf->write_cache_wp += total_size;
06235             return BM_SUCCESS;
06236          }
06237       }
06238 
06239       /* calculate some shorthands */
06240       pheader = _buffer[buffer_handle - 1].buffer_header;
06241       pdata = (char *) (pheader + 1);
06242       my_client_index = _buffer[buffer_handle - 1].client_index;
06243       pclient = pheader->client;
06244 
06245       /* check if buffer is large enough */
06246       if (total_size >= pheader->size) {
06247          cm_msg(MERROR, "bm_send_event",
06248                 "total event size (%d) larger than buffer size (%d)", total_size,
06249                 pheader->size);
06250          return BM_NO_MEMORY;
06251       }
06252 
06253       /* lock the buffer */
06254       bm_lock_buffer(buffer_handle);
06255 
06256       /* check if enough space in buffer left */
06257       do {
06258          size = pheader->read_pointer - pheader->write_pointer;
06259          if (size <= 0)
06260             size += pheader->size;
06261 
06262          if (size <= total_size) {      /* note the '<=' to avoid 100% filling */
06263             /* if not enough space, find out who's blocking */
06264             n_blocking = 0;
06265 
06266             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06267                if (pc->pid) {
06268                   if (pc->read_pointer == pheader->read_pointer) {
06269                      /*
06270                         First assume that the client with the "minimum" read pointer
06271                         is not really blocking due to a GET_ALL request.
06272                       */
06273                      blocking = FALSE;
06274                      request_id = -1;
06275 
06276                      /* check if this request blocks */
06277 
06278                      prequest = pc->event_request;
06279                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06280 
06281                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06282                         if (prequest->valid &&
06283                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06284                                            pevent_test)) {
06285                            request_id = prequest->id;
06286                            if (prequest->sampling_type & GET_ALL) {
06287                               blocking = TRUE;
06288                               break;
06289                            }
06290                         }
06291 
06292                      if (!blocking) {
06293                         /*
06294                            The blocking guy has no GET_ALL request for this event
06295                            -> shift its read pointer.
06296                          */
06297 
06298                         old_read_pointer = pc->read_pointer;
06299 
06300                         increment = sizeof(EVENT_HEADER) +
06301                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06302 
06303                         /* correct increment for DWORD boundary */
06304                         increment = ALIGN8(increment);
06305 
06306                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06307 
06308                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06309                            new_read_pointer = 0;
06310 
06311                         pc->read_pointer = new_read_pointer;
06312                      } else {
06313                         n_blocking++;
06314                      }
06315 
06316                      /* wake that client if it has a request */
06317                      if (pc->read_wait && request_id != -1) {
06318 #ifdef DEBUG_MSG
06319                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06320                                pheader->read_pointer, pheader->write_pointer);
06321 #endif
06322                         sprintf(str, "B %s %d", pheader->name, request_id);
06323                         ss_resume(pc->port, str);
06324                      }
06325 
06326 
06327                   }             /* read_pointer blocks */
06328                }
06329             /* client loop */
06330             if (n_blocking > 0) {
06331                /* at least one client is blocking */
06332 
06333                bm_unlock_buffer(buffer_handle);
06334 
06335                /* return now in ASYNC mode */
06336                if (async_flag)
06337                   return BM_ASYNC_RETURN;
06338 
06339 #ifdef DEBUG_MSG
06340                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06341                       pheader->read_pointer,
06342                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06343 #endif
06344 
06345                /* has the read pointer moved in between ? */
06346                size = pheader->read_pointer - pheader->write_pointer;
06347                if (size <= 0)
06348                   size += pheader->size;
06349 
06350                /* suspend process */
06351                if (size <= total_size) {
06352                   /* signal other clients wait mode */
06353                   pclient[my_client_index].write_wait = total_size;
06354 
06355                   status = ss_suspend(1000, MSG_BM);
06356 
06357                   pclient[my_client_index].write_wait = 0;
06358 
06359                   /* return if TCP connection broken */
06360                   if (status == SS_ABORT)
06361                      return SS_ABORT;
06362                }
06363 #ifdef DEBUG_MSG
06364                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06365                       pheader->read_pointer,
06366                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06367 #endif
06368 
06369                bm_lock_buffer(buffer_handle);
06370 
06371                /* has the write pointer moved in between ? */
06372                size = pheader->read_pointer - pheader->write_pointer;
06373                if (size <= 0)
06374                   size += pheader->size;
06375             } else {
06376                /*
06377                   calculate new global read pointer as "minimum" of
06378                   client read pointers
06379                 */
06380                min_wp = pheader->write_pointer;
06381 
06382                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06383                   if (pc->pid) {
06384                      if (pc->read_pointer < min_wp)
06385                         min_wp = pc->read_pointer;
06386 
06387                      if (pc->read_pointer > pheader->write_pointer &&
06388                          pc->read_pointer - pheader->size < min_wp)
06389                         min_wp = pc->read_pointer - pheader->size;
06390                   }
06391 
06392                if (min_wp < 0)
06393                   min_wp += pheader->size;
06394 
06395                pheader->read_pointer = min_wp;
06396             }
06397 
06398          }
06399          /* if (size <= total_size) */
06400       } while (size <= total_size);
06401 
06402       /* we have space, so let's copy the event */
06403       old_write_pointer = pheader->write_pointer;
06404 
06405       if (pheader->write_pointer + total_size <= pheader->size) {
06406          memcpy(pdata + pheader->write_pointer, pevent, total_size);
06407          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06408          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06409             pheader->write_pointer = 0;
06410       } else {
06411          /* split event */
06412          size = pheader->size - pheader->write_pointer;
06413 
06414          memcpy(pdata + pheader->write_pointer, pevent, size);
06415          memcpy(pdata, (char *) pevent + size, total_size - size);
06416 
06417          pheader->write_pointer = total_size - size;
06418       }
06419 
06420       /* check which clients have a request for this event */
06421       for (i = 0; i < pheader->max_client_index; i++)
06422          if (pclient[i].pid) {
06423             prequest = pclient[i].event_request;
06424             num_requests_client = 0;
06425             request_id = -1;
06426 
06427             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06428                if (prequest->valid &&
06429                    bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06430                   if (prequest->sampling_type & GET_ALL)
06431                      pclient[i].num_waiting_events++;
06432 
06433                   num_requests_client++;
06434                   request_id = prequest->id;
06435                }
06436 
06437             /* if that client has a request and is suspended, wake it up */
06438             if (num_requests_client && pclient[i].read_wait) {
06439 #ifdef DEBUG_MSG
06440                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06441                       pheader->write_pointer);
06442 #endif
06443                sprintf(str, "B %s %d", pheader->name, request_id);
06444                ss_resume(pclient[i].port, str);
06445             }
06446 
06447             /* if that client has no request, shift its read pointer */
06448             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
06449                pclient[i].read_pointer = pheader->write_pointer;
06450          }
06451 
06452       /* shift read pointer of own client */
06453 /* 16.4.99 SR, outcommented to receive own messages
06454 
06455   if (pclient[my_client_index].read_pointer == old_write_pointer)
06456     pclient[my_client_index].read_pointer = pheader->write_pointer;
06457 */
06458 
06459       /* calculate global read pointer as "minimum" of client read pointers */
06460       min_wp = pheader->write_pointer;
06461 
06462       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06463          if (pc->pid) {
06464             if (pc->read_pointer < min_wp)
06465                min_wp = pc->read_pointer;
06466 
06467             if (pc->read_pointer > pheader->write_pointer &&
06468                 pc->read_pointer - pheader->size < min_wp)
06469                min_wp = pc->read_pointer - pheader->size;
06470          }
06471 
06472       if (min_wp < 0)
06473          min_wp += pheader->size;
06474 
06475 #ifdef DEBUG_MSG
06476       if (min_wp == pheader->read_pointer)
06477          cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
06478       else
06479          cm_msg(MDEBUG, "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
06480                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06481 #endif
06482 
06483       pheader->read_pointer = min_wp;
06484 
06485       /* update statistics */
06486       pheader->num_in_events++;
06487 
06488       /* unlock the buffer */
06489       bm_unlock_buffer(buffer_handle);
06490    }
06491 #endif                          /* LOCAL_ROUTINES */
06492 
06493    return BM_SUCCESS;
06494 }
06495 
06496 /********************************************************************/
06497 /**
06498 Empty write cache.
06499 This function should be used if events in the write cache
06500 should be visible to the consumers immediately. It should be called at the
06501 end of each run, otherwise events could be kept in the write buffer and will
06502 flow to the data of the next run.
06503 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06504 @param async_flag Synchronous/asynchronous flag.
06505 If FALSE, the function blocks if the buffer has not
06506 enough free space to receive the full cache. If TRUE, the function returns
06507 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06508 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06509 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06510 and buffer has not enough space to receive cache<br>
06511 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06512 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
06513 and recompile.
06514 */
06515 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06516 {
06517    if (rpc_is_remote())
06518       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06519 
06520 #ifdef LOCAL_ROUTINES
06521    {
06522       BUFFER *pbuf;
06523       BUFFER_HEADER *pheader;
06524       BUFFER_CLIENT *pclient, *pc;
06525       EVENT_REQUEST *prequest;
06526       EVENT_HEADER *pevent, *pevent_test;
06527       INT i, j, min_wp, size, total_size, status;
06528       INT increment;
06529       INT my_client_index;
06530       INT old_write_pointer;
06531       INT old_read_pointer, new_read_pointer;
06532       char *pdata;
06533       BOOL blocking;
06534       INT n_blocking;
06535       INT request_id;
06536       char str[80];
06537 
06538       pbuf = &_buffer[buffer_handle - 1];
06539 
06540       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06541          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06542          return BM_INVALID_HANDLE;
06543       }
06544 
06545       if (!pbuf->attached) {
06546          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06547          return BM_INVALID_HANDLE;
06548       }
06549 
06550       if (pbuf->write_cache_size == 0)
06551          return BM_SUCCESS;
06552 
06553       /* check if anything needs to be flushed */
06554       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06555          return BM_SUCCESS;
06556 
06557       /* calculate some shorthands */
06558       pheader = _buffer[buffer_handle - 1].buffer_header;
06559       pdata = (char *) (pheader + 1);
06560       my_client_index = _buffer[buffer_handle - 1].client_index;
06561       pclient = pheader->client;
06562       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06563 
06564       /* lock the buffer */
06565       bm_lock_buffer(buffer_handle);
06566 
06567 #ifdef DEBUG_MSG
06568       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer,
06569              pheader->write_pointer);
06570 #endif
06571 
06572       /* check if enough space in buffer left */
06573       do {
06574          size = pheader->read_pointer - pheader->write_pointer;
06575          if (size <= 0)
06576             size += pheader->size;
06577 
06578          if (size <= pbuf->write_cache_wp) {
06579             /* if not enough space, find out who's blocking */
06580             n_blocking = 0;
06581 
06582             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06583                if (pc->pid) {
06584                   if (pc->read_pointer == pheader->read_pointer) {
06585                      /*
06586                         First assume that the client with the "minimum" read pointer
06587                         is not really blocking due to a GET_ALL request.
06588                       */
06589                      blocking = FALSE;
06590                      request_id = -1;
06591 
06592                      /* check if this request blocks. */
06593                      prequest = pc->event_request;
06594                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06595 
06596                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06597                         if (prequest->valid &&
06598                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06599                                            pevent_test)) {
06600                            request_id = prequest->id;
06601                            if (prequest->sampling_type & GET_ALL) {
06602                               blocking = TRUE;
06603                               break;
06604                            }
06605                         }
06606 
06607                      if (!blocking) {
06608                         /*
06609                            The blocking guy has no GET_ALL request for this event
06610                            -> shift its read pointer.
06611                          */
06612 
06613                         old_read_pointer = pc->read_pointer;
06614 
06615                         increment = sizeof(EVENT_HEADER) +
06616                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06617 
06618                         /* correct increment for DWORD boundary */
06619                         increment = ALIGN8(increment);
06620 
06621                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06622 
06623                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06624                            new_read_pointer = 0;
06625 
06626 #ifdef DEBUG_MSG
06627                         cm_msg(MDEBUG, "bm_flush_cache: shift client %d rp=%d -> =%d", i,
06628                                pc->read_pointer, new_read_pointer);
06629 #endif
06630 
06631                         pc->read_pointer = new_read_pointer;
06632                      } else {
06633                         n_blocking++;
06634                      }
06635 
06636                      /* wake that client if it has a request */
06637                      if (pc->read_wait && request_id != -1) {
06638 #ifdef DEBUG_MSG
06639                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06640                                pheader->read_pointer, pheader->write_pointer);
06641 #endif
06642                         sprintf(str, "B %s %d", pheader->name, request_id);
06643                         ss_resume(pc->port, str);
06644                      }
06645 
06646 
06647                   }             /* read_pointer blocks */
06648                }
06649             /* client loop */
06650             if (n_blocking > 0) {
06651                /* at least one client is blocking */
06652 
06653                bm_unlock_buffer(buffer_handle);
06654 
06655                /* return now in ASYNC mode */
06656                if (async_flag)
06657                   return BM_ASYNC_RETURN;
06658 
06659 #ifdef DEBUG_MSG
06660                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06661                       pheader->read_pointer,
06662                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06663 #endif
06664 
06665                /* has the read pointer moved in between ? */
06666                size = pheader->read_pointer - pheader->write_pointer;
06667                if (size <= 0)
06668                   size += pheader->size;
06669 
06670                /* suspend process */
06671                if (size <= pbuf->write_cache_wp) {
06672                   /* signal other clients wait mode */
06673                   pclient[my_client_index].write_wait = pbuf->write_cache_wp;
06674 
06675                   status = ss_suspend(1000, MSG_BM);
06676 
06677                   pclient[my_client_index].write_wait = 0;
06678 
06679                   /* return if TCP connection broken */
06680                   if (status == SS_ABORT)
06681                      return SS_ABORT;
06682                }
06683 #ifdef DEBUG_MSG
06684                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06685                       pheader->read_pointer,
06686                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06687 #endif
06688 
06689                bm_lock_buffer(buffer_handle);
06690 
06691                /* has the write pointer moved in between ? */
06692                size = pheader->read_pointer - pheader->write_pointer;
06693                if (size <= 0)
06694                   size += pheader->size;
06695             } else {
06696                /*
06697                   calculate new global read pointer as "minimum" of
06698                   client read pointers
06699                 */
06700                min_wp = pheader->write_pointer;
06701 
06702                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06703                   if (pc->pid) {
06704                      if (pc->read_pointer < min_wp)
06705                         min_wp = pc->read_pointer;
06706 
06707                      if (pc->read_pointer > pheader->write_pointer &&
06708                          pc->read_pointer - pheader->size < min_wp)
06709                         min_wp = pc->read_pointer - pheader->size;
06710                   }
06711 
06712                if (min_wp < 0)
06713                   min_wp += pheader->size;
06714 
06715                pheader->read_pointer = min_wp;
06716             }
06717 
06718          }
06719          /* if (size <= total_size) */
06720       } while (size <= pbuf->write_cache_wp);
06721 
06722 
06723       /* we have space, so let's copy the event */
06724       old_write_pointer = pheader->write_pointer;
06725 
06726 #ifdef DEBUG_MSG
06727       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06728              pheader->write_pointer);
06729 #endif
06730 
06731       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06732          /* loop over all events in cache */
06733 
06734          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06735          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06736 
06737          /* correct size for DWORD boundary */
06738          total_size = ALIGN8(total_size);
06739 
06740          if (pheader->write_pointer + total_size <= pheader->size) {
06741             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06742             pheader->write_pointer = (pheader->write_pointer + total_size) %
06743                 pheader->size;
06744             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06745                pheader->write_pointer = 0;
06746          } else {
06747             /* split event */
06748             size = pheader->size - pheader->write_pointer;
06749 
06750             memcpy(pdata + pheader->write_pointer, pevent, size);
06751             memcpy(pdata, (char *) pevent + size, total_size - size);
06752 
06753             pheader->write_pointer = total_size - size;
06754          }
06755 
06756          pbuf->write_cache_rp += total_size;
06757       }
06758 
06759       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06760 
06761       /* check which clients are waiting */
06762       for (i = 0; i < pheader->max_client_index; i++)
06763          if (pclient[i].pid && pclient[i].read_wait) {
06764 #ifdef DEBUG_MSG
06765             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06766                    pheader->write_pointer);
06767 #endif
06768             sprintf(str, "B %s %d", pheader->name, -1);
06769             ss_resume(pclient[i].port, str);
06770          }
06771 
06772       /* shift read pointer of own client */
06773 /* 16.4.99 SR, outcommented to receive own messages
06774 
06775   if (pclient[my_client_index].read_pointer == old_write_pointer)
06776     pclient[my_client_index].read_pointer = pheader->write_pointer;
06777 */
06778 
06779       /* calculate global read pointer as "minimum" of client read pointers */
06780       min_wp = pheader->write_pointer;
06781 
06782       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06783          if (pc->pid) {
06784 #ifdef DEBUG_MSG
06785             cm_msg(MDEBUG, "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06786 #endif
06787             if (pc->read_pointer < min_wp)
06788                min_wp = pc->read_pointer;
06789 
06790             if (pc->read_pointer > pheader->write_pointer &&
06791                 pc->read_pointer - pheader->size < min_wp)
06792                min_wp = pc->read_pointer - pheader->size;
06793          }
06794 
06795       if (min_wp < 0)
06796          min_wp += pheader->size;
06797 
06798 #ifdef DEBUG_MSG
06799       if (min_wp == pheader->read_pointer)
06800          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06801       else
06802          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06803                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06804 #endif
06805 
06806       pheader->read_pointer = min_wp;
06807 
06808       /* update statistics */
06809       pheader->num_in_events++;
06810 
06811       /* unlock the buffer */
06812       bm_unlock_buffer(buffer_handle);
06813    }
06814 #endif                          /* LOCAL_ROUTINES */
06815 
06816    return BM_SUCCESS;
06817 }
06818 
06819 /********************************************************************/
06820 /**
06821 Receives events directly.
06822 This function is an alternative way to receive events without
06823 a main loop.
06824 
06825 It can be used in analysis systems which actively receive events,
06826 rather than using callbacks. A analysis package could for example contain its own
06827 command line interface. A command
06828 like "receive 1000 events" could make it necessary to call bm_receive_event()
06829 1000 times in a row to receive these events and then return back to the
06830 command line prompt.
06831 The according bm_request_event() call contains NULL as the
06832 callback routine to indicate that bm_receive_event() is called to receive
06833 events.
06834 \code
06835 #include <stdio.h>
06836 #include "midas.h"
06837 void process_event(EVENT_HEADER *pheader)
06838 {
06839  printf("Received event #%d\r",
06840  pheader->serial_number);
06841 }
06842 main()
06843 {
06844   INT status, request_id;
06845   HNDLE hbuf;
06846   char event_buffer[1000];
06847   status = cm_connect_experiment("", "Sample",
06848   "Simple Analyzer", NULL);
06849   if (status != CM_SUCCESS)
06850    return 1;
06851   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06852   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06853 
06854   do
06855   {
06856    size = sizeof(event_buffer);
06857    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06858   if (status == CM_SUCCESS)
06859    process_event((EVENT_HEADER *) event_buffer);
06860    <...do something else...>
06861    status = cm_yield(0);
06862   } while (status != RPC_SHUTDOWN &&
06863   status != SS_ABORT);
06864   cm_disconnect_experiment();
06865   return 0;
06866 }
06867 \endcode
06868 @param buffer_handle buffer handle
06869 @param destination destination address where event is written to
06870 @param buf_size size of destination buffer on input, size of event plus
06871 header on return.
06872 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06873 blocks if no event is available. If TRUE, the function returns immediately
06874 with a value of BM_ASYNC_RETURN without receiving any event.
06875 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06876 BM_TRUNCATED   The event is larger than the destination buffer and was
06877                therefore truncated <br>
06878 BM_ASYNC_RETURN No event available
06879 */
06880 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06881 {
06882    if (rpc_is_remote()) {
06883       if (*buf_size > NET_BUFFER_SIZE) {
06884          cm_msg(MERROR, "bm_receive_event",
06885                 "max. event size larger than NET_BUFFER_SIZE");
06886          return RPC_NET_ERROR;
06887       }
06888 
06889       return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06890                       destination, buf_size, async_flag);
06891    }
06892 #ifdef LOCAL_ROUTINES
06893    {
06894       BUFFER *pbuf;
06895       BUFFER_HEADER *pheader;
06896       BUFFER_CLIENT *pclient, *pc, *pctmp;
06897       EVENT_REQUEST *prequest;
06898       EVENT_HEADER *pevent;
06899       char *pdata;
06900       INT convert_flags;
06901       INT i, min_wp, size, max_size, total_size, status = 0;
06902       INT my_client_index;
06903       BOOL found;
06904       INT old_read_pointer, new_read_pointer;
06905 
06906       pbuf = &_buffer[buffer_handle - 1];
06907 
06908       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06909          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06910          return BM_INVALID_HANDLE;
06911       }
06912 
06913       if (!pbuf->attached) {
06914          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06915          return BM_INVALID_HANDLE;
06916       }
06917 
06918       max_size = *buf_size;
06919       *buf_size = 0;
06920 
06921       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06922          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06923       else
06924          convert_flags = 0;
06925 
06926     CACHE_READ:
06927 
06928       /* look if there is anything in the cache */
06929       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06930          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06931          size = pevent->data_size + sizeof(EVENT_HEADER);
06932 
06933          if (size > max_size) {
06934             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, max_size);
06935             cm_msg(MERROR, "bm_receive_event", "event size larger than buffer size");
06936             *buf_size = max_size;
06937             status = BM_TRUNCATED;
06938          } else {
06939             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06940             *buf_size = size;
06941             status = BM_SUCCESS;
06942          }
06943 
06944          /* now convert event header */
06945          if (convert_flags) {
06946             pevent = (EVENT_HEADER *) destination;
06947             rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06948             rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06949                                convert_flags);
06950             rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06951                                convert_flags);
06952             rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06953                                convert_flags);
06954             rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06955                                convert_flags);
06956          }
06957 
06958          /* correct size for DWORD boundary */
06959          size = ALIGN8(size);
06960 
06961          pbuf->read_cache_rp += size;
06962 
06963          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06964             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06965 
06966          return status;
06967       }
06968 
06969       /* calculate some shorthands */
06970       pheader = pbuf->buffer_header;
06971       pdata = (char *) (pheader + 1);
06972       my_client_index = pbuf->client_index;
06973       pclient = pheader->client;
06974       pc = pheader->client + my_client_index;
06975 
06976       /* first do a quick check without locking the buffer */
06977       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06978          return BM_ASYNC_RETURN;
06979 
06980       /* lock the buffer */
06981       bm_lock_buffer(buffer_handle);
06982 
06983     LOOP:
06984 
06985       while (pheader->write_pointer == pc->read_pointer) {
06986          bm_unlock_buffer(buffer_handle);
06987 
06988          /* return now in ASYNC mode */
06989          if (async_flag == ASYNC)
06990             return BM_ASYNC_RETURN;
06991 
06992          pc->read_wait = TRUE;
06993 
06994          /* check again pointers (may have moved in between) */
06995          if (pheader->write_pointer == pc->read_pointer) {
06996 #ifdef DEBUG_MSG
06997             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06998                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06999 #endif
07000 
07001             status = ss_suspend(1000, MSG_BM);
07002 
07003 #ifdef DEBUG_MSG
07004             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
07005                    pheader->read_pointer, pheader->write_pointer);
07006 #endif
07007 
07008             /* return if TCP connection broken */
07009             if (status == SS_ABORT)
07010                return SS_ABORT;
07011          }
07012 
07013          pc->read_wait = FALSE;
07014 
07015          bm_lock_buffer(buffer_handle);
07016       }
07017 
07018       /* check if event at current read pointer matches a request */
07019       found = FALSE;
07020 
07021       do {
07022          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07023 
07024          total_size = pevent->data_size + sizeof(EVENT_HEADER);
07025          total_size = ALIGN8(total_size);
07026 
07027          prequest = pc->event_request;
07028 
07029          for (i = 0; i < pc->max_request_index; i++, prequest++)
07030             if (prequest->valid &&
07031                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
07032                /* we found one, so copy it */
07033 
07034                if (pbuf->read_cache_size > 0 &&
07035                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07036                   if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
07037                      goto CACHE_FULL;
07038 
07039                   if (pc->read_pointer + total_size <= pheader->size) {
07040                      /* copy event to cache */
07041                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
07042                   } else {
07043                      /* event is splitted */
07044                      size = pheader->size - pc->read_pointer;
07045                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07046                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
07047                             pdata, total_size - size);
07048                   }
07049                } else {
07050                   if (pc->read_pointer + total_size <= pheader->size) {
07051                      /* event is not splitted */
07052                      if (total_size > max_size)
07053                         memcpy(destination, pevent, max_size);
07054                      else
07055                         memcpy(destination, pevent, total_size);
07056                   } else {
07057                      /* event is splitted */
07058                      size = pheader->size - pc->read_pointer;
07059 
07060                      if (size > max_size)
07061                         memcpy(destination, pevent, max_size);
07062                      else
07063                         memcpy(destination, pevent, size);
07064 
07065                      if (total_size > max_size) {
07066                         if (size <= max_size)
07067                            memcpy((char *) destination + size, pdata, max_size - size);
07068                      } else
07069                         memcpy((char *) destination + size, pdata, total_size - size);
07070                   }
07071 
07072                   if (total_size < max_size)
07073                      *buf_size = total_size;
07074                   else
07075                      *buf_size = max_size;
07076 
07077                   /* now convert event header */
07078                   if (convert_flags) {
07079                      pevent = (EVENT_HEADER *) destination;
07080                      rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING,
07081                                         convert_flags);
07082                      rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
07083                                         convert_flags);
07084                      rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
07085                                         convert_flags);
07086                      rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
07087                                         convert_flags);
07088                      rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
07089                                         convert_flags);
07090                   }
07091                }
07092 
07093                if (pbuf->read_cache_size > 0 &&
07094                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07095                   pbuf->read_cache_wp += total_size;
07096                } else {
07097                   if (total_size > max_size) {
07098                      cm_msg(MERROR, "bm_receive_event",
07099                             "event size larger than buffer size");
07100                      status = BM_TRUNCATED;
07101                   } else
07102                      status = BM_SUCCESS;
07103                }
07104 
07105                /* update statistics */
07106                found = TRUE;
07107                pheader->num_out_events++;
07108                break;
07109             }
07110 
07111          old_read_pointer = pc->read_pointer;
07112 
07113          /* shift read pointer */
07114          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07115 
07116          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07117             new_read_pointer = 0;
07118 
07119 #ifdef DEBUG_MSG
07120          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07121                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
07122                 total_size);
07123 #endif
07124 
07125          pc->read_pointer = new_read_pointer;
07126 
07127          /*
07128             Repeat until a requested event is found or no more events
07129             are available.
07130           */
07131 
07132          if (pbuf->read_cache_size == 0 && found)
07133             break;
07134 
07135          /* break if event has bypassed read cache */
07136          if (pbuf->read_cache_size > 0 &&
07137              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
07138             break;
07139 
07140       } while (pheader->write_pointer != pc->read_pointer);
07141 
07142     CACHE_FULL:
07143 
07144       /* calculate global read pointer as "minimum" of client read pointers */
07145       min_wp = pheader->write_pointer;
07146 
07147       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07148          if (pctmp->pid) {
07149             if (pctmp->read_pointer < min_wp)
07150                min_wp = pctmp->read_pointer;
07151 
07152             if (pctmp->read_pointer > pheader->write_pointer &&
07153                 pctmp->read_pointer - pheader->size < min_wp)
07154                min_wp = pctmp->read_pointer - pheader->size;
07155          }
07156 
07157       if (min_wp < 0)
07158          min_wp += pheader->size;
07159 
07160       pheader->read_pointer = min_wp;
07161 
07162       /*
07163          If read pointer has been changed, it may have freed up some space
07164          for waiting producers. So check if free space is now more than 50%
07165          of the buffer size and wake waiting producers.
07166        */
07167       size = pc->read_pointer - pheader->write_pointer;
07168       if (size <= 0)
07169          size += pheader->size;
07170 
07171       if (size >= pheader->size * 0.5)
07172          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07173             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
07174                                                              (pctmp->pid == ss_getpid()
07175                                                               && pctmp->tid !=
07176                                                               ss_gettid()))) {
07177 #ifdef DEBUG_MSG
07178                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07179                       pheader->read_pointer,
07180                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
07181 #endif
07182                ss_resume(pctmp->port, "B  ");
07183             }
07184 
07185       /* if no matching event found, start again */
07186       if (!found)
07187          goto LOOP;
07188 
07189       bm_unlock_buffer(buffer_handle);
07190 
07191       if (pbuf->read_cache_size > 0 &&
07192           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
07193          goto CACHE_READ;
07194 
07195       return status;
07196    }
07197 #else                           /* LOCAL_ROUTINES */
07198 
07199    return SS_SUCCESS;
07200 #endif
07201 }
07202 
07203 /********************************************************************/
07204 /**
07205 Skip all events in current buffer.
07206 
07207 Useful for single event displays to see the newest events
07208 @param buffer_handle      Handle of the buffer. Must be obtained
07209                           via bm_open_buffer.
07210 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
07211 */
07212 INT bm_skip_event(INT buffer_handle)
07213 {
07214    if (rpc_is_remote())
07215       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
07216 
07217 #ifdef LOCAL_ROUTINES
07218    {
07219       BUFFER *pbuf;
07220       BUFFER_HEADER *pheader;
07221       BUFFER_CLIENT *pclient;
07222 
07223       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
07224          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07225          return BM_INVALID_HANDLE;
07226       }
07227 
07228       pbuf = &_buffer[buffer_handle - 1];
07229       pheader = pbuf->buffer_header;
07230 
07231       if (!pbuf->attached) {
07232          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07233          return BM_INVALID_HANDLE;
07234       }
07235 
07236       /* clear cache */
07237       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
07238          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07239 
07240       bm_lock_buffer(buffer_handle);
07241 
07242       /* forward read pointer to global write pointer */
07243       pclient = pheader->client + pbuf->client_index;
07244       pclient->read_pointer = pheader->write_pointer;
07245 
07246       bm_unlock_buffer(buffer_handle);
07247    }
07248 #endif
07249 
07250    return BM_SUCCESS;
07251 }
07252 
07253 /********************************************************************/
07254 /**
07255 Check a buffer if an event is available and call the dispatch function if found.
07256 @param buffer_name       Name of buffer
07257 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
07258                     RPC_NET_ERROR
07259 */
07260 INT bm_push_event(char *buffer_name)
07261 {
07262 #ifdef LOCAL_ROUTINES
07263    {
07264       BUFFER *pbuf;
07265       BUFFER_HEADER *pheader;
07266       BUFFER_CLIENT *pclient, *pc, *pctmp;
07267       EVENT_REQUEST *prequest;
07268       EVENT_HEADER *pevent;
07269       char *pdata;
07270       INT i, min_wp, size, total_size, buffer_handle;
07271       INT my_client_index;
07272       BOOL found;
07273       INT old_read_pointer, new_read_pointer;
07274 
07275       for (i = 0; i < _buffer_entries; i++)
07276          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07277             break;
07278       if (i == _buffer_entries)
07279          return BM_INVALID_HANDLE;
07280 
07281       buffer_handle = i + 1;
07282       pbuf = &_buffer[buffer_handle - 1];
07283 
07284       if (!pbuf->attached)
07285          return BM_INVALID_HANDLE;
07286 
07287       /* return immediately if no callback routine is defined */
07288       if (!pbuf->callback)
07289          return BM_SUCCESS;
07290 
07291       if (_event_buffer_size == 0) {
07292          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
07293          if (_event_buffer == NULL) {
07294             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
07295             return BM_NO_MEMORY;
07296          }
07297          _event_buffer_size = 1000;
07298       }
07299 
07300     CACHE_READ:
07301 
07302       /* look if there is anything in the cache */
07303       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
07304          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
07305          size = pevent->data_size + sizeof(EVENT_HEADER);
07306 
07307          /* correct size for DWORD boundary */
07308          size = ALIGN8(size);
07309 
07310          /* increment read pointer */
07311          pbuf->read_cache_rp += size;
07312          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
07313             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07314 
07315          /* call dispatcher */
07316          for (i = 0; i < _request_list_entries; i++)
07317             if (_request_list[i].buffer_handle == buffer_handle &&
07318                 bm_match_event(_request_list[i].event_id,
07319                                _request_list[i].trigger_mask, pevent)) {
07320                /* if event is fragmented, call defragmenter */
07321                if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
07322                    (pevent->event_id & 0xF000) == EVENTID_FRAG)
07323                   bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1),
07324                                       _request_list[i].dispatcher);
07325                else
07326                   _request_list[i].dispatcher(buffer_handle, i, pevent,
07327                                               (void *) (pevent + 1));
07328             }
07329 
07330          return BM_MORE_EVENTS;
07331       }
07332 
07333       /* calculate some shorthands */
07334       pheader = pbuf->buffer_header;
07335       pdata = (char *) (pheader + 1);
07336       my_client_index = pbuf->client_index;
07337       pclient = pheader->client;
07338       pc = pheader->client + my_client_index;
07339 
07340       /* first do a quick check without locking the buffer */
07341       if (pheader->write_pointer == pc->read_pointer)
07342          return BM_SUCCESS;
07343 
07344       /* lock the buffer */
07345       bm_lock_buffer(buffer_handle);
07346 
07347     LOOP:
07348 
07349       if (pheader->write_pointer == pc->read_pointer) {
07350          bm_unlock_buffer(buffer_handle);
07351 
07352          /* return if no event available */
07353          return BM_SUCCESS;
07354       }
07355 
07356       /* check if event at current read pointer matches a request */
07357       found = FALSE;
07358 
07359       do {
07360          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07361 
07362          total_size = pevent->data_size + sizeof(EVENT_HEADER);
07363          total_size = ALIGN8(total_size);
07364 
07365          prequest = pc->event_request;
07366 
07367          for (i = 0; i < pc->max_request_index; i++, prequest++)
07368             if (prequest->valid &&
07369                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
07370                /* we found one, so copy it */
07371 
07372                if (pbuf->read_cache_size > 0 &&
07373                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07374                   /* copy dispatch function and event to cache */
07375 
07376                   if (pbuf->read_cache_size - pbuf->read_cache_wp <
07377                       total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
07378                      goto CACHE_FULL;
07379 
07380                   if (pc->read_pointer + total_size <= pheader->size) {
07381                      /* copy event to cache */
07382                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
07383                   } else {
07384                      /* event is splitted */
07385 
07386                      size = pheader->size - pc->read_pointer;
07387                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07388                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
07389                             pdata, total_size - size);
07390                   }
07391 
07392                   pbuf->read_cache_wp += total_size;
07393                } else {
07394                   /* copy event to copy buffer, save dispatcher */
07395 
07396                   if (total_size > _event_buffer_size) {
07397                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
07398                      _event_buffer_size = total_size;
07399                   }
07400 
07401                   if (pc->read_pointer + total_size <= pheader->size) {
07402                      memcpy(_event_buffer, pevent, total_size);
07403                   } else {
07404                      /* event is splitted */
07405                      size = pheader->size - pc->read_pointer;
07406 
07407                      memcpy(_event_buffer, pevent, size);
07408                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
07409                   }
07410                }
07411 
07412                /* update statistics */
07413                found = TRUE;
07414                pheader->num_out_events++;
07415                break;
07416             }
07417 
07418          old_read_pointer = pc->read_pointer;
07419 
07420          /* shift read pointer */
07421          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07422 
07423          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07424             new_read_pointer = 0;
07425 
07426 #ifdef DEBUG_MSG
07427          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07428                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
07429                 total_size);
07430 #endif
07431 
07432          pc->read_pointer = new_read_pointer;
07433 
07434          /*
07435             Repeat until a requested event is found or no more events
07436             are available or large event received.
07437           */
07438 
07439          if (pbuf->read_cache_size == 0 && found)
07440             break;
07441 
07442          /* break if event has bypassed read cache */
07443          if (pbuf->read_cache_size > 0 &&
07444              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
07445             break;
07446 
07447       } while (pheader->write_pointer != pc->read_pointer);
07448 
07449     CACHE_FULL:
07450 
07451       /* calculate global read pointer as "minimum" of client read pointers */
07452       min_wp = pheader->write_pointer;
07453 
07454       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07455          if (pctmp->pid) {
07456             if (pctmp->read_pointer < min_wp)
07457                min_wp = pctmp->read_pointer;
07458 
07459             if (pctmp->read_pointer > pheader->write_pointer &&
07460                 pctmp->read_pointer - pheader->size < min_wp)
07461                min_wp = pctmp->read_pointer - pheader->size;
07462          }
07463 
07464       if (min_wp < 0)
07465          min_wp += pheader->size;
07466 
07467       pheader->read_pointer = min_wp;
07468 
07469       /*
07470          If read pointer has been changed, it may have freed up some space
07471          for waiting producers. So check if free space is now more than 50%
07472          of the buffer size and wake waiting producers.
07473        */
07474       size = pc->read_pointer - pheader->write_pointer;
07475       if (size <= 0)
07476          size += pheader->size;
07477 
07478       if (size >= pheader->size * 0.5)
07479          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07480             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
07481                                                              (pctmp->pid == ss_getpid()
07482                                                               && pctmp->tid !=
07483                                                               ss_gettid()))) {
07484 #ifdef DEBUG_MSG
07485                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07486                       pheader->read_pointer,
07487                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
07488 #endif
07489                ss_resume(pctmp->port, "B  ");
07490             }
07491 
07492       /* if no matching event found, start again */
07493       if (!found)
07494          goto LOOP;
07495 
07496       bm_unlock_buffer(buffer_handle);
07497 
07498       if (pbuf->read_cache_size > 0 &&
07499           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
07500          goto CACHE_READ;
07501 
07502       /* call dispatcher */
07503       for (i = 0; i < _request_list_entries; i++)
07504          if (_request_list[i].buffer_handle == buffer_handle &&
07505              bm_match_event(_request_list[i].event_id,
07506                             _request_list[i].trigger_mask, _event_buffer)) {
07507             if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07508                 (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07509                bm_defragment_event(buffer_handle, i, _event_buffer,
07510                                    (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07511                                    _request_list[i].dispatcher);
07512             else
07513                _request_list[i].dispatcher(buffer_handle, i, _event_buffer,
07514                                            (void *) (((EVENT_HEADER *) _event_buffer) +
07515                                                      1));
07516          }
07517 
07518       return BM_MORE_EVENTS;
07519    }
07520 #else                           /* LOCAL_ROUTINES */
07521 
07522    return BM_SUCCESS;
07523 #endif
07524 }
07525 
07526 /********************************************************************/
07527 /**
07528 Check if any requested event is waiting in a buffer
07529 @return TRUE             More events are waiting<br>
07530         FALSE            No more events are waiting
07531 */
07532 INT bm_check_buffers()
07533 {
07534 #ifdef LOCAL_ROUTINES
07535    {
07536       INT index, status = 0;
07537       INT server_type, server_conn, tid;
07538       BOOL bMore;
07539       DWORD start_time;
07540 
07541       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07542       server_conn = rpc_get_server_acception();
07543       tid = ss_gettid();
07544 
07545       /* if running as a server, buffer checking is done by client
07546          via ASYNC bm_receive_event */
07547       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
07548          return FALSE;
07549 
07550       bMore = FALSE;
07551       start_time = ss_millitime();
07552 
07553       /* go through all buffers */
07554       for (index = 0; index < _buffer_entries; index++) {
07555          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07556             continue;
07557 
07558          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07559             continue;
07560 
07561          if (!_buffer[index].attached)
07562             continue;
07563 
07564          do {
07565 
07566             /* one bm_push_event could cause a run stop and a buffer close, which
07567                would crash the next call to bm_push_event(). So check for valid
07568                buffer on each call */
07569             if (index < _buffer_entries && _buffer[index].buffer_header->name != NULL)
07570                status = bm_push_event(_buffer[index].buffer_header->name);
07571 
07572             if (status != BM_MORE_EVENTS)
07573                break;
07574 
07575             /* stop after one second */
07576             if (ss_millitime() - start_time > 1000) {
07577                bMore = TRUE;
07578                break;
07579             }
07580 
07581          } while (TRUE);
07582       }
07583 
07584       return bMore;
07585 
07586    }
07587 #else                           /* LOCAL_ROUTINES */
07588 
07589    return FALSE;
07590 
07591 #endif
07592 }
07593 
07594 /**dox***************************************************************/
07595 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07596 
07597 /********************************************************************/
07598 INT bm_mark_read_waiting(BOOL flag)
07599 /********************************************************************\
07600 
07601   Routine: bm_mark_read_waiting
07602 
07603   Purpose: Mark all open buffers ready for receiving events.
07604            Called internally by ss_suspend
07605 
07606 
07607   Input:
07608     BOOL flag               TRUE for waiting, FALSE for not waiting
07609 
07610   Output:
07611     none
07612 
07613   Function value:
07614     BM_SUCCESS              Successful completion
07615 
07616 \********************************************************************/
07617 {
07618    if (rpc_is_remote())
07619       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07620 
07621 #ifdef LOCAL_ROUTINES
07622    {
07623       INT i;
07624       BUFFER_HEADER *pheader;
07625       BUFFER_CLIENT *pclient;
07626 
07627       /* Mark all buffer for read waiting */
07628       for (i = 0; i < _buffer_entries; i++) {
07629          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
07630              _buffer[i].index != rpc_get_server_acception())
07631             continue;
07632 
07633          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
07634              _buffer[i].index != ss_gettid())
07635             continue;
07636 
07637          if (!_buffer[i].attached)
07638             continue;
07639 
07640          pheader = _buffer[i].buffer_header;
07641          pclient = pheader->client + _buffer[i].client_index;
07642          pclient->read_wait = flag;
07643       }
07644    }
07645 #endif                          /* LOCAL_ROUTINES */
07646 
07647    return BM_SUCCESS;
07648 }
07649 
07650 /********************************************************************/
07651 INT bm_notify_client(char *buffer_name, int socket)
07652 /********************************************************************\
07653 
07654   Routine: bm_notify_client
07655 
07656   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07657            the connected client
07658 
07659   Input:
07660     char  *buffer_name      Name of buffer
07661     int   socket            Network socket to client
07662 
07663   Output:
07664     none
07665 
07666   Function value:
07667     BM_SUCCESS              Successful completion
07668 
07669 \********************************************************************/
07670 {
07671    char buffer[32];
07672    NET_COMMAND *nc;
07673    INT i, convert_flags;
07674    static DWORD last_time = 0;
07675 
07676    for (i = 0; i < _buffer_entries; i++)
07677       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07678          break;
07679    if (i == _buffer_entries)
07680       return BM_INVALID_HANDLE;
07681 
07682    /* don't send notification if client has no callback defined
07683       to receive events -> client calls bm_receive_event manually */
07684    if (!_buffer[i].callback)
07685       return DB_SUCCESS;
07686 
07687    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07688 
07689    /* only send notification once each 500ms */
07690    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07691       return DB_SUCCESS;
07692 
07693    last_time = ss_millitime();
07694 
07695    if (convert_flags & CF_ASCII) {
07696       sprintf(buffer, "MSG_BM&%s", buffer_name);
07697       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
07698    } else {
07699       nc = (NET_COMMAND *) buffer;
07700 
07701       nc->header.routine_id = MSG_BM;
07702       nc->header.param_size = 0;
07703 
07704       if (convert_flags) {
07705          rpc_convert_single(&nc->header.routine_id, TID_DWORD,
07706                             RPC_OUTGOING, convert_flags);
07707          rpc_convert_single(&nc->header.param_size, TID_DWORD,
07708                             RPC_OUTGOING, convert_flags);
07709       }
07710 
07711       /* send the update notification to the client */
07712       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07713    }
07714 
07715    return BM_SUCCESS;
07716 }
07717 
07718 /********************************************************************/
07719 INT bm_poll_event(INT flag)
07720 /********************************************************************\
07721 
07722   Routine: bm_poll_event
07723 
07724   Purpose: Poll an event from a remote server. Gets called by
07725            rpc_client_dispatch
07726 
07727   Input:
07728     INT flag         TRUE if called from cm_yield
07729 
07730   Output:
07731     none
07732 
07733   Function value:
07734     TRUE             More events are waiting
07735     FALSE            No more events are waiting
07736     SS_ABORT         Network connection broken
07737 
07738 \********************************************************************/
07739 {
07740    INT status, size, i, request_id;
07741    DWORD start_time;
07742    BOOL bMore;
07743    static BOOL bMoreLast = FALSE;
07744 
07745    if (_event_buffer_size == 0) {
07746       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07747       if (!_event_buffer) {
07748          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
07749          return SS_ABORT;
07750       }
07751       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07752    }
07753 
07754    start_time = ss_millitime();
07755 
07756    /* if we got event notification, turn off read_wait */
07757    if (!flag)
07758       bm_mark_read_waiting(FALSE);
07759 
07760    /* if called from yield, return if no more events */
07761    if (flag) {
07762       if (!bMoreLast)
07763          return FALSE;
07764    }
07765 
07766    bMore = FALSE;
07767 
07768    /* loop over all requests */
07769    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07770       /* continue if no dispatcher set (manual bm_receive_event) */
07771       if (_request_list[request_id].dispatcher == NULL)
07772          continue;
07773 
07774       do {
07775          /* receive event */
07776          size = _event_buffer_size;
07777          status = bm_receive_event(_request_list[request_id].buffer_handle,
07778                                    _event_buffer, &size, ASYNC);
07779 
07780          /* call user function if successful */
07781          if (status == BM_SUCCESS)
07782             /* search proper request for this event */
07783             for (i = 0; i < _request_list_entries; i++)
07784                if ((_request_list[i].buffer_handle ==
07785                     _request_list[request_id].buffer_handle) &&
07786                    bm_match_event(_request_list[i].event_id,
07787                                   _request_list[i].trigger_mask, _event_buffer)) {
07788                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07789                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07790                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07791                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07792                                          _request_list[i].dispatcher);
07793                   else
07794                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07795                                                  _event_buffer,
07796                                                  (void
07797                                                   *) (((EVENT_HEADER *) _event_buffer) +
07798                                                       1));
07799                }
07800 
07801          /* break if no more events */
07802          if (status == BM_ASYNC_RETURN)
07803             break;
07804 
07805          /* break if server died */
07806          if (status == RPC_NET_ERROR)
07807             return SS_ABORT;
07808 
07809          /* stop after one second */
07810          if (ss_millitime() - start_time > 1000) {
07811             bMore = TRUE;
07812             break;
07813          }
07814 
07815       } while (TRUE);
07816    }
07817 
07818    if (!bMore)
07819       bm_mark_read_waiting(TRUE);
07820 
07821    bMoreLast = bMore;
07822 
07823    return bMore;
07824 }
07825 
07826 /**dox***************************************************************/
07827 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07828 
07829 /********************************************************************/
07830 /** 
07831 Clears event buffer and cache.
07832 If an event buffer is large and a consumer is slow in analyzing
07833 events, events are usually received some time after they are produced.
07834 This effect is even more experienced if a read cache is used
07835 (via bm_set_cache_size()).
07836 When changes to the hardware are made in the experience, the consumer will then
07837 still analyze old events before any new event which reflects the hardware change.
07838 Users can be fooled by looking at histograms which reflect the hardware change
07839 many seconds after they have been made.
07840 
07841 To overcome this potential problem, the analyzer can call
07842 bm_empty_buffers() just after the hardware change has been made which
07843 skips all old events contained in event buffers and read caches.
07844 Technically this is done by forwarding the read pointer of the client.
07845 No events are really deleted, they are still visible to other clients like
07846 the logger.
07847 
07848 Note that the front-end also contains write buffers which can delay the
07849 delivery of events.
07850 The standard front-end framework mfe.c reduces this effect by flushing
07851 all buffers once every second.
07852 @return BM_SUCCESS
07853 */
07854 INT bm_empty_buffers()
07855 {
07856    if (rpc_is_remote())
07857       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07858 
07859 #ifdef LOCAL_ROUTINES
07860    {
07861       INT index, server_type, server_conn, tid;
07862       BUFFER *pbuf;
07863       BUFFER_CLIENT *pclient;
07864 
07865       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07866       server_conn = rpc_get_server_acception();
07867       tid = ss_gettid();
07868 
07869       /* go through all buffers */
07870       for (index = 0; index < _buffer_entries; index++) {
07871          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07872             continue;
07873 
07874          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07875             continue;
07876 
07877          if (!_buffer[index].attached)
07878             continue;
07879 
07880          pbuf = &_buffer[index];
07881 
07882          /* empty cache */
07883          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07884 
07885          /* set read pointer to write pointer */
07886          pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07887          bm_lock_buffer(index + 1);
07888          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07889          bm_unlock_buffer(index + 1);
07890       }
07891 
07892    }
07893 #endif                          /* LOCAL_ROUTINES */
07894 
07895    return BM_SUCCESS;
07896 }
07897 
07898 /**dox***************************************************************/
07899 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07900 
07901 #define MAX_DEFRAG_EVENTS 10
07902 
07903 typedef struct {
07904    WORD event_id;
07905    DWORD data_size;
07906    DWORD received;
07907    EVENT_HEADER *pevent;
07908 } EVENT_DEFRAG_BUFFER;
07909 
07910 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07911 
07912 /********************************************************************/
07913 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07914                          EVENT_HEADER * pevent, void *pdata,
07915                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07916 /********************************************************************\
07917 
07918   Routine: bm_defragment_event
07919 
07920   Purpose: Called internally from the event receiving routines
07921            bm_push_event and bm_poll_event to recombine event
07922            fragments and call the user callback routine upon
07923            completion.
07924 
07925   Input:
07926     HNDLE buffer_handle  Handle for the buffer containing event
07927     HNDLE request_id     Handle for event request
07928     EVENT_HEADER *pevent Pointer to event header
07929     void *pata           Pointer to event data
07930     dispatcher()         User callback routine
07931 
07932   Output:
07933     <calls dispatcher() after successfull recombination of event>
07934 
07935   Function value:
07936     void
07937 
07938 \********************************************************************/
07939 {
07940    INT i;
07941    static int j = -1;
07942 
07943    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07944     /*---- start new event ----*/
07945 
07946       printf("First Frag detected : Ser#:%ld ID=0x%x \n", pevent->serial_number,
07947              pevent->event_id);
07948       /* check if fragments already stored */
07949       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07950          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07951             break;
07952 
07953       if (i < MAX_DEFRAG_EVENTS) {
07954          free(defrag_buffer[i].pevent);
07955          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07956          cm_msg(MERROR, "bm_defragement_event",
07957                 "Received new event with ID %d while old fragments were not completed",
07958                 (pevent->event_id & 0x0FFF));
07959       }
07960 
07961       /* search new slot */
07962       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07963          if (defrag_buffer[i].event_id == 0)
07964             break;
07965 
07966       if (i == MAX_DEFRAG_EVENTS) {
07967          cm_msg(MERROR, "bm_defragment_event",
07968                 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07969          return;
07970       }
07971 
07972       /* check event size */
07973       if (pevent->data_size != sizeof(DWORD)) {
07974          cm_msg(MERROR, "bm_defragment_event",
07975                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07976                 pevent->data_size, sizeof(DWORD));
07977          return;
07978       }
07979 
07980       /* setup defragment buffer */
07981       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07982       defrag_buffer[i].data_size = *(DWORD *) pdata;
07983       defrag_buffer[i].received = 0;
07984       defrag_buffer[i].pevent =
07985           (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07986 
07987       if (defrag_buffer[i].pevent == NULL) {
07988          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07989          cm_msg(MERROR, "bm_defragement_event",
07990                 "Not enough memory to allocate event defragment buffer");
07991          return;
07992       }
07993 
07994       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07995       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07996       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07997 
07998       printf("First frag[%d] (ID %d) Ser#:%ld sz:%ld\n", i, defrag_buffer[i].event_id,
07999              pevent->serial_number, defrag_buffer[i].data_size);
08000       j = 0;
08001 
08002       return;
08003    }
08004 
08005    /* search buffer for that event */
08006    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
08007       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
08008          break;
08009 
08010    if (i == MAX_DEFRAG_EVENTS) {
08011       /* no buffer available -> no first fragment received */
08012       free(defrag_buffer[i].pevent);
08013       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
08014       cm_msg(MERROR, "bm_defragement_event",
08015              "Received fragment without first fragment (ID %d) Ser#:%d",
08016              pevent->event_id & 0x0FFF, pevent->serial_number);
08017       printf("Received fragment without first fragment (ID 0x%x) Ser#:%ld Sz:%ld\n",
08018              pevent->event_id, pevent->serial_number, pevent->data_size);
08019       return;
08020    }
08021 
08022    /* add fragment to buffer */
08023    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
08024       free(defrag_buffer[i].pevent);
08025       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
08026       cm_msg(MERROR, "bm_defragement_event",
08027              "Received fragments with more data (%d) than event size (%d)",
08028              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
08029       return;
08030    }
08031 
08032    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
08033           defrag_buffer[i].received, pdata, pevent->data_size);
08034 
08035    defrag_buffer[i].received += pevent->data_size;
08036 
08037    printf("Other frag[%d][%d] (ID %d) Ser#:%ld sz:%ld\n", i, j++,
08038           defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
08039 
08040 
08041    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
08042       /* event complete */
08043       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent,
08044                  defrag_buffer[i].pevent + 1);
08045       free(defrag_buffer[i].pevent);
08046       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
08047    }
08048 }
08049 
08050 /**dox***************************************************************/
08051 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08052 
08053 /**dox***************************************************************/
08054                    /** @} *//* end of bmfunctionc */
08055 
08056 /**dox***************************************************************/
08057 /** @addtogroup rpcfunctionc
08058  *  
08059  *  @{  */
08060 
08061 /**dox***************************************************************/
08062 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08063 
08064 /********************************************************************\
08065 *                                                                    *
08066 *                         RPC functions                              *
08067 *                                                                    *
08068 \********************************************************************/
08069 
08070 /* globals */
08071 
08072 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
08073 RPC_SERVER_CONNECTION _server_connection;
08074 
08075 static int _lsock;
08076 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
08077 static INT _server_acception_index = 0;
08078 static INT _server_type;
08079 static char _server_name[256];
08080 
08081 static RPC_LIST *rpc_list = NULL;
08082 
08083 int _opt_tcp_size = OPT_TCP_SIZE;
08084 
08085 
08086 /********************************************************************\
08087 *                       conversion functions                         *
08088 \********************************************************************/
08089 
08090 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
08091 {
08092    *convert_flags = 0;
08093 
08094    /* big/little endian conversion */
08095    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
08096         (hw_type & DRI_LITTLE_ENDIAN)) ||
08097        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
08098       *convert_flags |= CF_ENDIAN;
08099 
08100    /* float conversion between IEEE and VAX G */
08101    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
08102       *convert_flags |= CF_VAX2IEEE;
08103 
08104    /* float conversion between VAX G and IEEE */
08105    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
08106       *convert_flags |= CF_IEEE2VAX;
08107 
08108    /* ASCII format */
08109    if (remote_hw_type & DR_ASCII)
08110       *convert_flags |= CF_ASCII;
08111 }
08112 
08113 /********************************************************************/
08114 void rpc_get_convert_flags(INT * convert_flags)
08115 {
08116    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
08117                           _server_connection.remote_hw_type, convert_flags);
08118 }
08119 
08120 /********************************************************************/
08121 void rpc_ieee2vax_float(float *var)
08122 {
08123    unsigned short int lo, hi;
08124 
08125    /* swap hi and lo word */
08126    lo = *((short int *) (var) + 1);
08127    hi = *((short int *) (var));
08128 
08129    /* correct exponent */
08130    if (lo != 0)
08131       lo += 0x100;
08132 
08133    *((short int *) (var) + 1) = hi;
08134    *((short int *) (var)) = lo;
08135 }
08136 
08137 void rpc_vax2ieee_float(float *var)
08138 {
08139    unsigned short int lo, hi;
08140 
08141    /* swap hi and lo word */
08142    lo = *((short int *) (var) + 1);
08143    hi = *((short int *) (var));
08144 
08145    /* correct exponent */
08146    if (hi != 0)
08147       hi -= 0x100;
08148 
08149    *((short int *) (var) + 1) = hi;
08150    *((short int *) (var)) = lo;
08151 
08152 }
08153 
08154 void rpc_vax2ieee_double(double *var)
08155 {
08156    unsigned short int i1, i2, i3, i4;
08157 
08158    /* swap words */
08159    i1 = *((short int *) (var) + 3);
08160    i2 = *((short int *) (var) + 2);
08161    i3 = *((short int *) (var) + 1);
08162    i4 = *((short int *) (var));
08163 
08164    /* correct exponent */
08165    if (i4 != 0)
08166       i4 -= 0x20;
08167 
08168    *((short int *) (var) + 3) = i4;
08169    *((short int *) (var) + 2) = i3;
08170    *((short int *) (var) + 1) = i2;
08171    *((short int *) (var)) = i1;
08172 }
08173 
08174 void rpc_ieee2vax_double(double *var)
08175 {
08176    unsigned short int i1, i2, i3, i4;
08177 
08178    /* swap words */
08179    i1 = *((short int *) (var) + 3);
08180    i2 = *((short int *) (var) + 2);
08181    i3 = *((short int *) (var) + 1);
08182    i4 = *((short int *) (var));
08183 
08184    /* correct exponent */
08185    if (i1 != 0)
08186       i1 += 0x20;
08187 
08188    *((short int *) (var) + 3) = i4;
08189    *((short int *) (var) + 2) = i3;
08190    *((short int *) (var) + 1) = i2;
08191    *((short int *) (var)) = i1;
08192 }
08193 
08194 /********************************************************************/
08195 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
08196 {
08197 
08198    if (convert_flags & CF_ENDIAN) {
08199       if (tid == TID_WORD || tid == TID_SHORT)
08200          WORD_SWAP(data);
08201       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
08202          DWORD_SWAP(data);
08203       if (tid == TID_DOUBLE)
08204          QWORD_SWAP(data);
08205    }
08206 
08207    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
08208        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
08209       if (tid == TID_FLOAT)
08210          rpc_ieee2vax_float((float *) data);
08211       if (tid == TID_DOUBLE)
08212          rpc_ieee2vax_double((double *) data);
08213    }
08214 
08215    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
08216        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
08217       if (tid == TID_FLOAT)
08218          rpc_vax2ieee_float((float *) data);
08219       if (tid == TID_DOUBLE)
08220          rpc_vax2ieee_double((double *) data);
08221    }
08222 }
08223 
08224 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
08225 /********************************************************************\
08226 
08227   Routine: rpc_convert_data
08228 
08229   Purpose: Convert data format between differenct computers
08230 
08231   Input:
08232     void   *data            Pointer to data
08233     INT    tid              Type ID of data, one of TID_xxx
08234     INT    flags            Combination of following flags:
08235                               RPC_IN: data is input parameter
08236                               RPC_OUT: data is output variable
08237                               RPC_FIXARRAY, RPC_VARARRAY: data is array
08238                                 of "size" bytes (see next param.)
08239                               RPC_OUTGOING: data is outgoing
08240     INT    total_size       Size of bytes of data. Used for variable
08241                             length arrays.
08242     INT    convert_flags    Flags for data conversion
08243 
08244   Output:
08245     void   *data            Is converted according to _convert_flag
08246                             value
08247 
08248   Function value:
08249     RPC_SUCCESS             Successful completion
08250 
08251 \********************************************************************/
08252 {
08253    INT i, n, single_size;
08254    char *p;
08255 
08256    /* convert array */
08257    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
08258       single_size = tid_size[tid];
08259       /* don't convert TID_ARRAY & TID_STRUCT */
08260       if (single_size == 0)
08261          return;
08262 
08263       n = total_size / single_size;
08264 
08265       for (i = 0; i < n; i++) {
08266          p = (char *) data + (i * single_size);
08267          rpc_convert_single(p, tid, flags, convert_flags);
08268       }
08269    } else {
08270       rpc_convert_single(data, tid, flags, convert_flags);
08271    }
08272 }
08273 
08274 /********************************************************************\
08275 *                       type ID functions                            *
08276 \********************************************************************/
08277 
08278 INT rpc_tid_size(INT id)
08279 {
08280    if (id < TID_LAST)
08281       return tid_size[id];
08282 
08283    return 0;
08284 }
08285 
08286 char *rpc_tid_name(INT id)
08287 {
08288    if (id < TID_LAST)
08289       return tid_name[id];
08290    else
08291       return "<unknown>";
08292 }
08293 
08294 
08295 /**dox***************************************************************/
08296 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08297 
08298 /********************************************************************\
08299 *                        client functions                            *
08300 \********************************************************************/
08301 
08302 /********************************************************************/
08303 /**
08304 Register RPC client for standalone mode (without standard
08305            midas server)
08306 @param list           Array of RPC_LIST structures containing
08307                             function IDs and parameter definitions.
08308                             The end of the list must be indicated by
08309                             a function ID of zero.
08310 @param name          Name of this client
08311 @return RPC_SUCCESS
08312 */
08313 INT rpc_register_client(char *name, RPC_LIST * list)
08314 {
08315    rpc_set_name(name);
08316    rpc_register_functions(rpc_get_internal_list(0), NULL);
08317    rpc_register_functions(list, NULL);
08318 
08319    return RPC_SUCCESS;
08320 }
08321 
08322 /********************************************************************/
08323 /**
08324 Register a set of RPC functions (both as clients or servers)
08325 @param new_list       Array of RPC_LIST structures containing
08326                             function IDs and parameter definitions.
08327                             The end of the list must be indicated by
08328                             a function ID of zero.
08329 @param func          Default dispatch function
08330 
08331 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
08332 */
08333 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
08334 {
08335    INT i, j, iold, inew;
08336 
08337    /* count number of new functions */
08338    for (i = 0; new_list[i].id != 0; i++) {
08339       /* check double defined functions */
08340       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
08341          if (rpc_list[j].id == new_list[i].id)
08342             return RPC_DOUBLE_DEFINED;
08343    }
08344    inew = i;
08345 
08346    /* count number of existing functions */
08347    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
08348    iold = i;
08349 
08350    /* allocate new memory for rpc_list */
08351    if (rpc_list == NULL)
08352       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
08353    else
08354       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
08355 
08356    if (rpc_list == NULL) {
08357       cm_msg(MERROR, "rpc_register_functions", "out of memory");
08358       return RPC_NO_MEMORY;
08359    }
08360 
08361    /* append new functions */
08362    for (i = iold; i < iold + inew; i++) {
08363       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
08364 
08365       /* set default dispatcher */
08366       if (rpc_list[i].dispatch == NULL)
08367          rpc_list[i].dispatch = func;
08368 
08369       /* check valid ID for user functions */
08370       if (new_list != rpc_get_internal_list(0) &&
08371           new_list != rpc_get_internal_list(1) &&
08372           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
08373          cm_msg(MERROR, "rpc_register_functions",
08374                 "registered RPC function with invalid ID");
08375    }
08376 
08377    /* mark end of list */
08378    rpc_list[i].id = 0;
08379 
08380    return RPC_SUCCESS;
08381 }
08382 
08383 
08384 
08385 /**dox***************************************************************/
08386 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08387 
08388 /********************************************************************/
08389 INT rpc_deregister_functions()
08390 /********************************************************************\
08391 
08392   Routine: rpc_deregister_functions
08393 
08394   Purpose: Free memory of previously registered functions
08395 
08396   Input:
08397     none
08398 
08399   Output:
08400     none
08401 
08402   Function value:
08403     RPC_SUCCESS              Successful completion
08404 
08405 \********************************************************************/
08406 {
08407    if (rpc_list)
08408       M_FREE(rpc_list);
08409    rpc_list = NULL;
08410 
08411    return RPC_SUCCESS;
08412 }
08413 
08414 
08415 /********************************************************************/
08416 INT rpc_register_function(INT id, INT(*func) (INT, void **))
08417 /********************************************************************\
08418 
08419   Routine: rpc_register_function
08420 
08421   Purpose: Replace a dispatch function for a specific rpc routine
08422 
08423   Input:
08424     INT      id             RPC ID
08425     INT      *func          New dispatch function
08426 
08427   Output:
08428    <implicit: func gets copied to rpc_list>
08429 
08430   Function value:
08431    RPC_SUCCESS              Successful completion
08432    RPC_INVALID_ID           RPC ID not found
08433 
08434 \********************************************************************/
08435 {
08436    INT i;
08437 
08438    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
08439       if (rpc_list[i].id == id)
08440          break;
08441 
08442    if (rpc_list[i].id == id)
08443       rpc_list[i].dispatch = func;
08444    else
08445       return RPC_INVALID_ID;
08446 
08447    return RPC_SUCCESS;
08448 }
08449 
08450 
08451 /********************************************************************/
08452 INT rpc_client_dispatch(int sock)
08453 /********************************************************************\
08454 
08455   Routine: rpc_client_dispatch
08456 
08457   Purpose: Gets called whenever a client receives data from the
08458            server. Get set via rpc_connect. Internal use only.
08459 
08460 \********************************************************************/
08461 {
08462    INT hDB, hKey, n;
08463    NET_COMMAND *nc;
08464    INT status = 0;
08465    char net_buffer[256];
08466 
08467    nc = (NET_COMMAND *) net_buffer;
08468 
08469    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08470    if (n <= 0)
08471       return SS_ABORT;
08472 
08473    if (nc->header.routine_id == MSG_ODB) {
08474       /* update a changed record */
08475       hDB = *((INT *) nc->param);
08476       hKey = *((INT *) nc->param + 1);
08477       status = db_update_record(hDB, hKey, 0);
08478    }
08479 
08480    else if (nc->header.routine_id == MSG_WATCHDOG) {
08481       nc->header.routine_id = 1;
08482       nc->header.param_size = 0;
08483       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08484       status = RPC_SUCCESS;
08485    }
08486 
08487    else if (nc->header.routine_id == MSG_BM) {
08488       fd_set readfds;
08489       struct timeval timeout;
08490 
08491       /* receive further messages to empty TCP queue */
08492       do {
08493          FD_ZERO(&readfds);
08494          FD_SET(sock, &readfds);
08495 
08496          timeout.tv_sec = 0;
08497          timeout.tv_usec = 0;
08498 
08499          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08500 
08501          if (FD_ISSET(sock, &readfds)) {
08502             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08503             if (n <= 0)
08504                return SS_ABORT;
08505 
08506             if (nc->header.routine_id == MSG_ODB) {
08507                /* update a changed record */
08508                hDB = *((INT *) nc->param);
08509                hKey = *((INT *) nc->param + 1);
08510                status = db_update_record(hDB, hKey, 0);
08511             }
08512 
08513             else if (nc->header.routine_id == MSG_WATCHDOG) {
08514                nc->header.routine_id = 1;
08515                nc->header.param_size = 0;
08516                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08517                status = RPC_SUCCESS;
08518             }
08519          }
08520 
08521       } while (FD_ISSET(sock, &readfds));
08522 
08523       /* poll event from server */
08524       status = bm_poll_event(FALSE);
08525    }
08526 
08527    return status;
08528 }
08529 
08530 
08531 /********************************************************************/
08532 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
08533 /********************************************************************\
08534 
08535   Routine: rpc_client_connect
08536 
08537   Purpose: Establish a network connection to a remote client
08538 
08539   Input:
08540     char *host_name          IP address of host to connect to.
08541     INT  port                TPC port to connect to.
08542     char *clinet_name        Client program name
08543 
08544   Output:
08545     HNDLE *hConnection       Handle for new connection which can be used
08546                              in future rpc_call(hConnection....) calls
08547 
08548   Function value:
08549     RPC_SUCCESS              Successful completion
08550     RPC_NET_ERROR            Error in socket call
08551     RPC_NO_CONNECTION        Maximum number of connections reached
08552     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08553 
08554 \********************************************************************/
08555 {
08556    INT i, status, index;
08557    struct sockaddr_in bind_addr;
08558    INT sock;
08559    INT remote_hw_type, hw_type;
08560    char str[200];
08561    char version[32], v1[32];
08562    char local_prog_name[NAME_LENGTH];
08563    char local_host_name[HOST_NAME_LENGTH];
08564    struct hostent *phe;
08565 
08566 #ifdef OS_WINNT
08567    {
08568       WSADATA WSAData;
08569 
08570       /* Start windows sockets */
08571       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08572          return RPC_NET_ERROR;
08573    }
08574 #endif
08575 
08576    /* check if cm_connect_experiment was called */
08577    if (_client_name[0] == 0) {
08578       cm_msg(MERROR, "rpc_client_connect",
08579              "cm_connect_experiment/rpc_set_name not called");
08580       return RPC_NOT_REGISTERED;
08581    }
08582 
08583    /* check for broken connections */
08584    rpc_client_check();
08585 
08586    /* check if connection already exists */
08587    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08588       if (_client_connection[i].send_sock != 0 &&
08589           strcmp(_client_connection[i].host_name, host_name) == 0 &&
08590           _client_connection[i].port == port) {
08591          *hConnection = i + 1;
08592          return RPC_SUCCESS;
08593       }
08594 
08595    /* search for free entry */
08596    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08597       if (_client_connection[i].send_sock == 0)
08598          break;
08599 
08600    /* open new network connection */
08601    if (i == MAX_RPC_CONNECTION) {
08602       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
08603       return RPC_NO_CONNECTION;
08604    }
08605 
08606    /* create a new socket for connecting to remote server */
08607    sock = socket(AF_INET, SOCK_STREAM, 0);
08608    if (sock == -1) {
08609       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08610       return RPC_NET_ERROR;
08611    }
08612 
08613    index = i;
08614    strcpy(_client_connection[index].host_name, host_name);
08615    strcpy(_client_connection[index].client_name, client_name);
08616    _client_connection[index].port = port;
08617    _client_connection[index].exp_name[0] = 0;
08618    _client_connection[index].transport = RPC_TCP;
08619    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08620    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08621 
08622    /* connect to remote node */
08623    memset(&bind_addr, 0, sizeof(bind_addr));
08624    bind_addr.sin_family = AF_INET;
08625    bind_addr.sin_addr.s_addr = 0;
08626    bind_addr.sin_port = htons((short) port);
08627 
08628 #ifdef OS_VXWORKS
08629    {
08630       INT host_addr;
08631 
08632       host_addr = hostGetByName(host_name);
08633       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08634    }
08635 #else
08636    phe = gethostbyname(host_name);
08637    if (phe == NULL) {
08638       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08639       return RPC_NET_ERROR;
08640    }
08641    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08642 #endif
08643 
08644 #ifdef OS_UNIX
08645    do {
08646       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08647 
08648       /* don't return if an alarm signal was cought */
08649    } while (status == -1 && errno == EINTR);
08650 #else
08651    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08652 #endif
08653 
08654    if (status != 0) {
08655       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08656          message should be displayed by application */
08657       return RPC_NET_ERROR;
08658    }
08659 
08660    /* set TCP_NODELAY option for better performance */
08661 #ifdef OS_VXWORKS
08662    i = 1;
08663    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08664 #endif
08665 
08666    /* send local computer info */
08667    rpc_get_name(local_prog_name);
08668    gethostname(local_host_name, sizeof(local_host_name));
08669 
08670    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08671    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name,
08672            local_host_name);
08673 
08674    send(sock, str, strlen(str) + 1, 0);
08675 
08676    /* receive remote computer info */
08677    i = recv_string(sock, str, sizeof(str), 10000);
08678    if (i <= 0) {
08679       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s",
08680              str);
08681       return RPC_NET_ERROR;
08682    }
08683 
08684    remote_hw_type = version[0] = 0;
08685    sscanf(str, "%d %s", &remote_hw_type, version);
08686    _client_connection[index].remote_hw_type = remote_hw_type;
08687    _client_connection[index].send_sock = sock;
08688 
08689    /* print warning if version patch level doesn't agree */
08690    strcpy(v1, version);
08691    if (strchr(v1, '.'))
08692       if (strchr(strchr(v1, '.') + 1, '.'))
08693          *strchr(strchr(v1, '.') + 1, '.') = 0;
08694 
08695    strcpy(str, cm_get_version());
08696    if (strchr(str, '.'))
08697       if (strchr(strchr(str, '.') + 1, '.'))
08698          *strchr(strchr(str, '.') + 1, '.') = 0;
08699 
08700    if (strcmp(v1, str) != 0) {
08701       sprintf(str, "remote MIDAS version %s differs from local version %s",
08702               version, cm_get_version());
08703       cm_msg(MERROR, "rpc_client_connect", str);
08704    }
08705 
08706    *hConnection = index + 1;
08707 
08708    return RPC_SUCCESS;
08709 }
08710 
08711 
08712 /********************************************************************/
08713 void rpc_client_check()
08714 /********************************************************************\
08715 
08716   Routine: rpc_client_check
08717 
08718   Purpose: Check all client connections if remote client closed link
08719 
08720   Function value:
08721     RPC_SUCCESS              Successful completion
08722     RPC_NET_ERROR            Error in socket call
08723     RPC_NO_CONNECTION        Maximum number of connections reached
08724     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08725 
08726 \********************************************************************/
08727 {
08728    INT i, status;
08729 
08730    /* check for broken connections */
08731    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08732       if (_client_connection[i].send_sock != 0) {
08733          int sock;
08734          fd_set readfds;
08735          struct timeval timeout;
08736          char buffer[64];
08737 
08738          sock = _client_connection[i].send_sock;
08739          FD_ZERO(&readfds);
08740          FD_SET(sock, &readfds);
08741 
08742          timeout.tv_sec = 0;
08743          timeout.tv_usec = 0;
08744 
08745          do {
08746             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08747          } while (status == -1);        /* dont return if an alarm signal was cought */
08748 
08749          if (FD_ISSET(sock, &readfds)) {
08750             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08751 
08752             if (equal_ustring(buffer, "EXIT")) {
08753                /* normal exit */
08754                closesocket(sock);
08755                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08756             }
08757 
08758             if (status <= 0) {
08759                cm_msg(MERROR, "rpc_client_check",
08760                       "Connection broken to \"%s\" on host %s",
08761                       _client_connection[i].client_name, _client_connection[i].host_name);
08762 
08763                /* connection broken -> reset */
08764                closesocket(sock);
08765                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08766             }
08767          }
08768       }
08769 }
08770 
08771 
08772 /********************************************************************/
08773 INT rpc_server_connect(char *host_name, char *exp_name)
08774 /********************************************************************\
08775 
08776   Routine: rpc_server_connect
08777 
08778   Purpose: Extablish a network connection to a remote MIDAS
08779            server using a callback scheme.
08780 
08781   Input:
08782     char *host_name         IP address of host to connect to.
08783 
08784     INT  port               TPC port to connect to.
08785 
08786     char *exp_name          Name of experiment to connect to. By using
08787                             this name, several experiments (e.g. online
08788                             DAQ and offline analysis) can run simultan-
08789                             eously on the same host.
08790 
08791   Output:
08792     none
08793 
08794   Function value:
08795     RPC_SUCCESS              Successful completion
08796     RPC_NET_ERROR            Error in socket call
08797     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08798     CM_UNDEF_EXP             Undefined experiment on server
08799 
08800 \********************************************************************/
08801 {
08802    INT i, status, flag;
08803    struct sockaddr_in bind_addr;
08804    INT sock, lsock1, lsock2, lsock3;
08805    INT listen_port1, listen_port2, listen_port3;
08806    INT remote_hw_type, hw_type;
08807    int size;
08808    char str[200], version[32], v1[32];
08809    char local_prog_name[NAME_LENGTH];
08810    struct hostent *phe;
08811 
08812 #ifdef OS_WINNT
08813    {
08814       WSADATA WSAData;
08815 
08816       /* Start windows sockets */
08817       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08818          return RPC_NET_ERROR;
08819    }
08820 #endif
08821 
08822    /* check if local connection */
08823    if (host_name[0] == 0)
08824       return RPC_SUCCESS;
08825 
08826    /* register system functions */
08827    rpc_register_functions(rpc_get_internal_list(0), NULL);
08828 
08829    /* check if cm_connect_experiment was called */
08830    if (_client_name[0] == 0) {
08831       cm_msg(MERROR, "rpc_server_connect",
08832              "cm_connect_experiment/rpc_set_name not called");
08833       return RPC_NOT_REGISTERED;
08834    }
08835 
08836    /* check if connection already exists */
08837    if (_server_connection.send_sock != 0)
08838       return RPC_SUCCESS;
08839 
08840    strcpy(_server_connection.host_name, host_name);
08841    strcpy(_server_connection.exp_name, exp_name);
08842    _server_connection.transport = RPC_TCP;
08843    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08844 
08845    /* create new TCP sockets for listening */
08846    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08847    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08848    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08849    if (lsock3 == -1) {
08850       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08851       return RPC_NET_ERROR;
08852    }
08853 
08854    flag = 1;
08855    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08856    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08857    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08858 
08859    /* let OS choose any port number */
08860    memset(&bind_addr, 0, sizeof(bind_addr));
08861    bind_addr.sin_family = AF_INET;
08862    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08863    bind_addr.sin_port = 0;
08864 
08865    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08866    bind_addr.sin_port = 0;
08867    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08868    bind_addr.sin_port = 0;
08869    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08870    if (status < 0) {
08871       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08872       return RPC_NET_ERROR;
08873    }
08874 
08875    /* listen for connection */
08876    status = listen(lsock1, 1);
08877    status = listen(lsock2, 1);
08878    status = listen(lsock3, 1);
08879    if (status < 0) {
08880       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08881       return RPC_NET_ERROR;
08882    }
08883 
08884    /* find out which port OS has chosen */
08885    size = sizeof(bind_addr);
08886    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08887    listen_port1 = ntohs(bind_addr.sin_port);
08888    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08889    listen_port2 = ntohs(bind_addr.sin_port);
08890    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08891    listen_port3 = ntohs(bind_addr.sin_port);
08892 
08893    /* create a new socket for connecting to remote server */
08894    sock = socket(AF_INET, SOCK_STREAM, 0);
08895    if (sock == -1) {
08896       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08897       return RPC_NET_ERROR;
08898    }
08899 
08900    /* connect to remote node */
08901    memset(&bind_addr, 0, sizeof(bind_addr));
08902    bind_addr.sin_family = AF_INET;
08903    bind_addr.sin_addr.s_addr = 0;
08904    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08905 
08906 #ifdef OS_VXWORKS
08907    {
08908       INT host_addr;
08909 
08910       host_addr = hostGetByName(host_name);
08911       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08912    }
08913 #else
08914    phe = gethostbyname(host_name);
08915    if (phe == NULL) {
08916       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08917       return RPC_NET_ERROR;
08918    }
08919    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08920 #endif
08921 
08922 #ifdef OS_UNIX
08923    do {
08924       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08925 
08926       /* don't return if an alarm signal was cought */
08927    } while (status == -1 && errno == EINTR);
08928 #else
08929    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08930 #endif
08931 
08932    if (status != 0) {
08933 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08934       return RPC_NET_ERROR;
08935    }
08936 
08937    /* connect to experiment */
08938    if (exp_name[0] == 0)
08939       sprintf(str, "C %d %d %d %s Default",
08940               listen_port1, listen_port2, listen_port3, cm_get_version());
08941    else
08942       sprintf(str, "C %d %d %d %s %s",
08943               listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08944 
08945    send(sock, str, strlen(str) + 1, 0);
08946    i = recv_string(sock, str, sizeof(str), 10000);
08947    closesocket(sock);
08948    if (i <= 0) {
08949       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08950       return RPC_NET_ERROR;
08951    }
08952 
08953    status = version[0] = 0;
08954    sscanf(str, "%d %s", &status, version);
08955 
08956    if (status == 2) {
08957 /*  message "undefined experiment" should be displayed by application */
08958       return CM_UNDEF_EXP;
08959    }
08960 
08961    /* print warning if version patch level doesn't agree */
08962    strcpy(v1, version);
08963    if (strchr(v1, '.'))
08964       if (strchr(strchr(v1, '.') + 1, '.'))
08965          *strchr(strchr(v1, '.') + 1, '.') = 0;
08966 
08967    strcpy(str, cm_get_version());
08968    if (strchr(str, '.'))
08969       if (strchr(strchr(str, '.') + 1, '.'))
08970          *strchr(strchr(str, '.') + 1, '.') = 0;
08971 
08972    if (strcmp(v1, str) != 0) {
08973       sprintf(str, "remote MIDAS version %s differs from local version %s",
08974               version, cm_get_version());
08975       cm_msg(MERROR, "rpc_server_connect", str);
08976    }
08977 
08978    /* wait for callback on send and recv socket */
08979    size = sizeof(bind_addr);
08980    _server_connection.send_sock =
08981        accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08982 
08983    _server_connection.recv_sock =
08984        accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08985 
08986    _server_connection.event_sock =
08987        accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08988 
08989    if (_server_connection.send_sock == -1 ||
08990        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08991       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08992       return RPC_NET_ERROR;
08993    }
08994 
08995    closesocket(lsock1);
08996    closesocket(lsock2);
08997    closesocket(lsock3);
08998 
08999    /* set TCP_NODELAY option for better performance */
09000 #ifdef OS_VXWORKS
09001    flag = 1;
09002    setsockopt(_server_connection.send_sock,
09003               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
09004    setsockopt(_server_connection.event_sock,
09005               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
09006 #endif
09007 
09008    /* increase send buffer size to 64kB */
09009    flag = 0x10000;
09010    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
09011               (char *) &flag, sizeof(flag));
09012 
09013    /* send local computer info */
09014    rpc_get_name(local_prog_name);
09015    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
09016    sprintf(str, "%d %s", hw_type, local_prog_name);
09017 
09018    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
09019 
09020    /* receive remote computer info */
09021    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
09022    if (i <= 0) {
09023       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
09024       return RPC_NET_ERROR;
09025    }
09026 
09027    sscanf(str, "%d", &remote_hw_type);
09028    _server_connection.remote_hw_type = remote_hw_type;
09029 
09030    /* set dispatcher which receives database updates */
09031    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
09032                            (int (*)(void)) rpc_client_dispatch);
09033 
09034    return RPC_SUCCESS;
09035 }
09036 
09037 
09038 /********************************************************************/
09039 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
09040 /********************************************************************\
09041 
09042   Routine: rpc_client_disconnect
09043 
09044   Purpose: Close a rpc connection to a MIDAS client
09045 
09046   Input:
09047     HNDLE  hConn           Handle of connection
09048     BOOL   bShutdown       Shut down remote server if TRUE
09049 
09050   Output:
09051     none
09052 
09053   Function value:
09054    RPC_SUCCESS             Successful completion
09055 
09056 \********************************************************************/
09057 {
09058    INT i;
09059 
09060    if (hConn == -1) {
09061       /* close all open connections */
09062       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
09063          if (_client_connection[i].send_sock != 0)
09064             rpc_client_disconnect(i + 1, FALSE);
09065 
09066       /* close server connection from other clients */
09067       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09068          if (_server_acception[i].recv_sock) {
09069             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
09070             closesocket(_server_acception[i].recv_sock);
09071          }
09072    } else {
09073       /* notify server about exit */
09074 
09075       /* set FTCP mode (helps for rebooted VxWorks nodes) */
09076       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
09077       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
09078 
09079       /* close socket */
09080       if (_client_connection[hConn - 1].send_sock)
09081          closesocket(_client_connection[hConn - 1].send_sock);
09082 
09083       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
09084    }
09085 
09086    return RPC_SUCCESS;
09087 }
09088 
09089 
09090 /********************************************************************/
09091 INT rpc_server_disconnect()
09092 /********************************************************************\
09093 
09094   Routine: rpc_server_disconnect
09095 
09096   Purpose: Close a rpc connection to a MIDAS server and close all
09097            server connections from other clients
09098 
09099   Input:
09100     none
09101 
09102   Output:
09103     none
09104 
09105   Function value:
09106    RPC_SUCCESS             Successful completion
09107    RPC_NET_ERROR           Error in socket call
09108    RPC_NO_CONNECTION       Maximum number of connections reached
09109 
09110 \********************************************************************/
09111 {
09112    static int rpc_server_disconnect_recursion_level = 0;
09113 
09114    if (rpc_server_disconnect_recursion_level)
09115       return RPC_SUCCESS;
09116 
09117    rpc_server_disconnect_recursion_level = 1;
09118 
09119    /* flush remaining events */
09120    rpc_flush_event();
09121 
09122    /* notify server about exit */
09123    rpc_call(RPC_ID_EXIT);
09124 
09125    /* close sockets */
09126    closesocket(_server_connection.send_sock);
09127    closesocket(_server_connection.recv_sock);
09128    closesocket(_server_connection.event_sock);
09129 
09130    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
09131 
09132    rpc_server_disconnect_recursion_level = 0;
09133    return RPC_SUCCESS;
09134 }
09135 
09136 
09137 /********************************************************************/
09138 INT rpc_is_remote(void)
09139 /********************************************************************\
09140 
09141   Routine: rpc_is_remote
09142 
09143   Purpose: Return true if program is connected to a remote server
09144 
09145   Input:
09146    none
09147 
09148   Output:
09149     none
09150 
09151   Function value:
09152     INT    RPC connection index
09153 
09154 \********************************************************************/
09155 {
09156    return _server_connection.send_sock != 0;
09157 }
09158 
09159 
09160 /********************************************************************/
09161 INT rpc_get_server_acception(void)
09162 /********************************************************************\
09163 
09164   Routine: rpc_get_server_acception
09165 
09166   Purpose: Return actual RPC server connection index
09167 
09168   Input:
09169    none
09170 
09171   Output:
09172     none
09173 
09174   Function value:
09175     INT    RPC server connection index
09176 
09177 \********************************************************************/
09178 {
09179    return _server_acception_index;
09180 }
09181 
09182 
09183 /********************************************************************/
09184 INT rpc_set_server_acception(INT index)
09185 /********************************************************************\
09186 
09187   Routine: rpc_set_server_acception
09188 
09189   Purpose: Set actual RPC server connection index
09190 
09191   Input:
09192     INT  index              Server index
09193 
09194   Output:
09195     none
09196 
09197   Function value:
09198     RPC_SUCCESS             Successful completion
09199 
09200 \********************************************************************/
09201 {
09202    _server_acception_index = index;
09203    return RPC_SUCCESS;
09204 }
09205 
09206 
09207 /********************************************************************/
09208 INT rpc_get_option(HNDLE hConn, INT item)
09209 /********************************************************************\
09210 
09211   Routine: rpc_get_option
09212 
09213   Purpose: Get actual RPC option
09214 
09215   Input:
09216     HNDLE hConn             RPC connection handle
09217     INT   item              One of RPC_Oxxx
09218 
09219   Output:
09220     none
09221 
09222   Function value:
09223     INT                     Actual option
09224 
09225 \********************************************************************/
09226 {
09227    switch (item) {
09228    case RPC_OTIMEOUT:
09229       if (hConn == -1)
09230          return _server_connection.rpc_timeout;
09231       return _client_connection[hConn - 1].rpc_timeout;
09232 
09233    case RPC_OTRANSPORT:
09234       if (hConn == -1)
09235          return _server_connection.transport;
09236       return _client_connection[hConn - 1].transport;
09237 
09238    case RPC_OHW_TYPE:
09239       {
09240          INT tmp_type, size;
09241          DWORD dummy;
09242          unsigned char *p;
09243          float f;
09244          double d;
09245 
09246          tmp_type = 0;
09247 
09248          /* test pointer size */
09249          size = sizeof(p);
09250          if (size == 2)
09251             tmp_type |= DRI_16;
09252          if (size == 4)
09253             tmp_type |= DRI_32;
09254          if (size == 8)
09255             tmp_type |= DRI_64;
09256 
09257          /* test if little or big endian machine */
09258          dummy = 0x12345678;
09259          p = (unsigned char *) &dummy;
09260          if (*p == 0x78)
09261             tmp_type |= DRI_LITTLE_ENDIAN;
09262          else if (*p == 0x12)
09263             tmp_type |= DRI_BIG_ENDIAN;
09264          else
09265             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
09266 
09267          /* floating point format */
09268          f = (float) 1.2345;
09269          dummy = 0;
09270          memcpy(&dummy, &f, sizeof(f));
09271          if ((dummy & 0xFF) == 0x19 &&
09272              ((dummy >> 8) & 0xFF) == 0x04 &&
09273              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
09274             tmp_type |= DRF_IEEE;
09275          else if ((dummy & 0xFF) == 0x9E &&
09276                   ((dummy >> 8) & 0xFF) == 0x40 &&
09277                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
09278             tmp_type |= DRF_G_FLOAT;
09279          else
09280             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09281 
09282          d = (double) 1.2345;
09283          dummy = 0;
09284          memcpy(&dummy, &d, sizeof(f));
09285          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
09286              ((dummy >> 8) & 0xFF) == 0x97 &&
09287              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
09288             tmp_type |= DRF_IEEE;
09289          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
09290                   ((dummy >> 8) & 0xFF) == 0xC0 &&
09291                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
09292             tmp_type |= DRF_IEEE;
09293          else if ((dummy & 0xFF) == 0x13 &&
09294                   ((dummy >> 8) & 0xFF) == 0x40 &&
09295                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
09296             tmp_type |= DRF_G_FLOAT;
09297          else if ((dummy & 0xFF) == 0x9E &&
09298                   ((dummy >> 8) & 0xFF) == 0x40 &&
09299                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
09300             cm_msg(MERROR, "rpc_get_option",
09301                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
09302          else
09303             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09304 
09305          return tmp_type;
09306       }
09307 
09308    default:
09309       cm_msg(MERROR, "rpc_get_option", "invalid argument");
09310       break;
09311    }
09312 
09313    return 0;
09314 }
09315 
09316 /**dox***************************************************************/
09317 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09318 
09319 /********************************************************************/
09320 /**
09321 Set RPC option
09322 @param hConn              RPC connection handle
09323 @param item               One of RPC_Oxxx
09324 @param value              Value to set
09325 @return RPC_SUCCESS
09326 */
09327 INT rpc_set_option(HNDLE hConn, INT item, INT value)
09328 {
09329    switch (item) {
09330    case RPC_OTIMEOUT:
09331       if (hConn == -1)
09332          _server_connection.rpc_timeout = value;
09333       else
09334          _client_connection[hConn - 1].rpc_timeout = value;
09335       break;
09336 
09337    case RPC_OTRANSPORT:
09338       if (hConn == -1)
09339          _server_connection.transport = value;
09340       else
09341          _client_connection[hConn - 1].transport = value;
09342       break;
09343 
09344    case RPC_NODELAY:
09345       if (hConn == -1)
09346          setsockopt(_server_connection.send_sock, IPPROTO_TCP,
09347                     TCP_NODELAY, (char *) &value, sizeof(value));
09348       else
09349          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
09350                     TCP_NODELAY, (char *) &value, sizeof(value));
09351       break;
09352 
09353    default:
09354       cm_msg(MERROR, "rpc_set_option", "invalid argument");
09355       break;
09356    }
09357 
09358    return 0;
09359 }
09360 
09361 
09362 /**dox***************************************************************/
09363 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09364 
09365 /********************************************************************/
09366 PTYPE rpc_get_server_option(INT item)
09367 /********************************************************************\
09368 
09369   Routine: rpc_get_server_option
09370 
09371   Purpose: Get actual RPC option for server connection
09372 
09373   Input:
09374     INT  item               One of RPC_Oxxx
09375 
09376   Output:
09377     none
09378 
09379   Function value:
09380     INT                     Actual option
09381 
09382 \********************************************************************/
09383 {
09384    INT i;
09385 
09386    if (item == RPC_OSERVER_TYPE)
09387       return _server_type;
09388 
09389    if (item == RPC_OSERVER_NAME)
09390       return (PTYPE) _server_name;
09391 
09392    /* return 0 for local calls */
09393    if (_server_type == ST_NONE)
09394       return 0;
09395 
09396    /* check which connections belongs to caller */
09397    if (_server_type == ST_MTHREAD) {
09398       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09399          if (_server_acception[i].tid == ss_gettid())
09400             break;
09401    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09402       i = MAX(0, _server_acception_index - 1);
09403    else
09404       i = 0;
09405 
09406    switch (item) {
09407    case RPC_CONVERT_FLAGS:
09408       return _server_acception[i].convert_flags;
09409    case RPC_ODB_HANDLE:
09410       return _server_acception[i].odb_handle;
09411    case RPC_CLIENT_HANDLE:
09412       return _server_acception[i].client_handle;
09413    case RPC_SEND_SOCK:
09414       return _server_acception[i].send_sock;
09415    case RPC_WATCHDOG_TIMEOUT:
09416       return _server_acception[i].watchdog_timeout;
09417    }
09418 
09419    return 0;
09420 }
09421 
09422 
09423 /********************************************************************/
09424 INT rpc_set_server_option(INT item, PTYPE value)
09425 /********************************************************************\
09426 
09427   Routine: rpc_set_server_option
09428 
09429   Purpose: Set RPC option for server connection
09430 
09431   Input:
09432    INT  item               One of RPC_Oxxx
09433    INT  value              Value to set
09434 
09435   Output:
09436     none
09437 
09438   Function value:
09439     RPC_SUCCESS             Successful completion
09440 
09441 \********************************************************************/
09442 {
09443    INT i;
09444 
09445    if (item == RPC_OSERVER_TYPE) {
09446       _server_type = value;
09447       return RPC_SUCCESS;
09448    }
09449    if (item == RPC_OSERVER_NAME) {
09450       strcpy(_server_name, (char *) value);
09451       return RPC_SUCCESS;
09452    }
09453 
09454    /* check which connections belongs to caller */
09455    if (_server_type == ST_MTHREAD) {
09456       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09457          if (_server_acception[i].tid == ss_gettid())
09458             break;
09459    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09460       i = MAX(0, _server_acception_index - 1);
09461    else
09462       i = 0;
09463 
09464    switch (item) {
09465    case RPC_CONVERT_FLAGS:
09466       _server_acception[i].convert_flags = value;
09467       break;
09468    case RPC_ODB_HANDLE:
09469       _server_acception[i].odb_handle = value;
09470       break;
09471    case RPC_CLIENT_HANDLE:
09472       _server_acception[i].client_handle = value;
09473       break;
09474    case RPC_WATCHDOG_TIMEOUT:
09475       _server_acception[i].watchdog_timeout = value;
09476       break;
09477    }
09478 
09479    return RPC_SUCCESS;
09480 }
09481 
09482 
09483 /********************************************************************/
09484 INT rpc_get_name(char *name)
09485 /********************************************************************\
09486 
09487   Routine: rpc_get_name
09488 
09489   Purpose: Get name set by rpc_set_name
09490 
09491   Input:
09492     none
09493 
09494   Output:
09495     char*  name             The location pointed by *name receives a
09496                             copy of the _prog_name
09497 
09498   Function value:
09499     RPC_SUCCESS             Successful completion
09500 
09501 \********************************************************************/
09502 {
09503    strcpy(name, _client_name);
09504 
09505    return RPC_SUCCESS;
09506 }
09507 
09508 
09509 /********************************************************************/
09510 INT rpc_set_name(char *name)
09511 /********************************************************************\
09512 
09513   Routine: rpc_set_name
09514 
09515   Purpose: Set name of actual program for further rpc connections
09516 
09517   Input:
09518    char *name               Program name, up to NAME_LENGTH chars,
09519                             no blanks
09520 
09521   Output:
09522     none
09523 
09524   Function value:
09525     RPC_SUCCESS             Successful completion
09526 
09527 \********************************************************************/
09528 {
09529    strcpy(_client_name, name);
09530 
09531    return RPC_SUCCESS;
09532 }
09533 
09534 
09535 /********************************************************************/
09536 INT rpc_set_debug(void (*func) (char *), INT mode)
09537 /********************************************************************\
09538 
09539   Routine: rpc_set_debug
09540 
09541   Purpose: Set a function which is called on every RPC call to
09542            display the function name and parameters of the RPC
09543            call.
09544 
09545   Input:
09546    void *func(char*)        Pointer to function.
09547    INT  mode                Debug mode
09548 
09549   Output:
09550     none
09551 
09552   Function value:
09553     RPC_SUCCESS             Successful completion
09554 
09555 \********************************************************************/
09556 {
09557    _debug_print = func;
09558    _debug_mode = mode;
09559    return RPC_SUCCESS;
09560 }
09561 
09562 /********************************************************************/
09563 void rpc_debug_printf(char *format, ...)
09564 /********************************************************************\
09565 
09566   Routine: rpc_debug_print
09567 
09568   Purpose: Calls function set via rpc_set_debug to output a string.
09569 
09570   Input:
09571    char *str                Debug string
09572 
09573   Output:
09574     none
09575 
09576 \********************************************************************/
09577 {
09578    va_list argptr;
09579    char str[1000];
09580 
09581    if (_debug_mode) {
09582       va_start(argptr, format);
09583       vsprintf(str, (char *) format, argptr);
09584       va_end(argptr);
09585 
09586       if (_debug_print) {
09587          strcat(str, "\n");
09588          _debug_print(str);
09589       } else
09590          puts(str);
09591    }
09592 }
09593 
09594 /********************************************************************/
09595 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09596 {
09597    switch (arg_type) {
09598       /* On the stack, the minimum parameter size is sizeof(int).
09599          To avoid problems on little endian systems, treat all
09600          smaller parameters as int's */
09601    case TID_BYTE:
09602    case TID_SBYTE:
09603    case TID_CHAR:
09604    case TID_WORD:
09605    case TID_SHORT:
09606       *((int *) arg) = va_arg(*arg_ptr, int);
09607       break;
09608 
09609    case TID_INT:
09610    case TID_BOOL:
09611       *((INT *) arg) = va_arg(*arg_ptr, INT);
09612       break;
09613 
09614    case TID_DWORD:
09615       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09616       break;
09617 
09618       /* float variables are passed as double by the compiler */
09619    case TID_FLOAT:
09620       *((float *) arg) = (float) va_arg(*arg_ptr, double);
09621       break;
09622 
09623    case TID_DOUBLE:
09624       *((double *) arg) = va_arg(*arg_ptr, double);
09625       break;
09626 
09627    case TID_ARRAY:
09628       *((char **) arg) = va_arg(*arg_ptr, char *);
09629       break;
09630    }
09631 }
09632 
09633 
09634 /********************************************************************/
09635 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09636 /********************************************************************\
09637 
09638   Routine: rpc_client_call
09639 
09640   Purpose: Call a function on a MIDAS client
09641 
09642   Input:
09643     INT  hConn              Client connection
09644     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09645 
09646     ...                     variable argument list
09647 
09648   Output:
09649     (depends on argument list)
09650 
09651   Function value:
09652     RPC_SUCCESS             Successful completion
09653     RPC_NET_ERROR           Error in socket call
09654     RPC_NO_CONNECTION       No active connection
09655     RPC_TIMEOUT             Timeout in RPC call
09656     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09657     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09658 
09659 \********************************************************************/
09660 {
09661    va_list ap, aptmp;
09662    char arg[8], arg_tmp[8];
09663    INT arg_type, transport, rpc_timeout;
09664    INT i, index, status, rpc_index;
09665    INT param_size, arg_size, send_size;
09666    INT tid, flags;
09667    fd_set readfds;
09668    struct timeval timeout;
09669    char *param_ptr, str[80];
09670    BOOL bpointer, bbig;
09671    NET_COMMAND *nc;
09672    int send_sock;
09673 
09674    index = hConn - 1;
09675 
09676    if (_client_connection[index].send_sock == 0) {
09677       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09678       return RPC_NO_CONNECTION;
09679    }
09680 
09681    send_sock = _client_connection[index].send_sock;
09682    rpc_timeout = _client_connection[index].rpc_timeout;
09683    transport = _client_connection[index].transport;
09684 
09685    /* init network buffer */
09686    if (_net_send_buffer_size == 0) {
09687       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09688       if (_net_send_buffer == NULL) {
09689          cm_msg(MERROR, "rpc_client_call",
09690                 "not enough memory to allocate network buffer");
09691          return RPC_EXCEED_BUFFER;
09692       }
09693       _net_send_buffer_size = NET_BUFFER_SIZE;
09694    }
09695 
09696    nc = (NET_COMMAND *) _net_send_buffer;
09697    nc->header.routine_id = routine_id;
09698 
09699    if (transport == RPC_FTCP)
09700       nc->header.routine_id |= TCP_FAST;
09701 
09702    for (i = 0;; i++)
09703       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09704          break;
09705    rpc_index = i;
09706    if (rpc_list[i].id == 0) {
09707       sprintf(str, "invalid rpc ID (%d)", routine_id);
09708       cm_msg(MERROR, "rpc_client_call", str);
09709       return RPC_INVALID_ID;
09710    }
09711 
09712    /* examine variable argument list and convert it to parameter array */
09713    va_start(ap, routine_id);
09714 
09715    /* find out if we are on a big endian system */
09716    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09717 
09718    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09719       tid = rpc_list[rpc_index].param[i].tid;
09720       flags = rpc_list[rpc_index].param[i].flags;
09721 
09722       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09723           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09724           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09725 
09726       if (bpointer)
09727          arg_type = TID_ARRAY;
09728       else
09729          arg_type = tid;
09730 
09731       /* floats are passed as doubles, at least under NT */
09732       if (tid == TID_FLOAT && !bpointer)
09733          arg_type = TID_DOUBLE;
09734 
09735       /* get pointer to argument */
09736       rpc_va_arg(&ap, arg_type, arg);
09737 
09738       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09739       if (bbig) {
09740          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09741             arg[0] = arg[3];
09742          }
09743          if (tid == TID_WORD || tid == TID_SHORT) {
09744             arg[0] = arg[2];
09745             arg[1] = arg[3];
09746          }
09747       }
09748 
09749       if (flags & RPC_IN) {
09750          if (bpointer)
09751             arg_size = tid_size[tid];
09752          else
09753             arg_size = tid_size[arg_type];
09754 
09755          /* for strings, the argument size depends on the string length */
09756          if (tid == TID_STRING || tid == TID_LINK)
09757             arg_size = 1 + strlen((char *) *((char **) arg));
09758 
09759          /* for varibale length arrays, the size is given by
09760             the next parameter on the stack */
09761          if (flags & RPC_VARARRAY) {
09762             memcpy(&aptmp, &ap, sizeof(ap));
09763             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09764 
09765             if (flags & RPC_OUT)
09766                arg_size = *((INT *) * ((void **) arg_tmp));
09767             else
09768                arg_size = *((INT *) arg_tmp);
09769 
09770             *((INT *) param_ptr) = ALIGN8(arg_size);
09771             param_ptr += ALIGN8(sizeof(INT));
09772          }
09773 
09774          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09775             arg_size = rpc_list[rpc_index].param[i].n;
09776 
09777          /* always align parameter size */
09778          param_size = ALIGN8(arg_size);
09779 
09780          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09781             cm_msg(MERROR, "rpc_client_call",
09782                    "parameters (%d) too large for network buffer (%d)",
09783                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09784             return RPC_EXCEED_BUFFER;
09785          }
09786 
09787          if (bpointer)
09788             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09789          else {
09790             /* floats are passed as doubles on most systems */
09791             if (tid != TID_FLOAT)
09792                memcpy(param_ptr, arg, arg_size);
09793             else
09794                *((float *) param_ptr) = (float) *((double *) arg);
09795          }
09796 
09797          param_ptr += param_size;
09798 
09799       }
09800    }
09801 
09802    va_end(ap);
09803 
09804    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09805 
09806    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09807 
09808    /* in FAST TCP mode, only send call and return immediately */
09809    if (transport == RPC_FTCP) {
09810       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09811 
09812       if (i != send_size) {
09813          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09814          return RPC_NET_ERROR;
09815       }
09816 
09817       return RPC_SUCCESS;
09818    }
09819 
09820    /* in TCP mode, send and wait for reply on send socket */
09821    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09822    if (i != send_size) {
09823       cm_msg(MERROR, "rpc_client_call",
09824              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09825              rpc_list[rpc_index].name, _client_connection[index].host_name);
09826       return RPC_NET_ERROR;
09827    }
09828 
09829    /* make some timeout checking */
09830    if (rpc_timeout > 0) {
09831       FD_ZERO(&readfds);
09832       FD_SET(send_sock, &readfds);
09833 
09834       timeout.tv_sec = rpc_timeout / 1000;
09835       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09836 
09837       do {
09838          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09839 
09840          /* if an alarm signal was cought, restart select with reduced timeout */
09841          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09842             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09843 
09844       } while (status == -1);   /* dont return if an alarm signal was cought */
09845 
09846       if (!FD_ISSET(send_sock, &readfds)) {
09847          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09848                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09849 
09850          /* disconnect to avoid that the reply to this rpc_call comes at
09851             the next rpc_call */
09852          rpc_client_disconnect(hConn, FALSE);
09853 
09854          return RPC_TIMEOUT;
09855       }
09856    }
09857 
09858    /* receive result on send socket */
09859    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09860 
09861    if (i <= 0) {
09862       cm_msg(MERROR, "rpc_client_call",
09863              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09864              rpc_list[rpc_index].name, _client_connection[index].host_name);
09865       return RPC_NET_ERROR;
09866    }
09867 
09868    /* extract result variables and place it to argument list */
09869    status = nc->header.routine_id;
09870 
09871    va_start(ap, routine_id);
09872 
09873    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09874       tid = rpc_list[rpc_index].param[i].tid;
09875       flags = rpc_list[rpc_index].param[i].flags;
09876 
09877       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09878           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09879           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09880 
09881       if (bpointer)
09882          arg_type = TID_ARRAY;
09883       else
09884          arg_type = rpc_list[rpc_index].param[i].tid;
09885 
09886       if (tid == TID_FLOAT && !bpointer)
09887          arg_type = TID_DOUBLE;
09888 
09889       rpc_va_arg(&ap, arg_type, arg);
09890 
09891       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09892          tid = rpc_list[rpc_index].param[i].tid;
09893          flags = rpc_list[rpc_index].param[i].flags;
09894 
09895          arg_size = tid_size[tid];
09896 
09897          if (tid == TID_STRING || tid == TID_LINK)
09898             arg_size = strlen((char *) (param_ptr)) + 1;
09899 
09900          if (flags & RPC_VARARRAY) {
09901             arg_size = *((INT *) param_ptr);
09902             param_ptr += ALIGN8(sizeof(INT));
09903          }
09904 
09905          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09906             arg_size = rpc_list[rpc_index].param[i].n;
09907 
09908          /* return parameters are always pointers */
09909          if (*((char **) arg))
09910             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09911 
09912          /* parameter size is always aligned */
09913          param_size = ALIGN8(arg_size);
09914 
09915          param_ptr += param_size;
09916       }
09917    }
09918 
09919    va_end(ap);
09920 
09921    return status;
09922 }
09923 
09924 
09925 /********************************************************************/
09926 INT rpc_call(const INT routine_id, ...)
09927 /********************************************************************\
09928 
09929   Routine: rpc_call
09930 
09931   Purpose: Call a function on a MIDAS server
09932 
09933   Input:
09934     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09935 
09936     ...                     variable argument list
09937 
09938   Output:
09939     (depends on argument list)
09940 
09941   Function value:
09942     RPC_SUCCESS             Successful completion
09943     RPC_NET_ERROR           Error in socket call
09944     RPC_NO_CONNECTION       No active connection
09945     RPC_TIMEOUT             Timeout in RPC call
09946     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09947     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09948 
09949 \********************************************************************/
09950 {
09951    va_list ap, aptmp;
09952    char arg[8], arg_tmp[8];
09953    INT arg_type, transport, rpc_timeout;
09954    INT i, index, status;
09955    INT param_size, arg_size, send_size;
09956    INT tid, flags;
09957    fd_set readfds;
09958    struct timeval timeout;
09959    char *param_ptr, str[80];
09960    BOOL bpointer, bbig;
09961    NET_COMMAND *nc;
09962    int send_sock;
09963 
09964    send_sock = _server_connection.send_sock;
09965    transport = _server_connection.transport;
09966    rpc_timeout = _server_connection.rpc_timeout;
09967 
09968    /* init network buffer */
09969    if (_net_send_buffer_size == 0) {
09970       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09971       if (_net_send_buffer == NULL) {
09972          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09973          return RPC_EXCEED_BUFFER;
09974       }
09975       _net_send_buffer_size = NET_BUFFER_SIZE;
09976    }
09977 
09978    nc = (NET_COMMAND *) _net_send_buffer;
09979    nc->header.routine_id = routine_id;
09980 
09981    if (transport == RPC_FTCP)
09982       nc->header.routine_id |= TCP_FAST;
09983 
09984    for (i = 0;; i++)
09985       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09986          break;
09987    index = i;
09988    if (rpc_list[i].id == 0) {
09989       sprintf(str, "invalid rpc ID (%d)", routine_id);
09990       cm_msg(MERROR, "rpc_call", str);
09991       return RPC_INVALID_ID;
09992    }
09993 
09994    /* examine variable argument list and convert it to parameter array */
09995    va_start(ap, routine_id);
09996 
09997    /* find out if we are on a big endian system */
09998    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09999 
10000    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
10001       tid = rpc_list[index].param[i].tid;
10002       flags = rpc_list[index].param[i].flags;
10003 
10004       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
10005           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
10006           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
10007 
10008       if (bpointer)
10009          arg_type = TID_ARRAY;
10010       else
10011          arg_type = tid;
10012 
10013       /* floats are passed as doubles, at least under NT */
10014       if (tid == TID_FLOAT && !bpointer)
10015          arg_type = TID_DOUBLE;
10016 
10017       /* get pointer to argument */
10018       rpc_va_arg(&ap, arg_type, arg);
10019 
10020       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
10021       if (bbig) {
10022          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
10023             arg[0] = arg[3];
10024          }
10025          if (tid == TID_WORD || tid == TID_SHORT) {
10026             arg[0] = arg[2];
10027             arg[1] = arg[3];
10028          }
10029       }
10030 
10031       if (flags & RPC_IN) {
10032          if (bpointer)
10033             arg_size = tid_size[tid];
10034          else
10035             arg_size = tid_size[arg_type];
10036 
10037          /* for strings, the argument size depends on the string length */
10038          if (tid == TID_STRING || tid == TID_LINK)
10039             arg_size = 1 + strlen((char *) *((char **) arg));
10040 
10041          /* for varibale length arrays, the size is given by
10042             the next parameter on the stack */
10043          if (flags & RPC_VARARRAY) {
10044             memcpy(&aptmp, &ap, sizeof(ap));
10045             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
10046 
10047             if (flags & RPC_OUT)
10048                arg_size = *((INT *) * ((void **) arg_tmp));
10049             else
10050                arg_size = *((INT *) arg_tmp);
10051 
10052             *((INT *) param_ptr) = ALIGN8(arg_size);
10053             param_ptr += ALIGN8(sizeof(INT));
10054          }
10055 
10056          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
10057             arg_size = rpc_list[index].param[i].n;
10058 
10059          /* always align parameter size */
10060          param_size = ALIGN8(arg_size);
10061 
10062          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
10063             cm_msg(MERROR, "rpc_call",
10064                    "parameters (%d) too large for network buffer (%d)",
10065                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
10066             return RPC_EXCEED_BUFFER;
10067          }
10068 
10069          if (bpointer)
10070             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
10071          else {
10072             /* floats are passed as doubles on most systems */
10073             if (tid != TID_FLOAT)
10074                memcpy(param_ptr, arg, arg_size);
10075             else
10076                *((float *) param_ptr) = (float) *((double *) arg);
10077          }
10078 
10079          param_ptr += param_size;
10080 
10081       }
10082    }
10083 
10084    va_end(ap);
10085 
10086    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
10087 
10088    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10089 
10090    /* in FAST TCP mode, only send call and return immediately */
10091    if (transport == RPC_FTCP) {
10092       i = send_tcp(send_sock, (char *) nc, send_size, 0);
10093 
10094       if (i != send_size) {
10095          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
10096          return RPC_NET_ERROR;
10097       }
10098 
10099       return RPC_SUCCESS;
10100    }
10101 
10102    /* in TCP mode, send and wait for reply on send socket */
10103    i = send_tcp(send_sock, (char *) nc, send_size, 0);
10104    if (i != send_size) {
10105       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
10106       return RPC_NET_ERROR;
10107    }
10108 
10109    /* make some timeout checking */
10110    if (rpc_timeout > 0) {
10111       FD_ZERO(&readfds);
10112       FD_SET(send_sock, &readfds);
10113 
10114       timeout.tv_sec = rpc_timeout / 1000;
10115       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
10116 
10117       do {
10118          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
10119 
10120          /* if an alarm signal was cought, restart select with reduced timeout */
10121          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
10122             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
10123 
10124       } while (status == -1);   /* dont return if an alarm signal was cought */
10125 
10126       if (!FD_ISSET(send_sock, &readfds)) {
10127          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"",
10128                 rpc_list[index].name);
10129 
10130          /* disconnect to avoid that the reply to this rpc_call comes at
10131             the next rpc_call */
10132          rpc_server_disconnect();
10133 
10134          return RPC_TIMEOUT;
10135       }
10136    }
10137 
10138    /* receive result on send socket */
10139    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
10140 
10141    if (i <= 0) {
10142       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"",
10143              rpc_list[index].name);
10144       return RPC_NET_ERROR;
10145    }
10146 
10147    /* extract result variables and place it to argument list */
10148    status = nc->header.routine_id;
10149 
10150    va_start(ap, routine_id);
10151 
10152    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
10153       tid = rpc_list[index].param[i].tid;
10154       flags = rpc_list[index].param[i].flags;
10155 
10156       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
10157           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
10158           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
10159 
10160       if (bpointer)
10161          arg_type = TID_ARRAY;
10162       else
10163          arg_type = rpc_list[index].param[i].tid;
10164 
10165       if (tid == TID_FLOAT && !bpointer)
10166          arg_type = TID_DOUBLE;
10167 
10168       rpc_va_arg(&ap, arg_type, arg);
10169 
10170       if (rpc_list[index].param[i].flags & RPC_OUT) {
10171          tid = rpc_list[index].param[i].tid;
10172          arg_size = tid_size[tid];
10173 
10174          if (tid == TID_STRING || tid == TID_LINK)
10175             arg_size = strlen((char *) (param_ptr)) + 1;
10176 
10177          if (flags & RPC_VARARRAY) {
10178             arg_size = *((INT *) param_ptr);
10179             param_ptr += ALIGN8(sizeof(INT));
10180          }
10181 
10182          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
10183             arg_size = rpc_list[index].param[i].n;
10184 
10185          /* return parameters are always pointers */
10186          if (*((char **) arg))
10187             memcpy((void *) *((char **) arg), param_ptr, arg_size);
10188 
10189          /* parameter size is always aligned */
10190          param_size = ALIGN8(arg_size);
10191 
10192          param_ptr += param_size;
10193       }
10194    }
10195 
10196    va_end(ap);
10197 
10198    return status;
10199 }
10200 
10201 
10202 /********************************************************************/
10203 INT rpc_set_opt_tcp_size(INT tcp_size)
10204 {
10205    INT old;
10206 
10207    old = _opt_tcp_size;
10208    _opt_tcp_size = tcp_size;
10209    return old;
10210 }
10211 
10212 INT rpc_get_opt_tcp_size()
10213 {
10214    return _opt_tcp_size;
10215 }
10216 
10217 /**dox***************************************************************/
10218 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10219 
10220 /********************************************************************/
10221 /**
10222 Fast send_event routine which bypasses the RPC layer and
10223            sends the event directly at the TCP level.
10224 @param buffer_handle      Handle of the buffer to send the event to.
10225                             Must be obtained via bm_open_buffer.
10226 @param source            Address of the event to send. It must have
10227                             a proper event header.
10228 @param buf_size           Size of event in bytes with header.
10229 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
10230                             function returns immediately if it cannot
10231                             send the event over the network. In SYNC
10232                             mode, it waits until the packet is sent
10233                             (blocking).
10234 
10235 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
10236         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
10237 */
10238 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
10239 {
10240    INT i;
10241    NET_COMMAND *nc;
10242    unsigned long flag;
10243    BOOL would_block = 0;
10244    DWORD aligned_buf_size;
10245 
10246    aligned_buf_size = ALIGN8(buf_size);
10247 
10248    if (aligned_buf_size !=
10249        (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
10250       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
10251       return BM_INVALID_PARAM;
10252    }
10253    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
10254       cm_msg(MERROR, "rpc_send_event",
10255              "event size (%d) larger than maximum event size (%d)",
10256              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
10257       return RPC_EXCEED_BUFFER;
10258    }
10259 
10260    if (!rpc_is_remote())
10261       return bm_send_event(buffer_handle, source, buf_size, async_flag);
10262 
10263    /* init network buffer */
10264    if (!_tcp_buffer)
10265       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
10266    if (!_tcp_buffer) {
10267       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
10268       return RPC_EXCEED_BUFFER;
10269    }
10270 
10271    /* check if not enough space in TCP buffer */
10272    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
10273        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
10274       /* set socket to nonblocking IO */
10275       if (async_flag == ASYNC) {
10276          flag = 1;
10277 #ifdef OS_VXWORKS
10278          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10279 #else
10280          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10281 #endif
10282       }
10283 
10284       i = send_tcp(_server_connection.send_sock,
10285                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10286 
10287       if (i < 0)
10288 #ifdef OS_WINNT
10289          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
10290 #else
10291          would_block = (errno == EWOULDBLOCK);
10292 #endif
10293 
10294       /* set socket back to blocking IO */
10295       if (async_flag == ASYNC) {
10296          flag = 0;
10297 #ifdef OS_VXWORKS
10298          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10299 #else
10300          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10301 #endif
10302       }
10303 
10304       /* increment read pointer */
10305       if (i > 0)
10306          _tcp_rp += i;
10307 
10308       /* check if whole buffer is sent */
10309       if (_tcp_rp == _tcp_wp)
10310          _tcp_rp = _tcp_wp = 0;
10311 
10312       if (i < 0 && !would_block) {
10313          printf("send_tcp() returned %d\n", i);
10314          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
10315          return RPC_NET_ERROR;
10316       }
10317 
10318       /* return if buffer is not emptied */
10319       if (_tcp_wp > 0)
10320          return BM_ASYNC_RETURN;
10321    }
10322 
10323    nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
10324    nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
10325    nc->header.param_size = 4 * 8 + aligned_buf_size;
10326 
10327    /* assemble parameters manually */
10328    *((INT *) (&nc->param[0])) = buffer_handle;
10329    *((INT *) (&nc->param[8])) = buf_size;
10330 
10331    /* send events larger than optimal buffer size directly */
10332    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
10333       /* send header */
10334       send_tcp(_server_connection.send_sock,
10335                _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
10336 
10337       /* send data */
10338       send_tcp(_server_connection.send_sock, (char *) source, aligned_buf_size, 0);
10339 
10340       /* send last two parameters */
10341       *((INT *) (&nc->param[0])) = buf_size;
10342       *((INT *) (&nc->param[8])) = 0;
10343       send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
10344    } else {
10345       /* copy event */
10346       memcpy(&nc->param[16], source, buf_size);
10347 
10348       /* last two parameters (buf_size and async_flag */
10349       *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
10350       *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
10351 
10352       _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10353    }
10354 
10355    return RPC_SUCCESS;
10356 }
10357 
10358 
10359 
10360 /**dox***************************************************************/
10361 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10362 
10363 /********************************************************************/
10364 int rpc_get_send_sock()
10365 /********************************************************************\
10366 
10367   Routine: rpc_get_send_sock
10368 
10369   Purpose: Return send socket to MIDAS server. Used by MFE.C for
10370            optimized event sending.
10371 
10372   Input:
10373     none
10374 
10375   Output:
10376     none
10377 
10378   Function value:
10379     int    socket
10380 
10381 \********************************************************************/
10382 {
10383    return _server_connection.send_sock;
10384 }
10385 
10386 
10387 /********************************************************************/
10388 int rpc_get_event_sock()
10389 /********************************************************************\
10390 
10391   Routine: rpc_get_event_sock
10392 
10393   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
10394            optimized event sending.
10395 
10396   Input:
10397     none
10398 
10399   Output:
10400     none
10401 
10402   Function value:
10403     int    socket
10404 
10405 \********************************************************************/
10406 {
10407    return _server_connection.event_sock;
10408 }
10409 
10410 /**dox***************************************************************/
10411 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10412 
10413 /********************************************************************/
10414 /**
10415 Send event residing in the TCP cache buffer filled by
10416            rpc_send_event. This routine should be called when a
10417            run is stopped.
10418 
10419 @return RPC_SUCCESS, RPC_NET_ERROR
10420 */
10421 INT rpc_flush_event()
10422 {
10423    INT i;
10424 
10425    if (!rpc_is_remote())
10426       return RPC_SUCCESS;
10427 
10428    /* return if rpc_send_event was not called */
10429    if (!_tcp_buffer || _tcp_wp == 0)
10430       return RPC_SUCCESS;
10431 
10432    /* empty TCP buffer */
10433    if (_tcp_wp > 0) {
10434       i = send_tcp(_server_connection.send_sock,
10435                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10436 
10437       if (i != _tcp_wp - _tcp_rp) {
10438          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
10439          return RPC_NET_ERROR;
10440       }
10441    }
10442 
10443    _tcp_rp = _tcp_wp = 0;
10444 
10445    return RPC_SUCCESS;
10446 }
10447 
10448 /**dox***************************************************************/
10449 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10450 
10451 /********************************************************************/
10452 
10453 typedef struct {
10454    int transition;
10455    int run_number;
10456    time_t trans_time;
10457    int sequence_number;
10458 } TR_FIFO;
10459 
10460 static TR_FIFO tr_fifo[10];
10461 static int trf_wp, trf_rp;
10462 
10463 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
10464 /********************************************************************\
10465 
10466   Routine: rpc_transition_dispatch
10467 
10468   Purpose: Gets called when a transition function was registered and
10469            a transition occured. Internal use only.
10470 
10471   Input:
10472     INT    index            RPC function ID
10473     void   *prpc_param      RPC parameters
10474 
10475   Output:
10476     none
10477 
10478   Function value:
10479     INT    return value from called user routine
10480 
10481 \********************************************************************/
10482 {
10483    INT status, i;
10484 
10485    /* erase error string */
10486    *(CSTRING(2)) = 0;
10487 
10488    if (index == RPC_RC_TRANSITION) {
10489       for (i = 0; i < MAX_TRANSITIONS; i++)
10490          if (_trans_table[i].transition == CINT(0) &&
10491              _trans_table[i].sequence_number == CINT(4))
10492             break;
10493 
10494       /* call registerd function */
10495       if (i < MAX_TRANSITIONS) {
10496          if (_trans_table[i].func)
10497             /* execute callback if defined */
10498             status = _trans_table[i].func(CINT(1), CSTRING(2));
10499          else {
10500             /* store transition in FIFO */
10501             tr_fifo[trf_wp].transition = CINT(0);
10502             tr_fifo[trf_wp].run_number = CINT(1);
10503             tr_fifo[trf_wp].trans_time = time(NULL);
10504             tr_fifo[trf_wp].sequence_number = CINT(4);
10505             trf_wp = (trf_wp + 1) % 10;
10506             status = RPC_SUCCESS;
10507          }
10508       } else
10509          status = RPC_SUCCESS;
10510 
10511    } else {
10512       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
10513       status = RPC_INVALID_ID;
10514    }
10515 
10516    return status;
10517 }
10518 
10519 /********************************************************************/
10520 int cm_query_transition(int *transition, int *run_number, int *trans_time)
10521 /********************************************************************\
10522 
10523   Routine: cm_query_transition
10524 
10525   Purpose: Query system if transition has occured. Normally, one 
10526            registers callbacks for transitions via 
10527            cm_register_transition. In some environments however,
10528            callbacks are not possible. In that case one spciefies
10529            a NULL pointer as the callback routine and can query
10530            transitions "manually" by calling this functions. A small
10531            FIFO takes care that no transition is lost if this functions
10532            did not get called between some transitions.
10533 
10534   Output:
10535     INT   *transition        Type of transition, one of TR_xxx
10536     INT   *run_nuber         Run number for transition
10537     time_t *trans_time       Time (in UNIX time) of transition
10538 
10539   Function value:
10540     FALSE  No transition occured since last call
10541     TRUE   Transition occured
10542 
10543 \********************************************************************/
10544 {
10545 
10546    if (trf_wp == trf_rp)
10547       return FALSE;
10548 
10549    if (transition)
10550       *transition = tr_fifo[trf_rp].transition;
10551 
10552    if (run_number)
10553       *run_number = tr_fifo[trf_rp].run_number;
10554 
10555    if (trans_time)
10556       *trans_time = (int) tr_fifo[trf_rp].trans_time;
10557 
10558    trf_rp = (trf_rp + 1) % 10;
10559 
10560    return TRUE;
10561 }
10562 
10563 /********************************************************************\
10564 *                        server functions                            *
10565 \********************************************************************/
10566 
10567 
10568 /********************************************************************/
10569 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10570                     INT * remaining)
10571 /********************************************************************\
10572 
10573   Routine: recv_tcp_server
10574 
10575   Purpose: TCP receive routine with local cache. To speed up network
10576            performance, a 64k buffer is read in at once and split into
10577            several RPC command on successive calls to recv_tcp_server.
10578            Therefore, the number of recv() calls is minimized.
10579 
10580            This routine is ment to be called by the server process.
10581            Clients should call recv_tcp instead.
10582 
10583   Input:
10584     INT   index              Index of server connection
10585     DWORD buffer_size        Size of the buffer in bytes.
10586     INT   flags              Flags passed to recv()
10587     INT   convert_flags      Convert flags needed for big/little
10588                              endian conversion
10589 
10590   Output:
10591     char  *buffer            Network receive buffer.
10592     INT   *remaining         Remaining data in cache
10593 
10594   Function value:
10595     INT                      Same as recv()
10596 
10597 \********************************************************************/
10598 {
10599    INT size, param_size;
10600    NET_COMMAND *nc;
10601    INT write_ptr, read_ptr, misalign;
10602    char *net_buffer;
10603    INT copied, status;
10604    INT sock;
10605 
10606    sock = _server_acception[index].recv_sock;
10607 
10608    if (flags & MSG_PEEK) {
10609       status = recv(sock, buffer, buffer_size, flags);
10610       if (status == -1)
10611          cm_msg(MERROR, "recv_tcp_server",
10612                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10613                 errno, strerror(errno));
10614       return status;
10615    }
10616 
10617    if (!_server_acception[index].net_buffer) {
10618       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10619          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
10620       else
10621          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
10622 
10623       _server_acception[index].net_buffer =
10624           (char *) M_MALLOC(_server_acception[index].net_buffer_size);
10625       _server_acception[index].write_ptr = 0;
10626       _server_acception[index].read_ptr = 0;
10627       _server_acception[index].misalign = 0;
10628    }
10629    if (!_server_acception[index].net_buffer) {
10630       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
10631       return -1;
10632    }
10633 
10634    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10635       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10636       return -1;
10637    }
10638 
10639    copied = 0;
10640    param_size = -1;
10641 
10642    write_ptr = _server_acception[index].write_ptr;
10643    read_ptr = _server_acception[index].read_ptr;
10644    misalign = _server_acception[index].misalign;
10645    net_buffer = _server_acception[index].net_buffer;
10646 
10647    do {
10648       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10649          if (param_size == -1) {
10650             if (copied > 0) {
10651                /* assemble split header */
10652                memcpy(buffer + copied, net_buffer + read_ptr,
10653                       (INT) sizeof(NET_COMMAND_HEADER) - copied);
10654                nc = (NET_COMMAND *) (buffer);
10655             } else
10656                nc = (NET_COMMAND *) (net_buffer + read_ptr);
10657 
10658             param_size = (INT) nc->header.param_size;
10659 
10660             if (_server_acception[index].convert_flags)
10661                rpc_convert_single(&param_size, TID_DWORD, 0,
10662                                   _server_acception[index].convert_flags);
10663          }
10664 
10665          /* check if parameters fit in buffer */
10666          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10667             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10668             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
10669             return -1;
10670          }
10671 
10672          /* check if we have all parameters in buffer */
10673          if (write_ptr - read_ptr >=
10674              param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10675             break;
10676       }
10677 
10678       /* not enough data, so copy partially and get new */
10679       size = write_ptr - read_ptr;
10680 
10681       if (size > 0) {
10682          memcpy(buffer + copied, net_buffer + read_ptr, size);
10683          copied += size;
10684          read_ptr = write_ptr;
10685       }
10686 #ifdef OS_UNIX
10687       do {
10688          write_ptr =
10689              recv(sock, net_buffer + misalign,
10690                   _server_acception[index].net_buffer_size - 8, flags);
10691 
10692          /* don't return if an alarm signal was cought */
10693       } while (write_ptr == -1 && errno == EINTR);
10694 #else
10695       write_ptr =
10696           recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8,
10697                flags);
10698 #endif
10699 
10700       /* abort if connection broken */
10701       if (write_ptr <= 0) {
10702          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
10703                 write_ptr, errno, strerror(errno));
10704 
10705          if (remaining)
10706             *remaining = 0;
10707 
10708          return write_ptr;
10709       }
10710 
10711       read_ptr = misalign;
10712       write_ptr += misalign;
10713 
10714       misalign = write_ptr % 8;
10715    } while (TRUE);
10716 
10717    /* copy rest of parameters */
10718    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10719    memcpy(buffer + copied, net_buffer + read_ptr, size);
10720    read_ptr += size;
10721 
10722    if (remaining) {
10723       /* don't keep rpc_server_receive in an infinite loop */
10724       if (write_ptr - read_ptr < param_size)
10725          *remaining = 0;
10726       else
10727          *remaining = write_ptr - read_ptr;
10728    }
10729 
10730    _server_acception[index].write_ptr = write_ptr;
10731    _server_acception[index].read_ptr = read_ptr;
10732    _server_acception[index].misalign = misalign;
10733 
10734    return size + copied;
10735 }
10736 
10737 
10738 /********************************************************************/
10739 INT recv_tcp_check(int sock)
10740 /********************************************************************\
10741 
10742   Routine: recv_tcp_check
10743 
10744   Purpose: Check if in TCP receive buffer associated with sock is
10745            some data. Called by ss_suspend.
10746 
10747   Input:
10748     INT   sock               TCP receive socket
10749 
10750   Output:
10751     none
10752 
10753   Function value:
10754     INT   count              Number of bytes remaining in TCP buffer
10755 
10756 \********************************************************************/
10757 {
10758    INT index;
10759 
10760    /* figure out to which connection socket belongs */
10761    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10762       if (_server_acception[index].recv_sock == sock)
10763          break;
10764 
10765    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10766 }
10767 
10768 
10769 /********************************************************************/
10770 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10771                       INT * remaining)
10772 /********************************************************************\
10773 
10774   Routine: recv_event_server
10775 
10776   Purpose: TCP event receive routine with local cache. To speed up
10777            network performance, a 64k buffer is read in at once and
10778            split into several RPC command on successive calls to
10779            recv_event_server. Therefore, the number of recv() calls
10780            is minimized.
10781 
10782            This routine is ment to be called by the server process.
10783            Clients should call recv_tcp instead.
10784 
10785   Input:
10786     INT   index              Index of server connection
10787     DWORD buffer_size        Size of the buffer in bytes.
10788     INT   flags              Flags passed to recv()
10789     INT   convert_flags      Convert flags needed for big/little
10790                              endian conversion
10791 
10792   Output:
10793     char  *buffer            Network receive buffer.
10794     INT   *remaining         Remaining data in cache
10795 
10796   Function value:
10797     INT                      Same as recv()
10798 
10799 \********************************************************************/
10800 {
10801    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10802    EVENT_HEADER *pevent;
10803    INT write_ptr, read_ptr, misalign;
10804    char *net_buffer;
10805    INT copied, status;
10806    INT sock;
10807    RPC_SERVER_ACCEPTION *psa;
10808 
10809    psa = &_server_acception[index];
10810    sock = psa->event_sock;
10811 
10812    if (flags & MSG_PEEK) {
10813       status = recv(sock, buffer, buffer_size, flags);
10814       if (status == -1)
10815          cm_msg(MERROR, "recv_event_server",
10816                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10817                 errno, strerror(errno));
10818       return status;
10819    }
10820 
10821    if (!psa->ev_net_buffer) {
10822       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10823          psa->net_buffer_size = NET_TCP_SIZE;
10824       else
10825          psa->net_buffer_size = NET_BUFFER_SIZE;
10826 
10827       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10828       psa->ev_write_ptr = 0;
10829       psa->ev_read_ptr = 0;
10830       psa->ev_misalign = 0;
10831    }
10832    if (!psa->ev_net_buffer) {
10833       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10834       return -1;
10835    }
10836 
10837    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10838 
10839    if ((INT) buffer_size < header_size) {
10840       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10841       return -1;
10842    }
10843 
10844    copied = 0;
10845    event_size = -1;
10846 
10847    write_ptr = psa->ev_write_ptr;
10848    read_ptr = psa->ev_read_ptr;
10849    misalign = psa->ev_misalign;
10850    net_buffer = psa->ev_net_buffer;
10851 
10852    do {
10853       if (write_ptr - read_ptr >= header_size - copied) {
10854          if (event_size == -1) {
10855             if (copied > 0) {
10856                /* assemble split header */
10857                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10858                pbh = (INT *) buffer;
10859             } else
10860                pbh = (INT *) (net_buffer + read_ptr);
10861 
10862             pevent = (EVENT_HEADER *) (pbh + 1);
10863 
10864             event_size = pevent->data_size;
10865             if (psa->convert_flags)
10866                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10867 
10868             aligned_event_size = ALIGN8(event_size);
10869          }
10870 
10871          /* check if data part fits in buffer */
10872          if ((INT) buffer_size < aligned_event_size + header_size) {
10873             cm_msg(MERROR, "recv_event_server",
10874                    "parameters too large for network buffer");
10875             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10876             return -1;
10877          }
10878 
10879          /* check if we have whole event in buffer */
10880          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10881             break;
10882       }
10883 
10884       /* not enough data, so copy partially and get new */
10885       size = write_ptr - read_ptr;
10886 
10887       if (size > 0) {
10888          memcpy(buffer + copied, net_buffer + read_ptr, size);
10889          copied += size;
10890          read_ptr = write_ptr;
10891       }
10892 #ifdef OS_UNIX
10893       do {
10894          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10895 
10896          /* don't return if an alarm signal was cought */
10897       } while (write_ptr == -1 && errno == EINTR);
10898 #else
10899       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10900 #endif
10901 
10902       /* abort if connection broken */
10903       if (write_ptr <= 0) {
10904          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10905                 write_ptr, errno, strerror(errno));
10906 
10907          if (remaining)
10908             *remaining = 0;
10909 
10910          return write_ptr;
10911       }
10912 
10913       read_ptr = misalign;
10914       write_ptr += misalign;
10915 
10916       misalign = write_ptr % 8;
10917    } while (TRUE);
10918 
10919    /* copy rest of event */
10920    size = aligned_event_size + header_size - copied;
10921    if (size > 0) {
10922       memcpy(buffer + copied, net_buffer + read_ptr, size);
10923       read_ptr += size;
10924    }
10925 
10926    if (remaining)
10927       *remaining = write_ptr - read_ptr;
10928 
10929    psa->ev_write_ptr = write_ptr;
10930    psa->ev_read_ptr = read_ptr;
10931    psa->ev_misalign = misalign;
10932 
10933    /* convert header little endian/big endian */
10934    if (psa->convert_flags) {
10935       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10936 
10937       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10938       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10939       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10940       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10941       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10942       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10943    }
10944 
10945    return header_size + event_size;
10946 }
10947 
10948 
10949 /********************************************************************/
10950 INT recv_event_check(int sock)
10951 /********************************************************************\
10952 
10953   Routine: recv_event_check
10954 
10955   Purpose: Check if in TCP event receive buffer associated with sock
10956            is some data. Called by ss_suspend.
10957 
10958   Input:
10959     INT   sock               TCP receive socket
10960 
10961   Output:
10962     none
10963 
10964   Function value:
10965     INT   count              Number of bytes remaining in TCP buffer
10966 
10967 \********************************************************************/
10968 {
10969    INT index;
10970 
10971    /* figure out to which connection socket belongs */
10972    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10973       if (_server_acception[index].event_sock == sock)
10974          break;
10975 
10976    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10977 }
10978 
10979 
10980 /********************************************************************/
10981 INT rpc_register_server(INT server_type, char *name, INT * port,
10982                         INT(*func) (INT, void **))
10983 /********************************************************************\
10984 
10985   Routine: rpc_register_server
10986 
10987   Purpose: Register the calling process as a MIDAS RPC server. Note
10988            that cm_connnect_experiment must be called prior to any call of
10989            rpc_register_server.
10990 
10991   Input:
10992     INT   server_type       One of the following constants:
10993                             ST_SINGLE: register a single process server
10994                             ST_MTHREAD: for each connection, start
10995                                         a new thread to serve it
10996                             ST_MPROCESS: for each connection, start
10997                                          a new process to server it
10998                             ST_SUBPROCESS: the routine was called from
10999                                            a multi process server
11000                             ST_REMOTE: register a client program server
11001                                        connected to the ODB
11002     char  *name             Name of .EXE file to start in MPROCESS mode
11003     INT   *port             TCP port for listen. NULL if listen as main
11004                             server (MIDAS_TCP_PORT is then used). If *port=0,
11005                             the OS chooses a free port and returns it. If
11006                             *port != 0, this port is used.
11007     INT   *func             Default dispatch function
11008 
11009   Output:
11010     INT   *port             Port under which server is listening.
11011 
11012   Function value:
11013     RPC_SUCCESS             Successful completion
11014     RPC_NET_ERROR           Error in socket call
11015     RPC_NOT_REGISTERED      cm_connect_experiment was not called
11016 
11017 \********************************************************************/
11018 {
11019    struct sockaddr_in bind_addr;
11020    INT status, flag;
11021    int size;
11022 
11023 #ifdef OS_WINNT
11024    {
11025       WSADATA WSAData;
11026 
11027       /* Start windows sockets */
11028       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
11029          return RPC_NET_ERROR;
11030    }
11031 #endif
11032 
11033    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
11034 
11035    /* register system functions */
11036    rpc_register_functions(rpc_get_internal_list(0), func);
11037 
11038    if (name != NULL)
11039       rpc_set_server_option(RPC_OSERVER_NAME, (PTYPE) name);
11040 
11041    /* in subprocess mode, don't start listener */
11042    if (server_type == ST_SUBPROCESS)
11043       return RPC_SUCCESS;
11044 
11045    /* create a socket for listening */
11046    _lsock = socket(AF_INET, SOCK_STREAM, 0);
11047    if (_lsock == -1) {
11048       cm_msg(MERROR, "rpc_register_server", "socket() failed");
11049       return RPC_NET_ERROR;
11050    }
11051 
11052    /* reuse address, needed if previous server stopped (30s timeout!) */
11053    flag = 1;
11054    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
11055    if (status < 0) {
11056       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
11057       return RPC_NET_ERROR;
11058    }
11059 
11060    /* bind local node name and port to socket */
11061    memset(&bind_addr, 0, sizeof(bind_addr));
11062    bind_addr.sin_family = AF_INET;
11063    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
11064 
11065    if (!port)
11066       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
11067    else
11068       bind_addr.sin_port = htons((short) (*port));
11069 
11070    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11071    if (status < 0) {
11072       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
11073       return RPC_NET_ERROR;
11074    }
11075 
11076    /* listen for connection */
11077 #ifdef OS_MSDOS
11078    status = listen(_lsock, 1);
11079 #else
11080    status = listen(_lsock, SOMAXCONN);
11081 #endif
11082    if (status < 0) {
11083       cm_msg(MERROR, "rpc_register_server", "listen() failed");
11084       return RPC_NET_ERROR;
11085    }
11086 
11087    /* return port wich OS has choosen */
11088    if (port && *port == 0) {
11089       size = sizeof(bind_addr);
11090       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
11091       *port = ntohs(bind_addr.sin_port);
11092    }
11093 
11094    /* define callbacks for ss_suspend */
11095    if (server_type == ST_REMOTE)
11096       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
11097    else
11098       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
11099 
11100    return RPC_SUCCESS;
11101 }
11102 
11103 
11104 /********************************************************************/
11105 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
11106 /********************************************************************\
11107 
11108   Routine: rpc_execute
11109 
11110   Purpose: Execute a RPC command received over the network
11111 
11112   Input:
11113     INT  sock               TCP socket to which the result should be
11114                             send back
11115 
11116     char *buffer            Command buffer
11117     INT  convert_flags      Flags for data conversion
11118 
11119   Output:
11120     none
11121 
11122   Function value:
11123     RPC_SUCCESS             Successful completion
11124     RPC_INVALID_ID          Invalid routine_id received
11125     RPC_NET_ERROR           Error in socket call
11126     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11127     RPC_SHUTDOWN            Shutdown requested
11128     SS_ABORT                TCP connection broken
11129     SS_EXIT                 TCP connection closed
11130 
11131 \********************************************************************/
11132 {
11133    INT i, index, routine_id, status;
11134    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11135    INT tid, flags;
11136    NET_COMMAND *nc_in, *nc_out;
11137    INT param_size, max_size;
11138    void *prpc_param[20];
11139    char str[1024], debug_line[1024];
11140 
11141 /* return buffer must be auto for multi-thread servers */
11142    char return_buffer[NET_BUFFER_SIZE];
11143 
11144 
11145    /* extract pointer array to parameters */
11146    nc_in = (NET_COMMAND *) buffer;
11147    nc_out = (NET_COMMAND *) return_buffer;
11148 
11149    /* convert header format (byte swapping) */
11150    if (convert_flags) {
11151       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
11152       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
11153    }
11154 
11155    /* no result return in FAST TCP mode */
11156    if (nc_in->header.routine_id & TCP_FAST)
11157       sock = 0;
11158 
11159    /* find entry in rpc_list */
11160    routine_id = nc_in->header.routine_id & ~TCP_FAST;
11161 
11162    for (i = 0;; i++)
11163       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
11164          break;
11165    index = i;
11166    if (rpc_list[i].id == 0) {
11167       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
11168       return RPC_INVALID_ID;
11169    }
11170 
11171    in_param_ptr = nc_in->param;
11172    out_param_ptr = nc_out->param;
11173 
11174    sprintf(debug_line, "%s(", rpc_list[index].name);
11175 
11176    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11177       tid = rpc_list[index].param[i].tid;
11178       flags = rpc_list[index].param[i].flags;
11179 
11180       if (flags & RPC_IN) {
11181          param_size = ALIGN8(tid_size[tid]);
11182 
11183          if (tid == TID_STRING || tid == TID_LINK)
11184             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11185 
11186          if (flags & RPC_VARARRAY) {
11187             /* for arrays, the size is stored as a INT in front of the array */
11188             param_size = *((INT *) in_param_ptr);
11189             if (convert_flags)
11190                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
11191             param_size = ALIGN8(param_size);
11192 
11193             in_param_ptr += ALIGN8(sizeof(INT));
11194          }
11195 
11196          if (tid == TID_STRUCT)
11197             param_size = ALIGN8(rpc_list[index].param[i].n);
11198 
11199          prpc_param[i] = in_param_ptr;
11200 
11201          /* convert data format */
11202          if (convert_flags) {
11203             if (flags & RPC_VARARRAY)
11204                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
11205             else
11206                rpc_convert_data(in_param_ptr, tid, flags,
11207                                 rpc_list[index].param[i].n * tid_size[tid],
11208                                 convert_flags);
11209          }
11210 
11211          db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
11212          if (rpc_list[index].param[i].tid == TID_STRING) {
11213             /* check for long strings (db_create_record...) */
11214             if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11215                strcat(debug_line, "\"");
11216                strcat(debug_line, str);
11217                strcat(debug_line, "\"");
11218             } else
11219                strcat(debug_line, "...");
11220          } else
11221             strcat(debug_line, str);
11222 
11223          in_param_ptr += param_size;
11224       }
11225 
11226       if (flags & RPC_OUT) {
11227          param_size = ALIGN8(tid_size[tid]);
11228 
11229          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11230             /* save maximum array length */
11231             max_size = *((INT *) in_param_ptr);
11232             if (convert_flags)
11233                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
11234             max_size = ALIGN8(max_size);
11235 
11236             *((INT *) out_param_ptr) = max_size;
11237 
11238             /* save space for return array length */
11239             out_param_ptr += ALIGN8(sizeof(INT));
11240 
11241             /* use maximum array length from input */
11242             param_size += max_size;
11243          }
11244 
11245          if (rpc_list[index].param[i].tid == TID_STRUCT)
11246             param_size = ALIGN8(rpc_list[index].param[i].n);
11247 
11248          if ((PTYPE) out_param_ptr - (PTYPE) nc_out + param_size > NET_BUFFER_SIZE) {
11249             cm_msg(MERROR, "rpc_execute",
11250                    "return parameters (%d) too large for network buffer (%d)",
11251                    (PTYPE) out_param_ptr - (PTYPE) nc_out + param_size, NET_BUFFER_SIZE);
11252             return RPC_EXCEED_BUFFER;
11253          }
11254 
11255          /* if parameter goes both directions, copy input to output */
11256          if (rpc_list[index].param[i].flags & RPC_IN)
11257             memcpy(out_param_ptr, prpc_param[i], param_size);
11258 
11259          if (_debug_print && !(flags & RPC_IN))
11260             strcat(debug_line, "-");
11261 
11262          prpc_param[i] = out_param_ptr;
11263          out_param_ptr += param_size;
11264       }
11265 
11266       if (rpc_list[index].param[i + 1].tid)
11267          strcat(debug_line, ", ");
11268    }
11269 
11270    strcat(debug_line, ")");
11271    rpc_debug_printf(debug_line);
11272 
11273    last_param_ptr = out_param_ptr;
11274 
11275   /*********************************\
11276   *   call dispatch function        *
11277   \*********************************/
11278    if (rpc_list[index].dispatch)
11279       status = rpc_list[index].dispatch(routine_id, prpc_param);
11280    else
11281       status = RPC_INVALID_ID;
11282 
11283    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
11284        routine_id == RPC_ID_WATCHDOG)
11285       status = RPC_SUCCESS;
11286 
11287    /* return immediately for closed down client connections */
11288    if (!sock && routine_id == RPC_ID_EXIT)
11289       return SS_EXIT;
11290 
11291    if (!sock && routine_id == RPC_ID_SHUTDOWN)
11292       return RPC_SHUTDOWN;
11293 
11294    /* Return if TCP connection broken */
11295    if (status == SS_ABORT)
11296       return SS_ABORT;
11297 
11298    /* if sock == 0, we are in FTCP mode and may not sent results */
11299    if (!sock)
11300       return RPC_SUCCESS;
11301 
11302    /* compress variable length arrays */
11303    out_param_ptr = nc_out->param;
11304    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11305       if (rpc_list[index].param[i].flags & RPC_OUT) {
11306          tid = rpc_list[index].param[i].tid;
11307          flags = rpc_list[index].param[i].flags;
11308          param_size = ALIGN8(tid_size[tid]);
11309 
11310          if (tid == TID_STRING) {
11311             max_size = *((INT *) out_param_ptr);
11312             param_size = strlen((char *) prpc_param[i]) + 1;
11313             param_size = ALIGN8(param_size);
11314 
11315             /* move string ALIGN8(sizeof(INT)) left */
11316             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
11317 
11318             /* move remaining parameters to end of string */
11319             memcpy(out_param_ptr + param_size,
11320                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11321                    (PTYPE) last_param_ptr -
11322                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11323          }
11324 
11325          if (flags & RPC_VARARRAY) {
11326             /* store array length at current out_param_ptr */
11327             max_size = *((INT *) out_param_ptr);
11328             param_size = *((INT *) prpc_param[i + 1]);
11329             *((INT *) out_param_ptr) = param_size;
11330             if (convert_flags)
11331                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
11332 
11333             out_param_ptr += ALIGN8(sizeof(INT));
11334 
11335             param_size = ALIGN8(param_size);
11336 
11337             /* move remaining parameters to end of array */
11338             memcpy(out_param_ptr + param_size,
11339                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11340                    (PTYPE) last_param_ptr -
11341                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11342          }
11343 
11344          if (tid == TID_STRUCT)
11345             param_size = ALIGN8(rpc_list[index].param[i].n);
11346 
11347          /* convert data format */
11348          if (convert_flags) {
11349             if (flags & RPC_VARARRAY)
11350                rpc_convert_data(out_param_ptr, tid,
11351                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11352                                 param_size, convert_flags);
11353             else
11354                rpc_convert_data(out_param_ptr, tid,
11355                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11356                                 rpc_list[index].param[i].n * tid_size[tid],
11357                                 convert_flags);
11358          }
11359 
11360          out_param_ptr += param_size;
11361       }
11362 
11363    /* send return parameters */
11364    param_size = (PTYPE) out_param_ptr - (PTYPE) nc_out->param;
11365    nc_out->header.routine_id = status;
11366    nc_out->header.param_size = param_size;
11367 
11368    /* convert header format (byte swapping) if necessary */
11369    if (convert_flags) {
11370       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
11371                          RPC_OUTGOING, convert_flags);
11372       rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
11373                          RPC_OUTGOING, convert_flags);
11374    }
11375 
11376    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
11377 
11378    if (status < 0) {
11379       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11380       return RPC_NET_ERROR;
11381    }
11382 
11383    /* print return buffer */
11384 /*
11385   printf("Return buffer, ID %d:\n", routine_id);
11386   for (i=0; i<param_size ; i++)
11387     {
11388     status = (char) nc_out->param[i];
11389     printf("%02X ", status);
11390     if (i%8 == 7)
11391       printf("\n");
11392     }
11393 */
11394    /* return SS_EXIT if RPC_EXIT is called */
11395    if (routine_id == RPC_ID_EXIT)
11396       return SS_EXIT;
11397 
11398    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11399    if (routine_id == RPC_ID_SHUTDOWN)
11400       return RPC_SHUTDOWN;
11401 
11402    return RPC_SUCCESS;
11403 }
11404 
11405 
11406 /********************************************************************/
11407 INT rpc_execute_ascii(INT sock, char *buffer)
11408 /********************************************************************\
11409 
11410   Routine: rpc_execute_ascii
11411 
11412   Purpose: Execute a RPC command received over the network in ASCII
11413            mode
11414 
11415   Input:
11416     INT  sock               TCP socket to which the result should be
11417                             send back
11418 
11419     char *buffer            Command buffer
11420 
11421   Output:
11422     none
11423 
11424   Function value:
11425     RPC_SUCCESS             Successful completion
11426     RPC_INVALID_ID          Invalid routine_id received
11427     RPC_NET_ERROR           Error in socket call
11428     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11429     RPC_SHUTDOWN            Shutdown requested
11430     SS_ABORT                TCP connection broken
11431     SS_EXIT                 TCP connection closed
11432 
11433 \********************************************************************/
11434 {
11435 #define ASCII_BUFFER_SIZE 64500
11436 #define N_APARAM           1024
11437 
11438    INT i, j, index, status, index_in;
11439    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11440    INT routine_id, tid, flags, array_tid, n_param;
11441    INT param_size, item_size, num_values;
11442    void *prpc_param[20];
11443    char *arpc_param[N_APARAM], *pc;
11444    char str[1024], debug_line[1024];
11445    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
11446    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
11447    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
11448 
11449    /* parse arguments */
11450    arpc_param[0] = buffer;
11451    for (i = 1; i < N_APARAM; i++) {
11452       arpc_param[i] = strchr(arpc_param[i - 1], '&');
11453       if (arpc_param[i] == NULL)
11454          break;
11455       *arpc_param[i] = 0;
11456       arpc_param[i]++;
11457    }
11458 
11459    /* decode '%' */
11460    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
11461       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
11462          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
11463             str[0] = pc[1];
11464             str[1] = pc[2];
11465             str[2] = 0;
11466             sscanf(str, "%02X", &i);
11467 
11468             *pc++ = i;
11469             while (pc[2]) {
11470                pc[0] = pc[2];
11471                pc++;
11472             }
11473          }
11474       }
11475 
11476    /* find entry in rpc_list */
11477    for (i = 0;; i++)
11478       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
11479          break;
11480    index = i;
11481    routine_id = rpc_list[i].id;
11482    if (rpc_list[i].id == 0) {
11483       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
11484       return RPC_INVALID_ID;
11485    }
11486 
11487    in_param_ptr = buffer1;
11488    out_param_ptr = buffer2;
11489    index_in = 1;
11490 
11491    sprintf(debug_line, "%s(", rpc_list[index].name);
11492 
11493    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11494       tid = rpc_list[index].param[i].tid;
11495       flags = rpc_list[index].param[i].flags;
11496 
11497       if (flags & RPC_IN) {
11498          if (flags & RPC_VARARRAY) {
11499             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
11500 
11501             prpc_param[i] = in_param_ptr;
11502             for (j = 0; j < n_param; j++) {
11503                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
11504                in_param_ptr += param_size;
11505             }
11506             in_param_ptr = (char *) ALIGN8(((PTYPE) in_param_ptr));
11507 
11508             strcat(debug_line, "<array>");
11509          } else {
11510             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
11511             param_size = ALIGN8(param_size);
11512 
11513             if (tid == TID_STRING || tid == TID_LINK)
11514                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11515 
11516             /*
11517                if (tid == TID_STRUCT)
11518                param_size = ALIGN8( rpc_list[index].param[i].n );
11519              */
11520             prpc_param[i] = in_param_ptr;
11521 
11522             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
11523             if (rpc_list[index].param[i].tid == TID_STRING) {
11524                /* check for long strings (db_create_record...) */
11525                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11526                   strcat(debug_line, "\"");
11527                   strcat(debug_line, str);
11528                   strcat(debug_line, "\"");
11529                } else
11530                   strcat(debug_line, "...");
11531             } else
11532                strcat(debug_line, str);
11533 
11534             in_param_ptr += param_size;
11535          }
11536 
11537          if ((PTYPE) in_param_ptr - (PTYPE) buffer1 > ASCII_BUFFER_SIZE) {
11538             cm_msg(MERROR, "rpc_ascii_execute",
11539                    "parameters (%d) too large for network buffer (%d)", param_size,
11540                    ASCII_BUFFER_SIZE);
11541             return RPC_EXCEED_BUFFER;
11542          }
11543 
11544       }
11545 
11546       if (flags & RPC_OUT) {
11547          param_size = ALIGN8(tid_size[tid]);
11548 
11549          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11550             /* reserve maximum array length */
11551             param_size = atoi(arpc_param[index_in]);
11552             param_size = ALIGN8(param_size);
11553          }
11554 
11555 /*
11556       if (rpc_list[index].param[i].tid == TID_STRUCT)
11557         param_size = ALIGN8( rpc_list[index].param[i].n );
11558 */
11559          if ((PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size > ASCII_BUFFER_SIZE) {
11560             cm_msg(MERROR, "rpc_execute",
11561                    "return parameters (%d) too large for network buffer (%d)",
11562                    (PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size,
11563                    ASCII_BUFFER_SIZE);
11564             return RPC_EXCEED_BUFFER;
11565          }
11566 
11567          /* if parameter goes both directions, copy input to output */
11568          if (rpc_list[index].param[i].flags & RPC_IN)
11569             memcpy(out_param_ptr, prpc_param[i], param_size);
11570 
11571          if (!(flags & RPC_IN))
11572             strcat(debug_line, "-");
11573 
11574          prpc_param[i] = out_param_ptr;
11575          out_param_ptr += param_size;
11576       }
11577 
11578       if (rpc_list[index].param[i + 1].tid)
11579          strcat(debug_line, ", ");
11580    }
11581 
11582    strcat(debug_line, ")");
11583    rpc_debug_printf(debug_line);
11584 
11585    last_param_ptr = out_param_ptr;
11586 
11587    /*********************************\
11588    *   call dispatch function        *
11589    \*********************************/
11590 
11591    if (rpc_list[index].dispatch)
11592       status = rpc_list[index].dispatch(routine_id, prpc_param);
11593    else
11594       status = RPC_INVALID_ID;
11595 
11596    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
11597        routine_id == RPC_ID_WATCHDOG)
11598       status = RPC_SUCCESS;
11599 
11600    /* Return if TCP connection broken */
11601    if (status == SS_ABORT)
11602       return SS_ABORT;
11603 
11604    /* if sock == 0, we are in FTCP mode and may not sent results */
11605    if (!sock)
11606       return RPC_SUCCESS;
11607 
11608    /* send return status */
11609    out_param_ptr = return_buffer;
11610    sprintf(out_param_ptr, "%d", status);
11611    out_param_ptr += strlen(out_param_ptr);
11612 
11613    /* convert return parameters */
11614    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11615       if (rpc_list[index].param[i].flags & RPC_OUT) {
11616          *out_param_ptr++ = '&';
11617 
11618          tid = rpc_list[index].param[i].tid;
11619          flags = rpc_list[index].param[i].flags;
11620          param_size = ALIGN8(tid_size[tid]);
11621 
11622          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11623             strcpy(out_param_ptr, (char *) prpc_param[i]);
11624             param_size = strlen((char *) prpc_param[i]);
11625          }
11626 
11627          else if (flags & RPC_VARARRAY) {
11628             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
11629                param_size = *((INT *) prpc_param[i + 1]);
11630                /* write number of bytes to output */
11631                sprintf(out_param_ptr, "%d", param_size);
11632                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
11633                memcpy(out_param_ptr, prpc_param[i], param_size);
11634                out_param_ptr += param_size;
11635                *out_param_ptr = 0;
11636             } else {
11637                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
11638                   param_size = *((INT *) prpc_param[i + 1]);
11639                   array_tid = *((INT *) prpc_param[i + 2]);
11640                   num_values = *((INT *) prpc_param[i + 3]);
11641                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
11642                   param_size = *((INT *) prpc_param[i + 1]);
11643                   array_tid = *((INT *) prpc_param[i + 3]);
11644                   num_values = 1;
11645                } else if (rpc_list[index].id == RPC_HS_READ) {
11646                   param_size = *((INT *) prpc_param[i + 1]);
11647                   if (i == 6) {
11648                      array_tid = TID_DWORD;
11649                      num_values = param_size / sizeof(DWORD);
11650                   } else {
11651                      array_tid = *((INT *) prpc_param[10]);
11652                      num_values = *((INT *) prpc_param[11]);
11653                   }
11654                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11655 
11656                   param_size = *((INT *) prpc_param[i + 1]);
11657                   array_tid = tid;
11658                   if (tid == TID_STRING)
11659                      num_values = param_size / NAME_LENGTH;
11660                   else
11661                      num_values = param_size / tid_size[tid];
11662                }
11663 
11664                /* derive size of individual item */
11665                if (array_tid == TID_STRING)
11666                   item_size = param_size / num_values;
11667                else
11668                   item_size = tid_size[array_tid];
11669 
11670                /* write number of elements to output */
11671                sprintf(out_param_ptr, "%d", num_values);
11672                out_param_ptr += strlen(out_param_ptr);
11673 
11674                /* write array of values to output */
11675                for (j = 0; j < num_values; j++) {
11676                   *out_param_ptr++ = '&';
11677                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
11678                   out_param_ptr += strlen(out_param_ptr);
11679                }
11680             }
11681          }
11682 
11683 /*
11684       else if (tid == TID_STRUCT)
11685         param_size = ALIGN8( rpc_list[index].param[i].n );
11686 */
11687          else
11688             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11689 
11690          out_param_ptr += strlen(out_param_ptr);
11691 
11692          if ((PTYPE) out_param_ptr - (PTYPE) return_buffer > ASCII_BUFFER_SIZE) {
11693             cm_msg(MERROR, "rpc_execute",
11694                    "return parameter (%d) too large for network buffer (%d)", param_size,
11695                    ASCII_BUFFER_SIZE);
11696             return RPC_EXCEED_BUFFER;
11697          }
11698       }
11699 
11700    /* send return parameters */
11701    param_size = (PTYPE) out_param_ptr - (PTYPE) return_buffer + 1;
11702 
11703    status = send_tcp(sock, return_buffer, param_size, 0);
11704 
11705    if (status < 0) {
11706       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11707       return RPC_NET_ERROR;
11708    }
11709 
11710    /* print return buffer */
11711    if (strlen(return_buffer) > sizeof(debug_line)) {
11712       memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11713       strcat(debug_line, "...");
11714    } else
11715       sprintf(debug_line, "-> %s", return_buffer);
11716    rpc_debug_printf(debug_line);
11717 
11718    /* return SS_EXIT if RPC_EXIT is called */
11719    if (routine_id == RPC_ID_EXIT)
11720       return SS_EXIT;
11721 
11722    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11723    if (routine_id == RPC_ID_SHUTDOWN)
11724       return RPC_SHUTDOWN;
11725 
11726    return RPC_SUCCESS;
11727 }
11728 
11729 
11730 /********************************************************************/
11731 INT rpc_server_accept(int lsock)
11732 /********************************************************************\
11733 
11734   Routine: rpc_server_accept
11735 
11736   Purpose: Accept new incoming connections
11737 
11738   Input:
11739     INT    lscok            Listen socket
11740 
11741   Output:
11742     none
11743 
11744   Function value:
11745     RPC_SUCCESS             Successful completion
11746     RPC_NET_ERROR           Error in socket call
11747     RPC_CONNCLOSED          Connection was closed
11748     RPC_SHUTDOWN            Listener shutdown
11749     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11750 
11751 \********************************************************************/
11752 {
11753    INT index, i;
11754    int size, status;
11755    char command, version[32], v1[32];
11756    INT sock, port1, port2, port3;
11757    struct sockaddr_in acc_addr;
11758    struct hostent *phe;
11759    char str[100];
11760    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11761    char debug_str[30];
11762    char *argv[10];
11763    char net_buffer[256];
11764    struct linger ling;
11765 
11766    static struct callback_addr callback;
11767 
11768    if (lsock > 0) {
11769       size = sizeof(acc_addr);
11770       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11771 
11772       if (sock == -1)
11773          return RPC_NET_ERROR;
11774    } else {
11775       /* lsock is stdin -> already connected from inetd */
11776 
11777       size = sizeof(acc_addr);
11778       sock = lsock;
11779       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11780    }
11781 
11782    /* receive string with timeout */
11783    i = recv_string(sock, net_buffer, 256, 10000);
11784    rpc_debug_printf("Received command: %s", net_buffer);
11785 
11786    if (i > 0) {
11787       command = (char) toupper(net_buffer[0]);
11788 
11789       switch (command) {
11790       case 'S':
11791 
11792          /*----------- shutdown listener ----------------------*/
11793          closesocket(sock);
11794          return RPC_SHUTDOWN;
11795 
11796       case 7:
11797          ss_shell(sock);
11798          closesocket(sock);
11799          break;
11800 
11801       case 'I':
11802 
11803          /*----------- return available experiments -----------*/
11804          cm_scan_experiments();
11805          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11806             sprintf(str, "%s", exptab[i].name);
11807             send(sock, str, strlen(str) + 1, 0);
11808          }
11809          send(sock, "", 1, 0);
11810          closesocket(sock);
11811          break;
11812 
11813       case 'C':
11814 
11815          /*----------- connect to experiment -----------*/
11816 
11817          /* get callback information */
11818          callback.experiment[0] = 0;
11819          port1 = port2 = version[0] = 0;
11820          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11821          strcpy(callback.experiment,
11822                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1,
11823                        ' ') + 1);
11824 
11825          /* print warning if version patch level doesn't agree */
11826          strcpy(v1, version);
11827          if (strchr(v1, '.'))
11828             if (strchr(strchr(v1, '.') + 1, '.'))
11829                *strchr(strchr(v1, '.') + 1, '.') = 0;
11830 
11831          strcpy(str, cm_get_version());
11832          if (strchr(str, '.'))
11833             if (strchr(strchr(str, '.') + 1, '.'))
11834                *strchr(strchr(str, '.') + 1, '.') = 0;
11835 
11836          if (strcmp(v1, str) != 0) {
11837             sprintf(str, "client MIDAS version %s differs from local version %s",
11838                     version, cm_get_version());
11839             cm_msg(MERROR, "rpc_server_accept", str);
11840 
11841             sprintf(str, "received string: %s", net_buffer + 2);
11842             cm_msg(MERROR, "rpc_server_accept", str);
11843          }
11844 
11845          callback.host_port1 = (short) port1;
11846          callback.host_port2 = (short) port2;
11847          callback.host_port3 = (short) port3;
11848          callback.debug = _debug_mode;
11849 
11850          /* get the name of the remote host */
11851 #ifdef OS_VXWORKS
11852          {
11853             INT status;
11854             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11855             if (status != 0) {
11856                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11857                break;
11858             }
11859          }
11860 #else
11861          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11862          if (phe == NULL) {
11863             /* use IP number instead */
11864             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11865          } else
11866             strcpy(callback.host_name, phe->h_name);
11867 #endif
11868 
11869          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11870             /* update experiment definition */
11871             cm_scan_experiments();
11872 
11873             /* lookup experiment */
11874             if (equal_ustring(callback.experiment, "Default"))
11875                index = 0;
11876             else
11877                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11878                   if (equal_ustring(callback.experiment, exptab[index].name))
11879                      break;
11880 
11881             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11882                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11883                cm_msg(MERROR, "rpc_server_accept", str);
11884 
11885                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11886                closesocket(sock);
11887                break;
11888             }
11889 
11890             strcpy(callback.directory, exptab[index].directory);
11891             strcpy(callback.user, exptab[index].user);
11892 
11893             /* create a new process */
11894             sprintf(host_port1_str, "%d", callback.host_port1);
11895             sprintf(host_port2_str, "%d", callback.host_port2);
11896             sprintf(host_port3_str, "%d", callback.host_port3);
11897             sprintf(debug_str, "%d", callback.debug);
11898 
11899             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11900             argv[1] = callback.host_name;
11901             argv[2] = host_port1_str;
11902             argv[3] = host_port2_str;
11903             argv[4] = host_port3_str;
11904             argv[5] = debug_str;
11905             argv[6] = callback.experiment;
11906             argv[7] = callback.directory;
11907             argv[8] = callback.user;
11908             argv[9] = NULL;
11909 
11910             rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
11911                              argv[0], argv[1], argv[2], argv[3], argv[4],
11912                              argv[5], argv[6], argv[7], argv[8], argv[9]);
11913 
11914             status = ss_spawnv(P_NOWAIT,
11915                                (char *) rpc_get_server_option(RPC_OSERVER_NAME), argv);
11916 
11917             if (status != SS_SUCCESS) {
11918                rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
11919 
11920                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11921                send(sock, str, strlen(str) + 1, 0);
11922                closesocket(sock);
11923                break;
11924             }
11925 
11926             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11927             send(sock, str, strlen(str) + 1, 0);
11928             closesocket(sock);
11929          } else {
11930             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11931             send(sock, str, strlen(str) + 1, 0);
11932             closesocket(sock);
11933          }
11934 
11935          /* look for next free entry */
11936          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11937             if (_server_acception[index].recv_sock == 0)
11938                break;
11939          if (index == MAX_RPC_CONNECTION)
11940             return RPC_NET_ERROR;
11941          callback.index = index;
11942 
11943         /*----- multi thread server ------------------------*/
11944          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11945             ss_thread_create(rpc_server_thread, (void *) (&callback));
11946 
11947         /*----- single thread server -----------------------*/
11948          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11949              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11950             rpc_server_callback(&callback);
11951 
11952          break;
11953 
11954       default:
11955          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11956          closesocket(sock);
11957          break;
11958 
11959       }
11960    } else {                     /* if i>0 */
11961 
11962       /* lingering needed for PCTCP */
11963       ling.l_onoff = 1;
11964       ling.l_linger = 0;
11965       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11966       closesocket(sock);
11967    }
11968 
11969    return RPC_SUCCESS;
11970 }
11971 
11972 
11973 /********************************************************************/
11974 INT rpc_client_accept(int lsock)
11975 /********************************************************************\
11976 
11977   Routine: rpc_client_accept
11978 
11979   Purpose: Accept new incoming connections as a client
11980 
11981   Input:
11982     INT    lsock            Listen socket
11983 
11984   Output:
11985     none
11986 
11987   Function value:
11988     RPC_SUCCESS             Successful completion
11989     RPC_NET_ERROR           Error in socket call
11990     RPC_CONNCLOSED          Connection was closed
11991     RPC_SHUTDOWN            Listener shutdown
11992     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11993 
11994 \********************************************************************/
11995 {
11996    INT index, i, version, status;
11997    int size, sock;
11998    struct sockaddr_in acc_addr;
11999    INT client_hw_type = 0, hw_type;
12000    char str[100], client_program[NAME_LENGTH];
12001    char host_name[HOST_NAME_LENGTH];
12002    INT convert_flags;
12003    char net_buffer[256], *p;
12004 
12005    size = sizeof(acc_addr);
12006    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
12007 
12008    if (sock == -1)
12009       return RPC_NET_ERROR;
12010 
12011    /* get the name of the calling host */
12012 /* outcommented for speed reasons SR 7.10.98
12013 #ifdef OS_VXWORKS
12014   {
12015   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
12016   if (status != 0)
12017     strcpy(host_name, "unknown");
12018   }
12019 #else
12020   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
12021   if (phe == NULL)
12022     strcpy(host_name, "unknown");
12023   strcpy(host_name, phe->h_name);
12024 #endif
12025 */
12026    strcpy(host_name, "");
12027 
12028    /* look for next free entry */
12029    for (index = 0; index < MAX_RPC_CONNECTION; index++)
12030       if (_server_acception[index].recv_sock == 0)
12031          break;
12032    if (index == MAX_RPC_CONNECTION) {
12033       closesocket(sock);
12034       return RPC_NET_ERROR;
12035    }
12036 
12037    /* receive string with timeout */
12038    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
12039    if (i <= 0) {
12040       closesocket(sock);
12041       return RPC_NET_ERROR;
12042    }
12043 
12044    /* get remote computer info */
12045    p = strtok(net_buffer, " ");
12046    if (p != NULL) {
12047       client_hw_type = atoi(p);
12048       p = strtok(NULL, " ");
12049    }
12050    if (p != NULL) {
12051       version = atoi(p);
12052       p = strtok(NULL, " ");
12053    }
12054    if (p != NULL) {
12055       strcpy(client_program, p);
12056       p = strtok(NULL, " ");
12057    }
12058    if (p != NULL) {
12059       strcpy(host_name, p);
12060       p = strtok(NULL, " ");
12061    }
12062 
12063    /* save information in _server_acception structure */
12064    _server_acception[index].recv_sock = sock;
12065    _server_acception[index].send_sock = 0;
12066    _server_acception[index].event_sock = 0;
12067    _server_acception[index].remote_hw_type = client_hw_type;
12068    strcpy(_server_acception[index].host_name, host_name);
12069    strcpy(_server_acception[index].prog_name, client_program);
12070    _server_acception[index].tid = ss_gettid();
12071    _server_acception[index].last_activity = ss_millitime();
12072    _server_acception[index].watchdog_timeout = 0;
12073 
12074    /* send my own computer id */
12075    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12076    sprintf(str, "%d %s", hw_type, cm_get_version());
12077    status = send(sock, str, strlen(str) + 1, 0);
12078    if (status != (INT) strlen(str) + 1)
12079       return RPC_NET_ERROR;
12080 
12081    rpc_set_server_acception(index + 1);
12082    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12083    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12084 
12085    /* set callback function for ss_suspend */
12086    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
12087                            (int (*)(void)) rpc_server_receive);
12088 
12089    return RPC_SUCCESS;
12090 }
12091 
12092 
12093 /********************************************************************/
12094 INT rpc_server_callback(struct callback_addr * pcallback)
12095 /********************************************************************\
12096 
12097   Routine: rpc_server_callback
12098 
12099   Purpose: Callback a remote client. Setup _server_acception entry
12100            with optional conversion flags and establish two-way
12101            TCP connection.
12102 
12103   Input:
12104     callback_addr pcallback Pointer to a callback structure
12105 
12106   Output:
12107     none
12108 
12109   Function value:
12110     RPC_SUCCESS             Successful completion
12111 
12112 \********************************************************************/
12113 {
12114    INT index, status;
12115    int recv_sock, send_sock, event_sock;
12116    struct sockaddr_in bind_addr;
12117    struct hostent *phe;
12118    char str[100], client_program[NAME_LENGTH];
12119    char host_name[HOST_NAME_LENGTH];
12120    INT client_hw_type, hw_type;
12121    INT convert_flags;
12122    char net_buffer[256];
12123    struct callback_addr callback;
12124    int flag;
12125 
12126    /* copy callback information */
12127    memcpy(&callback, pcallback, sizeof(callback));
12128    index = callback.index;
12129 
12130    /* create new sockets for TCP */
12131    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
12132    send_sock = socket(AF_INET, SOCK_STREAM, 0);
12133    event_sock = socket(AF_INET, SOCK_STREAM, 0);
12134    if (event_sock == -1)
12135       return RPC_NET_ERROR;
12136 
12137    /* callback to remote node */
12138    memset(&bind_addr, 0, sizeof(bind_addr));
12139    bind_addr.sin_family = AF_INET;
12140    bind_addr.sin_port = htons(callback.host_port1);
12141 
12142 #ifdef OS_VXWORKS
12143    {
12144       INT host_addr;
12145 
12146       host_addr = hostGetByName(callback.host_name);
12147       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
12148    }
12149 #else
12150    phe = gethostbyname(callback.host_name);
12151    if (phe == NULL) {
12152       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
12153       return RPC_NET_ERROR;
12154    }
12155    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
12156 #endif
12157 
12158    /* connect receive socket */
12159 #ifdef OS_UNIX
12160    do {
12161       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
12162 
12163       /* don't return if an alarm signal was cought */
12164    } while (status == -1 && errno == EINTR);
12165 #else
12166    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12167 #endif
12168 
12169    if (status != 0) {
12170       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
12171       goto error;
12172    }
12173 
12174    bind_addr.sin_port = htons(callback.host_port2);
12175 
12176    /* connect send socket */
12177 #ifdef OS_UNIX
12178    do {
12179       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12180 
12181       /* don't return if an alarm signal was cought */
12182    } while (status == -1 && errno == EINTR);
12183 #else
12184    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12185 #endif
12186 
12187    if (status != 0) {
12188       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
12189       goto error;
12190    }
12191 
12192    bind_addr.sin_port = htons(callback.host_port3);
12193 
12194    /* connect event socket */
12195 #ifdef OS_UNIX
12196    do {
12197       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12198 
12199       /* don't return if an alarm signal was cought */
12200    } while (status == -1 && errno == EINTR);
12201 #else
12202    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
12203 #endif
12204 
12205    if (status != 0) {
12206       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
12207       goto error;
12208    }
12209 
12210    /* increase receive buffer size to 64k */
12211 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
12212    flag = 0x10000;
12213    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
12214 #endif
12215 
12216    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
12217       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
12218       goto error;
12219    }
12220 
12221    /* get remote computer info */
12222    sscanf(net_buffer, "%d", &client_hw_type);
12223 
12224    strcpy(client_program, strchr(net_buffer, ' ') + 1);
12225 
12226    /* get the name of the remote host */
12227 #ifdef OS_VXWORKS
12228    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
12229    if (status != 0)
12230       strcpy(host_name, "unknown");
12231 #else
12232    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
12233    if (phe == NULL)
12234       strcpy(host_name, "unknown");
12235    else
12236       strcpy(host_name, phe->h_name);
12237 #endif
12238 
12239    /* save information in _server_acception structure */
12240    _server_acception[index].recv_sock = recv_sock;
12241    _server_acception[index].send_sock = send_sock;
12242    _server_acception[index].event_sock = event_sock;
12243    _server_acception[index].remote_hw_type = client_hw_type;
12244    strcpy(_server_acception[index].host_name, host_name);
12245    strcpy(_server_acception[index].prog_name, client_program);
12246    _server_acception[index].tid = ss_gettid();
12247    _server_acception[index].last_activity = ss_millitime();
12248    _server_acception[index].watchdog_timeout = 0;
12249 
12250    /* send my own computer id */
12251    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
12252    sprintf(str, "%d", hw_type);
12253    send(recv_sock, str, strlen(str) + 1, 0);
12254 
12255    rpc_set_server_acception(index + 1);
12256    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
12257    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
12258 
12259    /* set callback function for ss_suspend */
12260    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
12261                            (int (*)(void)) rpc_server_receive);
12262 
12263    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12264       rpc_debug_printf("Connection to %s:%s established\n",
12265                        _server_acception[index].host_name,
12266                        _server_acception[index].prog_name);
12267 
12268    return RPC_SUCCESS;
12269 
12270  error:
12271 
12272    closesocket(recv_sock);
12273    closesocket(send_sock);
12274    closesocket(event_sock);
12275 
12276    return RPC_NET_ERROR;
12277 }
12278 
12279 
12280 /********************************************************************/
12281 INT rpc_server_thread(void *pointer)
12282 /********************************************************************\
12283 
12284   Routine: rpc_server_thread
12285 
12286   Purpose: New thread for a multi-threaded server. Callback to the
12287            client and process RPC requests.
12288 
12289   Input:
12290     vcoid  pointer          pointer to callback_addr structure.
12291 
12292   Output:
12293     none
12294 
12295   Function value:
12296     RPC_SUCCESS             Successful completion
12297 
12298 \********************************************************************/
12299 {
12300    struct callback_addr callback;
12301    int status, mutex_alarm, mutex_elog;
12302    static DWORD last_checked = 0;
12303 
12304    memcpy(&callback, pointer, sizeof(callback));
12305 
12306    status = rpc_server_callback(&callback);
12307 
12308    if (status != RPC_SUCCESS)
12309       return status;
12310 
12311    /* create alarm and elog mutexes */
12312    ss_mutex_create("ALARM", &mutex_alarm);
12313    ss_mutex_create("ELOG", &mutex_elog);
12314    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
12315 
12316    do {
12317       status = ss_suspend(5000, 0);
12318 
12319       if (rpc_check_channels() == RPC_NET_ERROR)
12320          break;
12321 
12322       /* check alarms every 10 seconds */
12323       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
12324          al_check();
12325          last_checked = ss_time();
12326       }
12327 
12328    } while (status != SS_ABORT && status != SS_EXIT);
12329 
12330    /* delete entry in suspend table for this thread */
12331    ss_suspend_exit();
12332 
12333    return RPC_SUCCESS;
12334 }
12335 
12336 
12337 /********************************************************************/
12338 INT rpc_server_receive(INT index, int sock, BOOL check)
12339 /********************************************************************\
12340 
12341   Routine: rpc_server_receive
12342 
12343   Purpose: Receive rpc commands and execute them. Close the connection
12344            if client has broken TCP pipe.
12345 
12346   Input:
12347     INT    index            Index to _server_acception structure in-
12348                             dicating which connection got data.
12349     int    sock             Socket which got data
12350     BOOL   check            If TRUE, only check if connection is
12351                             broken. This may be called via
12352                             bm_receive_event/ss_suspend(..,MSG_BM)
12353 
12354   Output:
12355     none
12356 
12357   Function value:
12358     RPC_SUCCESS             Successful completion
12359     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
12360     SS_EXIT                 Server connection was closed
12361     SS_ABORT                Server connection was broken
12362 
12363 \********************************************************************/
12364 {
12365    INT status, n_received;
12366    INT remaining, *pbh, start_time;
12367    char test_buffer[256], str[80];
12368    EVENT_HEADER *pevent;
12369 
12370    /* init network buffer */
12371    if (_net_recv_buffer_size == 0) {
12372       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
12373       if (_net_recv_buffer == NULL) {
12374          cm_msg(MERROR, "rpc_server_receive",
12375                 "not enough memory to allocate network buffer");
12376          return RPC_EXCEED_BUFFER;
12377       }
12378       _net_recv_buffer_size = NET_BUFFER_SIZE;
12379    }
12380 
12381    /* only check if TCP connection is broken */
12382    if (check) {
12383       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
12384 
12385       if (n_received == -1)
12386          cm_msg(MERROR, "rpc_server_receive",
12387                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
12388                 n_received, errno, strerror(errno));
12389 
12390       if (n_received <= 0)
12391          return SS_ABORT;
12392 
12393       return SS_SUCCESS;
12394    }
12395 
12396    remaining = 0;
12397 
12398    /* receive command */
12399    if (sock == _server_acception[index].recv_sock) {
12400       do {
12401          if (_server_acception[index].remote_hw_type == DR_ASCII)
12402             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
12403                                      NET_BUFFER_SIZE, 10000);
12404          else
12405             n_received =
12406                 recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
12407 
12408          if (n_received <= 0) {
12409             status = SS_ABORT;
12410             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort",
12411                    n_received);
12412             goto error;
12413          }
12414 
12415          rpc_set_server_acception(index + 1);
12416 
12417          if (_server_acception[index].remote_hw_type == DR_ASCII)
12418             status = rpc_execute_ascii(_server_acception[index].recv_sock,
12419                                        _net_recv_buffer);
12420          else
12421             status = rpc_execute(_server_acception[index].recv_sock,
12422                                  _net_recv_buffer,
12423                                  _server_acception[index].convert_flags);
12424 
12425          if (status == SS_ABORT) {
12426             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort",
12427                    status);
12428             goto error;
12429          }
12430 
12431          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
12432             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12433                rpc_debug_printf("Connection to %s:%s closed\n",
12434                                 _server_acception[index].host_name,
12435                                 _server_acception[index].prog_name);
12436             goto exit;
12437          }
12438 
12439       } while (remaining);
12440    } else {
12441       /* receive event */
12442       if (sock == _server_acception[index].event_sock) {
12443          start_time = ss_millitime();
12444 
12445          do {
12446             n_received =
12447                 recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0,
12448                                   &remaining);
12449 
12450             if (n_received <= 0) {
12451                status = SS_ABORT;
12452                cm_msg(MERROR, "rpc_server_receive",
12453                       "recv_event_server() returned %d, abort", n_received);
12454                goto error;
12455             }
12456 
12457             /* send event to buffer */
12458             pbh = (INT *) _net_recv_buffer;
12459             pevent = (EVENT_HEADER *) (pbh + 1);
12460 
12461             status =
12462                 bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER),
12463                               SYNC);
12464             if (status != BM_SUCCESS)
12465                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d",
12466                       status);
12467 
12468             /* repeat for maximum 0.5 sec */
12469          } while (ss_millitime() - start_time < 500 && remaining);
12470       }
12471    }
12472 
12473    return RPC_SUCCESS;
12474 
12475  error:
12476 
12477    strcpy(str, _server_acception[index].host_name);
12478    if (strchr(str, '.'))
12479       *strchr(str, '.') = 0;
12480    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
12481           _server_acception[index].prog_name, str);
12482 
12483  exit:
12484 
12485    /* disconnect from experiment as MIDAS server */
12486    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12487       HNDLE hDB, hKey;
12488 
12489       cm_get_experiment_database(&hDB, &hKey);
12490 
12491       /* only disconnect from experiment if previously connected.
12492          Necessary for pure RPC servers (RPC_SRVR) */
12493       if (hDB) {
12494 #ifdef LOCAL_ROUTINES
12495          ss_alarm(0, cm_watchdog);
12496 #endif
12497 
12498          cm_delete_client_info(hDB, 0);
12499 
12500          bm_close_all_buffers();
12501          db_close_all_databases();
12502 
12503          rpc_deregister_functions();
12504 
12505          cm_set_experiment_database(0, 0);
12506 
12507          _msg_buffer = 0;
12508       }
12509    }
12510 
12511    /* close server connection */
12512    if (_server_acception[index].recv_sock)
12513       closesocket(_server_acception[index].recv_sock);
12514    if (_server_acception[index].send_sock)
12515       closesocket(_server_acception[index].send_sock);
12516    if (_server_acception[index].event_sock)
12517       closesocket(_server_acception[index].event_sock);
12518 
12519    /* free TCP cache */
12520    M_FREE(_server_acception[index].net_buffer);
12521    _server_acception[index].net_buffer = NULL;
12522 
12523    /* mark this entry as invalid */
12524    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12525 
12526    /* signal caller a shutdonw */
12527    if (status == RPC_SHUTDOWN)
12528       return status;
12529 
12530    /* don't abort if other than main connection is broken */
12531    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12532       return SS_SUCCESS;
12533 
12534    return status;
12535 }
12536 
12537 
12538 /********************************************************************/
12539 INT rpc_server_shutdown(void)
12540 /********************************************************************\
12541 
12542   Routine: rpc_server_shutdown
12543 
12544   Purpose: Shutdown RPC server, abort all connections
12545 
12546   Input:
12547     none
12548 
12549   Output:
12550     none
12551 
12552   Function value:
12553     RPC_SUCCESS             Successful completion
12554 
12555 \********************************************************************/
12556 {
12557    INT i;
12558    struct linger ling;
12559 
12560    /* close all open connections */
12561    for (i = 0; i < MAX_RPC_CONNECTION; i++)
12562       if (_server_acception[i].recv_sock != 0) {
12563          /* lingering needed for PCTCP */
12564          ling.l_onoff = 1;
12565          ling.l_linger = 0;
12566          setsockopt(_server_acception[i].recv_sock,
12567                     SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12568          closesocket(_server_acception[i].recv_sock);
12569 
12570          if (_server_acception[i].send_sock) {
12571             setsockopt(_server_acception[i].send_sock,
12572                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12573             closesocket(_server_acception[i].send_sock);
12574          }
12575 
12576          if (_server_acception[i].event_sock) {
12577             setsockopt(_server_acception[i].event_sock,
12578                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12579             closesocket(_server_acception[i].event_sock);
12580          }
12581 
12582          _server_acception[i].recv_sock = 0;
12583          _server_acception[i].send_sock = 0;
12584          _server_acception[i].event_sock = 0;
12585       }
12586 
12587    if (_lsock) {
12588       closesocket(_lsock);
12589       _lsock = 0;
12590       _server_registered = FALSE;
12591    }
12592 
12593    /* free suspend structures */
12594    ss_suspend_exit();
12595 
12596    return RPC_SUCCESS;
12597 }
12598 
12599 
12600 /********************************************************************/
12601 INT rpc_check_channels(void)
12602 /********************************************************************\
12603 
12604   Routine: rpc_check_channels
12605 
12606   Purpose: Check open rpc channels by sending watchdog messages
12607 
12608   Input:
12609     none
12610 
12611   Output:
12612     none
12613 
12614   Function value:
12615     RPC_SUCCESS             Channel is still alive
12616     RPC_NET_ERROR           Connection is broken
12617 
12618 \********************************************************************/
12619 {
12620    INT status, index, i, convert_flags;
12621    NET_COMMAND nc;
12622    fd_set readfds;
12623    struct timeval timeout;
12624 
12625    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
12626       if (_server_acception[index].recv_sock &&
12627           _server_acception[index].tid == ss_gettid() &&
12628           _server_acception[index].watchdog_timeout &&
12629           (ss_millitime() - _server_acception[index].last_activity >
12630            (DWORD) _server_acception[index].watchdog_timeout)) {
12631 /* printf("Send watchdog message to %s on %s\n",
12632                 _server_acception[index].prog_name,
12633                 _server_acception[index].host_name); */
12634 
12635          /* send a watchdog message */
12636          nc.header.routine_id = MSG_WATCHDOG;
12637          nc.header.param_size = 0;
12638 
12639          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12640          if (convert_flags) {
12641             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING,
12642                                convert_flags);
12643             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING,
12644                                convert_flags);
12645          }
12646 
12647          /* send the header to the client */
12648          i = send_tcp(_server_acception[index].send_sock,
12649                       (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12650 
12651          if (i < 0)
12652             goto exit;
12653 
12654          /* make some timeout checking */
12655          FD_ZERO(&readfds);
12656          FD_SET(_server_acception[index].send_sock, &readfds);
12657          FD_SET(_server_acception[index].recv_sock, &readfds);
12658 
12659          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
12660          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
12661 
12662          do {
12663             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12664 
12665             /* if an alarm signal was cought, restart select with reduced timeout */
12666             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12667                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12668 
12669          } while (status == -1);        /* dont return if an alarm signal was cought */
12670 
12671          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
12672              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
12673             goto exit;
12674 
12675          /* receive result on send socket */
12676          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
12677             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
12678             if (i <= 0)
12679                goto exit;
12680          }
12681       }
12682    }
12683 
12684    return RPC_SUCCESS;
12685 
12686  exit:
12687 
12688    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12689           _server_acception[index].host_name,
12690           _server_acception[index].prog_name,
12691           _server_acception[index].watchdog_timeout / 1000);
12692 
12693    /* disconnect from experiment */
12694    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12695       cm_disconnect_experiment();
12696 
12697    /* close server connection */
12698    if (_server_acception[index].recv_sock)
12699       closesocket(_server_acception[index].recv_sock);
12700    if (_server_acception[index].send_sock)
12701       closesocket(_server_acception[index].send_sock);
12702    if (_server_acception[index].event_sock)
12703       closesocket(_server_acception[index].event_sock);
12704 
12705    /* free TCP cache */
12706    M_FREE(_server_acception[index].net_buffer);
12707    _server_acception[index].net_buffer = NULL;
12708 
12709    /* mark this entry as invalid */
12710    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12711 
12712    return RPC_NET_ERROR;
12713 }
12714 
12715 /**dox***************************************************************/
12716 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12717 
12718 /**dox***************************************************************/
12719                    /** @} *//* end of rpcfunctionc */
12720 
12721 /**dox***************************************************************/
12722 /** @addtogroup bkfunctionc
12723  *  
12724  *  @{  */
12725 
12726 /********************************************************************\
12727 *                                                                    *
12728 *                 Bank functions                                     *
12729 *                                                                    *
12730 \********************************************************************/
12731 
12732 /********************************************************************/
12733 /**
12734 Initializes an event for Midas banks structure.
12735 Before banks can be created in an event, bk_init() has to be called first.
12736 @param event pointer to the area of event
12737 */
12738 void bk_init(void *event)
12739 {
12740    ((BANK_HEADER *) event)->data_size = 0;
12741    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12742 }
12743 
12744 /**dox***************************************************************/
12745 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12746 
12747 /********************************************************************/
12748 BOOL bk_is32(void *event)
12749 /********************************************************************\
12750 
12751   Routine: bk_is32
12752 
12753   Purpose: Return true if banks inside event are 32-bit banks
12754 
12755   Input:
12756     void   *event           pointer to the event
12757 
12758   Output:
12759     none
12760 
12761   Function value:
12762     none
12763 
12764 \********************************************************************/
12765 {
12766    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12767 }
12768 
12769 /**dox***************************************************************/
12770 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12771 
12772 /********************************************************************/
12773 /**
12774 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12775 Before banks can be created in an event, bk_init32() has to be called first.
12776 @param event pointer to the area of event
12777 @return void
12778 */
12779 void bk_init32(void *event)
12780 {
12781    ((BANK_HEADER *) event)->data_size = 0;
12782    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12783 }
12784 
12785 /********************************************************************/
12786 /**
12787 Returns the size of an event containing banks.
12788 The total size of an event is the value returned by bk_size() plus the size
12789 of the event header (sizeof(EVENT_HEADER)).
12790 @param event pointer to the area of event
12791 @return number of bytes contained in data area of event
12792 */
12793 INT bk_size(void *event)
12794 {
12795    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12796 }
12797 
12798 /********************************************************************/
12799 /**
12800 Create a Midas bank.
12801 The data pointer pdata must be used as an address to fill a
12802 bank. It is incremented with every value written to the bank and finally points
12803 to a location just after the last byte of the bank. It is then passed to
12804 the function bk_close() to finish the bank creation.
12805 \code
12806 INT *pdata;
12807 bk_init(pevent);
12808 bk_create(pevent, "ADC0", TID_INT, &pdata);
12809 *pdata++ = 123
12810 *pdata++ = 456
12811 bk_close(pevent, pdata);
12812 \endcode
12813 @param event pointer to the data area
12814 @param name of the bank, must be exactly 4 charaters
12815 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12816 midas.h
12817 @param pdata pointer to the data area of the newly created bank
12818 @return void
12819 */
12820 void bk_create(void *event, const char *name, WORD type, void *pdata)
12821 {
12822    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12823       BANK32 *pbk32;
12824 
12825       pbk32 =
12826           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12827                       ((BANK_HEADER *) event)->data_size);
12828       strncpy(pbk32->name, name, 4);
12829       pbk32->type = type;
12830       pbk32->data_size = 0;
12831       *((void **) pdata) = pbk32 + 1;
12832    } else {
12833       BANK *pbk;
12834 
12835       pbk =
12836           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12837                     ((BANK_HEADER *) event)->data_size);
12838       strncpy(pbk->name, name, 4);
12839       pbk->type = type;
12840       pbk->data_size = 0;
12841       *((void **) pdata) = pbk + 1;
12842    }
12843 }
12844 
12845 
12846 
12847 /**dox***************************************************************/
12848 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12849 
12850 /********************************************************************/
12851 int bk_delete(void *event, const char *name)
12852 /********************************************************************\
12853 
12854   Routine: bk_delete
12855 
12856   Purpose: Delete a MIDAS bank inside an event
12857 
12858   Input:
12859     void   *event           pointer to the event
12860     char   *name            Name of bank (exactly four letters)
12861 
12862   Function value:
12863     CM_SUCCESS              Bank has been deleted
12864     0                       Bank has not been found
12865 
12866 \********************************************************************/
12867 {
12868    BANK *pbk;
12869    BANK32 *pbk32;
12870    DWORD dname;
12871    int remaining;
12872 
12873    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12874       /* locate bank */
12875       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12876       strncpy((char *) &dname, name, 4);
12877       do {
12878          if (*((DWORD *) pbk32->name) == dname) {
12879             /* bank found, delete it */
12880             remaining =
12881                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12882                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk32 + 1) +
12883                                                      ALIGN8(pbk32->data_size));
12884 
12885             /* reduce total event size */
12886             ((BANK_HEADER *) event)->data_size -=
12887                 sizeof(BANK32) + ALIGN8(pbk32->data_size);
12888 
12889             /* copy remaining bytes */
12890             if (remaining > 0)
12891                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12892             return CM_SUCCESS;
12893          }
12894 
12895          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12896       } while ((DWORD) pbk32 - (DWORD) event <
12897                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12898    } else {
12899       /* locate bank */
12900       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12901       strncpy((char *) &dname, name, 4);
12902       do {
12903          if (*((DWORD *) pbk->name) == dname) {
12904             /* bank found, delete it */
12905             remaining =
12906                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12907                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk + 1) +
12908                                                      ALIGN8(pbk->data_size));
12909 
12910             /* reduce total event size */
12911             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12912 
12913             /* copy remaining bytes */
12914             if (remaining > 0)
12915                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12916             return CM_SUCCESS;
12917          }
12918 
12919          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12920       } while ((DWORD) pbk - (DWORD) event <
12921                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12922    }
12923 
12924    return 0;
12925 }
12926 
12927 /**dox***************************************************************/
12928 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12929 
12930 /********************************************************************/
12931 /**
12932 Close the Midas bank priviously created by bk_create().
12933 The data pointer pdata must be obtained by bk_create() and
12934 used as an address to fill a bank. It is incremented with every value written
12935 to the bank and finally points to a location just after the last byte of the
12936 bank. It is then passed to bk_close() to finish the bank creation
12937 @param event pointer to current composed event
12938 @param pdata  pointer to the data
12939 @return number of bytes contained in bank
12940 */
12941 INT bk_close(void *event, void *pdata)
12942 {
12943    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12944       BANK32 *pbk32;
12945 
12946       pbk32 =
12947           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12948                       ((BANK_HEADER *) event)->data_size);
12949       pbk32->data_size = (DWORD) (INT) pdata - (INT) (pbk32 + 1);
12950       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12951          printf("Warning: bank %c%c%c%c has zero size\n",
12952                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12953       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12954       return pbk32->data_size;
12955    } else {
12956       BANK *pbk;
12957 
12958       pbk =
12959           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12960                     ((BANK_HEADER *) event)->data_size);
12961       pbk->data_size = (WORD) (INT) pdata - (INT) (pbk + 1);
12962       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12963          printf("Warning: bank %c%c%c%c has zero size\n",
12964                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12965       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12966       return pbk->data_size;
12967    }
12968 }
12969 
12970 /********************************************************************/
12971 /**
12972 Extract the MIDAS bank name listing of an event.
12973 The bklist should be dimensioned with STRING_BANKLIST_MAX
12974 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12975 \code
12976 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12977 {
12978   INT    n_adc, nbanks;
12979   WORD   *pdata;
12980   char   banklist[STRING_BANKLIST_MAX];
12981 
12982   // Display # of banks and list of banks in the event
12983   nbanks = bk_list(pevent, banklist);
12984   printf("#banks:%d List:%s\n", nbanks, banklist);
12985 
12986   // look for ADC0 bank, return if not present
12987   n_adc = bk_locate(pevent, "ADC0", &pdata);
12988   ...
12989 }
12990 \endcode
12991 @param event pointer to current composed event
12992 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12993 @return number of bank found in this event.
12994 */
12995 INT bk_list(void *event, char *bklist)
12996 {                               /* Full event */
12997    INT nbk, size;
12998    BANK *pmbk = NULL;
12999    BANK32 *pmbk32 = NULL;
13000    char *pdata;
13001 
13002    /* compose bank list */
13003    bklist[0] = 0;
13004    nbk = 0;
13005    do {
13006       /* scan all banks for bank name only */
13007       if (bk_is32(event)) {
13008          size = bk_iterate32(event, &pmbk32, &pdata);
13009          if (pmbk32 == NULL)
13010             break;
13011       } else {
13012          size = bk_iterate(event, &pmbk, &pdata);
13013          if (pmbk == NULL)
13014             break;
13015       }
13016       nbk++;
13017 
13018       if (nbk > BANKLIST_MAX) {
13019          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
13020          return (nbk - 1);
13021       }
13022       if (bk_is32(event))
13023          strncat(bklist, (char *) pmbk32->name, 4);
13024       else
13025          strncat(bklist, (char *) pmbk->name, 4);
13026    }
13027    while (1);
13028    return (nbk);
13029 }
13030 
13031 /********************************************************************/
13032 /**
13033 Locates a MIDAS bank of given name inside an event.
13034 @param event pointer to current composed event
13035 @param name bank name to look for
13036 @param pdata pointer to data area of bank, NULL if bank not found
13037 @return number of values inside the bank
13038 */
13039 INT bk_locate(void *event, const char *name, void *pdata)
13040 {
13041    BANK *pbk;
13042    BANK32 *pbk32;
13043    DWORD dname;
13044 
13045    if (bk_is32(event)) {
13046       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
13047       strncpy((char *) &dname, name, 4);
13048       do {
13049          if (*((DWORD *) pbk32->name) == dname) {
13050             *((void **) pdata) = pbk32 + 1;
13051             if (tid_size[pbk32->type & 0xFF] == 0)
13052                return pbk32->data_size;
13053             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
13054          }
13055          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13056       } while ((DWORD) pbk32 - (DWORD) event <
13057                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13058    } else {
13059       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13060       strncpy((char *) &dname, name, 4);
13061       do {
13062          if (*((DWORD *) pbk->name) == dname) {
13063             *((void **) pdata) = pbk + 1;
13064             if (tid_size[pbk->type & 0xFF] == 0)
13065                return pbk->data_size;
13066             return pbk->data_size / tid_size[pbk->type & 0xFF];
13067          }
13068          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13069       } while ((DWORD) pbk - (DWORD) event <
13070                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
13071    }
13072 
13073    /* bank not found */
13074    *((void **) pdata) = NULL;
13075    return 0;
13076 }
13077 
13078 /********************************************************************/
13079 /**
13080 Finds a MIDAS bank of given name inside an event.
13081 @param pbkh pointer to current composed event
13082 @param name bank name to look for
13083 @param bklen number of elemtents in bank
13084 @param bktype bank type, one of TID_xxx
13085 @param pdata pointer to data area of bank, NULL if bank not found
13086 @return 1 if bank found, 0 otherwise
13087 */
13088 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype,
13089             void **pdata)
13090 {
13091    BANK *pbk;
13092    BANK32 *pbk32;
13093    DWORD dname;
13094 
13095    if (bk_is32(pbkh)) {
13096       pbk32 = (BANK32 *) (pbkh + 1);
13097       strncpy((char *) &dname, name, 4);
13098       do {
13099          if (*((DWORD *) pbk32->name) == dname) {
13100             *((void **) pdata) = pbk32 + 1;
13101             if (tid_size[pbk32->type & 0xFF] == 0)
13102                *bklen = pbk32->data_size;
13103             else
13104                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
13105 
13106             *bktype = pbk32->type;
13107             return 1;
13108          }
13109          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13110       } while ((DWORD) pbk32 - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
13111    } else {
13112       pbk = (BANK *) (pbkh + 1);
13113       strncpy((char *) &dname, name, 4);
13114       do {
13115          if (*((DWORD *) pbk->name) == dname) {
13116             *((void **) pdata) = pbk + 1;
13117             if (tid_size[pbk->type & 0xFF] == 0)
13118                *bklen = pbk->data_size;
13119             else
13120                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
13121 
13122             *bktype = pbk->type;
13123             return 1;
13124          }
13125          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13126       } while ((DWORD) pbk - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
13127    }
13128 
13129    /* bank not found */
13130    *((void **) pdata) = NULL;
13131    return 0;
13132 }
13133 
13134 /********************************************************************/
13135 /**
13136 Iterates through banks inside an event.
13137 The function can be used to enumerate all banks of an event.
13138 The returned pointer to the bank header has following structure:
13139 \code
13140 typedef struct {
13141 char   name[4];
13142 WORD   type;
13143 WORD   data_size;
13144 } BANK;
13145 \endcode
13146 where type is a TID_xxx value and data_size the size of the bank in bytes.
13147 \code
13148 BANK *pbk;
13149 INT  size;
13150 void *pdata;
13151 char name[5];
13152 pbk = NULL;
13153 do
13154 {
13155  size = bk_iterate(event, &pbk, &pdata);
13156  if (pbk == NULL)
13157   break;
13158  *((DWORD *)name) = *((DWORD *)(pbk)->name);
13159  name[4] = 0;
13160  printf("bank %s found\n", name);
13161 } while(TRUE);
13162 \endcode
13163 @param event Pointer to data area of event.
13164 @param pbk pointer to the bank header, must be NULL for the first call to
13165 this function.
13166 @param pdata Pointer to the bank header, must be NULL for the first
13167 call to this function
13168 @return Size of bank in bytes
13169 */
13170 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
13171 {
13172    if (*pbk == NULL)
13173       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
13174    else
13175       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13176 
13177    *((void **) pdata) = (*pbk) + 1;
13178 
13179    if ((DWORD) * pbk - (DWORD) event >=
13180        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13181       *pbk = *((BANK **) pdata) = NULL;
13182       return 0;
13183    }
13184 
13185    return (*pbk)->data_size;
13186 }
13187 
13188 
13189 /**dox***************************************************************/
13190 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13191 
13192 /********************************************************************/
13193 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
13194 /********************************************************************\
13195 
13196   Routine: bk_iterate
13197 
13198   Purpose: Iterate through 32 bit MIDAS banks inside an event
13199 
13200   Input:
13201     void   *event           pointer to the event
13202     BANK   **pbk32          must be NULL for the first call to bk_iterate
13203 
13204   Output:
13205     BANK   **pbk            pointer to the bank header
13206     void   *pdata           pointer to data area of the bank
13207 
13208   Function value:
13209     INT    size of the bank in bytes
13210 
13211 \********************************************************************/
13212 {
13213    if (*pbk == NULL)
13214       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
13215    else
13216       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
13217 
13218    *((void **) pdata) = (*pbk) + 1;
13219 
13220    if ((DWORD) * pbk - (DWORD) event >=
13221        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
13222       *pbk = *((BANK32 **) pdata) = NULL;
13223       return 0;
13224    }
13225 
13226    return (*pbk)->data_size;
13227 }
13228 
13229 /**dox***************************************************************/
13230 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13231 
13232 /********************************************************************/
13233 /**
13234 Swaps bytes from little endian to big endian or vice versa for a whole event.
13235 
13236 An event contains a flag which is set by bk_init() to identify
13237 the endian format of an event. If force is FALSE, this flag is evaluated and
13238 the event is only swapped if it is in the "wrong" format for this system.
13239 An event can be swapped to the "wrong" format on purpose for example by a
13240 front-end which wants to produce events in a "right" format for a back-end
13241 analyzer which has different byte ordering.
13242 @param event pointer to data area of event
13243 @param force If TRUE, the event is always swapped, if FALSE, the event
13244 is only swapped if it is in the wrong format.
13245 @return 1==event has been swap, 0==event has not been swapped.
13246 */
13247 INT bk_swap(void *event, BOOL force)
13248 {
13249    BANK_HEADER *pbh;
13250    BANK *pbk;
13251    BANK32 *pbk32;
13252    void *pdata;
13253    WORD type;
13254    BOOL b32;
13255 
13256    pbh = (BANK_HEADER *) event;
13257 
13258    /* only swap if flags in high 16-bit */
13259    if (pbh->flags < 0x10000 && !force)
13260       return 0;
13261 
13262    /* swap bank header */
13263    DWORD_SWAP(&pbh->data_size);
13264    DWORD_SWAP(&pbh->flags);
13265 
13266    /* check for 32bit banks */
13267    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
13268 
13269    pbk = (BANK *) (pbh + 1);
13270    pbk32 = (BANK32 *) pbk;
13271 
13272    /* scan event */
13273    while ((PTYPE) pbk - (PTYPE) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
13274       /* swap bank header */
13275       if (b32) {
13276          DWORD_SWAP(&pbk32->type);
13277          DWORD_SWAP(&pbk32->data_size);
13278          pdata = pbk32 + 1;
13279          type = (WORD) pbk32->type;
13280       } else {
13281          WORD_SWAP(&pbk->type);
13282          WORD_SWAP(&pbk->data_size);
13283          pdata = pbk + 1;
13284          type = pbk->type;
13285       }
13286 
13287       /* pbk points to next bank */
13288       if (b32) {
13289          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
13290          pbk = (BANK *) pbk32;
13291       } else {
13292          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
13293          pbk32 = (BANK32 *) pbk;
13294       }
13295 
13296       switch (type) {
13297       case TID_WORD:
13298       case TID_SHORT:
13299          while ((PTYPE) pdata < (PTYPE) pbk) {
13300             WORD_SWAP(pdata);
13301             pdata = (void *) (((WORD *) pdata) + 1);
13302          }
13303          break;
13304 
13305       case TID_DWORD:
13306       case TID_INT:
13307       case TID_BOOL:
13308       case TID_FLOAT:
13309          while ((PTYPE) pdata < (PTYPE) pbk) {
13310             DWORD_SWAP(pdata);
13311             pdata = (void *) (((DWORD *) pdata) + 1);
13312          }
13313          break;
13314 
13315       case TID_DOUBLE:
13316          while ((PTYPE) pdata < (PTYPE) pbk) {
13317             QWORD_SWAP(pdata);
13318             pdata = (void *) (((double *) pdata) + 1);
13319          }
13320          break;
13321       }
13322    }
13323 
13324    return CM_SUCCESS;
13325 }
13326 
13327 /**dox***************************************************************/
13328                    /** @} *//* end of bkfunctionc */
13329 
13330 /**dox***************************************************************/
13331 /** @addtogroup hsfunctionc
13332  *  
13333  *  @{  */
13334 
13335 #if !defined(OS_VXWORKS)
13336 /********************************************************************\
13337 *                                                                    *
13338 *                 History functions                                  *
13339 *                                                                    *
13340 \********************************************************************/
13341 
13342 /**dox***************************************************************/
13343 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13344 
13345 static HISTORY *_history;
13346 static INT _history_entries = 0;
13347 static char _hs_path_name[MAX_STRING_LENGTH];
13348 
13349 /**dox***************************************************************/
13350 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13351 
13352 /********************************************************************/
13353 /**
13354 Sets the path for future history file accesses. Should
13355 be called before any other history function is called.
13356 @param path             Directory where history files reside
13357 @return HS_SUCCESS
13358 */
13359 INT hs_set_path(char *path)
13360 {
13361    /* set path locally and remotely */
13362    if (rpc_is_remote())
13363       rpc_call(RPC_HS_SET_PATH, path);
13364 
13365    strcpy(_hs_path_name, path);
13366 
13367    /* check for trailing directory seperator */
13368    if (strlen(_hs_path_name) > 0 &&
13369        _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
13370       strcat(_hs_path_name, DIR_SEPARATOR_STR);
13371 
13372    return HS_SUCCESS;
13373 }
13374 
13375 
13376 /********************************************************************/
13377 /**
13378 Open history file belonging to certain date. Internal use
13379            only.
13380 @param ltime          Date for which a history file should be opened.
13381 @param suffix         File name suffix like "hst", "idx", "idf"
13382 @param mode           R/W access mode
13383 @param fh             File handle
13384 @return HS_SUCCESS
13385 */
13386 INT hs_open_file(DWORD ltime, char *suffix, INT mode, int *fh)
13387 {
13388    struct tm *tms;
13389    char file_name[256];
13390 
13391    /* generate new file name YYMMDD.xxx */
13392 #if !defined(OS_VXWORKS)
13393 #if !defined(OS_VMS)
13394    tzset();
13395 #endif
13396 #endif
13397    tms = localtime((const time_t *) &ltime);
13398 
13399    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
13400            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
13401 
13402    /* open file, add O_BINARY flag for Windows NT */
13403    *fh = open(file_name, mode | O_BINARY, 0644);
13404 
13405    return HS_SUCCESS;
13406 }
13407 
13408 /**dox***************************************************************/
13409 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13410 
13411 /********************************************************************/
13412 INT hs_gen_index(DWORD ltime)
13413 /********************************************************************\
13414 
13415   Routine: hs_gen_index
13416 
13417   Purpose: Regenerate index files ("idx" and "idf" files) for a given
13418            history file ("hst"). Interal use only.
13419 
13420   Input:
13421     DWORD  ltime            Date for which a history file should
13422                             be analyzed.
13423 
13424   Output:
13425     none
13426 
13427   Function value:
13428     HS_SUCCESS              Successful completion
13429     HS_FILE_ERROR           Index files cannot be created
13430 
13431 \********************************************************************/
13432 {
13433    char event_name[NAME_LENGTH];
13434    int fh, fhd, fhi;
13435    INT n;
13436    HIST_RECORD rec;
13437    INDEX_RECORD irec;
13438    DEF_RECORD def_rec;
13439 
13440    printf("Recovering index files...\n");
13441 
13442    if (ltime == 0)
13443       ltime = time(NULL);
13444 
13445    /* open new index file */
13446    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
13447    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
13448 
13449    if (fhd < 0 || fhi < 0) {
13450       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
13451       return HS_FILE_ERROR;
13452    }
13453 
13454    /* open history file */
13455    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13456    if (fh < 0)
13457       return HS_FILE_ERROR;
13458    lseek(fh, 0, SEEK_SET);
13459 
13460    /* loop over file records in .hst file */
13461    do {
13462       n = read(fh, (char *) &rec, sizeof(rec));
13463       if (n < sizeof(rec))
13464          break;
13465 
13466       /* check if record type is definition */
13467       if (rec.record_type == RT_DEF) {
13468          /* read name */
13469          read(fh, event_name, sizeof(event_name));
13470 
13471          printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
13472 
13473          /* write definition index record */
13474          def_rec.event_id = rec.event_id;
13475          memcpy(def_rec.event_name, event_name, sizeof(event_name));
13476          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
13477          write(fhd, (char *) &def_rec, sizeof(def_rec));
13478 
13479          /* skip tags */
13480          lseek(fh, rec.data_size, SEEK_CUR);
13481       } else {
13482          /* write index record */
13483          irec.event_id = rec.event_id;
13484          irec.time = rec.time;
13485          irec.offset = TELL(fh) - sizeof(rec);
13486          write(fhi, (char *) &irec, sizeof(irec));
13487 
13488          /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
13489 
13490          /* skip data */
13491          lseek(fh, rec.data_size, SEEK_CUR);
13492       }
13493 
13494    } while (TRUE);
13495 
13496    close(fh);
13497    close(fhi);
13498    close(fhd);
13499 
13500    printf("...done.\n");
13501 
13502    return HS_SUCCESS;
13503 }
13504 
13505 
13506 /********************************************************************/
13507 INT hs_search_file(DWORD * ltime, INT direction)
13508 /********************************************************************\
13509 
13510   Routine: hs_search_file
13511 
13512   Purpose: Search an history file for a given date. If not found,
13513            look for files after date (direction==1) or before date
13514            (direction==-1) up to one year.
13515 
13516   Input:
13517     DWORD  *ltime           Date of history file
13518     INT    direction        Search direction
13519 
13520   Output:
13521     DWORD  *ltime           Date of history file found
13522 
13523   Function value:
13524     HS_SUCCESS              Successful completion
13525     HS_FILE_ERROR           No file found
13526 
13527 \********************************************************************/
13528 {
13529    DWORD lt;
13530    int fh, fhd, fhi;
13531    struct tm *tms;
13532 
13533    if (*ltime == 0)
13534       *ltime = time(NULL);
13535 
13536    lt = *ltime;
13537    do {
13538       /* try to open history file for date "lt" */
13539       hs_open_file(lt, "hst", O_RDONLY, &fh);
13540 
13541       /* if not found, look for next day */
13542       if (fh < 0)
13543          lt += direction * 3600 * 24;
13544 
13545       /* stop if more than a year before starting point or in the future */
13546    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 &&
13547             lt < (DWORD) time(NULL));
13548 
13549    if (fh < 0)
13550       return HS_FILE_ERROR;
13551 
13552    if (lt != *ltime) {
13553       /* if switched to new day, set start_time to 0:00 */
13554       tms = localtime((const time_t *) &lt);
13555       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13556       *ltime = mktime(tms);
13557    }
13558 
13559    /* check if index files are there */
13560    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
13561    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
13562 
13563    close(fh);
13564    close(fhd);
13565    close(fhi);
13566 
13567    /* generate them if not */
13568    if (fhd < 0 || fhi < 0)
13569       hs_gen_index(*ltime);
13570 
13571    return HS_SUCCESS;
13572 }
13573 
13574 
13575 /********************************************************************/
13576 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
13577 /********************************************************************\
13578 
13579   Routine: hs_define_evnet
13580 
13581   Purpose: Define a new event for which a history should be recorded.
13582            This routine must be called before any call to
13583            hs_write_event. It also should be called if the definition
13584            of the event has changed.
13585 
13586            The event definition is written directly to the history
13587            file. If the definition is identical to a previous
13588            definition, it is not written to the file.
13589 
13590 
13591   Input:
13592     DWORD  event_id         ID for this event. Must be unique.
13593     char   name             Name of this event
13594     TAG    tag              Tag list containing names and types of
13595                             variables in this event.
13596     DWORD  size             Size of tag array
13597 
13598   Output:
13599     <none>
13600 
13601   Function value:
13602     HS_SUCCESS              Successful completion
13603     HS_NO_MEMEORY           Out of memory
13604     HS_FILE_ERROR           Cannot open history file
13605 
13606 \********************************************************************/
13607 {
13608 /* History events are only written locally (?)
13609 
13610   if (rpc_is_remote())
13611     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
13612 */
13613    {
13614       HIST_RECORD rec, prev_rec;
13615       DEF_RECORD def_rec;
13616       char str[256], event_name[NAME_LENGTH], *buffer;
13617       int fh, fhi, fhd;
13618       INT i, n, len, index;
13619       struct tm *tmb;
13620 
13621       /* allocate new space for the new history descriptor */
13622       if (_history_entries == 0) {
13623          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
13624          memset(_history, 0, sizeof(HISTORY));
13625          if (_history == NULL)
13626             return HS_NO_MEMORY;
13627 
13628          _history_entries = 1;
13629          index = 0;
13630       } else {
13631          /* check if history already open */
13632          for (i = 0; i < _history_entries; i++)
13633             if (_history[i].event_id == event_id)
13634                break;
13635 
13636          /* if not found, create new one */
13637          if (i == _history_entries) {
13638             _history =
13639                 (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
13640             memset(&_history[_history_entries], 0, sizeof(HISTORY));
13641 
13642             _history_entries++;
13643             if (_history == NULL) {
13644                _history_entries--;
13645                return HS_NO_MEMORY;
13646             }
13647          }
13648          index = i;
13649       }
13650 
13651       /* assemble definition record header */
13652       rec.record_type = RT_DEF;
13653       rec.event_id = event_id;
13654       rec.time = time(NULL);
13655       rec.data_size = size;
13656       strncpy(event_name, name, NAME_LENGTH);
13657 
13658       /* pad tag names with zeos */
13659       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
13660          len = strlen(tag[i].name);
13661          memset(tag[i].name + len, 0, NAME_LENGTH - len);
13662       }
13663 
13664       /* if history structure not set up, do so now */
13665       if (!_history[index].hist_fh) {
13666          /* open history file */
13667          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13668          if (fh < 0)
13669             return HS_FILE_ERROR;
13670 
13671          /* open index files */
13672          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13673          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13674          lseek(fh, 0, SEEK_END);
13675          lseek(fhi, 0, SEEK_END);
13676          lseek(fhd, 0, SEEK_END);
13677 
13678          /* regenerate index if missing */
13679          if (TELL(fh) > 0 && TELL(fhd) == 0) {
13680             close(fh);
13681             close(fhi);
13682             close(fhd);
13683             hs_gen_index(rec.time);
13684             hs_open_file(rec.time, "hst", O_RDWR, &fh);
13685             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
13686             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
13687             lseek(fh, 0, SEEK_END);
13688             lseek(fhi, 0, SEEK_END);
13689             lseek(fhd, 0, SEEK_END);
13690          }
13691 
13692          tmb = localtime((const time_t *) &rec.time);
13693          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
13694 
13695          /* setup history structure */
13696          _history[index].hist_fh = fh;
13697          _history[index].index_fh = fhi;
13698          _history[index].def_fh = fhd;
13699          _history[index].def_offset = TELL(fh);
13700          _history[index].event_id = event_id;
13701          strcpy(_history[index].event_name, event_name);
13702          _history[index].base_time = mktime(tmb);
13703          _history[index].n_tag = size / sizeof(TAG);
13704          _history[index].tag = (TAG *) M_MALLOC(size);
13705          memcpy(_history[index].tag, tag, size);
13706 
13707          /* search previous definition */
13708          n = TELL(fhd) / sizeof(def_rec);
13709          def_rec.event_id = 0;
13710          for (i = n - 1; i >= 0; i--) {
13711             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13712             read(fhd, (char *) &def_rec, sizeof(def_rec));
13713             if (def_rec.event_id == event_id)
13714                break;
13715          }
13716          lseek(fhd, 0, SEEK_END);
13717 
13718          /* if definition found, compare it with new one */
13719          if (def_rec.event_id == event_id) {
13720             buffer = (char *) M_MALLOC(size);
13721             memset(buffer, 0, size);
13722 
13723             lseek(fh, def_rec.def_offset, SEEK_SET);
13724             read(fh, (char *) &prev_rec, sizeof(prev_rec));
13725             read(fh, str, NAME_LENGTH);
13726             read(fh, buffer, size);
13727             lseek(fh, 0, SEEK_END);
13728 
13729             if (prev_rec.data_size != size ||
13730                 strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13731                /* write definition to history file */
13732                write(fh, (char *) &rec, sizeof(rec));
13733                write(fh, event_name, NAME_LENGTH);
13734                write(fh, (char *) tag, size);
13735 
13736                /* write index record */
13737                def_rec.event_id = event_id;
13738                memcpy(def_rec.event_name, event_name, sizeof(event_name));
13739                def_rec.def_offset = _history[index].def_offset;
13740                write(fhd, (char *) &def_rec, sizeof(def_rec));
13741             } else
13742                /* definition identical, just remember old offset */
13743                _history[index].def_offset = def_rec.def_offset;
13744 
13745             M_FREE(buffer);
13746          } else {
13747             /* write definition to history file */
13748             write(fh, (char *) &rec, sizeof(rec));
13749             write(fh, event_name, NAME_LENGTH);
13750             write(fh, (char *) tag, size);
13751 
13752             /* write definition index record */
13753             def_rec.event_id = event_id;
13754             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13755             def_rec.def_offset = _history[index].def_offset;
13756             write(fhd, (char *) &def_rec, sizeof(def_rec));
13757          }
13758       } else {
13759          fh = _history[index].hist_fh;
13760          fhd = _history[index].def_fh;
13761 
13762          /* compare definition with previous definition */
13763          buffer = (char *) M_MALLOC(size);
13764          memset(buffer, 0, size);
13765 
13766          lseek(fh, _history[index].def_offset, SEEK_SET);
13767          read(fh, (char *) &prev_rec, sizeof(prev_rec));
13768          read(fh, str, NAME_LENGTH);
13769          read(fh, buffer, size);
13770 
13771          lseek(fh, 0, SEEK_END);
13772          lseek(fhd, 0, SEEK_END);
13773 
13774          if (prev_rec.data_size != size ||
13775              strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13776             /* save new definition offset */
13777             _history[index].def_offset = TELL(fh);
13778 
13779             /* write definition to history file */
13780             write(fh, (char *) &rec, sizeof(rec));
13781             write(fh, event_name, NAME_LENGTH);
13782             write(fh, (char *) tag, size);
13783 
13784             /* write index record */
13785             def_rec.event_id = event_id;
13786             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13787             def_rec.def_offset = _history[index].def_offset;
13788             write(fhd, (char *) &def_rec, sizeof(def_rec));
13789          }
13790 
13791          M_FREE(buffer);
13792       }
13793 
13794    }
13795 
13796    return HS_SUCCESS;
13797 }
13798 
13799 
13800 /********************************************************************/
13801 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13802 /********************************************************************\
13803 
13804   Routine: hs_write_event
13805 
13806   Purpose: Write an event to a history file.
13807 
13808   Input:
13809     DWORD  event_id         Event ID
13810     void   *data            Data buffer containing event
13811     DWORD  size             Data buffer size in bytes
13812 
13813   Output:
13814     none
13815                             future hs_write_event
13816 
13817   Function value:
13818     HS_SUCCESS              Successful completion
13819     HS_NO_MEMEORY           Out of memory
13820     HS_FILE_ERROR           Cannot write to history file
13821     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13822 
13823 \********************************************************************/
13824 {
13825 /* history events are only written locally (?)
13826 
13827   if (rpc_is_remote())
13828     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13829 */
13830    HIST_RECORD rec, drec;
13831    DEF_RECORD def_rec;
13832    INDEX_RECORD irec;
13833    int fh, fhi, fhd;
13834    INT index;
13835    struct tm tmb, tmr;
13836 
13837    /* find index to history structure */
13838    for (index = 0; index < _history_entries; index++)
13839       if (_history[index].event_id == event_id)
13840          break;
13841    if (index == _history_entries)
13842       return HS_UNDEFINED_EVENT;
13843 
13844    /* assemble record header */
13845    rec.record_type = RT_DATA;
13846    rec.event_id = _history[index].event_id;
13847    rec.time = time(NULL);
13848    rec.def_offset = _history[index].def_offset;
13849    rec.data_size = size;
13850 
13851    irec.event_id = _history[index].event_id;
13852    irec.time = rec.time;
13853 
13854    /* check if new day */
13855    memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13856    memcpy(&tmb, localtime((const time_t *) &_history[index].base_time), sizeof(tmb));
13857 
13858    if (tmr.tm_yday != tmb.tm_yday) {
13859       /* close current history file */
13860       close(_history[index].hist_fh);
13861       close(_history[index].def_fh);
13862       close(_history[index].index_fh);
13863 
13864       /* open new history file */
13865       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13866       if (fh < 0)
13867          return HS_FILE_ERROR;
13868 
13869       /* open new index file */
13870       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13871       if (fhi < 0)
13872          return HS_FILE_ERROR;
13873 
13874       /* open new definition index file */
13875       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13876       if (fhd < 0)
13877          return HS_FILE_ERROR;
13878 
13879       lseek(fh, 0, SEEK_END);
13880       lseek(fhi, 0, SEEK_END);
13881       lseek(fhd, 0, SEEK_END);
13882 
13883       /* remember new file handles */
13884       _history[index].hist_fh = fh;
13885       _history[index].index_fh = fhi;
13886       _history[index].def_fh = fhd;
13887 
13888       _history[index].def_offset = TELL(fh);
13889       rec.def_offset = _history[index].def_offset;
13890 
13891       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13892       _history[index].base_time = mktime(&tmr);
13893 
13894       /* write definition from _history structure */
13895       drec.record_type = RT_DEF;
13896       drec.event_id = _history[index].event_id;
13897       drec.time = rec.time;
13898       drec.data_size = _history[index].n_tag * sizeof(TAG);
13899 
13900       write(fh, (char *) &drec, sizeof(drec));
13901       write(fh, _history[index].event_name, NAME_LENGTH);
13902       write(fh, (char *) _history[index].tag, drec.data_size);
13903 
13904       /* write definition index record */
13905       def_rec.event_id = _history[index].event_id;
13906       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13907       def_rec.def_offset = _history[index].def_offset;
13908       write(fhd, (char *) &def_rec, sizeof(def_rec));
13909    }
13910 
13911    /* got to end of file */
13912    lseek(_history[index].hist_fh, 0, SEEK_END);
13913    irec.offset = TELL(_history[index].hist_fh);
13914 
13915    /* write record header */
13916    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13917 
13918    /* write data */
13919    write(_history[index].hist_fh, (char *) data, size);
13920 
13921    /* write index record */
13922    lseek(_history[index].index_fh, 0, SEEK_END);
13923    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13924       return HS_FILE_ERROR;
13925 
13926    return HS_SUCCESS;
13927 }
13928 
13929 
13930 /********************************************************************/
13931 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size,
13932                    INT event_id[], DWORD * id_size)
13933 /********************************************************************\
13934 
13935   Routine: hs_enum_events
13936 
13937   Purpose: Enumerate events for a given date
13938 
13939   Input:
13940     DWORD  ltime            Date at which events should be enumerated
13941 
13942   Output:
13943     char   *event_name      Array containing event names
13944     DWORD  *name_size       Size of name array
13945     char   *event_id        Array containing event IDs
13946     DWORD  *id_size         Size of ID array
13947 
13948   Function value:
13949     HS_SUCCESS              Successful completion
13950     HS_NO_MEMEORY           Out of memory
13951     HS_FILE_ERROR           Cannot open history file
13952 
13953 \********************************************************************/
13954 {
13955    int fh, fhd;
13956    INT status, i, j, n;
13957    DEF_RECORD def_rec;
13958 
13959    if (rpc_is_remote())
13960       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id,
13961                       id_size);
13962 
13963    /* search latest history file */
13964    status = hs_search_file(&ltime, -1);
13965    if (status != HS_SUCCESS) {
13966       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13967       return HS_FILE_ERROR;
13968    }
13969 
13970    /* open history and definition files */
13971    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13972    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13973    if (fh < 0 || fhd < 0) {
13974       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13975       return HS_FILE_ERROR;
13976    }
13977    lseek(fhd, 0, SEEK_SET);
13978 
13979    /* loop over definition index file */
13980    n = 0;
13981    do {
13982       /* read event definition */
13983       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13984       if (j < (int) sizeof(def_rec))
13985          break;
13986 
13987       /* look for existing entry for this event id */
13988       for (i = 0; i < n; i++)
13989          if (event_id[i] == (INT) def_rec.event_id) {
13990             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13991             break;
13992          }
13993 
13994       /* new entry found */
13995       if (i == n) {
13996          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13997             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13998             close(fh);
13999             close(fhd);
14000             return HS_NO_MEMORY;
14001          }
14002 
14003          /* copy definition record */
14004          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
14005          event_id[i] = def_rec.event_id;
14006          n++;
14007       }
14008    } while (TRUE);
14009 
14010    close(fh);
14011    close(fhd);
14012    *name_size = n * NAME_LENGTH;
14013    *id_size = n * sizeof(INT);
14014 
14015    return HS_SUCCESS;
14016 }
14017 
14018 
14019 /********************************************************************/
14020 INT hs_count_events(DWORD ltime, DWORD * count)
14021 /********************************************************************\
14022 
14023   Routine: hs_count_events
14024 
14025   Purpose: Count number of different events for a given date
14026 
14027   Input:
14028     DWORD  ltime            Date at which events should be counted
14029 
14030   Output:
14031     DWORD  *count           Number of different events found
14032 
14033   Function value:
14034     HS_SUCCESS              Successful completion
14035     HS_FILE_ERROR           Cannot open history file
14036 
14037 \********************************************************************/
14038 {
14039    int fh, fhd;
14040    INT status, i, j, n;
14041    DWORD *id;
14042    DEF_RECORD def_rec;
14043 
14044    if (rpc_is_remote())
14045       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
14046 
14047    /* search latest history file */
14048    status = hs_search_file(&ltime, -1);
14049    if (status != HS_SUCCESS) {
14050       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
14051       return HS_FILE_ERROR;
14052    }
14053 
14054    /* open history and definition files */
14055    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14056    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14057    if (fh < 0 || fhd < 0) {
14058       cm_msg(MERROR, "hs_count_events", "cannot open index files");
14059       return HS_FILE_ERROR;
14060    }
14061 
14062    /* allocate event id array */
14063    lseek(fhd, 0, SEEK_END);
14064    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
14065    lseek(fhd, 0, SEEK_SET);
14066 
14067    /* loop over index file */
14068    n = 0;
14069    do {
14070       /* read definition index record */
14071       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
14072       if (j < (int) sizeof(def_rec))
14073          break;
14074 
14075       /* look for existing entries */
14076       for (i = 0; i < n; i++)
14077          if (id[i] == def_rec.event_id)
14078             break;
14079 
14080       /* new entry found */
14081       if (i == n) {
14082          id[i] = def_rec.event_id;
14083          n++;
14084       }
14085    } while (TRUE);
14086 
14087 
14088    M_FREE(id);
14089    close(fh);
14090    close(fhd);
14091    *count = n;
14092 
14093    return HS_SUCCESS;
14094 }
14095 
14096 
14097 /********************************************************************/
14098 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
14099 /********************************************************************\
14100 
14101   Routine: hs_get_event_id
14102 
14103   Purpose: Return event ID for a given name. If event cannot be found
14104            in current definition file, go back in time until found
14105 
14106   Input:
14107     DWORD  ltime            Date at which event ID should be looked for
14108 
14109   Output:
14110     DWORD  *id              Event ID
14111 
14112   Function value:
14113     HS_SUCCESS              Successful completion
14114     HS_FILE_ERROR           Cannot open history file
14115     HS_UNDEFINED_EVENT      Event "name" not found
14116 
14117 \********************************************************************/
14118 {
14119    int fh, fhd;
14120    INT status, i;
14121    DWORD lt;
14122    DEF_RECORD def_rec;
14123 
14124    if (rpc_is_remote())
14125       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
14126 
14127    /* search latest history file */
14128    if (ltime == 0)
14129       ltime = time(NULL);
14130 
14131    lt = ltime;
14132 
14133    do {
14134       status = hs_search_file(&lt, -1);
14135       if (status != HS_SUCCESS) {
14136          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
14137          return HS_FILE_ERROR;
14138       }
14139 
14140       /* open history and definition files */
14141       hs_open_file(lt, "hst", O_RDONLY, &fh);
14142       hs_open_file(lt, "idf", O_RDONLY, &fhd);
14143       if (fh < 0 || fhd < 0) {
14144          cm_msg(MERROR, "hs_count_events", "cannot open index files");
14145          return HS_FILE_ERROR;
14146       }
14147 
14148       /* loop over index file */
14149       *id = 0;
14150       do {
14151          /* read definition index record */
14152          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
14153          if (i < (int) sizeof(def_rec))
14154             break;
14155 
14156          if (strcmp(name, def_rec.event_name) == 0) {
14157             *id = def_rec.event_id;
14158             close(fh);
14159             close(fhd);
14160             return HS_SUCCESS;
14161          }
14162       } while (TRUE);
14163 
14164       close(fh);
14165       close(fhd);
14166 
14167       /* not found -> go back one day */
14168       lt -= 3600 * 24;
14169 
14170    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
14171 
14172    return HS_UNDEFINED_EVENT;
14173 }
14174 
14175 
14176 /********************************************************************/
14177 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
14178 /********************************************************************\
14179 
14180   Routine: hs_count_vars
14181 
14182   Purpose: Count number of variables for a given date and event id
14183 
14184   Input:
14185     DWORD  ltime            Date at which tags should be counted
14186 
14187   Output:
14188     DWORD  *count           Number of tags
14189 
14190   Function value:
14191     HS_SUCCESS              Successful completion
14192     HS_FILE_ERROR           Cannot open history file
14193 
14194 \********************************************************************/
14195 {
14196    int fh, fhd;
14197    INT i, n, status;
14198    DEF_RECORD def_rec;
14199    HIST_RECORD rec;
14200 
14201    if (rpc_is_remote())
14202       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
14203 
14204    /* search latest history file */
14205    status = hs_search_file(&ltime, -1);
14206    if (status != HS_SUCCESS) {
14207       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
14208       return HS_FILE_ERROR;
14209    }
14210 
14211    /* open history and definition files */
14212    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14213    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14214    if (fh < 0 || fhd < 0) {
14215       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
14216       return HS_FILE_ERROR;
14217    }
14218 
14219    /* search last definition */
14220    lseek(fhd, 0, SEEK_END);
14221    n = TELL(fhd) / sizeof(def_rec);
14222    def_rec.event_id = 0;
14223    for (i = n - 1; i >= 0; i--) {
14224       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14225       read(fhd, (char *) &def_rec, sizeof(def_rec));
14226       if (def_rec.event_id == event_id)
14227          break;
14228    }
14229    if (def_rec.event_id != event_id) {
14230       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
14231       return HS_FILE_ERROR;
14232    }
14233 
14234    /* read definition */
14235    lseek(fh, def_rec.def_offset, SEEK_SET);
14236    read(fh, (char *) &rec, sizeof(rec));
14237    *count = rec.data_size / sizeof(TAG);
14238 
14239    close(fh);
14240    close(fhd);
14241 
14242    return HS_SUCCESS;
14243 }
14244 
14245 
14246 /********************************************************************/
14247 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size,
14248                  DWORD * var_n, DWORD * n_size)
14249 /********************************************************************\
14250 
14251   Routine: hs_enum_vars
14252 
14253   Purpose: Enumerate variable tags for a given date and event id
14254 
14255   Input:
14256     DWORD  ltime            Date at which tags should be enumerated
14257     DWORD  event_id         Event ID
14258 
14259   Output:
14260     char   *var_name        Array containing variable names
14261     DWORD  *size            Size of name array
14262     DWORD  *var_n           Array size of variable
14263     DWORD  *n_size          Size of n array
14264 
14265   Function value:
14266     HS_SUCCESS              Successful completion
14267     HS_NO_MEMEORY           Out of memory
14268     HS_FILE_ERROR           Cannot open history file
14269 
14270 \********************************************************************/
14271 {
14272    char str[256];
14273    int fh, fhd;
14274    INT i, n, status;
14275    DEF_RECORD def_rec;
14276    HIST_RECORD rec;
14277    TAG *tag;
14278 
14279    if (rpc_is_remote())
14280       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
14281 
14282    /* search latest history file */
14283    status = hs_search_file(&ltime, -1);
14284    if (status != HS_SUCCESS) {
14285       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
14286       return HS_FILE_ERROR;
14287    }
14288 
14289    /* open history and definition files */
14290    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14291    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14292    if (fh < 0 || fhd < 0) {
14293       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
14294       return HS_FILE_ERROR;
14295    }
14296 
14297    /* search last definition */
14298    lseek(fhd, 0, SEEK_END);
14299    n = TELL(fhd) / sizeof(def_rec);
14300    def_rec.event_id = 0;
14301    for (i = n - 1; i >= 0; i--) {
14302       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14303       read(fhd, (char *) &def_rec, sizeof(def_rec));
14304       if (def_rec.event_id == event_id)
14305          break;
14306    }
14307    if (def_rec.event_id != event_id) {
14308       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
14309       return HS_FILE_ERROR;
14310    }
14311 
14312    /* read definition header */
14313    lseek(fh, def_rec.def_offset, SEEK_SET);
14314    read(fh, (char *) &rec, sizeof(rec));
14315    read(fh, str, NAME_LENGTH);
14316 
14317    /* read event definition */
14318    n = rec.data_size / sizeof(TAG);
14319    tag = (TAG *) M_MALLOC(rec.data_size);
14320    read(fh, (char *) tag, rec.data_size);
14321 
14322    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
14323 
14324       /* store partial definition */
14325       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
14326          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14327          var_n[i] = tag[i].n_data;
14328       }
14329 
14330       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
14331       M_FREE(tag);
14332       close(fh);
14333       close(fhd);
14334       return HS_NO_MEMORY;
14335    }
14336 
14337    /* store full definition */
14338    for (i = 0; i < n; i++) {
14339       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14340       var_n[i] = tag[i].n_data;
14341    }
14342    *size = n * NAME_LENGTH;
14343    *n_size = n * sizeof(DWORD);
14344 
14345    M_FREE(tag);
14346    close(fh);
14347    close(fhd);
14348 
14349    return HS_SUCCESS;
14350 }
14351 
14352 
14353 /********************************************************************/
14354 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
14355 /********************************************************************\
14356 
14357   Routine: hs_get_var
14358 
14359   Purpose: Get definition for certain variable
14360 
14361   Input:
14362     DWORD  ltime            Date at which variable definition should
14363                             be returned
14364     DWORD  event_id         Event ID
14365     char   *var_name        Name of variable
14366 
14367   Output:
14368     INT    *type            Type of variable
14369     INT    *n_data          Number of items in variable
14370 
14371   Function value:
14372     HS_SUCCESS              Successful completion
14373     HS_NO_MEMEORY           Out of memory
14374     HS_FILE_ERROR           Cannot open history file
14375 
14376 \********************************************************************/
14377 {
14378    char str[256];
14379    int fh, fhd;
14380    INT i, n, status;
14381    DEF_RECORD def_rec;
14382    HIST_RECORD rec;
14383    TAG *tag;
14384 
14385    if (rpc_is_remote())
14386       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
14387 
14388    /* search latest history file */
14389    status = hs_search_file(&ltime, -1);
14390    if (status != HS_SUCCESS) {
14391       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
14392       return HS_FILE_ERROR;
14393    }
14394 
14395    /* open history and definition files */
14396    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14397    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14398    if (fh < 0 || fhd < 0) {
14399       cm_msg(MERROR, "hs_get_var", "cannot open index files");
14400       return HS_FILE_ERROR;
14401    }
14402 
14403    /* search last definition */
14404    lseek(fhd, 0, SEEK_END);
14405    n = TELL(fhd) / sizeof(def_rec);
14406    def_rec.event_id = 0;
14407    for (i = n - 1; i >= 0; i--) {
14408       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14409       read(fhd, (char *) &def_rec, sizeof(def_rec));
14410       if (def_rec.event_id == event_id)
14411          break;
14412    }
14413    if (def_rec.event_id != event_id) {
14414       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
14415       return HS_FILE_ERROR;
14416    }
14417 
14418    /* read definition header */
14419    lseek(fh, def_rec.def_offset, SEEK_SET);
14420    read(fh, (char *) &rec, sizeof(rec));
14421    read(fh, str, NAME_LENGTH);
14422 
14423    /* read event definition */
14424    n = rec.data_size / sizeof(TAG);
14425    tag = (TAG *) M_MALLOC(rec.data_size);
14426    read(fh, (char *) tag, rec.data_size);
14427 
14428    /* search variable */
14429    for (i = 0; i < n; i++)
14430       if (strcmp(tag[i].name, var_name) == 0)
14431          break;
14432 
14433    close(fh);
14434    close(fhd);
14435 
14436    if (i < n) {
14437       *type = tag[i].type;
14438       *n_data = tag[i].n_data;
14439    } else {
14440       *type = *n_data = 0;
14441       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
14442       M_FREE(tag);
14443       return HS_UNDEFINED_VAR;
14444    }
14445 
14446    M_FREE(tag);
14447    return HS_SUCCESS;
14448 }
14449 
14450 
14451 /********************************************************************/
14452 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
14453             DWORD interval, char *tag_name, DWORD var_index,
14454             DWORD * time_buffer, DWORD * tbsize,
14455             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
14456 /********************************************************************\
14457 
14458   Routine: hs_read
14459 
14460   Purpose: Read history for a variable at a certain time interval
14461 
14462   Input:
14463     DWORD  event_id         Event ID
14464     DWORD  start_time       Starting Date/Time
14465     DWORD  end_time         End Date/Time
14466     DWORD  interval         Minimum time in seconds between reported
14467                             events. Can be used to skip events
14468     char   *tag_name        Variable name inside event
14469     DWORD  var_index        Index if variable is array
14470 
14471   Output:
14472     DWORD  *time_buffer     Buffer containing times for each value
14473     DWORD  *tbsize          Size of time buffer
14474     void   *data_buffer     Buffer containing variable values
14475     DWORD  *dbsize          Data buffer size
14476     DWORD  *type            Type of variable (one of TID_xxx)
14477     DWORD  *n               Number of time/value pairs found
14478                             in specified interval and placed into
14479                             time_buffer and data_buffer
14480 
14481 
14482   Function value:
14483     HS_SUCCESS              Successful completion
14484     HS_NO_MEMEORY           Out of memory
14485     HS_FILE_ERROR           Cannot open history file
14486     HS_WRONG_INDEX          var_index exceeds array size of variable
14487     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
14488     HS_TRUNCATED            Buffer too small, data has been truncated
14489 
14490 \********************************************************************/
14491 {
14492    DWORD prev_time, last_irec_time;
14493    int fh, fhd, fhi, cp = 0;
14494    INT i, delta, index = 0, status, cache_size;
14495    INDEX_RECORD irec, *pirec;
14496    HIST_RECORD rec, drec;
14497    INT old_def_offset, var_size = 0, var_offset = 0;
14498    TAG *tag;
14499    char str[NAME_LENGTH];
14500    struct tm *tms;
14501    char *cache;
14502 
14503    if (rpc_is_remote())
14504       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
14505                       tag_name, var_index, time_buffer, tbsize, data_buffer,
14506                       dbsize, type, n);
14507 
14508    /* if not time given, use present to one hour in past */
14509    if (start_time == 0)
14510       start_time = time(NULL) - 3600;
14511    if (end_time == 0)
14512       end_time = time(NULL);
14513 
14514    /* search history file for start_time */
14515    status = hs_search_file(&start_time, 1);
14516    if (status != HS_SUCCESS) {
14517       cm_msg(MERROR, "hs_read", "cannot find recent history file");
14518       *tbsize = *dbsize = *n = 0;
14519       return HS_FILE_ERROR;
14520    }
14521 
14522    /* open history and definition files */
14523    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14524    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14525    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14526    if (fh < 0 || fhd < 0 || fhi < 0) {
14527       cm_msg(MERROR, "hs_read", "cannot open index files");
14528       *tbsize = *dbsize = *n = 0;
14529       return HS_FILE_ERROR;
14530    }
14531 
14532    /* try to read index file into cache */
14533    lseek(fhi, 0, SEEK_END);
14534    cache_size = TELL(fhi);
14535 
14536    if (cache_size > 0) {
14537       cache = (char *) M_MALLOC(cache_size);
14538       if (cache) {
14539          lseek(fhi, 0, SEEK_SET);
14540          i = read(fhi, cache, cache_size);
14541          if (i < cache_size) {
14542             M_FREE(cache);
14543             close(fh);
14544             close(fhd);
14545             close(fhi);
14546             return HS_FILE_ERROR;
14547          }
14548       }
14549 
14550       /* search record closest to start time */
14551       if (cache == NULL) {
14552          lseek(fhi, 0, SEEK_END);
14553          delta = (TELL(fhi) / sizeof(irec)) / 2;
14554          lseek(fhi, delta * sizeof(irec), SEEK_SET);
14555          do {
14556             delta = (int) (abs(delta) / 2.0 + 0.5);
14557             read(fhi, (char *) &irec, sizeof(irec));
14558             if (irec.time > start_time)
14559                delta = -delta;
14560 
14561             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14562          } while (abs(delta) > 1 && irec.time != start_time);
14563          read(fhi, (char *) &irec, sizeof(irec));
14564          if (irec.time > start_time)
14565             delta = -abs(delta);
14566 
14567          i = TELL(fhi) + (delta - 1) * sizeof(irec);
14568          if (i <= 0)
14569             lseek(fhi, 0, SEEK_SET);
14570          else
14571             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14572          read(fhi, (char *) &irec, sizeof(irec));
14573       } else {
14574          delta = (cache_size / sizeof(irec)) / 2;
14575          cp = delta * sizeof(irec);
14576          do {
14577             delta = (int) (abs(delta) / 2.0 + 0.5);
14578             pirec = (INDEX_RECORD *) (cache + cp);
14579             if (pirec->time > start_time)
14580                delta = -delta;
14581 
14582             cp = cp + delta * sizeof(irec);
14583          } while (abs(delta) > 1 && pirec->time != start_time);
14584          pirec = (INDEX_RECORD *) (cache + cp);
14585          if (pirec->time > start_time)
14586             delta = -abs(delta);
14587 
14588          if (cp <= delta * (int) sizeof(irec))
14589             cp = 0;
14590          else
14591             cp = cp + delta * sizeof(irec);
14592 
14593          if (cp >= cache_size)
14594             cp = cache_size - sizeof(irec);
14595          if (cp < 0)
14596             cp = 0;
14597 
14598          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
14599          cp += sizeof(irec);
14600       }
14601    } else {                     /* file size > 0 */
14602 
14603       cache = NULL;
14604       irec.time = start_time;
14605    }
14606 
14607    /* read records, skip wrong IDs */
14608    old_def_offset = -1;
14609    *n = 0;
14610    prev_time = 0;
14611    last_irec_time = 0;
14612    do {
14613       if (irec.time < last_irec_time) {
14614          cm_msg(MERROR, "hs_read",
14615                 "corrupted history data: time does not increase: %d -> %d",
14616                 last_irec_time, irec.time);
14617          *tbsize = *dbsize = *n = 0;
14618          return HS_FILE_ERROR;
14619       }
14620       last_irec_time = irec.time;
14621       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14622          /* check if record time more than "interval" seconds after previous time */
14623          if (irec.time >= prev_time + interval) {
14624             prev_time = irec.time;
14625             lseek(fh, irec.offset, SEEK_SET);
14626             read(fh, (char *) &rec, sizeof(rec));
14627 
14628             /* if definition changed, read new definition */
14629             if ((INT) rec.def_offset != old_def_offset) {
14630                lseek(fh, rec.def_offset, SEEK_SET);
14631                read(fh, (char *) &drec, sizeof(drec));
14632                read(fh, str, NAME_LENGTH);
14633 
14634                tag = (TAG *) M_MALLOC(drec.data_size);
14635                if (tag == NULL) {
14636                   *n = *tbsize = *dbsize = 0;
14637                   if (cache)
14638                      M_FREE(cache);
14639                   close(fh);
14640                   close(fhd);
14641                   close(fhi);
14642                   return HS_NO_MEMORY;
14643                }
14644                read(fh, (char *) tag, drec.data_size);
14645 
14646                /* find index of tag_name in new definition */
14647                index = -1;
14648                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
14649                   if (equal_ustring(tag[i].name, tag_name)) {
14650                      index = i;
14651                      break;
14652                   }
14653 
14654                /*
14655                   if ((DWORD) i == drec.data_size/sizeof(TAG))
14656                   {
14657                   *n = *tbsize = *dbsize = 0;
14658                   if (cache)
14659                   M_FREE(cache);
14660 
14661                   return HS_UNDEFINED_VAR;
14662                   }
14663                 */
14664 
14665                if (index >= 0 && var_index >= tag[i].n_data) {
14666                   *n = *tbsize = *dbsize = 0;
14667                   if (cache)
14668                      M_FREE(cache);
14669                   M_FREE(tag);
14670                   close(fh);
14671                   close(fhd);
14672                   close(fhi);
14673                   return HS_WRONG_INDEX;
14674                }
14675 
14676                /* calculate offset for variable */
14677                if (index >= 0) {
14678                   *type = tag[i].type;
14679 
14680                   /* loop over all previous variables */
14681                   for (i = 0, var_offset = 0; i < index; i++)
14682                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
14683 
14684                   /* strings have size n_data */
14685                   if (tag[index].type == TID_STRING)
14686                      var_size = tag[i].n_data;
14687                   else
14688                      var_size = rpc_tid_size(tag[index].type);
14689 
14690                   var_offset += var_size * var_index;
14691                }
14692 
14693                M_FREE(tag);
14694                old_def_offset = rec.def_offset;
14695                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14696             }
14697 
14698             if (index >= 0) {
14699                /* check if data fits in buffers */
14700                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
14701                   *dbsize = (*n) * var_size;
14702                   *tbsize = (*n) * sizeof(DWORD);
14703                   if (cache)
14704                      M_FREE(cache);
14705                   close(fh);
14706                   close(fhd);
14707                   close(fhi);
14708                   return HS_TRUNCATED;
14709                }
14710 
14711                /* copy time from header */
14712                time_buffer[*n] = irec.time;
14713 
14714                /* copy data from record */
14715                lseek(fh, var_offset, SEEK_CUR);
14716                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
14717 
14718                /* increment counter */
14719                (*n)++;
14720             }
14721          }
14722       }
14723 
14724       /* read next index record */
14725       if (cache) {
14726          if (cp >= cache_size) {
14727             i = -1;
14728             M_FREE(cache);
14729             cache = NULL;
14730          } else
14731             i = sizeof(irec);
14732 
14733          if (cp < cache_size) {
14734             memcpy(&irec, cache + cp, sizeof(irec));
14735             cp += sizeof(irec);
14736          }
14737       } else
14738          i = read(fhi, (char *) &irec, sizeof(irec));
14739 
14740       /* end of file: search next history file */
14741       if (i <= 0) {
14742          close(fh);
14743          close(fhd);
14744          close(fhi);
14745 
14746          /* advance one day */
14747          tms = localtime((const time_t *) &last_irec_time);
14748          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14749          last_irec_time = mktime(tms);
14750 
14751          last_irec_time += 3600 * 24;
14752 
14753          if (last_irec_time > end_time)
14754             break;
14755 
14756          /* search next file */
14757          status = hs_search_file(&last_irec_time, 1);
14758          if (status != HS_SUCCESS)
14759             break;
14760 
14761          /* open history and definition files */
14762          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14763          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14764          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14765          if (fh < 0 || fhd < 0 || fhi < 0) {
14766             cm_msg(MERROR, "hs_read", "cannot open index files");
14767             break;
14768          }
14769 
14770          /* try to read index file into cache */
14771          lseek(fhi, 0, SEEK_END);
14772          cache_size = TELL(fhi);
14773          lseek(fhi, 0, SEEK_SET);
14774          cache = (char *) M_MALLOC(cache_size);
14775          if (cache) {
14776             i = read(fhi, cache, cache_size);
14777             if (i < cache_size)
14778                break;
14779             /* read first record */
14780             cp = 0;
14781             memcpy(&irec, cache, sizeof(irec));
14782          } else {
14783             /* read first record */
14784             i = read(fhi, (char *) &irec, sizeof(irec));
14785             if (i <= 0)
14786                break;
14787          }
14788 
14789          /* old definition becomes invalid */
14790          old_def_offset = -1;
14791       }
14792    } while (irec.time < end_time);
14793 
14794    if (cache)
14795       M_FREE(cache);
14796    close(fh);
14797    close(fhd);
14798    close(fhi);
14799 
14800    *dbsize = *n * var_size;
14801    *tbsize = *n * sizeof(DWORD);
14802 
14803    return HS_SUCCESS;
14804 }
14805 
14806 
14807 /********************************************************************/
14808 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time,
14809             DWORD interval, BOOL binary_time)
14810 /********************************************************************\
14811 
14812   Routine: hs_dump
14813 
14814   Purpose: Display history for a given event at stdout. The output
14815            can be redirected to be read by Excel for example.
14816 
14817   Input:
14818     DWORD  event_id         Event ID
14819     DWORD  start_time       Starting Date/Time
14820     DWORD  end_time         End Date/Time
14821     DWORD  interval         Minimum time in seconds between reported
14822                             events. Can be used to skip events
14823     BOOL   binary_time      Display DWORD time stamp
14824   Output:
14825     <screen output>
14826 
14827   Function value:
14828     HS_SUCCESS              Successful completion
14829     HS_FILE_ERROR           Cannot open history file
14830 
14831 \********************************************************************/
14832 {
14833    DWORD prev_time, last_irec_time;
14834    int fh, fhd, fhi;
14835    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14836    INDEX_RECORD irec;
14837    HIST_RECORD rec, drec;
14838    INT old_def_offset, offset;
14839    TAG *tag = NULL, *old_tag = NULL;
14840    char str[NAME_LENGTH], data_buffer[10000];
14841    struct tm *tms;
14842 
14843    /* if not time given, use present to one hour in past */
14844    if (start_time == 0)
14845       start_time = time(NULL) - 3600;
14846    if (end_time == 0)
14847       end_time = time(NULL);
14848 
14849    /* search history file for start_time */
14850    status = hs_search_file(&start_time, 1);
14851    if (status != HS_SUCCESS) {
14852       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14853       return HS_FILE_ERROR;
14854    }
14855 
14856    /* open history and definition files */
14857    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14858    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14859    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14860    if (fh < 0 || fhd < 0 || fhi < 0) {
14861       cm_msg(MERROR, "hs_dump", "cannot open index files");
14862       return HS_FILE_ERROR;
14863    }
14864 
14865    /* search record closest to start time */
14866    lseek(fhi, 0, SEEK_END);
14867    delta = (TELL(fhi) / sizeof(irec)) / 2;
14868    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14869    do {
14870       delta = (int) (abs(delta) / 2.0 + 0.5);
14871       read(fhi, (char *) &irec, sizeof(irec));
14872       if (irec.time > start_time)
14873          delta = -delta;
14874 
14875       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14876    } while (abs(delta) > 1 && irec.time != start_time);
14877    read(fhi, (char *) &irec, sizeof(irec));
14878    if (irec.time > start_time)
14879       delta = -abs(delta);
14880 
14881    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14882    if (i <= 0)
14883       lseek(fhi, 0, SEEK_SET);
14884    else
14885       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14886    read(fhi, (char *) &irec, sizeof(irec));
14887 
14888    /* read records, skip wrong IDs */
14889    old_def_offset = -1;
14890    prev_time = 0;
14891    last_irec_time = 0;
14892    do {
14893       if (irec.time < last_irec_time) {
14894          cm_msg(MERROR, "hs_dump",
14895                 "corrupted history data: time does not increase: %d -> %d",
14896                 last_irec_time, irec.time);
14897          return HS_FILE_ERROR;
14898       }
14899       last_irec_time = irec.time;
14900       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14901          if (irec.time >= prev_time + interval) {
14902             prev_time = irec.time;
14903             lseek(fh, irec.offset, SEEK_SET);
14904             read(fh, (char *) &rec, sizeof(rec));
14905 
14906             /* if definition changed, read new definition */
14907             if ((INT) rec.def_offset != old_def_offset) {
14908                lseek(fh, rec.def_offset, SEEK_SET);
14909                read(fh, (char *) &drec, sizeof(drec));
14910                read(fh, str, NAME_LENGTH);
14911 
14912                if (tag == NULL)
14913                   tag = (TAG *) M_MALLOC(drec.data_size);
14914                else
14915                   tag = (TAG *) realloc(tag, drec.data_size);
14916                if (tag == NULL)
14917                   return HS_NO_MEMORY;
14918                read(fh, (char *) tag, drec.data_size);
14919                n_tag = drec.data_size / sizeof(TAG);
14920 
14921                /* print tag names if definition has changed */
14922                if (old_tag == NULL || old_n_tag != n_tag ||
14923                    memcmp(old_tag, tag, drec.data_size) != 0) {
14924                   printf("Date\t");
14925                   for (i = 0; i < n_tag; i++) {
14926                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14927                         printf("%s\t", tag[i].name);
14928                      else
14929                         for (j = 0; j < (INT) tag[i].n_data; j++)
14930                            printf("%s%d\t", tag[i].name, j);
14931                   }
14932                   printf("\n");
14933 
14934                   if (old_tag == NULL)
14935                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14936                   else
14937                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14938                   memcpy(old_tag, tag, drec.data_size);
14939                   old_n_tag = n_tag;
14940                }
14941 
14942                old_def_offset = rec.def_offset;
14943                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14944             }
14945 
14946             /* print time from header */
14947             if (binary_time)
14948                printf("%li ", irec.time);
14949             else {
14950                sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14951                str[20] = '\t';
14952                printf(str);
14953             }
14954 
14955             /* read data */
14956             read(fh, data_buffer, rec.data_size);
14957 
14958             /* interprete data from tag definition */
14959             offset = 0;
14960             for (i = 0; i < n_tag; i++) {
14961                /* strings have a length of n_data */
14962                if (tag[i].type == TID_STRING) {
14963                   printf("%s\t", data_buffer + offset);
14964                   offset += tag[i].n_data;
14965                } else if (tag[i].n_data == 1) {
14966                   /* non-array data */
14967                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14968                              tag[i].type);
14969                   printf("%s\t", str);
14970                   offset += rpc_tid_size(tag[i].type);
14971                } else
14972                   /* loop over array data */
14973                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14974                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14975                                 tag[i].type);
14976                      printf("%s\t", str);
14977                      offset += rpc_tid_size(tag[i].type);
14978                   }
14979             }
14980             printf("\n");
14981          }
14982       }
14983 
14984       /* read next index record */
14985       i = read(fhi, (char *) &irec, sizeof(irec));
14986 
14987       /* end of file: search next history file */
14988       if (i <= 0) {
14989          close(fh);
14990          close(fhd);
14991          close(fhi);
14992 
14993          /* advance one day */
14994          tms = localtime((const time_t *) &last_irec_time);
14995          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14996          last_irec_time = mktime(tms);
14997 
14998          last_irec_time += 3600 * 24;
14999          if (last_irec_time > end_time)
15000             break;
15001 
15002          /* search next file */
15003          status = hs_search_file(&last_irec_time, 1);
15004          if (status != HS_SUCCESS)
15005             break;
15006 
15007          /* open history and definition files */
15008          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
15009          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
15010          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
15011          if (fh < 0 || fhd < 0 || fhi < 0) {
15012             cm_msg(MERROR, "hs_dump", "cannot open index files");
15013             break;
15014          }
15015 
15016          /* read first record */
15017          i = read(fhi, (char *) &irec, sizeof(irec));
15018          if (i <= 0)
15019             break;
15020 
15021          /* old definition becomes invalid */
15022          old_def_offset = -1;
15023       }
15024    } while (irec.time < end_time);
15025 
15026    M_FREE(tag);
15027    M_FREE(old_tag);
15028    close(fh);
15029    close(fhd);
15030    close(fhi);
15031 
15032    return HS_SUCCESS;
15033 }
15034 
15035 
15036 /********************************************************************/
15037 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
15038 /********************************************************************\
15039 
15040   Routine: hs_fdump
15041 
15042   Purpose: Display history for a given history file
15043 
15044   Input:
15045     char   *file_name       Name of file to dump
15046     DWORD  event_id         Event ID
15047     BOOL   binary_time      Display DWORD time stamp
15048 
15049   Output:
15050     <screen output>
15051 
15052   Function value:
15053     HS_SUCCESS              Successful completion
15054     HS_FILE_ERROR           Cannot open history file
15055 
15056 \********************************************************************/
15057 {
15058    int fh;
15059    INT n;
15060    HIST_RECORD rec;
15061    char event_name[NAME_LENGTH];
15062    char str[80];
15063 
15064    /* open file, add O_BINARY flag for Windows NT */
15065    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
15066    if (fh < 0) {
15067       cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
15068       return HS_FILE_ERROR;
15069    }
15070 
15071    /* loop over file records in .hst file */
15072    do {
15073       n = read(fh, (char *) &rec, sizeof(rec));
15074       if (n < sizeof(rec))
15075          break;
15076 
15077       /* check if record type is definition */
15078       if (rec.record_type == RT_DEF) {
15079          /* read name */
15080          read(fh, event_name, sizeof(event_name));
15081 
15082          if (rec.event_id == id || id == 0)
15083             printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
15084 
15085          /* skip tags */
15086          lseek(fh, rec.data_size, SEEK_CUR);
15087       } else {
15088          /* print data record */
15089          if (binary_time)
15090             sprintf(str, "%li ", rec.time);
15091          else {
15092             strcpy(str, ctime((const time_t *) &rec.time) + 4);
15093             str[15] = 0;
15094          }
15095          if (rec.event_id == id || id == 0)
15096             printf("ID %ld, %s, size %ld\n", rec.event_id, str, rec.data_size);
15097 
15098          /* skip data */
15099          lseek(fh, rec.data_size, SEEK_CUR);
15100       }
15101 
15102    } while (TRUE);
15103 
15104    close(fh);
15105 
15106    return HS_SUCCESS;
15107 }
15108 #endif                          /* OS_VXWORKS hs section */
15109 
15110 /**dox***************************************************************/
15111 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15112 
15113 /**dox***************************************************************/
15114                    /** @} *//* end of hsfunctionc */
15115 
15116 /**dox***************************************************************/
15117 /** @addtogroup elfunctionc
15118  *  
15119  *  @{  */
15120 
15121 /**dox***************************************************************/
15122 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15123 
15124 /********************************************************************\
15125 *                                                                    *
15126 *               Electronic logbook functions                         *
15127 *                                                                    *
15128 \********************************************************************/
15129 
15130 /********************************************************************/
15131 void el_decode(char *message, char *key, char *result, int size)
15132 {
15133    char *rstart = result;
15134    char *pc;
15135 
15136    if (result == NULL)
15137       return;
15138 
15139    *result = 0;
15140 
15141    if (strstr(message, key)) {
15142       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
15143          *result++ = *pc++;
15144       *result = 0;
15145    }
15146 
15147    assert((int) strlen(rstart) < size);
15148 }
15149 
15150 /**dox***************************************************************/
15151 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15152 
15153 /********************************************************************/
15154 /**
15155 Submit an ELog entry.
15156 @param run  Run Number.
15157 @param author Message author.
15158 @param type Message type.
15159 @param system Message system.
15160 @param subject Subject.
15161 @param text Message text.
15162 @param reply_to In reply to this message.
15163 @param encoding Text encoding, either HTML or plain.
15164 @param afilename1   File name of attachment.
15165 @param buffer1      File contents.
15166 @param buffer_size1 Size of buffer in bytes.
15167 @param afilename2   File name of attachment.
15168 @param buffer2      File contents.
15169 @param buffer_size2 Size of buffer in bytes.
15170 @param afilename3   File name of attachment.
15171 @param buffer3      File contents.
15172 @param buffer_size3 Size of buffer in bytes.
15173 @param tag          If given, edit existing message.
15174 @param tag_size     Maximum size of tag.
15175 @return EL_SUCCESS
15176 */
15177 INT el_submit(int run, char *author, char *type, char *system, char *subject,
15178               char *text, char *reply_to, char *encoding,
15179               char *afilename1, char *buffer1, INT buffer_size1,
15180               char *afilename2, char *buffer2, INT buffer_size2,
15181               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
15182 {
15183    if (rpc_is_remote())
15184       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
15185                       text, reply_to, encoding,
15186                       afilename1, buffer1, buffer_size1,
15187                       afilename2, buffer2, buffer_size2,
15188                       afilename3, buffer3, buffer_size3, tag, tag_size);
15189 
15190 #ifdef LOCAL_ROUTINES
15191    {
15192       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset =
15193           0, tail_size = 0;
15194       struct tm *tms = NULL;
15195       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
15196           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
15197       HNDLE hDB;
15198       time_t now;
15199       char message[10000], *p, *buffer = NULL;
15200       BOOL bedit;
15201 
15202       cm_get_experiment_database(&hDB, NULL);
15203 
15204       bedit = (tag[0] != 0);
15205 
15206       /* request semaphore */
15207       cm_get_experiment_mutex(NULL, &mutex);
15208       status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
15209       if (status != SS_SUCCESS) {
15210          cm_msg(MERROR, "el_submit",
15211                 "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
15212          abort();
15213       }
15214 
15215       /* get run number from ODB if not given */
15216       if (run > 0)
15217          run_number = run;
15218       else {
15219          /* get run number */
15220          size = sizeof(run_number);
15221          status =
15222              db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
15223                           TRUE);
15224          assert(status == SUCCESS);
15225       }
15226 
15227       if (run_number < 0) {
15228          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d",
15229                 run_number);
15230          abort();
15231       }
15232 
15233       for (index = 0; index < 3; index++) {
15234          /* generate filename for attachment */
15235          afile_name[index][0] = file_name[0] = 0;
15236 
15237          if (index == 0) {
15238             strcpy(afilename, afilename1);
15239             buffer = buffer1;
15240             buffer_size = buffer_size1;
15241          } else if (index == 1) {
15242             strcpy(afilename, afilename2);
15243             buffer = buffer2;
15244             buffer_size = buffer_size2;
15245          } else if (index == 2) {
15246             strcpy(afilename, afilename3);
15247             buffer = buffer3;
15248             buffer_size = buffer_size3;
15249          }
15250 
15251          if (afilename[0]) {
15252             strcpy(file_name, afilename);
15253             p = file_name;
15254             while (strchr(p, ':'))
15255                p = strchr(p, ':') + 1;
15256             while (strchr(p, '\\'))
15257                p = strchr(p, '\\') + 1; /* NT */
15258             while (strchr(p, '/'))
15259                p = strchr(p, '/') + 1;  /* Unix */
15260             while (strchr(p, ']'))
15261                p = strchr(p, ']') + 1;  /* VMS */
15262 
15263             /* assemble ELog filename */
15264             if (p[0]) {
15265                dir[0] = 0;
15266                if (hDB > 0) {
15267                   size = sizeof(dir);
15268                   memset(dir, 0, size);
15269                   status =
15270                       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
15271                                    FALSE);
15272                   if (status != DB_SUCCESS)
15273                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING,
15274                                   TRUE);
15275 
15276                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15277                      strcat(dir, DIR_SEPARATOR_STR);
15278                }
15279 #if !defined(OS_VXWORKS)
15280 #if !defined(OS_VMS)
15281                tzset();
15282 #endif
15283 #endif
15284 
15285                time((time_t *) & now);
15286                tms = localtime((const time_t *) &now);
15287 
15288                strcpy(str, p);
15289                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
15290                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
15291                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
15292                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
15293                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
15294                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
15295 
15296                /* save attachment */
15297                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15298                if (fh < 0) {
15299                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"",
15300                          file_name);
15301                } else {
15302                   write(fh, buffer, buffer_size);
15303                   close(fh);
15304                }
15305             }
15306          }
15307       }
15308 
15309       /* generate new file name YYMMDD.log in data directory */
15310       cm_get_experiment_database(&hDB, NULL);
15311 
15312       size = sizeof(dir);
15313       memset(dir, 0, size);
15314       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15315       if (status != DB_SUCCESS)
15316          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15317 
15318       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15319          strcat(dir, DIR_SEPARATOR_STR);
15320 
15321 #if !defined(OS_VXWORKS)
15322 #if !defined(OS_VMS)
15323       tzset();
15324 #endif
15325 #endif
15326 
15327       if (bedit) {
15328          /* edit existing message */
15329          strcpy(str, tag);
15330          if (strchr(str, '.')) {
15331             offset = atoi(strchr(str, '.') + 1);
15332             *strchr(str, '.') = 0;
15333          }
15334          sprintf(file_name, "%s%s.log", dir, str);
15335          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15336          if (fh < 0) {
15337             ss_mutex_release(mutex);
15338             return EL_FILE_ERROR;
15339          }
15340          lseek(fh, offset, SEEK_SET);
15341          read(fh, str, 16);
15342          size = atoi(str + 9);
15343          read(fh, message, size);
15344 
15345          el_decode(message, "Date: ", date, sizeof(date));
15346          el_decode(message, "Thread: ", thread, sizeof(thread));
15347          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
15348 
15349          /* buffer tail of logfile */
15350          lseek(fh, 0, SEEK_END);
15351          tail_size = TELL(fh) - (offset + size);
15352 
15353          if (tail_size > 0) {
15354             buffer = (char *) M_MALLOC(tail_size);
15355             if (buffer == NULL) {
15356                close(fh);
15357                ss_mutex_release(mutex);
15358                return EL_FILE_ERROR;
15359             }
15360 
15361             lseek(fh, offset + size, SEEK_SET);
15362             n = read(fh, buffer, tail_size);
15363          }
15364          lseek(fh, offset, SEEK_SET);
15365       } else {
15366          /* create new message */
15367          time((time_t *) & now);
15368          tms = localtime((const time_t *) &now);
15369 
15370          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15371                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15372 
15373          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15374          if (fh < 0) {
15375             ss_mutex_release(mutex);
15376             return EL_FILE_ERROR;
15377          }
15378 
15379          strcpy(date, ctime(&now));
15380          date[24] = 0;
15381 
15382          if (reply_to[0])
15383             sprintf(thread, "%16s %16s", reply_to, "0");
15384          else
15385             sprintf(thread, "%16s %16s", "0", "0");
15386 
15387          lseek(fh, 0, SEEK_END);
15388       }
15389 
15390       /* compose message */
15391 
15392       sprintf(message, "Date: %s\n", date);
15393       sprintf(message + strlen(message), "Thread: %s\n", thread);
15394       sprintf(message + strlen(message), "Run: %d\n", run_number);
15395       sprintf(message + strlen(message), "Author: %s\n", author);
15396       sprintf(message + strlen(message), "Type: %s\n", type);
15397       sprintf(message + strlen(message), "System: %s\n", system);
15398       sprintf(message + strlen(message), "Subject: %s\n", subject);
15399 
15400       /* keep original attachment if edit and no new attachment */
15401       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 &&
15402           afile_name[2][0] == 0)
15403          sprintf(message + strlen(message), "Attachment: %s", attachment);
15404       else {
15405          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
15406          if (afile_name[1][0])
15407             sprintf(message + strlen(message), ",%s", afile_name[1]);
15408          if (afile_name[2][0])
15409             sprintf(message + strlen(message), ",%s", afile_name[2]);
15410       }
15411       sprintf(message + strlen(message), "\n");
15412 
15413       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
15414       sprintf(message + strlen(message), "========================================\n");
15415       strcat(message, text);
15416 
15417       assert(strlen(message) < sizeof(message));        /* bomb out on array overrun. */
15418 
15419       size = 0;
15420       sprintf(start_str, "$Start$: %6d\n", size);
15421       sprintf(end_str, "$End$:   %6d\n\f", size);
15422 
15423       size = strlen(message) + strlen(start_str) + strlen(end_str);
15424 
15425       if (tag != NULL && !bedit)
15426          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1,
15427                  tms->tm_mday, (int) TELL(fh));
15428 
15429       /* size has to fit in 6 digits */
15430       assert(size < 999999);
15431 
15432       sprintf(start_str, "$Start$: %6d\n", size);
15433       sprintf(end_str, "$End$:   %6d\n\f", size);
15434 
15435       write(fh, start_str, strlen(start_str));
15436       write(fh, message, strlen(message));
15437       write(fh, end_str, strlen(end_str));
15438 
15439       if (bedit) {
15440          if (tail_size > 0) {
15441             n = write(fh, buffer, tail_size);
15442             M_FREE(buffer);
15443          }
15444 
15445          /* truncate file here */
15446 #ifdef OS_WINNT
15447          chsize(fh, TELL(fh));
15448 #else
15449          ftruncate(fh, TELL(fh));
15450 #endif
15451       }
15452 
15453       close(fh);
15454 
15455       /* if reply, mark original message */
15456       if (reply_to[0] && !bedit) {
15457          strcpy(last, reply_to);
15458          do {
15459             status = el_search_message(last, &fh, FALSE);
15460             if (status == EL_SUCCESS) {
15461                /* position to next thread location */
15462                lseek(fh, 72, SEEK_CUR);
15463                memset(str, 0, sizeof(str));
15464                read(fh, str, 16);
15465                lseek(fh, -16, SEEK_CUR);
15466 
15467                /* if no reply yet, set it */
15468                if (atoi(str) == 0) {
15469                   sprintf(str, "%16s", tag);
15470                   write(fh, str, 16);
15471                   close(fh);
15472                   break;
15473                } else {
15474                   /* if reply set, find last one in chain */
15475                   strcpy(last, strtok(str, " "));
15476                   close(fh);
15477                }
15478             } else
15479                /* stop on error */
15480                break;
15481 
15482          } while (TRUE);
15483       }
15484 
15485       /* release elog mutex */
15486       ss_mutex_release(mutex);
15487    }
15488 #endif                          /* LOCAL_ROUTINES */
15489 
15490    return EL_SUCCESS;
15491 }
15492 
15493 /**dox***************************************************************/
15494 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15495 
15496 /********************************************************************/
15497 INT el_search_message(char *tag, int *fh, BOOL walk)
15498 {
15499    int i, size, offset, direction, last, status;
15500    struct tm *tms, ltms;
15501    DWORD lt, ltime, lact;
15502    char str[256], file_name[256], dir[256];
15503    HNDLE hDB;
15504 
15505 #if !defined(OS_VXWORKS)
15506 #if !defined(OS_VMS)
15507    tzset();
15508 #endif
15509 #endif
15510 
15511    /* open file */
15512    cm_get_experiment_database(&hDB, NULL);
15513 
15514    size = sizeof(dir);
15515    memset(dir, 0, size);
15516    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15517    if (status != DB_SUCCESS)
15518       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15519 
15520    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15521       strcat(dir, DIR_SEPARATOR_STR);
15522 
15523    /* check tag for direction */
15524    direction = 0;
15525    if (strpbrk(tag, "+-")) {
15526       direction = atoi(strpbrk(tag, "+-"));
15527       *strpbrk(tag, "+-") = 0;
15528    }
15529 
15530    /* if tag is given, open file directly */
15531    if (tag[0]) {
15532       /* extract time structure from tag */
15533       tms = &ltms;
15534       memset(tms, 0, sizeof(struct tm));
15535       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
15536       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
15537       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
15538       tms->tm_hour = 12;
15539 
15540       if (tms->tm_year < 90)
15541          tms->tm_year += 100;
15542       ltime = lt = mktime(tms);
15543 
15544       strcpy(str, tag);
15545       if (strchr(str, '.')) {
15546          offset = atoi(strchr(str, '.') + 1);
15547          *strchr(str, '.') = 0;
15548       } else
15549          return EL_FILE_ERROR;
15550 
15551       do {
15552          tms = localtime((const time_t *) &ltime);
15553 
15554          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15555                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15556          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15557 
15558          if (*fh < 0) {
15559             if (!walk)
15560                return EL_FILE_ERROR;
15561 
15562             if (direction == -1)
15563                ltime -= 3600 * 24;      /* one day back */
15564             else
15565                ltime += 3600 * 24;      /* go forward one day */
15566 
15567             /* set new tag */
15568             tms = localtime((const time_t *) &ltime);
15569             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1,
15570                     tms->tm_mday);
15571          }
15572 
15573          /* in forward direction, stop today */
15574          if (direction != -1 && ltime > (DWORD) time(NULL) + 3600 * 24)
15575             break;
15576 
15577          /* in backward direction, go back 10 years */
15578          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
15579             break;
15580 
15581       } while (*fh < 0);
15582 
15583       if (*fh < 0)
15584          return EL_FILE_ERROR;
15585 
15586       lseek(*fh, offset, SEEK_SET);
15587 
15588       /* check if start of message */
15589       i = read(*fh, str, 15);
15590       if (i <= 0) {
15591          close(*fh);
15592          return EL_FILE_ERROR;
15593       }
15594 
15595       if (strncmp(str, "$Start$: ", 9) != 0) {
15596          close(*fh);
15597          return EL_FILE_ERROR;
15598       }
15599 
15600       lseek(*fh, offset, SEEK_SET);
15601    }
15602 
15603    /* open most recent file if no tag given */
15604    if (tag[0] == 0) {
15605       time((long *) &lt);
15606       ltime = lt;
15607       do {
15608          tms = localtime((const time_t *) &ltime);
15609 
15610          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15611                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15612          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15613 
15614          if (*fh < 0)
15615             ltime -= 3600 * 24; /* one day back */
15616 
15617       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
15618 
15619       if (*fh < 0)
15620          return EL_FILE_ERROR;
15621 
15622       /* remember tag */
15623       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15624 
15625       lseek(*fh, 0, SEEK_END);
15626 
15627       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
15628    }
15629 
15630 
15631    if (direction == -1) {
15632       /* seek previous message */
15633 
15634       if (TELL(*fh) == 0) {
15635          /* go back one day */
15636          close(*fh);
15637 
15638          lt = ltime;
15639          do {
15640             lt -= 3600 * 24;
15641             tms = localtime((const time_t *) &lt);
15642             sprintf(str, "%02d%02d%02d.0",
15643                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15644 
15645             status = el_search_message(str, fh, FALSE);
15646 
15647          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
15648 
15649          if (status != EL_SUCCESS)
15650             return EL_FIRST_MSG;
15651 
15652          /* adjust tag */
15653          strcpy(tag, str);
15654 
15655          /* go to end of current file */
15656          lseek(*fh, 0, SEEK_END);
15657       }
15658 
15659       /* read previous message size */
15660       lseek(*fh, -17, SEEK_CUR);
15661       i = read(*fh, str, 17);
15662       if (i <= 0) {
15663          close(*fh);
15664          return EL_FILE_ERROR;
15665       }
15666 
15667       if (strncmp(str, "$End$: ", 7) != 0) {
15668          close(*fh);
15669          return EL_FILE_ERROR;
15670       }
15671 
15672       /* make sure the input string to atoi() is zero-terminated:
15673        * $End$:      355garbage
15674        * 01234567890123456789 */
15675       str[15] = 0;
15676 
15677       size = atoi(str + 7);
15678       assert(size > 15);
15679 
15680       lseek(*fh, -size, SEEK_CUR);
15681 
15682       /* adjust tag */
15683       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15684    }
15685 
15686    if (direction == 1) {
15687       /* seek next message */
15688 
15689       /* read current message size */
15690       last = TELL(*fh);
15691 
15692       i = read(*fh, str, 15);
15693       if (i <= 0) {
15694          close(*fh);
15695          return EL_FILE_ERROR;
15696       }
15697       lseek(*fh, -15, SEEK_CUR);
15698 
15699       if (strncmp(str, "$Start$: ", 9) != 0) {
15700          close(*fh);
15701          return EL_FILE_ERROR;
15702       }
15703 
15704       /* make sure the input string to atoi() is zero-terminated
15705        * $Start$:    606garbage
15706        * 01234567890123456789 */
15707       str[15] = 0;
15708 
15709       size = atoi(str + 9);
15710       assert(size > 15);
15711 
15712       lseek(*fh, size, SEEK_CUR);
15713 
15714       /* if EOF, goto next day */
15715       i = read(*fh, str, 15);
15716       if (i < 15) {
15717          close(*fh);
15718          time((long *) &lact);
15719 
15720          lt = ltime;
15721          do {
15722             lt += 3600 * 24;
15723             tms = localtime((const time_t *) &lt);
15724             sprintf(str, "%02d%02d%02d.0",
15725                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15726 
15727             status = el_search_message(str, fh, FALSE);
15728 
15729          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
15730 
15731          if (status != EL_SUCCESS)
15732             return EL_LAST_MSG;
15733 
15734          /* adjust tag */
15735          strcpy(tag, str);
15736 
15737          /* go to beginning of current file */
15738          lseek(*fh, 0, SEEK_SET);
15739       } else
15740          lseek(*fh, -15, SEEK_CUR);
15741 
15742       /* adjust tag */
15743       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15744    }
15745 
15746    return EL_SUCCESS;
15747 }
15748 
15749 
15750 /********************************************************************/
15751 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
15752                 char *system, char *subject, char *text, int *textsize,
15753                 char *orig_tag, char *reply_tag,
15754                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
15755 /********************************************************************\
15756 
15757   Routine: el_retrieve
15758 
15759   Purpose: Retrieve an ELog entry by its message tab
15760 
15761   Input:
15762     char   *tag             tag in the form YYMMDD.offset
15763     int    *size            Size of text buffer
15764 
15765   Output:
15766     char   *tag             tag of retrieved message
15767     char   *date            Date/time of message recording
15768     int    *run             Run number
15769     char   *author          Message author
15770     char   *type            Message type
15771     char   *system          Message system
15772     char   *subject         Subject
15773     char   *text            Message text
15774     char   *orig_tag        Original message if this one is a reply
15775     char   *reply_tag       Reply for current message
15776     char   *attachment1/2/3 File attachment
15777     char   *encoding        Encoding of message
15778     int    *size            Actual message text size
15779 
15780   Function value:
15781     EL_SUCCESS              Successful completion
15782     EL_LAST_MSG             Last message in log
15783 
15784 \********************************************************************/
15785 {
15786    int size, fh = 0, offset, search_status, rd;
15787    char str[256], *p;
15788    char message[10000], thread[256], attachment_all[256];
15789 
15790    if (tag[0]) {
15791       search_status = el_search_message(tag, &fh, TRUE);
15792       if (search_status != EL_SUCCESS)
15793          return search_status;
15794    } else {
15795       /* open most recent message */
15796       strcpy(tag, "-1");
15797       search_status = el_search_message(tag, &fh, TRUE);
15798       if (search_status != EL_SUCCESS)
15799          return search_status;
15800    }
15801 
15802    /* extract message size */
15803    offset = TELL(fh);
15804    rd = read(fh, str, 15);
15805    assert(rd == 15);
15806 
15807    /* make sure the input string is zero-terminated before we call atoi() */
15808    str[15] = 0;
15809 
15810    /* get size */
15811    size = atoi(str + 9);
15812 
15813    assert(strncmp(str, "$Start$:", 8) == 0);
15814    assert(size > 15);
15815    assert(size < sizeof(message));
15816 
15817    memset(message, 0, sizeof(message));
15818 
15819    rd = read(fh, message, size);
15820    assert(rd > 0);
15821    assert((rd + 15 == size) || (rd == size));
15822 
15823    close(fh);
15824 
15825    /* decode message */
15826    if (strstr(message, "Run: ") && run)
15827       *run = atoi(strstr(message, "Run: ") + 5);
15828 
15829    el_decode(message, "Date: ", date, 80);      /* size from show_elog_submit_query() */
15830    el_decode(message, "Thread: ", thread, sizeof(thread));
15831    el_decode(message, "Author: ", author, 80);  /* size from show_elog_submit_query() */
15832    el_decode(message, "Type: ", type, 80);      /* size from show_elog_submit_query() */
15833    el_decode(message, "System: ", system, 80);  /* size from show_elog_submit_query() */
15834    el_decode(message, "Subject: ", subject, 256);       /* size from show_elog_submit_query() */
15835    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15836    el_decode(message, "Encoding: ", encoding, 80);      /* size from show_elog_submit_query() */
15837 
15838    /* break apart attachements */
15839    if (attachment1 && attachment2 && attachment3) {
15840       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15841       p = strtok(attachment_all, ",");
15842       if (p != NULL) {
15843          strcpy(attachment1, p);
15844          p = strtok(NULL, ",");
15845          if (p != NULL) {
15846             strcpy(attachment2, p);
15847             p = strtok(NULL, ",");
15848             if (p != NULL)
15849                strcpy(attachment3, p);
15850          }
15851       }
15852 
15853       assert(strlen(attachment1) < 256);        /* size from show_elog_submit_query() */
15854       assert(strlen(attachment2) < 256);        /* size from show_elog_submit_query() */
15855       assert(strlen(attachment3) < 256);        /* size from show_elog_submit_query() */
15856    }
15857 
15858    /* conver thread in reply-to and reply-from */
15859    if (orig_tag != NULL && reply_tag != NULL) {
15860       p = strtok(thread, " \r");
15861       if (p != NULL)
15862          strcpy(orig_tag, p);
15863       else
15864          strcpy(orig_tag, "");
15865       p = strtok(NULL, " \r");
15866       if (p != NULL)
15867          strcpy(reply_tag, p);
15868       else
15869          strcpy(reply_tag, "");
15870       if (atoi(orig_tag) == 0)
15871          orig_tag[0] = 0;
15872       if (atoi(reply_tag) == 0)
15873          reply_tag[0] = 0;
15874    }
15875 
15876    p = strstr(message, "========================================\n");
15877 
15878    if (text != NULL) {
15879       if (p != NULL) {
15880          p += 41;
15881          if ((int) strlen(p) >= *textsize) {
15882             strncpy(text, p, *textsize - 1);
15883             text[*textsize - 1] = 0;
15884             return EL_TRUNCATED;
15885          } else {
15886             strcpy(text, p);
15887 
15888             /* strip end tag */
15889             if (strstr(text, "$End$"))
15890                *strstr(text, "$End$") = 0;
15891 
15892             *textsize = strlen(text);
15893          }
15894       } else {
15895          text[0] = 0;
15896          *textsize = 0;
15897       }
15898    }
15899 
15900    if (search_status == EL_LAST_MSG)
15901       return EL_LAST_MSG;
15902 
15903    return EL_SUCCESS;
15904 }
15905 
15906 
15907 /********************************************************************/
15908 INT el_search_run(int run, char *return_tag)
15909 /********************************************************************\
15910 
15911   Routine: el_search_run
15912 
15913   Purpose: Find first message belonging to a specific run
15914 
15915   Input:
15916     int    run              Run number
15917 
15918   Output:
15919     char   *tag             tag of retrieved message
15920 
15921   Function value:
15922     EL_SUCCESS              Successful completion
15923     EL_LAST_MSG             Last message in log
15924 
15925 \********************************************************************/
15926 {
15927    int actual_run, fh, status;
15928    char tag[256];
15929 
15930    tag[0] = return_tag[0] = 0;
15931 
15932    do {
15933       /* open first message in file */
15934       strcat(tag, "-1");
15935       status = el_search_message(tag, &fh, TRUE);
15936       if (status == EL_FIRST_MSG)
15937          break;
15938       if (status != EL_SUCCESS)
15939          return status;
15940       close(fh);
15941 
15942       if (strchr(tag, '.') != NULL)
15943          strcpy(strchr(tag, '.'), ".0");
15944 
15945       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15946                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15947    } while (actual_run >= run);
15948 
15949    while (actual_run < run) {
15950       strcat(tag, "+1");
15951       status = el_search_message(tag, &fh, TRUE);
15952       if (status == EL_LAST_MSG)
15953          break;
15954       if (status != EL_SUCCESS)
15955          return status;
15956       close(fh);
15957 
15958       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15959                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15960    }
15961 
15962    strcpy(return_tag, tag);
15963 
15964    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15965       return status;
15966 
15967    return EL_SUCCESS;
15968 }
15969 
15970 
15971 /********************************************************************/
15972 INT el_delete_message(char *tag)
15973 /********************************************************************\
15974 
15975   Routine: el_submit
15976 
15977   Purpose: Submit an ELog entry
15978 
15979   Input:
15980     char   *tag             Message tage
15981 
15982   Output:
15983     <none>
15984 
15985   Function value:
15986     EL_SUCCESS              Successful completion
15987 
15988 \********************************************************************/
15989 {
15990 #ifdef LOCAL_ROUTINES
15991    INT n, size, fh, mutex, offset = 0, tail_size, status;
15992    char dir[256], str[256], file_name[256];
15993    HNDLE hDB;
15994    char *buffer = NULL;
15995 
15996    cm_get_experiment_database(&hDB, NULL);
15997 
15998    /* request semaphore */
15999    cm_get_experiment_mutex(NULL, &mutex);
16000    status = ss_mutex_wait_for(mutex, 5 * 60 * 1000);
16001    if (status != SS_SUCCESS) {
16002       cm_msg(MERROR, "el_delete_message",
16003              "Cannot lock experiment mutex, ss_mutex_wait_for() status %d", status);
16004       abort();
16005    }
16006 
16007 
16008    /* generate file name YYMMDD.log in data directory */
16009    cm_get_experiment_database(&hDB, NULL);
16010 
16011    size = sizeof(dir);
16012    memset(dir, 0, size);
16013    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
16014    if (status != DB_SUCCESS)
16015       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
16016 
16017    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
16018       strcat(dir, DIR_SEPARATOR_STR);
16019 
16020    strcpy(str, tag);
16021    if (strchr(str, '.')) {
16022       offset = atoi(strchr(str, '.') + 1);
16023       *strchr(str, '.') = 0;
16024    }
16025    sprintf(file_name, "%s%s.log", dir, str);
16026    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
16027    if (fh < 0) {
16028       ss_mutex_release(mutex);
16029       return EL_FILE_ERROR;
16030    }
16031    lseek(fh, offset, SEEK_SET);
16032    read(fh, str, 16);
16033    size = atoi(str + 9);
16034 
16035    /* buffer tail of logfile */
16036    lseek(fh, 0, SEEK_END);
16037    tail_size = TELL(fh) - (offset + size);
16038 
16039    if (tail_size > 0) {
16040       buffer = (char *) M_MALLOC(tail_size);
16041       if (buffer == NULL) {
16042          close(fh);
16043          ss_mutex_release(mutex);
16044          return EL_FILE_ERROR;
16045       }
16046 
16047       lseek(fh, offset + size, SEEK_SET);
16048       n = read(fh, buffer, tail_size);
16049    }
16050    lseek(fh, offset, SEEK_SET);
16051 
16052    if (tail_size > 0) {
16053       n = write(fh, buffer, tail_size);
16054       M_FREE(buffer);
16055    }
16056 
16057    /* truncate file here */
16058 #ifdef OS_WINNT
16059    chsize(fh, TELL(fh));
16060 #else
16061    ftruncate(fh, TELL(fh));
16062 #endif
16063 
16064    /* if file length gets zero, delete file */
16065    tail_size = lseek(fh, 0, SEEK_END);
16066    close(fh);
16067 
16068    if (tail_size == 0)
16069       remove(file_name);
16070 
16071    /* release elog mutex */
16072    ss_mutex_release(mutex);
16073 #endif                          /* LOCAL_ROUTINES */
16074 
16075    return EL_SUCCESS;
16076 }
16077 
16078 /**dox***************************************************************/
16079 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16080 
16081 /**dox***************************************************************/
16082                    /** @} *//* end of elfunctionc */
16083 
16084 /**dox***************************************************************/
16085 /** @addtogroup alfunctionc
16086  *  
16087  *  @{  */
16088 
16089 /**dox***************************************************************/
16090 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16091 
16092 /********************************************************************\
16093 *                                                                    *
16094 *                     Alarm functions                                *
16095 *                                                                    *
16096 \********************************************************************/
16097 
16098 /********************************************************************/
16099 BOOL al_evaluate_condition(char *condition, char *value)
16100 {
16101    HNDLE hDB, hkey;
16102    int i, j, index, size;
16103    KEY key;
16104    double value1, value2;
16105    char str[256], op[3], function[80];
16106    char data[10000];
16107    DWORD time;
16108 
16109    strcpy(str, condition);
16110    op[1] = op[2] = 0;
16111    value1 = value2 = 0;
16112    index = 0;
16113 
16114    /* find value and operator */
16115    for (i = strlen(str) - 1; i > 0; i--)
16116       if (strchr("<>=!", str[i]) != NULL)
16117          break;
16118    op[0] = str[i];
16119    value2 = atof(str + i + 1);
16120    str[i] = 0;
16121 
16122    if (i > 0 && strchr("<>=!", str[i - 1])) {
16123       op[1] = op[0];
16124       op[0] = str[--i];
16125       str[i] = 0;
16126    }
16127 
16128    i--;
16129    while (i > 0 && str[i] == ' ')
16130       i--;
16131    str[i + 1] = 0;
16132 
16133    /* check if function */
16134    function[0] = 0;
16135    if (str[i] == ')') {
16136       str[i--] = 0;
16137       if (strchr(str, '(')) {
16138          *strchr(str, '(') = 0;
16139          strcpy(function, str);
16140          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
16141             str[j] = str[i];
16142          str[j] = 0;
16143          i = j - 1;
16144       }
16145    }
16146 
16147    /* find key */
16148    if (str[i] == ']') {
16149       str[i--] = 0;
16150       while (i > 0 && isdigit(str[i]))
16151          i--;
16152       index = atoi(str + i + 1);
16153       str[i] = 0;
16154    }
16155 
16156    cm_get_experiment_database(&hDB, NULL);
16157    db_find_key(hDB, 0, str, &hkey);
16158    if (!hkey) {
16159       cm_msg(MERROR, "al_evaluate_condition",
16160              "Cannot find key %s to evaluate alarm condition", str);
16161       if (value)
16162          strcpy(value, "unknown");
16163       return FALSE;
16164    }
16165 
16166    if (equal_ustring(function, "access")) {
16167       /* check key access time */
16168       db_get_key_time(hDB, hkey, &time);
16169       sprintf(str, "%ld", time);
16170       value1 = atof(str);
16171    } else {
16172       /* get key data and convert to double */
16173       db_get_key(hDB, hkey, &key);
16174       size = sizeof(data);
16175       db_get_data(hDB, hkey, data, &size, key.type);
16176       db_sprintf(str, data, size, index, key.type);
16177       value1 = atof(str);
16178    }
16179 
16180    /* return value */
16181    if (value)
16182       strcpy(value, str);
16183 
16184    /* now do logical operation */
16185    if (strcmp(op, "=") == 0)
16186       return value1 == value2;
16187    if (strcmp(op, "==") == 0)
16188       return value1 == value2;
16189    if (strcmp(op, "!=") == 0)
16190       return value1 != value2;
16191    if (strcmp(op, "<") == 0)
16192       return value1 < value2;
16193    if (strcmp(op, ">") == 0)
16194       return value1 > value2;
16195    if (strcmp(op, "<=") == 0)
16196       return value1 <= value2;
16197    if (strcmp(op, ">=") == 0)
16198       return value1 >= value2;
16199 
16200    return FALSE;
16201 }
16202 
16203 /**dox***************************************************************/
16204 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16205 
16206 /********************************************************************/
16207 /**
16208 Trigger a certain alarm.
16209 \code  ...
16210   lazy.alarm[0] = 0;
16211   size = sizeof(lazy.alarm);
16212   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
16213 
16214   // trigger alarm if defined
16215   if (lazy.alarm[0])
16216     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
16217   ...
16218 \endcode
16219 @param alarm_name Alarm name, defined in /alarms/alarms
16220 @param alarm_message Optional message which goes with alarm
16221 @param default_class If alarm is not yet defined under
16222                     /alarms/alarms/<alarm_name>, a new one
16223                     is created and this default class is used.
16224 @param cond_str String displayed in alarm condition
16225 @param type Alarm type, one of AT_xxx
16226 @return AL_SUCCESS, AL_INVALID_NAME
16227 */
16228 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class,
16229                      char *cond_str, INT type)
16230 {
16231    if (rpc_is_remote())
16232       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message,
16233                       default_class, cond_str, type);
16234 
16235 #ifdef LOCAL_ROUTINES
16236    {
16237       int status, size;
16238       HNDLE hDB, hkeyalarm;
16239       char str[256];
16240       ALARM alarm;
16241       BOOL flag;
16242       ALARM_ODB_STR(alarm_odb_str);
16243 
16244       cm_get_experiment_database(&hDB, NULL);
16245 
16246       /* check online mode */
16247       flag = TRUE;
16248       size = sizeof(flag);
16249       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
16250       if (!flag)
16251          return AL_SUCCESS;
16252 
16253       /* find alarm */
16254       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16255       db_find_key(hDB, 0, str, &hkeyalarm);
16256       if (!hkeyalarm) {
16257          /* alarm must be an internal analyzer alarm, so create a default alarm */
16258          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
16259          db_find_key(hDB, 0, str, &hkeyalarm);
16260          if (!hkeyalarm) {
16261             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
16262             return AL_ERROR_ODB;
16263          }
16264 
16265          if (default_class && default_class[0])
16266             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
16267          status = TRUE;
16268          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
16269       }
16270 
16271       /* set parameters for internal alarms */
16272       if (type != AT_EVALUATED && type != AT_PERIODIC) {
16273          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
16274          strcpy(str, cond_str);
16275          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
16276       }
16277 
16278       size = sizeof(alarm);
16279       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16280       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16281          /* make sure alarm record has right structure */
16282          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
16283 
16284          size = sizeof(alarm);
16285          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16286          if (status != DB_SUCCESS) {
16287             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
16288             return AL_ERROR_ODB;
16289          }
16290       }
16291 
16292       /* if internal alarm, check if active and check interval */
16293       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
16294          /* check global alarm flag */
16295          flag = TRUE;
16296          size = sizeof(flag);
16297          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL,
16298                       TRUE);
16299          if (!flag)
16300             return AL_SUCCESS;
16301 
16302          if (!alarm.active)
16303             return AL_SUCCESS;
16304 
16305          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
16306             return AL_SUCCESS;
16307 
16308          /* now the alarm will be triggered, so save time */
16309          alarm.checked_last = ss_time();
16310       }
16311 
16312       /* write back alarm message for internal alarms */
16313       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
16314          strncpy(alarm.alarm_message, alarm_message, 79);
16315          alarm.alarm_message[79] = 0;
16316       }
16317 
16318       /* now trigger alarm class defined in this alarm */
16319       if (alarm.alarm_class[0])
16320          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
16321 
16322       /* signal alarm being triggered */
16323       cm_asctime(str, sizeof(str));
16324 
16325       if (!alarm.triggered)
16326          strcpy(alarm.time_triggered_first, str);
16327 
16328       alarm.triggered++;
16329       strcpy(alarm.time_triggered_last, str);
16330 
16331       alarm.checked_last = ss_time();
16332 
16333       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16334       if (status != DB_SUCCESS) {
16335          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
16336          return AL_ERROR_ODB;
16337       }
16338 
16339    }
16340 #endif                          /* LOCAL_ROUTINES */
16341 
16342    return AL_SUCCESS;
16343 }
16344 
16345 /**dox***************************************************************/
16346 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16347 
16348 /********************************************************************/
16349 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
16350 /********************************************************************\
16351 
16352   Routine: al_trigger_class
16353 
16354   Purpose: Trigger a certain alarm class
16355 
16356   Input:
16357     char   *alarm_class     Alarm class, must be defined in
16358                             /alarms/classes
16359     char   *alarm_message   Optional message which goes with alarm
16360     BOOL   first            TRUE if alarm is triggered first time
16361                             (used for elog)
16362 
16363   Output:
16364 
16365   Function value:
16366     AL_INVALID_NAME         Alarm class not defined
16367     AL_SUCCESS              Successful completion
16368 
16369 \********************************************************************/
16370 {
16371    int status, size, state;
16372    HNDLE hDB, hkeyclass;
16373    char str[256], command[256], tag[32];
16374    ALARM_CLASS ac;
16375 
16376    cm_get_experiment_database(&hDB, NULL);
16377 
16378    /* get alarm class */
16379    sprintf(str, "/Alarms/Classes/%s", alarm_class);
16380    db_find_key(hDB, 0, str, &hkeyclass);
16381    if (!hkeyclass) {
16382       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
16383       return AL_INVALID_NAME;
16384    }
16385 
16386    size = sizeof(ac);
16387    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16388    if (status != DB_SUCCESS) {
16389       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
16390       return AL_ERROR_ODB;
16391    }
16392 
16393    /* write system message */
16394    if (ac.write_system_message &&
16395        (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
16396       sprintf(str, "%s: %s", alarm_class, alarm_message);
16397       cm_msg(MTALK, "al_trigger_class", str);
16398       ac.system_message_last = ss_time();
16399    }
16400 
16401    /* write elog message on first trigger */
16402    if (ac.write_elog_message && first)
16403       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
16404                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
16405 
16406    /* execute command */
16407    if (ac.execute_command[0] &&
16408        ac.execute_interval > 0 &&
16409        (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
16410       sprintf(str, "%s: %s", alarm_class, alarm_message);
16411       sprintf(command, ac.execute_command, str);
16412       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
16413       ss_system(command);
16414       ac.execute_last = ss_time();
16415    }
16416 
16417    /* stop run */
16418    if (ac.stop_run) {
16419       state = STATE_STOPPED;
16420       size = sizeof(state);
16421       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
16422       if (state != STATE_STOPPED)
16423          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
16424    }
16425 
16426    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16427    if (status != DB_SUCCESS) {
16428       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
16429       return AL_ERROR_ODB;
16430    }
16431 
16432    return AL_SUCCESS;
16433 }
16434 
16435 
16436 /********************************************************************/
16437 INT al_reset_alarm(char *alarm_name)
16438 /********************************************************************\
16439 
16440   Routine: al_reset_alarm
16441 
16442   Purpose: Reset (acknowledge) alarm
16443 
16444   Input:
16445     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
16446                             If NULL reset all alarms
16447 
16448   Output:
16449     <none>
16450 
16451   Function value:
16452     AL_INVALID_NAME         Alarm name not defined
16453     AL_RESET                Alarm was triggered and reset
16454     AL_SUCCESS              Successful completion
16455 
16456 \********************************************************************/
16457 {
16458    int status, size, i;
16459    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
16460    KEY key;
16461    char str[256];
16462    ALARM alarm;
16463    ALARM_CLASS ac;
16464 
16465    cm_get_experiment_database(&hDB, NULL);
16466 
16467    if (alarm_name == NULL) {
16468       /* reset all alarms */
16469       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
16470       if (hkeyalarm) {
16471          for (i = 0;; i++) {
16472             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
16473 
16474             if (!hsubkey)
16475                break;
16476 
16477             db_get_key(hDB, hsubkey, &key);
16478             al_reset_alarm(key.name);
16479          }
16480       }
16481       return AL_SUCCESS;
16482    }
16483 
16484    /* find alarm and alarm class */
16485    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16486    db_find_key(hDB, 0, str, &hkeyalarm);
16487    if (!hkeyalarm) {
16488       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
16489       return AL_INVALID_NAME;
16490    }
16491 
16492    size = sizeof(alarm);
16493    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16494    if (status != DB_SUCCESS) {
16495       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
16496       return AL_ERROR_ODB;
16497    }
16498 
16499    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
16500    db_find_key(hDB, 0, str, &hkeyclass);
16501    if (!hkeyclass) {
16502       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB",
16503              alarm.alarm_class);
16504       return AL_INVALID_NAME;
16505    }
16506 
16507    size = sizeof(ac);
16508    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16509    if (status != DB_SUCCESS) {
16510       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
16511       return AL_ERROR_ODB;
16512    }
16513 
16514    if (alarm.triggered) {
16515       alarm.triggered = 0;
16516       alarm.time_triggered_first[0] = 0;
16517       alarm.time_triggered_last[0] = 0;
16518       alarm.checked_last = 0;
16519 
16520       ac.system_message_last = 0;
16521       ac.execute_last = 0;
16522 
16523       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16524       if (status != DB_SUCCESS) {
16525          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
16526          return AL_ERROR_ODB;
16527       }
16528       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16529       if (status != DB_SUCCESS) {
16530          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
16531          return AL_ERROR_ODB;
16532       }
16533       return AL_RESET;
16534    }
16535 
16536    return AL_SUCCESS;
16537 }
16538 
16539 
16540 /********************************************************************/
16541 INT al_check()
16542 /********************************************************************\
16543 
16544   Routine: al_scan
16545 
16546   Purpose: Scan ODB alarams and programs
16547 
16548   Input:
16549 
16550   Output:
16551 
16552   Function value:
16553     AL_SUCCESS              Successful completion
16554 
16555 \********************************************************************/
16556 {
16557    if (rpc_is_remote())
16558       return rpc_call(RPC_AL_CHECK);
16559 
16560 #ifdef LOCAL_ROUTINES
16561    {
16562       INT i, status, size, mutex;
16563       HNDLE hDB, hkeyroot, hkey;
16564       KEY key;
16565       ALARM alarm;
16566       char str[256], value[256];
16567       DWORD now;
16568       PROGRAM_INFO program_info;
16569       BOOL flag;
16570 
16571       ALARM_CLASS_STR(alarm_class_str);
16572       ALARM_ODB_STR(alarm_odb_str);
16573       ALARM_PERIODIC_STR(alarm_periodic_str);
16574 
16575       cm_get_experiment_database(&hDB, NULL);
16576 
16577       if (hDB == 0)
16578          return AL_SUCCESS;     /* called from server not yet connected */
16579 
16580       /* check online mode */
16581       flag = TRUE;
16582       size = sizeof(flag);
16583       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
16584       if (!flag)
16585          return AL_SUCCESS;
16586 
16587       /* check global alarm flag */
16588       flag = TRUE;
16589       size = sizeof(flag);
16590       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
16591       if (!flag)
16592          return AL_SUCCESS;
16593 
16594       /* request semaphore */
16595       cm_get_experiment_mutex(&mutex, NULL);
16596       status = ss_mutex_wait_for(mutex, 100);
16597       if (status != SS_SUCCESS)
16598          return SUCCESS;        /* someone else is doing alarm business */
16599 
16600       /* check ODB alarms */
16601       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16602       if (!hkeyroot) {
16603          /* create default ODB alarm */
16604          status =
16605              db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
16606          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16607          if (!hkeyroot) {
16608             ss_mutex_release(mutex);
16609             return SUCCESS;
16610          }
16611 
16612          status =
16613              db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic",
16614                               strcomb(alarm_periodic_str));
16615          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16616          if (!hkeyroot) {
16617             ss_mutex_release(mutex);
16618             return SUCCESS;
16619          }
16620 
16621          /* create default alarm classes */
16622          status =
16623              db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
16624          status =
16625              db_create_record(hDB, 0, "/Alarms/Classes/Warning",
16626                               strcomb(alarm_class_str));
16627          if (status != DB_SUCCESS) {
16628             ss_mutex_release(mutex);
16629             return SUCCESS;
16630          }
16631       }
16632 
16633       for (i = 0;; i++) {
16634          status = db_enum_key(hDB, hkeyroot, i, &hkey);
16635          if (status == DB_NO_MORE_SUBKEYS)
16636             break;
16637 
16638          db_get_key(hDB, hkey, &key);
16639 
16640          size = sizeof(alarm);
16641          status = db_get_record(hDB, hkey, &alarm, &size, 0);
16642          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16643             /* make sure alarm record has right structure */
16644             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
16645             size = sizeof(alarm);
16646             status = db_get_record(hDB, hkey, &alarm, &size, 0);
16647             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16648                cm_msg(MERROR, "al_check", "Cannot get alarm record");
16649                continue;
16650             }
16651          }
16652 
16653          /* check periodic alarm only when active */
16654          if (alarm.active &&
16655              alarm.type == AT_PERIODIC &&
16656              alarm.check_interval > 0 &&
16657              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16658             /* if checked_last has not been set, set it to current time */
16659             if (alarm.checked_last == 0) {
16660                alarm.checked_last = ss_time();
16661                db_set_record(hDB, hkey, &alarm, size, 0);
16662             } else
16663                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "",
16664                                 AT_PERIODIC);
16665          }
16666 
16667          /* check alarm only when active and not internal */
16668          if (alarm.active &&
16669              alarm.type == AT_EVALUATED &&
16670              alarm.check_interval > 0 &&
16671              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16672             /* if condition is true, trigger alarm */
16673             if (al_evaluate_condition(alarm.condition, value)) {
16674                sprintf(str, alarm.alarm_message, value);
16675                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
16676             } else {
16677                alarm.checked_last = ss_time();
16678                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
16679                if (status != DB_SUCCESS) {
16680                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
16681                   continue;
16682                }
16683             }
16684          }
16685       }
16686 
16687       /* check /programs alarms */
16688       db_find_key(hDB, 0, "/Programs", &hkeyroot);
16689       if (hkeyroot) {
16690          for (i = 0;; i++) {
16691             status = db_enum_key(hDB, hkeyroot, i, &hkey);
16692             if (status == DB_NO_MORE_SUBKEYS)
16693                break;
16694 
16695             db_get_key(hDB, hkey, &key);
16696 
16697             /* don't check "execute on xxx" */
16698             if (key.type != TID_KEY)
16699                continue;
16700 
16701             size = sizeof(program_info);
16702             status = db_get_record(hDB, hkey, &program_info, &size, 0);
16703             if (status != DB_SUCCESS) {
16704                cm_msg(MERROR, "al_check", "Cannot get program info record");
16705                continue;
16706             }
16707 
16708             now = ss_time();
16709 
16710             rpc_get_name(str);
16711             str[strlen(key.name)] = 0;
16712             if (!equal_ustring(str, key.name) &&
16713                 cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
16714                if (program_info.first_failed == 0)
16715                   program_info.first_failed = now;
16716 
16717                /* fire alarm when not running for more than what specified in check interval */
16718                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
16719                   /* if not running and alarm calss defined, trigger alarm */
16720                   if (program_info.alarm_class[0]) {
16721                      sprintf(str, "Program %s is not running", key.name);
16722                      al_trigger_alarm(key.name, str, program_info.alarm_class,
16723                                       "Program not running", AT_PROGRAM);
16724                   }
16725 
16726                   /* auto restart program */
16727                   if (program_info.auto_restart && program_info.start_command[0]) {
16728                      ss_system(program_info.start_command);
16729                      program_info.first_failed = 0;
16730                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
16731                   }
16732                }
16733             } else
16734                program_info.first_failed = 0;
16735 
16736             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
16737          }
16738       }
16739 
16740       ss_mutex_release(mutex);
16741    }
16742 #endif                          /* LOCAL_COUTINES */
16743 
16744    return SUCCESS;
16745 }
16746 
16747 /**dox***************************************************************/
16748 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16749 
16750                    /** @} *//* end of alfunctionc */
16751 
16752 /***** sKIP eb_xxx **************************************************/
16753 /**dox***************************************************************/
16754 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16755 /***** sKIP eb_xxx **************************************************/
16756 
16757 #if !defined(OS_VXWORKS)
16758 /********************************************************************\
16759 *                                                                    *
16760 *                 Event buffer functions                             *
16761 *                                                                    *
16762 \********************************************************************/
16763 
16764 /* PAA several modification in the eb_xxx()
16765    also new function eb_buffer_full()
16766 */
16767 static char *_event_ring_buffer = NULL;
16768 static INT _eb_size;
16769 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16770 
16771 /********************************************************************/
16772 INT eb_create_buffer(INT size)
16773 /********************************************************************\
16774 
16775   Routine: eb_create_buffer
16776 
16777   Purpose: Create an event buffer. Has to be called initially before
16778            any other eb_xxx function
16779 
16780   Input:
16781     INT    size             Size in bytes
16782 
16783   Output:
16784     none
16785 
16786   Function value:
16787     CM_SUCCESS              Successful completion
16788     BM_NO_MEMEORY           Out of memory
16789 
16790 \********************************************************************/
16791 {
16792    _event_ring_buffer = (char *) M_MALLOC(size);
16793    if (_event_ring_buffer == NULL)
16794       return BM_NO_MEMORY;
16795 
16796    memset(_event_ring_buffer, 0, size);
16797    _eb_size = size;
16798 
16799    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16800 
16801    _send_sock = rpc_get_event_sock();
16802 
16803    return CM_SUCCESS;
16804 }
16805 
16806 /********************************************************************/
16807 INT eb_free_buffer()
16808 /********************************************************************\
16809 
16810   Routine: eb_free_buffer
16811 
16812   Purpose: Free memory allocated voa eb_create_buffer
16813 
16814   Input:
16815     none
16816 
16817   Output:
16818     none
16819 
16820   Function value:
16821     CM_SUCCESS              Successful completion
16822 
16823 \********************************************************************/
16824 {
16825    if (_event_ring_buffer)
16826       M_FREE(_event_ring_buffer);
16827 
16828    _eb_size = 0;
16829    return CM_SUCCESS;
16830 }
16831 
16832 
16833 /********************************************************************/
16834 INT eb_free_space(void)
16835 /********************************************************************\
16836 
16837   Routine: eb_free_space
16838 
16839   Purpose: Compute and return usable free space in the event buffer
16840 
16841   Input:
16842     none
16843 
16844   Output:
16845     none
16846 
16847   Function value:
16848     INT    Number of usable free bytes in the event buffer
16849 
16850 \********************************************************************/
16851 {
16852    INT free;
16853 
16854    if (_event_ring_buffer == NULL) {
16855       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16856       return -1;
16857    }
16858 
16859    if (_eb_write_pointer >= _eb_read_pointer) {
16860       free = _eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer);
16861    } else if (_eb_write_pointer >= _event_ring_buffer) {
16862       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16863    } else if (_eb_end_pointer == _event_ring_buffer) {
16864       _eb_write_pointer = _event_ring_buffer;
16865       free = _eb_size;
16866    } else if (_eb_read_pointer == _event_ring_buffer) {
16867       free = 0;
16868    } else {
16869       _eb_write_pointer = _event_ring_buffer;
16870       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16871    }
16872 
16873    return free;
16874 }
16875 
16876 
16877 /********************************************************************/
16878 DWORD eb_get_level()
16879 /********************************************************************\
16880 
16881   Routine: eb_get_level
16882 
16883   Purpose: Return filling level of event buffer in percent
16884 
16885   Input:
16886     none
16887 
16888   Output:
16889     none
16890 
16891   Function value:
16892     DWORD level              0..99
16893 
16894 \********************************************************************/
16895 {
16896    INT size;
16897 
16898    size = _eb_size - eb_free_space();
16899 
16900    return (100 * size) / _eb_size;
16901 }
16902 
16903 
16904 /********************************************************************/
16905 BOOL eb_buffer_full(void)
16906 /********************************************************************\
16907 
16908   Routine: eb_buffer_full
16909 
16910   Purpose: Test if there is sufficient space in the event buffer
16911     for another event
16912 
16913   Input:
16914     none
16915 
16916   Output:
16917     none
16918 
16919   Function value:
16920     BOOL  Is there enough space for another event in the event buffer
16921 
16922 \********************************************************************/
16923 {
16924    INT free;
16925 
16926    free = eb_free_space();
16927 
16928    /* if max. event won't fit, return zero */
16929    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16930 }
16931 
16932 
16933 /********************************************************************/
16934 EVENT_HEADER *eb_get_pointer()
16935 /********************************************************************\
16936 
16937   Routine: eb_get_pointer
16938 
16939   Purpose: Get pointer to next free location in event buffer
16940 
16941   Input:
16942     none
16943 
16944   Output:
16945     none
16946 
16947   Function value:
16948     EVENT_HEADER *            Pointer to free location
16949 
16950 \********************************************************************/
16951 {
16952    /* if max. event won't fit, return zero */
16953    if (eb_buffer_full()) {
16954 #ifdef OS_VXWORKS
16955       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16956              _eb_read_pointer - _event_ring_buffer,
16957              _eb_write_pointer - _event_ring_buffer,
16958              _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16959 #endif
16960       return NULL;
16961    }
16962 
16963    /* leave space for buffer handle */
16964    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16965 }
16966 
16967 
16968 /********************************************************************/
16969 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16970 /********************************************************************\
16971 
16972   Routine: eb_increment_pointer
16973 
16974   Purpose: Increment write pointer of event buffer after an event
16975            has been copied into the buffer (at an address previously
16976            obtained via eb_get_pointer)
16977 
16978   Input:
16979     INT buffer_handle         Buffer handle event should be sent to
16980     INT event_size            Event size in bytes including header
16981 
16982   Output:
16983     none
16984 
16985   Function value:
16986     CM_SUCCESS                Successful completion
16987 
16988 \********************************************************************/
16989 {
16990    INT aligned_event_size;
16991 
16992    /* if not connected remotely, use bm_send_event */
16993    if (_send_sock == 0)
16994       return bm_send_event(buffer_handle,
16995                            _eb_write_pointer + sizeof(INT), event_size, SYNC);
16996 
16997    aligned_event_size = ALIGN8(event_size);
16998 
16999    /* copy buffer handle */
17000    *((INT *) _eb_write_pointer) = buffer_handle;
17001    _eb_write_pointer += sizeof(INT) + aligned_event_size;
17002 
17003    if (_eb_write_pointer > _eb_end_pointer)
17004       _eb_end_pointer = _eb_write_pointer;
17005 
17006    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
17007       cm_msg(MERROR, "eb_increment_pointer",
17008              "event size (%d) exeeds maximum event size (%d)", event_size,
17009              MAX_EVENT_SIZE);
17010 
17011    if (_eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer) <
17012        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
17013       _eb_write_pointer = _event_ring_buffer;
17014 
17015       /* avoid rp==wp */
17016       if (_eb_read_pointer == _event_ring_buffer)
17017          _eb_write_pointer--;
17018    }
17019 
17020    return CM_SUCCESS;
17021 }
17022 
17023 
17024 /********************************************************************/
17025 INT eb_send_events(BOOL send_all)
17026 /********************************************************************\
17027 
17028   Routine: eb_send_events
17029 
17030   Purpose: Send events from the event buffer to the server
17031 
17032   Input:
17033     BOOL send_all             If FALSE, only send events if buffer
17034                               contains more than _opt_tcp_size bytes
17035 
17036   Output:
17037     none
17038 
17039   Function value:
17040     CM_SUCCESS                Successful completion
17041 
17042 \********************************************************************/
17043 {
17044    char *eb_wp, *eb_ep;
17045    INT size, i;
17046 
17047    /* write pointers are volatile, so make copy */
17048    eb_ep = _eb_end_pointer;
17049    eb_wp = _eb_write_pointer;
17050 
17051    if (eb_wp == _eb_read_pointer)
17052       return CM_SUCCESS;
17053    if (eb_wp > _eb_read_pointer) {
17054       size = (PTYPE) eb_wp - (PTYPE) _eb_read_pointer;
17055 
17056       /* don't send if less than optimal TCP buffer size available */
17057       if (size < (INT) _opt_tcp_size && !send_all)
17058          return CM_SUCCESS;
17059    } else {
17060       /* send last piece of event buffer */
17061       size = (PTYPE) eb_ep - (PTYPE) _eb_read_pointer;
17062    }
17063 
17064    while (size > _opt_tcp_size) {
17065       /* send buffer */
17066       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
17067       if (i < 0) {
17068          printf("send_tcp() returned %d\n", i);
17069          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
17070          return RPC_NET_ERROR;
17071       }
17072 
17073       _eb_read_pointer += _opt_tcp_size;
17074       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
17075          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
17076 
17077       size -= _opt_tcp_size;
17078    }
17079 
17080    if (send_all || eb_wp < _eb_read_pointer) {
17081       /* send buffer */
17082       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
17083       if (i < 0) {
17084          printf("send_tcp() returned %d\n", i);
17085          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
17086          return RPC_NET_ERROR;
17087       }
17088 
17089       _eb_read_pointer += size;
17090       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
17091          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
17092    }
17093 
17094    /* Check for case where eb_wp = eb_ring_buffer - 1 */
17095    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
17096       return CM_SUCCESS;
17097    }
17098 
17099    if (eb_wp != _eb_read_pointer)
17100       return BM_MORE_EVENTS;
17101 
17102    return CM_SUCCESS;
17103 }
17104 
17105 #endif                          /* OS_VXWORKS  eb section */
17106 
17107 /**dox***************************************************************/
17108 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17109 
17110 /**dox***************************************************************/
17111 /** @addtogroup dmfunctionc
17112  *  
17113  *  @{  */
17114 
17115 /**dox***************************************************************/
17116 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17117 
17118 /********************************************************************\
17119 *                                                                    *
17120 *                 Dual memory buffer functions                       *
17121 *                                                                    *
17122 * Provide a dual memory buffer scheme for handling front-end         *
17123 * event. This code as been requested for allowing contemporary       *
17124 * task handling a)acquisition, b)network transfer if possible.       *
17125 * The pre-compiler switch will determine the mode of operation.      *
17126 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
17127 * a seperate task taking care of the dm_area_send                    *
17128 *                                                                    *
17129 * "*" : visible functions                                            *
17130 * dm_buffer_create():     *Setup the dual memory buffer              *
17131 *                          Setup semaphore                           *
17132 *                          Spawn second thread                       *
17133 * dm_buffer_release():    *Release memory allocation for dm          *
17134 *                          Force a kill of 2nd thread                *
17135 *                          Remove semaphore                          *
17136 * dm_area_full():         *Check for both area being full            *
17137 *                          None blocking, may be used for interrupt  *
17138 *                          disable.                                  *
17139 * dm_pointer_get()     :  *Check memory space and return pointer     *
17140 *                          Blocking function with timeout if no more *
17141 *                          space for next event. If error will abort.*
17142 * dm_pointer_increment(): *Move pointer to next free location        *
17143 *                          None blocking. performs bm_send_event if  *
17144 *                          local connection.                         *
17145 * dm_area_send():         *Transfer FULL buffer(s)                   *
17146 *                          None blocking function.                   *
17147 *                          if DUAL_THREAD: Give sem_send semaphore   *
17148 *                          else transfer FULL buffer                 *
17149 * dm_area_flush():        *Transfer all remaining events from dm     *
17150 *                          Blocking function with timeout            *
17151 *                          if DUAL_THREAD: Give sem_flush semaphore. *
17152 * dm_task():               Secondary thread handling DUAL_THREAD     *
17153 *                          mechanism. Serves 2 requests:             *
17154 *                          dm_send:  Transfer FULL buffer only.      *
17155 *                          dm_flush: Transfer ALL buffers.           *
17156 * dm_area_switch():        internal, used by dm_pointer_get()        *
17157 * dm_active_full():        internal: check space in current buffer   *
17158 * dm_buffer_send():        internal: send data for given area        *
17159 * dm_buffer_time_get():    interal: return the time stamp of the     *
17160 *                          last switch                               *
17161 \********************************************************************/
17162 
17163 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
17164 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
17165 #define DM_KILL        12       /* Kill request for 2nd thread */
17166 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
17167 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
17168 
17169 typedef struct {
17170    char *pt;                    /* top pointer    memory buffer          */
17171    char *pw;                    /* write pointer  memory buffer          */
17172    char *pe;                    /* end   pointer  memory buffer          */
17173    char *pb;                    /* bottom pointer memory buffer          */
17174    BOOL full;                   /* TRUE if memory buffer is full         */
17175    DWORD serial;                /* full buffer serial# for evt order     */
17176 } DMEM_AREA;
17177 
17178 typedef struct {
17179    DMEM_AREA *pa;               /* active memory buffer */
17180    DMEM_AREA area1;             /* mem buffer area 1 */
17181    DMEM_AREA area2;             /* mem buffer area 2 */
17182    DWORD serial;                /* overall buffer serial# for evt order     */
17183    INT action;                  /* for multi thread configuration */
17184    DWORD last_active;           /* switch time stamp */
17185    HNDLE sem_send;              /* semaphore for dm_task */
17186    HNDLE sem_flush;             /* semaphore for dm_task */
17187 } DMEM_BUFFER;
17188 
17189 DMEM_BUFFER dm;
17190 INT dm_user_max_event_size;
17191 
17192 /**dox***************************************************************/
17193 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17194 
17195 /********************************************************************/
17196 /**
17197 Setup a dual memory buffer. Has to be called initially before
17198            any other dm_xxx function
17199 @param size             Size in bytes
17200 @param user_max_event_size max event size
17201 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
17202 */
17203 INT dm_buffer_create(INT size, INT user_max_event_size)
17204 {
17205 
17206    dm.area1.pt = (char *) M_MALLOC(size);
17207    if (dm.area1.pt == NULL)
17208       return (BM_NO_MEMORY);
17209    dm.area2.pt = (char *) M_MALLOC(size);
17210    if (dm.area2.pt == NULL)
17211       return (BM_NO_MEMORY);
17212 
17213    /* check user event size against the system MAX_EVENT_SIZE */
17214    if (user_max_event_size > MAX_EVENT_SIZE) {
17215       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
17216       return BM_MEMSIZE_MISMATCH;
17217    }
17218    dm_user_max_event_size = user_max_event_size;
17219 
17220    memset(dm.area1.pt, 0, size);
17221    memset(dm.area2.pt, 0, size);
17222 
17223    /* initialize pointers */
17224    dm.area1.pb = dm.area1.pt + size - 1024;
17225    dm.area1.pw = dm.area1.pe = dm.area1.pt;
17226    dm.area2.pb = dm.area2.pt + size - 1024;
17227    dm.area2.pw = dm.area2.pe = dm.area2.pt;
17228 
17229   /*-PAA-*/
17230 #ifdef DM_DEBUG
17231    printf(" in dm_buffer_create ---------------------------------\n");
17232    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
17233    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
17234 #endif
17235 
17236    /* activate first area */
17237    dm.pa = &dm.area1;
17238 
17239    /* Default not full */
17240    dm.area1.full = dm.area2.full = FALSE;
17241 
17242    /* Reset serial buffer number with proper starting sequence */
17243    dm.area1.serial = dm.area2.serial = 0;
17244    /* ensure proper serial on next increment */
17245    dm.serial = 1;
17246 
17247    /* set active buffer time stamp */
17248    dm.last_active = ss_millitime();
17249 
17250    /* get socket for event sending */
17251    _send_sock = rpc_get_event_sock();
17252 
17253 #ifdef DM_DUAL_THREAD
17254    {
17255       INT status;
17256       VX_TASK_SPAWN starg;
17257 
17258       /* create semaphore */
17259       status = ss_mutex_create("send", &dm.sem_send);
17260       if (status != SS_CREATED && status != SS_SUCCESS) {
17261          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
17262          return status;
17263       }
17264       status = ss_mutex_create("flush", &dm.sem_flush);
17265       if (status != SS_CREATED && status != SS_SUCCESS) {
17266          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
17267          return status;
17268       }
17269       /* spawn dm_task */
17270       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
17271 
17272 #ifdef OS_VXWORKS
17273       /* Fill up the necessary arguments */
17274       strcpy(starg.name, "areaSend");
17275       starg.priority = 120;
17276       starg.stackSize = 20000;
17277 #endif
17278 
17279       if ((status = ss_thread_create(dm_task, (void *) &starg))
17280           != SS_SUCCESS) {
17281          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
17282          return status;
17283       }
17284 #ifdef OS_WINNT
17285       /* necessary for true MUTEX (NT) */
17286       ss_mutex_wait_for(dm.sem_send, 0);
17287 #endif
17288    }
17289 #endif                          /* DM_DUAL_THREAD */
17290 
17291    return CM_SUCCESS;
17292 }
17293 
17294 /**dox***************************************************************/
17295 #ifndef DOXYGEN_SHOULD_SKIP_THIS
17296 
17297 /********************************************************************/
17298 INT dm_buffer_release(void)
17299 /********************************************************************\
17300   Routine: dm_buffer_release
17301 
17302   Purpose: Release dual memory buffers
17303   Input:
17304     none
17305   Output:
17306     none
17307   Function value:
17308     CM_SUCCESS              Successful completion
17309 \********************************************************************/
17310 {
17311    if (dm.area1.pt) {
17312       free(dm.area1.pt);
17313       dm.area1.pt = NULL;
17314    }
17315    if (dm.area2.pt) {
17316       free(dm.area2.pt);
17317       dm.area2.pt = NULL;
17318    }
17319    dm.serial = 0;
17320    dm.area1.full = dm.area2.full = TRUE;
17321    dm.area1.serial = dm.area2.serial = 0;
17322 
17323 #ifdef DM_DUAL_THREAD
17324    /* kill spawned dm_task */
17325    dm.action = DM_KILL;
17326    ss_mutex_release(dm.sem_send);
17327    ss_mutex_release(dm.sem_flush);
17328 
17329    /* release semaphore */
17330    ss_mutex_delete(dm.sem_send, 0);
17331    ss_mutex_delete(dm.sem_flush, 0);
17332 #endif
17333 
17334    return CM_SUCCESS;
17335 }
17336 
17337 /********************************************************************/
17338 INLINE DMEM_AREA *dm_area_switch(void)
17339 /********************************************************************\
17340   Routine: dm_area_switch
17341 
17342   Purpose: set active area to the other empty area or NULL if both
17343            area are full. May have to check the serial consistancy...
17344   Input:
17345     none
17346   Output:
17347     none
17348   Function value:
17349     DMEM_AREA *            Pointer to active area or both full
17350 \********************************************************************/
17351 {
17352    volatile BOOL full1, full2;
17353 
17354    full1 = dm.area1.full;
17355    full2 = dm.area2.full;
17356 
17357    if (!full1 && !full2) {
17358       if (dm.area1.serial <= dm.area2.serial)
17359          return (&(dm.area1));
17360       else
17361          return (&(dm.area2));
17362    }
17363 
17364    if (!full1) {
17365       return (&(dm.area1));
17366    } else if (!full2) {
17367       return (&(dm.area2));
17368    }
17369    return (NULL);
17370 }
17371 
17372 /********************************************************************/
17373 INLINE BOOL dm_area_full(void)
17374 /********************************************************************\
17375   Routine: dm_area_full
17376 
17377   Purpose: Test if both area are full in order to block interrupt
17378   Input:
17379     none
17380   Output:
17381     none
17382   Function value:
17383     BOOL         TRUE if not enough space for another event
17384 \********************************************************************/
17385 {
17386    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
17387       return TRUE;
17388    return FALSE;
17389 }
17390 
17391 /********************************************************************/
17392 INLINE BOOL dm_active_full(void)
17393 /********************************************************************\
17394   Routine: dm_active_full
17395 
17396   Purpose: Test if there is sufficient space in either event buffer
17397            for another event.
17398   Input:
17399     none
17400   Output:
17401     none
17402   Function value:
17403     BOOL         TRUE if not enough space for another event
17404 \********************************************************************/
17405 {
17406    /* catch both full areas, waiting for transfer */
17407    if (dm.pa == NULL)
17408       return TRUE;
17409    /* Check the space in the active buffer only
17410       as I don't switch buffer here */
17411    if (dm.pa->full)
17412       return TRUE;
17413    return (((PTYPE) dm.pa->pb - (PTYPE) dm.pa->pw) < (INT)
17414            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
17415 }
17416 
17417 /********************************************************************/
17418 DWORD dm_buffer_time_get(void)
17419 /********************************************************************\
17420   Routine: dm_buffer_time_get
17421 
17422   Purpose: return the time from the last buffer switch.
17423 
17424   Input:
17425     none
17426   Output:
17427     none
17428   Function value:
17429     DWORD        time stamp
17430 
17431 \********************************************************************/
17432 {
17433    return (dm.last_active);
17434 }
17435 
17436 
17437 /********************************************************************/
17438 EVENT_HEADER *dm_pointer_get(void)
17439 /********************************************************************\
17440   Routine: dm_pointer_get
17441 
17442   Purpose: Get pointer to next free location in event buffer.
17443            after 10sec tries, it times out return NULL indicating a
17444            serious problem, i.e. abort.
17445   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
17446   Input:
17447     none
17448   Output:
17449     DM_BUFFER * dm    local valid dm to work on
17450   Function value:
17451     EVENT_HEADER *    Pointer to free location
17452     NULL              cannot after several attempt get free space => abort
17453 \********************************************************************/
17454 {
17455    int timeout, status;
17456 
17457    /* Is there still space in the active area ? */
17458    if (!dm_active_full())
17459       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17460 
17461    /* no more space => switch area */
17462 
17463    /* Tag current area with global dm.serial for order consistency */
17464    dm.pa->serial = dm.serial++;
17465 
17466    /* set active buffer time stamp */
17467    dm.last_active = ss_millitime();
17468 
17469    /* mark current area full */
17470    dm.pa->full = TRUE;
17471 
17472    /* Trigger/do data transfer (Now/don't wait) */
17473    if ((status = dm_area_send()) == RPC_NET_ERROR) {
17474       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
17475       return NULL;
17476    }
17477 
17478    /* wait switch completion (max 10 sec) */
17479    timeout = ss_millitime();    /* with timeout */
17480    while ((ss_millitime() - timeout) < 10000) {
17481       dm.pa = dm_area_switch();
17482       if (dm.pa != NULL)
17483          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17484       ss_sleep(200);
17485 #ifdef DM_DEBUG
17486       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
17487              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
17488              dm.area2.serial, dm.serial);
17489 #endif
17490    }
17491 
17492    /* Time running out abort */
17493    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
17494    return NULL;
17495 }
17496 
17497 
17498 /********************************************************************/
17499 int dm_pointer_increment(INT buffer_handle, INT event_size)
17500 /********************************************************************\
17501   Routine: dm_pointer_increment
17502 
17503   Purpose: Increment write pointer of event buffer after an event
17504            has been copied into the buffer (at an address previously
17505            obtained via dm_pointer_get)
17506   Input:
17507     INT buffer_handle         Buffer handle event should be sent to
17508     INT event_size            Event size in bytes including header
17509   Output:
17510     none
17511   Function value:
17512     CM_SUCCESS                Successful completion
17513     status                    from bm_send_event for local connection
17514 \********************************************************************/
17515 {
17516    INT aligned_event_size;
17517 
17518    /* if not connected remotely, use bm_send_event */
17519    if (_send_sock == 0) {
17520       *((INT *) dm.pa->pw) = buffer_handle;
17521       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
17522    }
17523    aligned_event_size = ALIGN8(event_size);
17524 
17525    *((INT *) dm.pa->pw) = buffer_handle;
17526 
17527    /* adjust write pointer */
17528    dm.pa->pw += sizeof(INT) + aligned_event_size;
17529 
17530    /* adjust end pointer */
17531    dm.pa->pe = dm.pa->pw;
17532 
17533    return CM_SUCCESS;
17534 }
17535 
17536 /********************************************************************/
17537 INLINE INT dm_buffer_send(DMEM_AREA * larea)
17538 /********************************************************************\
17539   Routine: dm_buffer_send
17540 
17541   Purpose: Ship data to the cache in fact!
17542            Basically the same do loop is done in the send_tcp.
17543            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
17544            Introduced for bringing tcp option to user code.
17545   Input:
17546     DMEM_AREA * larea   The area to work with.
17547   Output:
17548     none
17549   Function value:
17550     CM_SUCCESS       Successful completion
17551     DM_ACTIVE_NULL   Both area were/are full
17552     RPC_NET_ERROR    send error
17553 \********************************************************************/
17554 {
17555    INT tot_size, nwrite;
17556    char *lpt;
17557 
17558    /* if not connected remotely, use bm_send_event */
17559    if (_send_sock == 0)
17560       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
17561 
17562    /* alias */
17563    lpt = larea->pt;
17564 
17565    /* Get overall buffer size */
17566    tot_size = (PTYPE) larea->pe - (PTYPE) lpt;
17567 
17568    /* shortcut for speed */
17569    if (tot_size == 0)
17570       return CM_SUCCESS;
17571 
17572 #ifdef DM_DEBUG
17573    printf("lpt:%p size:%i ", lpt, tot_size);
17574 #endif
17575    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
17576 #ifdef DM_DEBUG
17577    printf("nwrite:%i  errno:%i\n", nwrite, errno);
17578 #endif
17579    if (nwrite < 0)
17580       return RPC_NET_ERROR;
17581 
17582    /* reset area */
17583    larea->pw = larea->pe = larea->pt;
17584    larea->full = FALSE;
17585    return CM_SUCCESS;
17586 }
17587 
17588 /********************************************************************/
17589 INT dm_area_send(void)
17590 /********************************************************************\
17591   Routine: dm_area_send
17592 
17593   Purpose: Empty the FULL area only in proper event order
17594            Meant to be use either in mfe.c scheduler on every event
17595 
17596   Dual memory scheme:
17597    DM_DUAL_THREAD : Trigger sem_send
17598    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
17599                     if dm.pa is NULL (were both full) and now both are empty
17600 
17601   Input:
17602     none
17603   Output:
17604     none
17605   Function value:
17606     CM_SUCCESS                Successful completion
17607     RPC_NET_ERROR             send error
17608 \********************************************************************/
17609 {
17610 #ifdef DM_DUAL_THREAD
17611    INT status;
17612 
17613    /* force a DM_SEND if possible. Don't wait for completion */
17614    dm.action = DM_SEND;
17615    ss_mutex_release(dm.sem_send);
17616 #ifdef OS_WINNT
17617    /* necessary for true MUTEX (NT) */
17618    status = ss_mutex_wait_for(dm.sem_send, 1);
17619    if (status == SS_NO_MUTEX) {
17620       printf(" timeout while waiting for sem_send\n");
17621       return RPC_NET_ERROR;
17622    }
17623 #endif
17624 
17625    return CM_SUCCESS;
17626 #else
17627    /* ---------- NOT IN DUAL THREAD ----------- */
17628    INT status = 0;
17629 
17630    /* if no DUAL thread everything is local then */
17631    /* select the full area */
17632    if (dm.area1.full && dm.area2.full)
17633       if (dm.area1.serial <= dm.area2.serial)
17634          status = dm_buffer_send(&dm.area1);
17635       else
17636          status = dm_buffer_send(&dm.area2);
17637    else if (dm.area1.full)
17638       status = dm_buffer_send(&dm.area1);
17639    else if (dm.area2.full)
17640       status = dm_buffer_send(&dm.area2);
17641    if (status != CM_SUCCESS)
17642       return status;            /* catch transfer error too */
17643 
17644    if (dm.pa == NULL) {
17645       printf(" sync send dm.pa:%p full 1%li 2%li\n", dm.pa, dm.area1.full, dm.area2.full);
17646       dm.pa = &dm.area1;
17647    }
17648    return CM_SUCCESS;
17649 #endif
17650 }
17651 
17652 /********************************************************************/
17653 INT dm_task(void *pointer)
17654 /********************************************************************\
17655   Routine: dm_task
17656 
17657   Purpose: async send events doing a double purpose:
17658   a) send full buffer if found (DM_SEND) set by dm_active_full
17659   b) flush full areas (DM_FLUSH) set by dm_area_flush
17660   Input:
17661   none
17662   Output:
17663   none
17664   Function value:
17665   none
17666   \********************************************************************/
17667 {
17668 #ifdef DM_DUAL_THREAD
17669    INT status, timeout;
17670 
17671    printf("Semaphores initialization ... in areaSend ");
17672    /* Check or Wait for semaphore to be setup */
17673    timeout = ss_millitime();
17674    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
17675       ss_sleep(200);
17676    if (dm.sem_send == 0)
17677       goto kill;
17678 
17679 #ifdef OS_WINNT
17680    /* necessary for true MUTEX (NT) get semaphore */
17681    ss_mutex_wait_for(dm.sem_flush, 0);
17682 #endif
17683 
17684    /* Main FOREVER LOOP */
17685    printf("task areaSend ready...\n");
17686    while (1) {
17687       if (!dm_area_full()) {
17688          /* wait semaphore here ........ 0 == forever */
17689          ss_mutex_wait_for(dm.sem_send, 0);
17690 #ifdef OS_WINNT
17691          /* necessary for true MUTEX (NT) give semaphore */
17692          ss_mutex_release(dm.sem_send);
17693 #endif
17694       }
17695       if (dm.action == DM_SEND) {
17696 #ifdef DM_DEBUG
17697          printf("Send %i %i ", dm.area1.full, dm.area2.full);
17698 #endif
17699          /* DM_SEND : Empty the oldest buffer only. */
17700          if (dm.area1.full && dm.area2.full) {
17701             if (dm.area1.serial <= dm.area2.serial)
17702                status = dm_buffer_send(&dm.area1);
17703             else
17704                status = dm_buffer_send(&dm.area2);
17705          } else if (dm.area1.full)
17706             status = dm_buffer_send(&dm.area1);
17707          else if (dm.area2.full)
17708             status = dm_buffer_send(&dm.area2);
17709 
17710          if (status != CM_SUCCESS) {
17711             cm_msg(MERROR, "dm_task", "network error %i", status);
17712             goto kill;
17713          }
17714       } /* if DM_SEND */
17715       else if (dm.action == DM_FLUSH) {
17716          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
17717             events) Empty both area in order independently of being full or not */
17718          if (dm.area1.serial <= dm.area2.serial) {
17719             status = dm_buffer_send(&dm.area1);
17720             if (status != CM_SUCCESS)
17721                goto error;
17722             status = dm_buffer_send(&dm.area2);
17723             if (status != CM_SUCCESS)
17724                goto error;
17725          } else {
17726             status = dm_buffer_send(&dm.area2);
17727             if (status != CM_SUCCESS)
17728                goto error;
17729             status = dm_buffer_send(&dm.area1);
17730             if (status != CM_SUCCESS)
17731                goto error;
17732          }
17733          /* reset counter */
17734          dm.area1.serial = 0;
17735          dm.area2.serial = dm.serial = 1;
17736 #ifdef DM_DEBUG
17737          printf("dm.action: Flushing ...\n");
17738 #endif
17739          /* reset area to #1 */
17740          dm.pa = &dm.area1;
17741 
17742          /* release user */
17743          ss_mutex_release(dm.sem_flush);
17744 #ifdef OS_WINNT
17745          /* necessary for true MUTEX (NT) get semaphore back */
17746          ss_mutex_wait_for(dm.sem_flush, 0);
17747 #endif
17748       }
17749       /* if FLUSH */
17750       if (dm.action == DM_KILL)
17751          goto kill;
17752 
17753    }                            /* FOREVER (go back wainting for semaphore) */
17754 
17755    /* kill spawn now */
17756  error:
17757    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
17758  kill:
17759    ss_mutex_release(dm.sem_flush);
17760 #ifdef OS_WINNT
17761    ss_mutex_wait_for(dm.sem_flush, 1);
17762 #endif
17763    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
17764    exit;
17765    return 1;
17766 #else
17767    printf("DM_DUAL_THREAD not defined\n");
17768    return 0;
17769 #endif
17770 }
17771 
17772 /********************************************************************/
17773 INT dm_area_flush(void)
17774 /********************************************************************\
17775   Routine: dm_area_flush
17776 
17777   Purpose: Flush all the events in the areas.
17778            Used in mfe for BOR events, periodic events and
17779            if rate to low in main loop once a second. The standard
17780            data transfer should be done/triggered by dm_area_send (sync/async)
17781            in dm_pointer_get().
17782   Input:
17783     none
17784   Output:
17785     none
17786   Function value:
17787     CM_SUCCESS       Successful completion
17788     RPC_NET_ERROR    send error
17789 \********************************************************************/
17790 {
17791    INT status;
17792 #ifdef DM_DUAL_THREAD
17793    /* request FULL flush */
17794    dm.action = DM_FLUSH;
17795    ss_mutex_release(dm.sem_send);
17796 #ifdef OS_WINNT
17797    /* necessary for true MUTEX (NT) get semaphore back */
17798    ss_mutex_wait_for(dm.sem_send, 0);
17799 #endif
17800 
17801    /* important to wait for completion before continue with timeout
17802       timeout specified milliseconds */
17803    status = ss_mutex_wait_for(dm.sem_flush, 10000);
17804 #ifdef DM_DEBUG
17805    printf("dm_area_flush after waiting %i\n", status);
17806 #endif
17807 #ifdef OS_WINNT
17808    ss_mutex_release(dm.sem_flush);      /* give it back now */
17809 #endif
17810 
17811    return status;
17812 #else
17813    /* full flush done here */
17814    /* select in order both area independently of being full or not */
17815    if (dm.area1.serial <= dm.area2.serial) {
17816       status = dm_buffer_send(&dm.area1);
17817       if (status != CM_SUCCESS)
17818          return status;
17819       status = dm_buffer_send(&dm.area2);
17820       if (status != CM_SUCCESS)
17821          return status;
17822    } else {
17823       status = dm_buffer_send(&dm.area2);
17824       if (status != CM_SUCCESS)
17825          return status;
17826       status = dm_buffer_send(&dm.area1);
17827       if (status != CM_SUCCESS)
17828          return status;
17829    }
17830    /* reset serial counter */
17831    dm.area1.serial = dm.area2.serial = 0;
17832    dm.last_active = ss_millitime();
17833    return CM_SUCCESS;
17834 #endif
17835 }
17836 
17837 /********************************************************************/
17838 
17839 /**dox***************************************************************/
17840 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17841 
17842 /**dox***************************************************************/
17843                    /** @} *//* end of dmfunctionc */
17844 
17845 /**dox***************************************************************/
17846                    /** @} *//* 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