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.204  2004/03/31 17:35:40  olchansk
00010   fix the infamous problem with "last NN days broken" in Elog
00011   catch more memory overruns in the elog code
00012 
00013   Revision 1.203  2004/03/19 09:58:22  midas
00014   Changed client inactivity time check in cm_watchdog
00015 
00016   Revision 1.202  2004/01/17 05:35:53  olchansk
00017   replace #define ALIGN() with ALIGN8() to dodge namespace pollution under macosx
00018   hide strlcpy() & co #ifdef HAVE_STRLCPY (macosx already has strlcpy())
00019   correct inconsistent prototype of dbg_malloc() and dbg_calloc()
00020 
00021   Revision 1.201  2004/01/13 00:52:18  pierre
00022   fix dox comment for vxworks
00023 
00024   Revision 1.200  2004/01/08 08:40:10  midas
00025   Implemented standard indentation
00026 
00027   Revision 1.199  2004/01/08 06:44:00  pierre
00028   Doxygen the file
00029 
00030   Revision 1.198  2003/11/24 08:22:46  midas
00031   Changed timeouts from INT to DWORD, added ignore_timeout to cm_cleanup, adde '-f' flag to ODBEdit 'cleanup'
00032 
00033   Revision 1.197  2003/11/20 11:29:44  midas
00034   Implemented db_check_record and use it in most places instead of db_create_record
00035 
00036   Revision 1.196  2003/11/01 01:27:58  olchansk
00037   abort if cannot read /runinfo/run number
00038 
00039   Revision 1.195  2003/10/30 12:03:11  midas
00040   Removed tabs
00041 
00042   Revision 1.194  2003/10/13 00:07:40  olchansk
00043   refuse run number zero and abort on corrupted run numbers
00044 
00045   Revision 1.193  2003/10/12 22:56:33  olchansk
00046   when submitting new Elog message, add the message text to the outgoing email.
00047   add traps for some array overruns (see http://midas.triumf.ca/forum/Development%20Area/12)
00048 
00049   Revision 1.192  2003/09/04 11:47:48  midas
00050   Fixed problem with hKey in cm_transition
00051 
00052   Revision 1.191  2003/05/09 07:40:05  midas
00053   Added extra parameter to cm_get_environment
00054 
00055   Revision 1.190  2003/05/08 19:36:32  midas
00056   Changed size_t into INT
00057 
00058   Revision 1.189  2003/05/02 09:03:01  midas
00059   Fixed buffer overflows by strlcpy()
00060 
00061   Revision 1.188  2003/04/25 13:54:04  midas
00062   Fixed compiler warnings
00063 
00064   Revision 1.187  2003/04/22 12:01:29  midas
00065   Added graceful shutdown of odbedit->frontend connection
00066 
00067   Revision 1.186  2003/04/22 10:09:06  midas
00068   Added RPC_NODELAY option
00069 
00070   Revision 1.185  2003/04/16 19:34:42  pierre
00071   mv stdio.h, ctype.h into midasinc.h
00072 
00073   Revision 1.184  2003/04/15 08:37:04  midas
00074   Removed error message in rpc_client_connect on broken connection, since this is normal if the frontend for example is restarted
00075 
00076   Revision 1.183  2003/04/14 11:01:04  midas
00077   Added bk_find(), added htonl(INADDR_ANY) for bind()
00078 
00079   Revision 1.182  2003/04/09 13:42:47  midas
00080   Made file compile under C++
00081 
00082   Revision 1.181  2003/03/28 07:59:50  midas
00083   Removed old code
00084 
00085   Revision 1.180  2003/03/27 19:40:27  olchansk
00086   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.
00087 
00088   Revision 1.179  2003/03/22 07:06:47  olchansk
00089   prevent infinite loop in hs_read() and hs_dump() when reading broken history files.
00090 
00091   Revision 1.178  2003/01/14 12:19:23  midas
00092   Removed unnecessary code
00093 
00094   Revision 1.177  2003/01/14 08:14:32  midas
00095   Removed unnecessary bind()
00096 
00097   Revision 1.176  2003/01/13 17:07:01  midas
00098   Fixed problem with missing history in recent files
00099 
00100   Revision 1.175  2002/11/27 12:54:49  midas
00101   Removed unnecessary bind()
00102 
00103   Revision 1.174  2002/10/21 00:14:38  olchansk
00104   add missing error reporting
00105 
00106   Revision 1.173  2002/10/15 19:14:06  olchansk
00107   disallow recursive rpc_server_disconnect() (->rpc_call->rpc_server_disconnect)
00108 
00109   Revision 1.172  2002/10/04 09:05:39  midas
00110   Set timeout to 0 in cm_enable_watchdog(FALSE)
00111 
00112   Revision 1.171  2002/09/23 18:13:49  pierre
00113   correct cm_cleanup() rpc_call arg list
00114 
00115   Revision 1.170  2002/09/23 09:50:23  midas
00116   Fixed problem with odbedit 'cleanup' command
00117 
00118   Revision 1.169  2002/09/18 16:39:16  pierre
00119   add bk_list()
00120 
00121   Revision 1.168  2002/09/17 08:58:43  midas
00122   Fix for watchdog timeout after tape operations
00123 
00124   Revision 1.167  2002/09/13 07:32:47  midas
00125   Added client name to cm_cleanup()
00126 
00127   Revision 1.166  2002/09/12 10:42:20  midas
00128   Added note to cm_cleanup()
00129 
00130   Revision 1.165  2002/09/09 17:57:14  pierre
00131   #if !defined(OS_VXWORKS) for eb_ & hs_ section
00132 
00133   Revision 1.164  2002/06/25 19:39:48  pierre
00134   doc++ functions  strstr
00135 
00136   Revision 1.163  2002/06/25 19:00:36  pierre
00137   doc++ functions
00138 
00139   Revision 1.162  2002/05/29 18:49:37  midas
00140   Fixed bug with 'shutdown all' in odbedit
00141 
00142   Revision 1.161  2002/05/29 07:25:13  midas
00143   Fixed bug with shutting down programs
00144 
00145   Revision 1.160  2002/05/28 12:47:46  midas
00146   Shut down client connection in FTCP mode
00147 
00148   Revision 1.159  2002/05/27 14:29:10  midas
00149   Improved rpc timeout error reports
00150 
00151   Revision 1.158  2002/05/22 06:07:01  midas
00152   Call bm_defragment_event for both EVENTID_FRAG1 and EVENTID_FRAG
00153 
00154   Revision 1.157  2002/05/22 05:43:32  midas
00155   Added extra variables to hs_enum_vars for mhist to display array size
00156 
00157   Revision 1.156  2002/05/22 05:26:38  midas
00158   Fixed problem with empty history files
00159 
00160   Revision 1.155  2002/05/16 18:01:13  midas
00161   Added subdir creation in logger and improved program restart scheme
00162 
00163   Revision 1.154  2002/05/15 23:43:39  midas
00164   Added bm_defragment_event()
00165 
00166   Revision 1.153  2002/05/14 04:24:53  midas
00167   Fixed bug on nonexisting message file
00168 
00169   Revision 1.152  2002/05/11 01:22:48  midas
00170   Improved malloc/free debugging
00171 
00172   Revision 1.151  2002/05/10 16:49:37  midas
00173   Moved 'execute on start' before TR_PRESTART
00174 
00175   Revision 1.150  2002/05/10 01:41:19  midas
00176   Added optional debug output to cm_transition
00177 
00178   Revision 1.149  2002/05/10 00:17:05  midas
00179   Run start abort causes logger to delete old data file on next run start
00180 
00181   Revision 1.148  2002/05/08 22:15:24  pierre
00182   add db_get_value arg doc
00183 
00184   Revision 1.147  2002/05/08 19:54:40  midas
00185   Added extra parameter to function db_get_value()
00186 
00187   Revision 1.146  2002/05/07 22:27:56  midas
00188   Fixed bug that history files did not get closed on hs_read
00189 
00190   Revision 1.145  2002/03/13 08:38:00  midas
00191   Added periodic alarms
00192 
00193   Revision 1.144  2002/02/02 11:33:45  midas
00194   Fixed bug in hs_read with small history files
00195 
00196   Revision 1.143  2002/01/30 13:03:34  midas
00197   Fixed small bug in history function
00198 
00199   Revision 1.142  2001/12/12 18:27:03  pierre
00200   add doc++, fix comments
00201 
00202   Revision 1.141  2001/12/12 17:46:21  pierre
00203   1.8.3-2 doc++ comments
00204 
00205   Revision 1.140  2001/12/05 11:29:51  midas
00206   Changed creation of "/Loger/xxx dir"
00207 
00208   Revision 1.139  2001/11/20 14:42:15  midas
00209   Added "/logger/history dir" and "/logger/elog dir"
00210 
00211   Revision 1.138  2001/10/25 22:18:48  pierre
00212   added doc++ comments
00213 
00214   Revision 1.137  2001/10/05 22:35:46  pierre
00215   - change doc \_ to _
00216   - Change MALLOC to M_MALLOC, FREE to M_FREE macros.
00217 
00218   Revision 1.136  2001/08/07 13:07:01  midas
00219   Return error if subprocess creation in rpc_server_accept fails
00220 
00221   Revision 1.135  2001/08/07 08:07:09  midas
00222   Fixed bug in el_retrieve with attachment decoding
00223 
00224   Revision 1.134  2001/06/27 11:55:50  midas
00225   Fixed compiler warnings (came from IRIX)
00226 
00227   Revision 1.133  2001/06/15 08:49:56  midas
00228   Fixed bug when query gave no result if only messages from today are present
00229 
00230   Revision 1.132  2001/05/22 09:27:13  midas
00231   Fixed bug when searching old messages
00232 
00233   Revision 1.131  2001/04/10 01:17:44  midas
00234   Fixed bug in cm_msg_retrieve which screwed up message display in mhttpd
00235 
00236   Revision 1.130  2001/02/19 11:29:05  midas
00237   Set run stop time in ODB before run is stopped in order to have the proper
00238   value in the runxxx.odb file
00239 
00240   Revision 1.129  2001/01/30 08:28:13  midas
00241   Use va_arg(..., double) for float numbers
00242 
00243   Revision 1.128  2000/12/06 02:58:24  midas
00244   Put extended error information for bind() call
00245 
00246   Revision 1.127  2000/11/14 12:19:24  midas
00247   Fixed bug in cm_msg_retrieve, added improved "more" feature in message display
00248 
00249   Revision 1.126  2000/11/14 08:17:04  midas
00250   Added number of messages for cm_msg_retrieve and in odbedit "old" command
00251 
00252   Revision 1.125  2000/11/06 14:19:19  midas
00253   Don't return from hs_read if variable not found (could be present later...)
00254 
00255   Revision 1.124  2000/10/21 12:26:12  midas
00256   Fixed bug with cache pointer in hs_read
00257 
00258   Revision 1.123  2000/09/29 13:31:12  midas
00259   ODBEdit cleanup now deletes open record with no client attached to
00260 
00261   Revision 1.122  2000/08/21 14:18:39  midas
00262   bk_close returns bank size
00263 
00264   Revision 1.121  2000/08/21 07:05:48  midas
00265   Added cm_msg_log1(...,facility) to be compatible with older programs
00266 
00267   Revision 1.120  2000/08/11 12:16:44  midas
00268   Fixed bug with "facility" being NULL
00269 
00270   Revision 1.119  2000/08/11 11:43:51  midas
00271   Added cm_msg1 to produce messages which go to a differnt logging file
00272 
00273   Revision 1.118  2000/08/10 08:04:56  midas
00274   Create default /runinfo structure in cm_connect_experiment
00275 
00276   Revision 1.117  2000/05/16 10:38:17  midas
00277   - Set MIDAS_DIR as the default /logger/data dir on cm_connect_experiment
00278   - Remove elog file if all messages are deleted
00279 
00280   Revision 1.116  2000/05/09 09:06:12  midas
00281   Added MIDAS_EXPTAB environment variable and hashmark comments in exptab
00282 
00283   Revision 1.115  2000/05/08 14:29:38  midas
00284   Added delete option in ELog
00285 
00286   Revision 1.114  2000/05/05 14:20:05  midas
00287   Do online mode check in al_trigger_alarm
00288 
00289   Revision 1.113  2000/04/26 20:27:06  pierre
00290   -Added doc++ comments on some functions.
00291 
00292   Revision 1.112  2000/04/25 11:55:42  midas
00293   Adjusted tabs for history functions
00294 
00295   Revision 1.111  2000/04/17 16:28:21  pierre
00296   - Added arg "BOOL binary_time" to hs_dump(), hs_fdump() for mhist -b
00297 
00298   Revision 1.110  2000/03/29 09:14:47  midas
00299   Fixed bug with original message tagging having the wrong offset
00300 
00301   Revision 1.109  2000/03/17 13:00:06  midas
00302   Frontends use default timeout fo 60 sec.
00303 
00304   Revision 1.108  2000/03/17 10:55:15  midas
00305   Don't trigger internal alarms if alarm system is off
00306 
00307   Revision 1.107  2000/03/04 00:42:29  midas
00308   Delete elog & alarm mutexes correctly
00309 
00310   Revision 1.106  2000/03/03 22:46:07  midas
00311   Remove elog and alarm mutex on exit
00312 
00313   Revision 1.105  2000/03/03 01:45:13  midas
00314   Added web password for mhttpd, added webpasswd command in odbedit
00315 
00316   Revision 1.104  2000/03/01 23:06:19  midas
00317   bk_xxx functions now don't use global variable _pbk
00318 
00319   Revision 1.103  2000/02/29 21:59:05  midas
00320   Fixec bug with order of actions in cm_transition
00321 
00322   Revision 1.102  2000/02/29 02:10:26  midas
00323   Added cm_is_ctrlc_pressed and cm_ack_ctrlc_pressed
00324 
00325   Revision 1.101  2000/02/25 22:49:29  midas
00326   Increased timeouts
00327 
00328   Revision 1.100  2000/02/25 22:19:09  midas
00329   Improved Ctrl-C handling
00330 
00331   Revision 1.99  2000/02/24 23:58:29  midas
00332   Fixed problem with _requested_transition being update by hotlink too late
00333 
00334   Revision 1.98  2000/02/24 22:29:25  midas
00335   Added deferred transitions
00336 
00337   Revision 1.97  2000/02/23 21:07:44  midas
00338   Changed spaces and tabulators
00339 
00340   Revision 1.96  2000/02/15 11:07:51  midas
00341   Changed GET_xxx to bit flags
00342 
00343   Revision 1.95  2000/02/09 08:03:52  midas
00344   Fixed bracket indention
00345 
00346   Revision 1.94  1999/12/08 16:10:43  midas
00347   Fixed another watchdog bug causing remote clients to crash
00348 
00349   Revision 1.93  1999/12/08 11:44:25  midas
00350   Fixed bug with watchdog timeout
00351 
00352   Revision 1.92  1999/12/08 10:00:41  midas
00353   Changed error string to single line
00354 
00355   Revision 1.91  1999/12/08 00:25:20  pierre
00356   - add cm_get_path in cm_msg_retrieve for midas.log location.
00357   - mod dm_buffer_create for arg "max user event size".
00358   - fix dm_area_flush.
00359   - fix other compilation warnings for OSF/1
00360 
00361   Revision 1.90  1999/11/26 08:31:58  midas
00362   midas.log is now places in the same directory as the .SHM files in case
00363   there is no data dir in the ODB
00364 
00365   Revision 1.89  1999/11/25 13:29:55  midas
00366   Fixed bug in cm_msg_retrieve
00367 
00368   Revision 1.88  1999/11/23 15:52:40  midas
00369   If an event is larger than the buffer read or write cache, it bypasses the
00370   cache.
00371 
00372   Revision 1.87  1999/11/19 09:49:58  midas
00373   Fixed bug with wrong default watchdog timeout in cm_connect_experiment1
00374 
00375   Revision 1.86  1999/11/12 10:04:59  midas
00376   Fixed bug with WATCHDOG_INTERVAL
00377 
00378   Revision 1.85  1999/11/10 15:05:16  midas
00379   Did some additional database locking
00380 
00381   Revision 1.84  1999/11/10 13:56:12  midas
00382   Alarm record only gets created when old one mismatches
00383 
00384   Revision 1.83  1999/11/10 10:39:11  midas
00385   Changed initialization of alarms
00386 
00387   Revision 1.82  1999/11/10 08:30:44  midas
00388   Fixed bug when editing the last elog message
00389 
00390   Revision 1.81  1999/11/09 14:44:08  midas
00391   Changed ODB locking in cm_cleanup
00392 
00393   Revision 1.80  1999/11/09 13:17:25  midas
00394   Added secure ODB feature
00395 
00396   Revision 1.79  1999/11/08 13:56:09  midas
00397   Added different alarm types
00398 
00399   Revision 1.78  1999/10/27 15:13:56  midas
00400   Added "access(<key>)" in alarm system
00401 
00402   Revision 1.77  1999/10/27 13:37:57  midas
00403   Added event size check in bm_send_event
00404 
00405   Revision 1.76  1999/10/18 15:52:12  midas
00406   Use "alarm count" to declare programs dead if inactive for 5 minutes
00407 
00408   Revision 1.75  1999/10/18 14:41:51  midas
00409   Use /programs/<name>/Watchdog timeout in all programs as timeout value. The
00410   default value can be submitted by calling cm_connect_experiment1(..., timeout)
00411 
00412   Revision 1.74  1999/10/13 08:03:28  midas
00413   Fixed bug displaying executed message as %d
00414 
00415   Revision 1.73  1999/10/11 14:14:03  midas
00416   Use ss_system in certain places
00417 
00418   Revision 1.72  1999/10/11 13:01:22  midas
00419   Produce system message when executing an alarm script
00420 
00421   Revision 1.71  1999/10/08 22:15:03  midas
00422   Added ftruncate for LINUX
00423 
00424   Revision 1.70  1999/10/08 22:00:30  midas
00425   Finished editing of elog messages
00426 
00427   Revision 1.69  1999/10/08 15:07:06  midas
00428   Program check creates new internal alarm when triggered
00429 
00430   Revision 1.68  1999/10/08 13:21:20  midas
00431   Alarm system disabled when running offline
00432 
00433   Revision 1.67  1999/10/07 13:50:49  midas
00434   Fixed bug with date in el_submit
00435 
00436   Revision 1.66  1999/10/07 13:31:18  midas
00437   Fixed truncated date in el_submit, cut off @host in author search
00438 
00439   Revision 1.65  1999/10/06 06:56:02  midas
00440   Include weekday in elog
00441 
00442   Revision 1.64  1999/10/05 13:16:10  midas
00443   Added global alarm flag "/alarms/alarm system active"
00444 
00445   Revision 1.63  1999/10/04 11:54:14  midas
00446   Submit full alarm string to execute command
00447 
00448   Revision 1.62  1999/09/30 22:59:06  pierre
00449   - fix bk_close for BK32
00450 
00451   Revision 1.61  1999/09/29 19:23:33  pierre
00452   - Fix bk_iterate,swap,locate for bank32
00453 
00454   Revision 1.60  1999/09/27 13:49:04  midas
00455   Added bUnique parameter to cm_shutdown
00456 
00457   Revision 1.59  1999/09/27 12:54:08  midas
00458   Finished alarm system
00459 
00460   Revision 1.58  1999/09/27 08:56:53  midas
00461   Fixed bug with missing run number in elog
00462 
00463   Revision 1.57  1999/09/23 14:00:48  midas
00464   Used capital names for mutexes
00465 
00466   Revision 1.56  1999/09/23 12:45:49  midas
00467   Added 32 bit banks
00468 
00469   Revision 1.55  1999/09/22 08:57:08  midas
00470   Implemented auto start and auto stop in /programs
00471 
00472   Revision 1.54  1999/09/21 14:57:39  midas
00473   Added "execute on start/stop" under /programs
00474 
00475   Revision 1.53  1999/09/21 14:15:04  midas
00476   Replaces cm_execute by system()
00477 
00478   Revision 1.52  1999/09/21 13:48:04  midas
00479   Added programs check in al_check
00480 
00481   Revision 1.51  1999/09/17 15:59:03  midas
00482   Added internal alarms
00483 
00484   Revision 1.50  1999/09/17 15:06:48  midas
00485   Moved al_check into cm_yield() and rpc_server_thread
00486 
00487   Revision 1.49  1999/09/17 11:50:53  midas
00488   Added al_xxx functions
00489 
00490   Revision 1.48  1999/09/17 11:48:06  midas
00491   Alarm system half finished
00492 
00493   Revision 1.47  1999/09/15 13:33:34  midas
00494   Added remote el_submit functionality
00495 
00496   Revision 1.46  1999/09/14 15:15:45  midas
00497   Moved el_xxx funtions into midas.c
00498 
00499   Revision 1.45  1999/09/13 11:08:24  midas
00500   Check NULL as experiment in cm_connect_experiment
00501 
00502   Revision 1.44  1999/09/10 06:11:15  midas
00503   Used %100 for year in tms structure
00504 
00505   Revision 1.43  1999/08/03 14:41:09  midas
00506   Lock buffer in bm_skip_event
00507 
00508   Revision 1.42  1999/08/03 11:15:07  midas
00509   Added bm_skip_event
00510 
00511   Revision 1.41  1999/07/21 09:22:01  midas
00512   Added Ctrl-C handler to cm_connect_experiment and cm_yield
00513 
00514   Revision 1.40  1999/06/28 12:01:21  midas
00515   Added hs_fdump
00516 
00517   Revision 1.39  1999/06/25 12:01:54  midas
00518   Added bk_delete function
00519 
00520   Revision 1.38  1999/06/23 09:36:24  midas
00521   - Fixed "too many connections" bug
00522   - incorporated PAAs dm_xxx changes
00523 
00524   Revision 1.37  1999/05/05 12:02:33  midas
00525   Added and modified history functions, added db_set_num_values
00526 
00527   Revision 1.36  1999/04/30 14:22:01  midas
00528   Send buffer name via bm_notify_client to java application
00529 
00530   Revision 1.35  1999/04/30 13:19:54  midas
00531   Changed inter-process communication (ss_resume, bm_notify_clients, etc)
00532   to strings so that server process can receive it's own watchdog produced
00533   messages (pass buffer name insteas buffer handle)
00534 
00535   Revision 1.34  1999/04/30 10:58:58  midas
00536   Added -D debug to screen for mserver
00537 
00538   Revision 1.33  1999/04/29 10:48:02  midas
00539   Implemented "/System/Client Notify" key
00540 
00541   Revision 1.32  1999/04/28 15:27:28  midas
00542   Made hs_read working for Java
00543 
00544   Revision 1.31  1999/04/27 15:16:14  midas
00545   Increased ASCII_BUFFER_SIZE to 64500
00546 
00547   Revision 1.30  1999/04/27 11:11:26  midas
00548   Added rpc_register_client
00549 
00550   Revision 1.29  1999/04/23 11:42:52  midas
00551   Made db_get_data_index working for Java
00552 
00553   Revision 1.28  1999/04/19 07:47:00  midas
00554   Added cm_msg_retrieve
00555 
00556   Revision 1.27  1999/04/16 15:13:28  midas
00557   bm_notify_client notifies ASCII client (Java) always
00558 
00559   Revision 1.26  1999/04/15 15:43:06  midas
00560   Added functionality for bm_receive_event in ASCII mode
00561 
00562   Revision 1.25  1999/04/15 09:58:42  midas
00563   Switched if (rpc_list[i].id == 0) statements
00564 
00565   Revision 1.24  1999/04/13 12:20:43  midas
00566   Added db_get_data1 (for Java)
00567 
00568   Revision 1.23  1999/04/08 15:26:05  midas
00569   Worked on rpc_execute_ascii
00570 
00571   Revision 1.22  1999/03/23 10:37:39  midas
00572   Fixed bug in cm_set_watchdog_params which causes mtape report ODB errors
00573 
00574   Revision 1.21  1999/02/12 10:55:03  midas
00575   Accepted PAA's modification in cm_set_watchdog_params()
00576 
00577   Revision 1.20  1999/02/11 13:14:46  midas
00578   Basic ASCII protocol implemented in server
00579 
00580   Revision 1.19  1999/02/09 14:38:23  midas
00581   Added debug logging facility
00582 
00583   Revision 1.18  1999/02/06 00:17:12  pierre
00584   - Fix local watchdog timeout in cm_set_watchdog_params()
00585   - Touch dm_xxx functions for OS_WINNT
00586 
00587   Revision 1.17  1999/02/02 07:42:22  midas
00588   Only print warning about zero length bank in bk_close if bank has type TID_STRUCT
00589 
00590   Revision 1.16  1999/02/01 15:41:23  midas
00591   Added warning for zero length bank in bk_close
00592 
00593   Revision 1.15  1999/02/01 13:03:49  midas
00594   Added /system/clients/xxx/link timeout to show current TCP timeout value
00595 
00596   Revision 1.14  1999/01/22 09:31:16  midas
00597   Fixed again status return from ss_mutex_create in bm_open_buffer
00598 
00599   Revision 1.13  1999/01/21 23:09:17  pierre
00600   - Incorporate dm_semaphore_...() functionality into ss_mutex_...()
00601   - Remove dm_semaphore_...(), adjust dm_...() accordingly.
00602   - Incorporate taskSpawn into ss_thread_create (system.c).
00603   - Adjust status value returnd from ss_mutex_create().
00604 
00605   Revision 1.12  1999/01/20 08:55:44  midas
00606   - Renames ss_xxx_mutex to ss_mutex_xxx
00607   - Added timout flag to ss_mutex_wait_for
00608 
00609   Revision 1.11  1999/01/19 19:58:56  pierre
00610   - Fix compiler warning in dm_buffer_send
00611 
00612   Revision 1.10  1999/01/18 17:50:35  pierre
00613   - Added dm_...() functions for Dual Memory buffer handling.
00614 
00615   Revision 1.9  1998/12/11 17:00:02  midas
00616   Fixed a few typos
00617 
00618   Revision 1.8  1998/10/28 12:05:57  midas
00619   Fixed minor compiler warning
00620 
00621   Revision 1.7  1998/10/28 12:01:30  midas
00622   Added version number to run start notification
00623 
00624   Revision 1.6  1998/10/27 10:53:48  midas
00625   - Added run start notification
00626   - Added ss_shell() for NT
00627 
00628   Revision 1.5  1998/10/23 14:21:50  midas
00629   - Modified version scheme from 1.06 to 1.6.0
00630   - cm_get_version() now returns versino as string
00631 
00632   Revision 1.4  1998/10/13 07:34:42  midas
00633   Reopened database in case of wrong password
00634 
00635   Revision 1.3  1998/10/12 12:19:02  midas
00636   Added Log tag in header
00637 
00638   Revision 1.2  1998/10/12 11:59:10  midas
00639   Added Log tag in header
00640 
00641 \********************************************************************/
00642 
00643 #include "midas.h"
00644 #include "msystem.h"
00645 #include <assert.h>
00646 
00647 /**dox***************************************************************/
00648 /** @file midas.c
00649 The main core C-code for Midas.
00650 */
00651 
00652 /** @defgroup cmfunctionc Midas Common Functions (cm_xxx)
00653  */
00654 /** @defgroup bmfunctionc Midas Buffer Manager Functions (bm_xxx)
00655  */
00656 /** @defgroup msgfunctionc Midas Message Functions (msg_xxx)
00657  */
00658 /** @defgroup bkfunctionc Midas Bank Functions (bk_xxx)
00659  */
00660 /** @defgroup alfunctionc Midas Alarm Functions (al_xxx)
00661  */
00662 /** @defgroup hsfunctionc Midas History Functions (hs_xxx)
00663  */
00664 /** @defgroup elfunctionc Midas Elog Functions (el_xxx)
00665  */
00666 /** @defgroup rpcfunctionc Midas RPC Functions (rpc_xxx)
00667  */
00668 /** @defgroup dmfunctionc Midas Dual Buffer Memory Functions (dm_xxx)
00669  */
00670 
00671 /**dox***************************************************************/
00672 /** @addtogroup midasincludecode
00673  *  
00674  *  @{  */
00675 
00676 /**dox***************************************************************/
00677 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00678 
00679 /********************************************************************/
00680 /* data type sizes */
00681 INT tid_size[] = {
00682    0,                           /* tid == 0 not defined                               */
00683    1,                           /* TID_BYTE      unsigned byte         0       255    */
00684    1,                           /* TID_SBYTE     signed byte         -128      127    */
00685    1,                           /* TID_CHAR      single character      0       255    */
00686    2,                           /* TID_WORD      two bytes             0      65535   */
00687    2,                           /* TID_SHORT     signed word        -32768    32767   */
00688    4,                           /* TID_DWORD     four bytes            0      2^32-1  */
00689    4,                           /* TID_INT       signed dword        -2^31    2^31-1  */
00690    4,                           /* TID_BOOL      four bytes bool       0        1     */
00691    4,                           /* TID_FLOAT     4 Byte float format                  */
00692    8,                           /* TID_DOUBLE    8 Byte float format                  */
00693    1,                           /* TID_BITFIELD  8 Bits Bitfield    00000000 11111111 */
00694    0,                           /* TID_STRING    zero terminated string               */
00695    0,                           /* TID_ARRAY     variable length array of unkown type */
00696    0,                           /* TID_STRUCT    C structure                          */
00697    0,                           /* TID_KEY       key in online database               */
00698    0                            /* TID_LINK      link in online database              */
00699 };
00700 
00701 /* data type names */
00702 char *tid_name[] = {
00703    "NULL",
00704    "BYTE",
00705    "SBYTE",
00706    "CHAR",
00707    "WORD",
00708    "SHORT",
00709    "DWORD",
00710    "INT",
00711    "BOOL",
00712    "FLOAT",
00713    "DOUBLE",
00714    "BITFIELD",
00715    "STRING",
00716    "ARRAY",
00717    "STRUCT",
00718    "KEY",
00719    "LINK"
00720 };
00721 
00722 /* Globals */
00723 #ifdef OS_MSDOS
00724 extern unsigned _stklen = 60000U;
00725 #endif
00726 
00727 extern DATABASE *_database;
00728 extern INT _database_entries;
00729 
00730 static BUFFER *_buffer;
00731 static INT _buffer_entries = 0;
00732 
00733 static INT _msg_buffer = 0;
00734 static void (*_msg_dispatch) (HNDLE, HNDLE, EVENT_HEADER *, void *);
00735 
00736 static REQUEST_LIST *_request_list;
00737 static INT _request_list_entries = 0;
00738 
00739 static EVENT_HEADER *_event_buffer;
00740 static INT _event_buffer_size = 0;
00741 
00742 static char *_net_recv_buffer;
00743 static INT _net_recv_buffer_size = 0;
00744 
00745 static char *_net_send_buffer;
00746 static INT _net_send_buffer_size = 0;
00747 
00748 static char *_tcp_buffer = NULL;
00749 static INT _tcp_wp = 0;
00750 static INT _tcp_rp = 0;
00751 
00752 static INT _send_sock;
00753 
00754 static void (*_debug_print) (char *) = NULL;
00755 static INT _debug_mode = 0;
00756 
00757 static INT _watchdog_last_called = 0;
00758 
00759 /* table for transition functions */
00760 
00761 typedef struct {
00762    INT transition;
00763     INT(*func) (INT, char *);
00764 } TRANS_TABLE;
00765 
00766 TRANS_TABLE _trans_table[] = {
00767    {TR_START, NULL},
00768    {TR_STOP, NULL},
00769    {TR_PAUSE, NULL},
00770    {TR_RESUME, NULL},
00771    {TR_PRESTART, NULL},
00772    {TR_POSTSTART, NULL},
00773    {TR_PRESTOP, NULL},
00774    {TR_POSTSTOP, NULL},
00775    {TR_PREPAUSE, NULL},
00776    {TR_POSTPAUSE, NULL},
00777    {TR_PRERESUME, NULL},
00778    {TR_POSTRESUME, NULL},
00779    {0, NULL}
00780 };
00781 
00782 TRANS_TABLE _deferred_trans_table[] = {
00783    {TR_START, NULL},
00784    {TR_STOP, NULL},
00785    {TR_PAUSE, NULL},
00786    {TR_RESUME, NULL},
00787    {TR_PRESTART, NULL},
00788    {TR_POSTSTART, NULL},
00789    {TR_PRESTOP, NULL},
00790    {TR_POSTSTOP, NULL},
00791    {TR_PREPAUSE, NULL},
00792    {TR_POSTPAUSE, NULL},
00793    {TR_PRERESUME, NULL},
00794    {TR_POSTRESUME, NULL},
00795    {0, NULL}
00796 };
00797 
00798 static BOOL _server_registered = FALSE;
00799 
00800 static INT rpc_transition_dispatch(INT index, void *prpc_param[]);
00801 
00802 void cm_ctrlc_handler(int sig);
00803 
00804 typedef struct {
00805    INT code;
00806    char *string;
00807 } ERROR_TABLE;
00808 
00809 ERROR_TABLE _error_table[] = {
00810    {CM_WRONG_PASSWORD, "Wrong password"},
00811    {CM_UNDEF_EXP, "Experiment not defined"},
00812    {CM_UNDEF_ENVIRON,
00813     "\"exptab\" file not found and MIDAS_DIR environment variable not defined"},
00814    {RPC_NET_ERROR, "Cannot connect to remote host"},
00815    {0, NULL}
00816 };
00817 
00818 
00819 typedef struct {
00820    void *adr;
00821    int size;
00822    char file[80];
00823    int line;
00824 } DBG_MEM_LOC;
00825 
00826 DBG_MEM_LOC *_mem_loc = NULL;
00827 INT _n_mem = 0;
00828 
00829 void *dbg_malloc(unsigned int size, char *file, int line)
00830 {
00831    FILE *f;
00832    void *adr;
00833    int i;
00834 
00835    adr = malloc(size);
00836 
00837    /* search for deleted entry */
00838    for (i = 0; i < _n_mem; i++)
00839       if (_mem_loc[i].adr == NULL)
00840          break;
00841 
00842    if (i == _n_mem) {
00843       _n_mem++;
00844       if (!_mem_loc)
00845          _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
00846       else
00847          _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
00848    }
00849 
00850    _mem_loc[i].adr = adr;
00851    _mem_loc[i].size = size;
00852    strcpy(_mem_loc[i].file, file);
00853    _mem_loc[i].line = line;
00854 
00855    f = fopen("mem.txt", "w");
00856    for (i = 0; i < _n_mem; i++)
00857       if (_mem_loc[i].adr)
00858          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00859                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00860    fclose(f);
00861 
00862    return adr;
00863 }
00864 
00865 void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
00866 {
00867    void *adr;
00868 
00869    adr = dbg_malloc(size * count, file, line);
00870    if (adr)
00871       memset(adr, 0, size * count);
00872 
00873    return adr;
00874 }
00875 
00876 void dbg_free(void *adr, char *file, int line)
00877 {
00878    FILE *f;
00879    int i;
00880 
00881    free(adr);
00882 
00883    for (i = 0; i < _n_mem; i++)
00884       if (_mem_loc[i].adr == adr)
00885          break;
00886 
00887    if (i < _n_mem)
00888       _mem_loc[i].adr = NULL;
00889 
00890    f = fopen("mem.txt", "w");
00891    for (i = 0; i < _n_mem; i++)
00892       if (_mem_loc[i].adr)
00893          fprintf(f, "%s:%d size=%d adr=%X\n", _mem_loc[i].file, _mem_loc[i].line,
00894                  _mem_loc[i].size, (unsigned int) _mem_loc[i].adr);
00895    fclose(f);
00896 
00897 }
00898 
00899 #ifndef HAVE_STRLCPY
00900 /*---- strlcpy and strlcat to avoid buffer overflow ----------------*/
00901 
00902 /*
00903  * Copy src to string dst of size siz.  At most siz-1 characters
00904  * will be copied.  Always NUL terminates (unless size == 0).
00905  * Returns strlen(src); if retval >= siz, truncation occurred.
00906  */
00907 INT strlcpy(char *dst, const char *src, INT size)
00908 {
00909    char *d = dst;
00910    const char *s = src;
00911    INT n = size;
00912 
00913    /* Copy as many bytes as will fit */
00914    if (n != 0 && --n != 0) {
00915       do {
00916          if ((*d++ = *s++) == 0)
00917             break;
00918       } while (--n != 0);
00919    }
00920 
00921    /* Not enough room in dst, add NUL and traverse rest of src */
00922    if (n == 0) {
00923       if (size != 0)
00924          *d = '\0';             /* NUL-terminate dst */
00925       while (*s++);
00926    }
00927 
00928    return (s - src - 1);        /* count does not include NUL */
00929 }
00930 
00931 /*
00932  * Appends src to string dst of size siz (unlike strncat, siz is the
00933  * full size of dst, not space left).  At most siz-1 characters
00934  * will be copied.  Always NUL terminates (unless size <= strlen(dst)).
00935  * Returns strlen(src) + MIN(size, strlen(initial dst)).
00936  * If retval >= size, truncation occurred.
00937  */
00938 INT strlcat(char *dst, const char *src, INT size)
00939 {
00940    char *d = dst;
00941    const char *s = src;
00942    INT n = size;
00943    INT dlen;
00944 
00945    /* Find the end of dst and adjust bytes left but don't go past end */
00946    while (n-- != 0 && *d != '\0')
00947       d++;
00948    dlen = d - dst;
00949    n = size - dlen;
00950 
00951    if (n == 0)
00952       return (dlen + strlen(s));
00953    while (*s != '\0') {
00954       if (n != 1) {
00955          *d++ = *s;
00956          n--;
00957       }
00958       s++;
00959    }
00960    *d = '\0';
00961 
00962    return (dlen + (s - src));   /* count does not include NUL */
00963 }
00964 #endif
00965 
00966 /********************************************************************\
00967 *                                                                    *
00968 *              Common message functions                              *
00969 *                                                                    *
00970 \********************************************************************/
00971 
00972 static int (*_message_print) (const char *) = puts;
00973 static INT _message_mask_system = MT_ALL;
00974 static INT _message_mask_user = MT_ALL;
00975 
00976 
00977 /**dox***************************************************************/
00978 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00979 
00980 /**dox***************************************************************/
00981 /** @addtogroup msgfunctionc
00982  *  
00983  *  @{  */
00984 
00985 /********************************************************************/
00986 /**
00987 Convert error code to string. Used after cm_connect_experiment to print
00988 error string in command line programs or windows programs.
00989 @param code Error code as defined in midas.h
00990 @param string Error string
00991 @return CM_SUCCESS
00992 */
00993 INT cm_get_error(INT code, char *string)
00994 {
00995    INT i;
00996 
00997    for (i = 0; _error_table[i].code; i++)
00998       if (_error_table[i].code == code) {
00999          strcpy(string, _error_table[i].string);
01000          return CM_SUCCESS;
01001       }
01002 
01003    sprintf(string, "Unexpected error #%d", code);
01004    return CM_SUCCESS;
01005 }
01006 
01007 /********************************************************************/
01008 /** 
01009 Set message masks. When a message is generated by calling cm_msg(),
01010 it can got to two destinatinons. First a user defined callback routine
01011 and second to the "SYSMSG" buffer.
01012 
01013 A user defined callback receives all messages which satisfy the user_mask.
01014 
01015 \code
01016 int message_print(const char *msg)
01017 {
01018   char str[160];
01019 
01020   memset(str, ' ', 159);
01021   str[159] = 0;
01022   if (msg[0] == '[')
01023     msg = strchr(msg, ']')+2;
01024   memcpy(str, msg, strlen(msg));
01025   ss_printf(0, 20, str);
01026   return 0;
01027 }
01028 ...
01029   cm_set_msg_print(MT_ALL, MT_ALL, message_print);
01030 ...
01031 \endcode
01032 @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
01033 @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
01034 @param func Function which receives all printout. By setting "puts",
01035        messages are just printed to the screen.
01036 @return CM_SUCCESS
01037 */
01038 INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func) (const char *))
01039 {
01040    _message_mask_system = system_mask;
01041    _message_mask_user = user_mask;
01042    _message_print = func;
01043 
01044    return BM_SUCCESS;
01045 }
01046 
01047 /********************************************************************/
01048 /**
01049 Write message to logging file. Called by cm_msg.
01050 @attention May burn your fingers
01051 @param message_type      Message type
01052 @param message          Message string
01053 @return CM_SUCCESS
01054 */
01055 INT cm_msg_log(INT message_type, const char *message)
01056 {
01057    char dir[256];
01058    char filename[256];
01059    char path[256];
01060    char str[256];
01061    FILE *f;
01062    INT status, size;
01063    HNDLE hDB, hKey;
01064 
01065    if (rpc_is_remote())
01066       return rpc_call(RPC_CM_MSG_LOG, message_type, message);
01067 
01068    if (message_type != MT_DEBUG) {
01069       cm_get_experiment_database(&hDB, NULL);
01070 
01071       if (hDB) {
01072          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01073          if (status == DB_SUCCESS) {
01074             size = sizeof(dir);
01075             memset(dir, 0, size);
01076             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01077             if (dir[0] != 0)
01078                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01079                   strcat(dir, DIR_SEPARATOR_STR);
01080 
01081             strcpy(filename, "midas.log");
01082             db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01083                          TRUE);
01084 
01085             strcpy(path, dir);
01086             strcat(path, filename);
01087          } else {
01088             cm_get_path(dir);
01089             if (dir[0] != 0)
01090                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01091                   strcat(dir, DIR_SEPARATOR_STR);
01092 
01093             strcpy(path, dir);
01094             strcat(path, "midas.log");
01095          }
01096       } else
01097          strcpy(path, "midas.log");
01098 
01099       f = fopen(path, "a");
01100       if (f == NULL) {
01101          printf("Cannot open message log file %s\n", path);
01102       } else {
01103          strcpy(str, ss_asctime());
01104          fprintf(f, str);
01105          fprintf(f, " %s\n", message);
01106 
01107          fclose(f);
01108       }
01109    }
01110 
01111    return CM_SUCCESS;
01112 }
01113 
01114 /********************************************************************/
01115 /**
01116 Write message to logging file. Called by cm_msg().
01117 @internal 
01118 @param message_type      Message type
01119 @param message          Message string
01120 @param facility         Message facility, filename in which messages will be written
01121 @return CM_SUCCESS
01122 */
01123 INT cm_msg_log1(INT message_type, const char *message, const char *facility)
01124 /********************************************************************\
01125 
01126   Routine: cm_msg_log1
01127 
01128   Purpose: Write message to logging file. Called by cm_msg.
01129            Internal use only
01130 
01131   Input:
01132     INT    message_type      Message type
01133     char   *message          Message string
01134     char   *
01135 
01136   Output:
01137     none
01138 
01139   Function value:
01140     CM_SUCCESS
01141 
01142 \********************************************************************/
01143 {
01144    char dir[256];
01145    char filename[256];
01146    char path[256];
01147    char str[256];
01148    FILE *f;
01149    INT status, size;
01150    HNDLE hDB, hKey;
01151 
01152 
01153    if (rpc_is_remote())
01154       return rpc_call(RPC_CM_MSG_LOG1, message_type, message, facility);
01155 
01156    if (message_type != MT_DEBUG) {
01157       cm_get_experiment_database(&hDB, NULL);
01158 
01159       if (hDB) {
01160          status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01161          if (status == DB_SUCCESS) {
01162             size = sizeof(dir);
01163             memset(dir, 0, size);
01164             db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01165             if (dir[0] != 0)
01166                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01167                   strcat(dir, DIR_SEPARATOR_STR);
01168 
01169             if (facility[0]) {
01170                strcpy(filename, facility);
01171                strcat(filename, ".log");
01172             } else {
01173                strcpy(filename, "midas.log");
01174                db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING,
01175                             TRUE);
01176             }
01177 
01178             strcpy(path, dir);
01179             strcat(path, filename);
01180          } else {
01181             cm_get_path(dir);
01182             if (dir[0] != 0)
01183                if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01184                   strcat(dir, DIR_SEPARATOR_STR);
01185 
01186             strcpy(path, dir);
01187             if (facility[0]) {
01188                strcat(path, facility);
01189                strcat(path, ".log");
01190             } else
01191                strcat(path, "midas.log");
01192          }
01193       } else {
01194          if (facility[0]) {
01195             strcpy(path, facility);
01196             strcat(path, ".log");
01197          } else
01198             strcpy(path, "midas.log");
01199       }
01200 
01201       f = fopen(path, "a");
01202       if (f == NULL) {
01203          printf("Cannot open message log file %s\n", path);
01204       } else {
01205          strcpy(str, ss_asctime());
01206          fprintf(f, str);
01207          fprintf(f, " %s\n", message);
01208          fclose(f);
01209       }
01210    }
01211 
01212    return CM_SUCCESS;
01213 }
01214 
01215 /********************************************************************/
01216 /** 
01217 This routine can be called whenever an internal error occurs
01218 or an informative message is produced. Different message
01219 types can be enabled or disabled by setting the type bits
01220 via cm_set_msg_print().
01221 @attention Do not add the "\n" escape carriage control at the end of the
01222 formated line as it is already added by the client on the receiving side.
01223 \code
01224    ...
01225    cm_msg(MINFO, "my program", "This is a information message only);
01226    cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
01227    cm_msg(MTALK, "my_program", My program is Done!");
01228    ...
01229 \endcode
01230 @param message_type (See @ref midas_macro).
01231 @param filename Name of source file where error occured
01232 @param line Line number where error occured
01233 @param routine Routine name.
01234 @param format message to printout, ... Parameters like for printf()
01235 @return CM_SUCCESS
01236 */
01237 INT cm_msg(INT message_type, char *filename, INT line,
01238            const char *routine, const char *format, ...)
01239 {
01240    va_list argptr;
01241    char event[1000], str[256], local_message[256], send_message[256], *pc;
01242    EVENT_HEADER *pevent;
01243    INT status;
01244    static BOOL in_routine = FALSE;
01245 
01246    /* avoid recursive calls */
01247    if (in_routine)
01248       return 0;
01249 
01250    in_routine = TRUE;
01251 
01252    /* strip path */
01253    pc = filename + strlen(filename);
01254    while (*pc != '\\' && *pc != '/' && pc != filename)
01255       pc--;
01256    if (pc != filename)
01257       pc++;
01258 
01259    /* print client name into string */
01260    if (message_type == MT_USER)
01261       sprintf(send_message, "[%s] ", routine);
01262    else {
01263       rpc_get_name(str);
01264       if (str[0])
01265          sprintf(send_message, "[%s] ", str);
01266       else
01267          send_message[0] = 0;
01268    }
01269 
01270    local_message[0] = 0;
01271 
01272    /* preceed error messages with file and line info */
01273    if (message_type == MT_ERROR) {
01274       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01275       strcat(send_message, str);
01276       strcat(local_message, str);
01277    }
01278 
01279    /* print argument list into message */
01280    va_start(argptr, format);
01281    vsprintf(str, (char *) format, argptr);
01282    va_end(argptr);
01283    strcat(send_message, str);
01284    strcat(local_message, str);
01285 
01286    /* call user function if set via cm_set_msg_print */
01287    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01288       _message_print(local_message);
01289 
01290    /* return if system mask is not set */
01291    if ((message_type & _message_mask_system) == 0) {
01292       in_routine = FALSE;
01293       return CM_SUCCESS;
01294    }
01295 
01296    /* copy message to event */
01297    pevent = (EVENT_HEADER *) event;
01298    strcpy(event + sizeof(EVENT_HEADER), send_message);
01299 
01300    /* send event if not of type MLOG */
01301    if (message_type != MT_LOG) {
01302       /* if no message buffer already opened, do so now */
01303       if (_msg_buffer == 0) {
01304          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01305          if (status != BM_SUCCESS && status != BM_CREATED) {
01306             in_routine = FALSE;
01307             return status;
01308          }
01309       }
01310 
01311       /* setup the event header and send the message */
01312       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01313                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01314       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01315    }
01316 
01317    /* log message */
01318    cm_msg_log(message_type, send_message);
01319 
01320    in_routine = FALSE;
01321 
01322    return CM_SUCCESS;
01323 }
01324 
01325 /********************************************************************/
01326 /**
01327 This routine is similar to @ref cm_msg().
01328 It differs from cm_msg() only by the logging destination being a file
01329 given through the argument list i.e:\b facility
01330 @internal
01331 @attention Do not add the "\n" escape carriage control at the end of the
01332 formated line as it is already added by the client on the receiving side.
01333 The first arg in the following example uses the predefined
01334 macro MINFO which handles automatically the first 3 arguments of the function
01335 (see @ref midas_macro).
01336 \code   ...
01337    cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
01338    ...
01339 //----- File my_log_file.log
01340 Thu Nov  8 17:59:28 2001 [my_program] My message status:1
01341 \endcode
01342 @param message_type See @ref midas_macro.
01343 @param filename Name of source file where error occured
01344 @param line Line number where error occured
01345 @param facility Logging file name
01346 @param routine Routine name
01347 @param format message to printout, ... Parameters like for printf()
01348 @return CM_SUCCESS
01349 */
01350 INT cm_msg1(INT message_type, char *filename, INT line,
01351             const char *facility, const char *routine, const char *format, ...)
01352 {
01353    va_list argptr;
01354    char event[1000], str[256], local_message[256], send_message[256], *pc;
01355    EVENT_HEADER *pevent;
01356    INT status;
01357    static BOOL in_routine = FALSE;
01358 
01359    /* avoid recursive calles */
01360    if (in_routine)
01361       return 0;
01362 
01363    in_routine = TRUE;
01364 
01365    /* strip path */
01366    pc = filename + strlen(filename);
01367    while (*pc != '\\' && *pc != '/' && pc != filename)
01368       pc--;
01369    if (pc != filename)
01370       pc++;
01371 
01372    /* print client name into string */
01373    if (message_type == MT_USER)
01374       sprintf(send_message, "[%s] ", routine);
01375    else {
01376       rpc_get_name(str);
01377       if (str[0])
01378          sprintf(send_message, "[%s] ", str);
01379       else
01380          send_message[0] = 0;
01381    }
01382 
01383    local_message[0] = 0;
01384 
01385    /* preceed error messages with file and line info */
01386    if (message_type == MT_ERROR) {
01387       sprintf(str, "[%s:%d:%s] ", pc, line, routine);
01388       strcat(send_message, str);
01389       strcat(local_message, str);
01390    }
01391 
01392    /* print argument list into message */
01393    va_start(argptr, format);
01394    vsprintf(str, (char *) format, argptr);
01395    va_end(argptr);
01396 
01397    if (facility)
01398       sprintf(local_message + strlen(local_message), "{%s} ", facility);
01399 
01400    strcat(send_message, str);
01401    strcat(local_message, str);
01402 
01403    /* call user function if set via cm_set_msg_print */
01404    if (_message_print != NULL && (message_type & _message_mask_user) != 0)
01405       _message_print(local_message);
01406 
01407    /* return if system mask is not set */
01408    if ((message_type & _message_mask_system) == 0) {
01409       in_routine = FALSE;
01410       return CM_SUCCESS;
01411    }
01412 
01413    /* copy message to event */
01414    pevent = (EVENT_HEADER *) event;
01415    strcpy(event + sizeof(EVENT_HEADER), send_message);
01416 
01417    /* send event if not of type MLOG */
01418    if (message_type != MT_LOG) {
01419       /* if no message buffer already opened, do so now */
01420       if (_msg_buffer == 0) {
01421          status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
01422          if (status != BM_SUCCESS && status != BM_CREATED) {
01423             in_routine = FALSE;
01424             return status;
01425          }
01426       }
01427 
01428       /* setup the event header and send the message */
01429       bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type,
01430                        strlen(event + sizeof(EVENT_HEADER)) + 1, 0);
01431       bm_send_event(_msg_buffer, event, pevent->data_size + sizeof(EVENT_HEADER), SYNC);
01432    }
01433 
01434    /* log message */
01435    cm_msg_log1(message_type, send_message, facility);
01436 
01437    in_routine = FALSE;
01438 
01439    return CM_SUCCESS;
01440 }
01441 
01442 /********************************************************************/
01443 /** 
01444 Register a dispatch function for receiving system messages.
01445 - example code from mlxspeaker.c
01446 \code
01447 void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
01448 {
01449   char str[256], *pc, *sp;
01450   // print message
01451   printf("%s\n", (char *)(message));
01452 
01453   printf("evID:%x Mask:%x Serial:%i Size:%d\n"
01454                  ,header->event_id
01455                  ,header->trigger_mask
01456                  ,header->serial_number
01457                  ,header->data_size);
01458   pc = strchr((char *)(message),']')+2;
01459   ...
01460   // skip none talking message
01461   if (header->trigger_mask == MT_TALK ||
01462       header->trigger_mask == MT_USER)
01463    ...
01464 }
01465 
01466 int main(int argc, char *argv[])
01467 {
01468   ...
01469   // now connect to server
01470   status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
01471   if (status != CM_SUCCESS)
01472     return 1;
01473   // Register callback for messages
01474   cm_msg_register(receive_message);
01475   ...
01476 }
01477 \endcode
01478 @param func Dispatch function.
01479 @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
01480 */
01481 INT cm_msg_register(void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
01482 {
01483    INT status, id;
01484 
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          return status;
01490    }
01491 
01492    _msg_dispatch = func;
01493 
01494    status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_SOME, &id, func);
01495 
01496    return status;
01497 }
01498 
01499 /********************************************************************/
01500 /**
01501 Retrieve old messages from log file 
01502 @param  n_message        Number of messages to retrieve
01503 @param  message          buf_size bytes of messages, separated
01504                          by \n characters. The returned number
01505                          of bytes is normally smaller than the
01506                          initial buf_size, since only full
01507                          lines are returned.
01508 @param *buf_size         Size of message buffer to fill
01509 @return CM_SUCCESS
01510 */
01511 INT cm_msg_retrieve(INT n_message, char *message, INT * buf_size)
01512 {
01513    char dir[256];
01514    char filename[256];
01515    char path[256], *p;
01516    FILE *f;
01517    INT status, size, offset, i;
01518    HNDLE hDB, hKey;
01519 
01520 
01521    if (rpc_is_remote())
01522       return rpc_call(RPC_CM_MSG_RETRIEVE, message, buf_size);
01523 
01524    cm_get_experiment_database(&hDB, NULL);
01525 
01526    if (hDB) {
01527       status = db_find_key(hDB, 0, "/Logger/Data dir", &hKey);
01528       if (status == DB_SUCCESS) {
01529          size = sizeof(dir);
01530          memset(dir, 0, size);
01531          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
01532          if (dir[0] != 0)
01533             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01534                strcat(dir, DIR_SEPARATOR_STR);
01535 
01536          strcpy(filename, "midas.log");
01537          db_get_value(hDB, 0, "/Logger/Message file", filename, &size, TID_STRING, TRUE);
01538 
01539          strcpy(path, dir);
01540          strcat(path, filename);
01541       } else {
01542          cm_get_path(dir);
01543          if (dir[0] != 0)
01544             if (dir[strlen(dir) - 1] != DIR_SEPARATOR)
01545                strcat(dir, DIR_SEPARATOR_STR);
01546 
01547          strcpy(path, dir);
01548          strcat(path, "midas.log");
01549       }
01550    } else
01551       strcpy(path, "midas.log");
01552 
01553    f = fopen(path, "rb");
01554    if (f == NULL) {
01555       sprintf(message, "Cannot open message log file %s\n", path);
01556       *buf_size = strlen(message);
01557       return CM_DB_ERROR;
01558    } else {
01559       /* position buf_size bytes before the EOF */
01560       fseek(f, -(*buf_size - 1), SEEK_END);
01561       offset = ftell(f);
01562       if (offset != 0) {
01563          /* go to end of line */
01564          fgets(message, *buf_size - 1, f);
01565          offset = ftell(f) - offset;
01566          *buf_size -= offset;
01567       }
01568 
01569       memset(message, 0, *buf_size);
01570       fread(message, 1, *buf_size - 1, f);
01571       message[*buf_size - 1] = 0;
01572       fclose(f);
01573 
01574       p = message + (*buf_size - 2);
01575 
01576       /* goto end of buffer */
01577       while (p != message && *p == 0)
01578          p--;
01579 
01580       /* strip line break */
01581       while (p != message && (*p == '\n' || *p == '\r'))
01582          *(p--) = 0;
01583 
01584       /* trim buffer so that last n_messages remain */
01585       for (i = 0; i < n_message; i++) {
01586          while (p != message && *p != '\n')
01587             p--;
01588 
01589          while (p != message && (*p == '\n' || *p == '\r'))
01590             p--;
01591       }
01592       if (p != message) {
01593          p++;
01594          while (*p == '\n' || *p == '\r')
01595             p++;
01596       }
01597 
01598       *buf_size = (*buf_size - 1) - ((PTYPE) p - (PTYPE) message);
01599 
01600       memmove(message, p, *buf_size);
01601       message[*buf_size] = 0;
01602    }
01603 
01604    return CM_SUCCESS;
01605 }
01606 
01607 /**dox***************************************************************/
01608 /** @} */ /* end of msgfunctionc */
01609 
01610 /**dox***************************************************************/
01611 /** @addtogroup cmfunctionc
01612  *  
01613  *  @{  */
01614 
01615 /********************************************************************/
01616 /**
01617 Get time from MIDAS server and set local time.
01618 @param    seconds         Time in seconds
01619 @return CM_SUCCESS
01620 */
01621 INT cm_synchronize(DWORD * seconds)
01622 {
01623    INT sec, status;
01624 
01625    /* if connected to server, get time from there */
01626    if (rpc_is_remote()) {
01627       status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
01628 
01629       /* set local time */
01630       if (status == CM_SUCCESS)
01631          ss_settime(sec);
01632    }
01633 
01634    /* return time to caller */
01635    if (seconds != NULL) {
01636       *seconds = ss_time();
01637    }
01638 
01639    return CM_SUCCESS;
01640 }
01641 
01642 /********************************************************************/
01643 /**
01644 Get time from MIDAS server and set local time.
01645 @param    str            return time string
01646 @param    buf_size       Maximum size of str
01647 @return   CM_SUCCESS
01648 */
01649 INT cm_asctime(char *str, INT buf_size)
01650 {
01651    /* if connected to server, get time from there */
01652    if (rpc_is_remote())
01653       return rpc_call(RPC_CM_ASCTIME, str, buf_size);
01654 
01655    /* return local time */
01656    strcpy(str, ss_asctime());
01657 
01658    return CM_SUCCESS;
01659 }
01660 
01661 /********************************************************************/
01662 /**
01663 Get time from ss_time on server.
01664 @param    time string
01665 @return   CM_SUCCESS
01666 */
01667 INT cm_time(DWORD * time)
01668 {
01669    /* if connected to server, get time from there */
01670    if (rpc_is_remote())
01671       return rpc_call(RPC_CM_TIME, time);
01672 
01673    /* return local time */
01674    *time = ss_time();
01675 
01676    return CM_SUCCESS;
01677 }
01678 
01679 /**dox***************************************************************/
01680 /** @} */ /* end of cmfunctionc */
01681 
01682 /********************************************************************\
01683 *                                                                    *
01684 *           cm_xxx  -  Common Functions to buffer & database         *
01685 *                                                                    *
01686 \********************************************************************/
01687 
01688 /* Globals */
01689 
01690 static HNDLE _hKeyClient = 0;   /* key handle for client in ODB */
01691 static HNDLE _hDB = 0;          /* Database handle */
01692 static char _client_name[NAME_LENGTH];
01693 static char _path_name[MAX_STRING_LENGTH];
01694 static INT _call_watchdog = TRUE;
01695 static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
01696 INT _mutex_alarm, _mutex_elog;
01697 
01698 /**dox***************************************************************/
01699 /** @addtogroup cmfunctionc
01700  *  
01701  *  @{  */
01702 
01703 /**
01704 Return version number of current MIDAS library as a string
01705 @return version number * 100
01706 */
01707 char *cm_get_version()
01708 {
01709    return MIDAS_VERSION;
01710 }
01711 
01712 /********************************************************************/
01713 /**
01714 Set path to actual experiment. This function gets called
01715 by cm_connect_experiment if the connection is established
01716 to a local experiment (not through the TCP/IP server).
01717 The path is then used for all shared memory routines.
01718 @param  path             Pathname
01719 @return CM_SUCCESS
01720 */
01721 INT cm_set_path(char *path)
01722 {
01723    strcpy(_path_name, path);
01724 
01725    /* check for trailing directory seperator */
01726    if (strlen(_path_name) > 0 && _path_name[strlen(_path_name) - 1] != DIR_SEPARATOR)
01727       strcat(_path_name, DIR_SEPARATOR_STR);
01728 
01729    return CM_SUCCESS;
01730 }
01731 
01732 /********************************************************************/
01733 /**
01734 Return the path name previously set with cm_set_path.
01735 @param  path             Pathname
01736 @return CM_SUCCESS
01737 */
01738 INT cm_get_path(char *path)
01739 {
01740    strcpy(path, _path_name);
01741 
01742    return CM_SUCCESS;
01743 }
01744 
01745 /**dox***************************************************************/
01746 /** @} */ /* end of cmfunctionc */
01747 
01748 /**dox***************************************************************/
01749 /** @addtogroup cmfunctionc
01750  *  
01751  *  @{  */
01752 
01753 /**dox***************************************************************/
01754 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01755 
01756 typedef struct {
01757    char name[NAME_LENGTH];
01758    char directory[MAX_STRING_LENGTH];
01759    char user[NAME_LENGTH];
01760 } experiment_table;
01761 
01762 static experiment_table exptab[MAX_EXPERIMENT];
01763 
01764 /**dox***************************************************************/
01765 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01766 
01767 /**
01768 Scan the "exptab" file for MIDAS experiment names and save them
01769 for later use by rpc_server_accept(). The file is first searched
01770 under $MIDAS/exptab if present, then the directory from argv[0] is probed.
01771 @return CM_SUCCESS<br>
01772         CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
01773 */
01774 INT cm_scan_experiments(void)
01775 {
01776    INT i;
01777    FILE *f;
01778    char str[MAX_STRING_LENGTH], alt_str[MAX_STRING_LENGTH], *pdir;
01779 
01780    for (i = 0; i < MAX_EXPERIMENT; i++)
01781       exptab[i].name[0] = 0;
01782 
01783    /* MIDAS_DIR overrides exptab */
01784    if (getenv("MIDAS_DIR")) {
01785       strlcpy(str, getenv("MIDAS_DIR"), sizeof(str));
01786 
01787       strcpy(exptab[0].name, "Default");
01788       strlcpy(exptab[0].directory, getenv("MIDAS_DIR"), sizeof(exptab[0].directory));
01789       exptab[0].user[0] = 0;
01790 
01791       return CM_SUCCESS;
01792    }
01793 
01794    /* default directory for different OSes */
01795 #if defined (OS_WINNT)
01796    if (getenv("SystemRoot"))
01797       strlcpy(str, getenv("SystemRoot"), sizeof(str));
01798    else if (getenv("windir"))
01799       strlcpy(str, getenv("windir"), sizeof(str));
01800    else
01801       strcpy(str, "");
01802 
01803    strcpy(alt_str, str);
01804    strcat(str, "\\system32\\exptab");
01805    strcat(alt_str, "\\system\\exptab");
01806 #elif defined (OS_UNIX)
01807    strcpy(str, "/etc/exptab");
01808    strcpy(alt_str, "/exptab");
01809 #else
01810    strcpy(str, "exptab");
01811    strcpy(alt_str, "exptab");
01812 #endif
01813 
01814    /* MIDAS_EXPTAB overrides default directory */
01815    if (getenv("MIDAS_EXPTAB")) {
01816       strlcpy(str, getenv("MIDAS_EXPTAB"), sizeof(str));
01817       strlcpy(alt_str, getenv("MIDAS_EXPTAB"), sizeof(alt_str));
01818    }
01819 
01820    /* read list of available experiments */
01821    f = fopen(str, "r");
01822    if (f == NULL) {
01823       f = fopen(alt_str, "r");
01824       if (f == NULL)
01825          return CM_UNDEF_ENVIRON;
01826    }
01827 
01828    i = 0;
01829    if (f != NULL) {
01830       do {
01831          str[0] = 0;
01832          if (fgets(str, 100, f) == NULL)
01833             break;
01834          if (str[0] && str[0] != '#') {
01835             sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
01836 
01837             /* check for trailing directory separator */
01838             pdir = exptab[i].directory;
01839             if (pdir[strlen(pdir) - 1] != DIR_SEPARATOR)
01840                strcat(pdir, DIR_SEPARATOR_STR);
01841 
01842             i++;
01843          }
01844       } while (!feof(f));
01845       fclose(f);
01846    }
01847 
01848    /*
01849       for (j=0 ; j<i ; j++)
01850       {
01851       sprintf(str, "Scanned experiment %s", exptab[j].name);
01852       cm_msg(MINFO, str);
01853       }
01854     */
01855 
01856    return CM_SUCCESS;
01857 }
01858 
01859 /********************************************************************/
01860 /**
01861 Delete client info from database
01862 @param hDB               Database handle
01863 @param pid               PID of entry to delete, zero for this process.
01864 @return CM_SUCCESS
01865 */
01866 INT cm_delete_client_info(HNDLE hDB, INT pid)
01867 {
01868 #ifdef LOCAL_ROUTINES
01869 
01870    /* only do it if local */
01871    if (!rpc_is_remote()) {
01872       INT status;
01873       HNDLE hKey;
01874       char str[256];
01875 
01876       if (!pid)
01877          pid = ss_gettid();
01878 
01879       /* don't delete info from a closed database */
01880       if (_database_entries == 0)
01881          return CM_SUCCESS;
01882 
01883       /* make operation atomic by locking database */
01884       db_lock_database(hDB);
01885 
01886       sprintf(str, "System/Clients/%0d", pid);
01887       status = db_find_key1(hDB, 0, str, &hKey);
01888       if (status != DB_SUCCESS) {
01889          db_unlock_database(hDB);
01890          return status;
01891       }
01892 
01893       /* unlock client entry and delete it without locking DB */
01894       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, 2);
01895       db_delete_key1(hDB, hKey, 1, TRUE);
01896 
01897       db_unlock_database(hDB);
01898 
01899       /* touch notify key to inform others */
01900       status = 0;
01901       db_set_value(hDB, 0, "/System/Client Notify", &status, sizeof(status), 1, TID_INT);
01902    }
01903 #endif                          /*LOCAL_ROUTINES */
01904 
01905    return CM_SUCCESS;
01906 }
01907 
01908 /********************************************************************/
01909 /**
01910 Check if a client with a /system/client/xxx entry has
01911 a valid entry in the ODB client table. If not, remove
01912 that client from the /system/client tree. 
01913 @param   hDB               Handle to online database
01914 @param   hKeyClient        Handle to client key
01915 @return  CM_SUCCESS, CM_NO_CLIENT
01916 */
01917 INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
01918 {
01919 #ifdef LOCAL_ROUTINES
01920 
01921    KEY key;
01922    DATABASE_HEADER *pheader;
01923    DATABASE_CLIENT *pclient;
01924    INT i, client_pid, status;
01925    char name[NAME_LENGTH];
01926 
01927    db_get_key(hDB, hKeyClient, &key);
01928    client_pid = atoi(key.name);
01929 
01930    i = sizeof(name);
01931    db_get_value(hDB, hKeyClient, "Name", name, &i, TID_STRING, TRUE);
01932 
01933    db_lock_database(hDB);
01934    if (_database[hDB - 1].attached) {
01935       pheader = _database[hDB - 1].database_header;
01936       pclient = pheader->client;
01937 
01938       /* loop through clients */
01939       for (i = 0; i < pheader->max_client_index; i++, pclient++)
01940          if (pclient->tid == client_pid)
01941             break;
01942 
01943       if (i == pheader->max_client_index) {
01944          /* client not found : delete ODB stucture */
01945          db_unlock_database(hDB);
01946 
01947          status = cm_delete_client_info(hDB, client_pid);
01948          if (status != CM_SUCCESS)
01949             cm_msg(MERROR, "cm_check_client", "cannot delete client info");
01950          else
01951             cm_msg(MINFO, "cm_check_clinet",
01952                    "Deleted /System/Clients/%d entry for client %s\n", client_pid, name);
01953 
01954          return CM_NO_CLIENT;
01955       }
01956    }
01957 
01958    db_unlock_database(hDB);
01959 
01960 #endif                          /*LOCAL_ROUTINES */
01961 
01962    return CM_SUCCESS;
01963 }
01964 
01965 /********************************************************************/
01966 /**
01967 Set client information in online database and return handle 
01968 @param  hDB              Handle to online database  
01969 @param  hKeyClient       returned key
01970 @param  host_name        server name 
01971 @param  client_name      Name of this program as it will be seen
01972                          by other clients.
01973 @param  hw_type          Type of byte order
01974 @param  password         MIDAS password  
01975 @param  watchdog_timeout Default watchdog timeout, can be overwritten
01976                          by ODB setting /programs/<name>/Watchdog timeout
01977 @return   CM_SUCCESS
01978 */
01979 INT cm_set_client_info(HNDLE hDB, HNDLE * hKeyClient, char *host_name,
01980                        char *client_name, INT hw_type, char *password,
01981                        DWORD watchdog_timeout)
01982 {
01983    if (rpc_is_remote())
01984       return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
01985                       host_name, client_name, hw_type, password, watchdog_timeout);
01986 
01987 #ifdef LOCAL_ROUTINES
01988    {
01989       INT status, pid, data, i, index, size;
01990       HNDLE hKey, hSubkey;
01991       char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
01992       BOOL call_watchdog, allow;
01993       PROGRAM_INFO_STR(program_info_str);
01994 
01995       /* check security if password is presend */
01996       status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
01997       if (hKey) {
01998          /* get password */
01999          size = sizeof(pwd);
02000          db_get_data(hDB, hKey, pwd, &size, TID_STRING);
02001 
02002          /* first check allowed hosts list */
02003          allow = FALSE;
02004          db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
02005          if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
02006             allow = TRUE;
02007 
02008          /* check allowed programs list */
02009          db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
02010          if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
02011             allow = TRUE;
02012 
02013          /* now check password */
02014          if (!allow &&
02015              strcmp(password, pwd) != 0 && strcmp(password, "mid7qBxsNMHux") != 0) {
02016             if (password[0])
02017                cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s",
02018                       host_name);
02019             db_close_all_databases();
02020             bm_close_all_buffers();
02021             _msg_buffer = 0;
02022             return CM_WRONG_PASSWORD;
02023          }
02024       }
02025 
02026       /* check if entry with this pid exists already */
02027       pid = ss_gettid();
02028 
02029       sprintf(str, "System/Clients/%0d", pid);
02030       status = db_find_key(hDB, 0, str, &hKey);
02031       if (status == DB_SUCCESS) {
02032          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
02033          db_delete_key(hDB, hKey, TRUE);
02034       }
02035 
02036       if (strlen(client_name) >= NAME_LENGTH)
02037          client_name[NAME_LENGTH] = 0;
02038 
02039       strcpy(name, client_name);
02040       strcpy(orig_name, client_name);
02041 
02042       /* check if client name already exists */
02043       status = db_find_key(hDB, 0, "System/Clients", &hKey);
02044 
02045       for (index = 1; status != DB_NO_MORE_SUBKEYS; index++) {
02046          for (i = 0;; i++) {
02047             status = db_enum_key(hDB, hKey, i, &hSubkey);
02048             if (status == DB_NO_MORE_SUBKEYS)
02049                break;
02050 
02051             if (status == DB_SUCCESS) {
02052                size = sizeof(str);
02053                status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
02054             }
02055 
02056             /* check if client is living */
02057             if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
02058                continue;
02059 
02060             if (equal_ustring(str, name)) {
02061                sprintf(name, "%s%d", client_name, index);
02062                break;
02063             }
02064          }
02065       }
02066 
02067       /* set name */
02068       sprintf(str, "System/Clients/%0d/Name", pid);
02069       status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
02070       if (status != DB_SUCCESS) {
02071          cm_msg(MERROR, "cm_set_client_info", "cannot set client name");
02072          return status;
02073       }
02074 
02075       /* copy new client name */
02076       strcpy(client_name, name);
02077       db_set_client_name(hDB, client_name);
02078 
02079       /* set also as rpc name */
02080       rpc_set_name(client_name);
02081 
02082       /* use /system/clients/PID as root */
02083       sprintf(str, "System/Clients/%0d", pid);
02084       db_find_key(hDB, 0, str, &hKey);
02085 
02086       /* set host name */
02087       status =
02088           db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
02089       if (status != DB_SUCCESS)
02090          return status;
02091 
02092       /* set computer id */
02093       status = db_set_value(hDB, hKey, "Hardware type", &hw_type,
02094                             sizeof(hw_type), 1, TID_INT);
02095       if (status != DB_SUCCESS)
02096          return status;
02097 
02098       /* set server port */
02099       data = 0;
02100       status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT);
02101       if (status != DB_SUCCESS)
02102          return status;
02103 
02104       /* set transition mask */
02105       data = 0;
02106       status = db_set_value(hDB, hKey, "Transition Mask", &data,
02107                             sizeof(DWORD), 1, TID_DWORD);
02108       if (status != DB_SUCCESS)
02109          return status;
02110 
02111       /* set deferred transition mask */
02112       data = 0;
02113       status = db_set_value(hDB, hKey, "Deferred Transition Mask", &data,
02114                             sizeof(DWORD), 1, TID_DWORD);
02115       if (status != DB_SUCCESS)
02116          return status;
02117 
02118       /* lock client entry */
02119       db_set_mode(hDB, hKey, MODE_READ, TRUE);
02120 
02121       /* get (set) default watchdog timeout */
02122       size = sizeof(watchdog_timeout);
02123       sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
02124       db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02125 
02126       /* define /programs entry */
02127       sprintf(str, "/Programs/%s", orig_name);
02128       db_create_record(hDB, 0, str, strcomb(program_info_str));
02129 
02130       /* save handle for ODB and client */
02131       rpc_set_server_option(RPC_ODB_HANDLE, hDB);
02132       rpc_set_server_option(RPC_CLIENT_HANDLE, hKey);
02133 
02134       /* save watchdog timeout */
02135       cm_get_watchdog_params(&call_watchdog, NULL);
02136       cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02137       if (call_watchdog)
02138          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
02139 
02140       /* touch notify key to inform others */
02141       data = 0;
02142       db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT);
02143 
02144       *hKeyClient = hKey;
02145    }
02146 #endif                          /* LOCAL_ROUTINES */
02147 
02148    return CM_SUCCESS;
02149 }
02150 
02151 /********************************************************************/
02152 /**
02153 Get info about the current client 
02154 @param  *client_name       Client name.  
02155 @return   CM_SUCCESS, CM_UNDEF_EXP  
02156 */
02157 INT cm_get_client_info(char *client_name)
02158 {
02159    INT status, length;
02160    HNDLE hDB, hKey;
02161 
02162    /* get root key of client */
02163    cm_get_experiment_database(&hDB, &hKey);
02164    if (!hDB) {
02165       client_name[0] = 0;
02166       return CM_UNDEF_EXP;
02167    }
02168 
02169    status = db_find_key(hDB, hKey, "Name", &hKey);
02170    if (status != DB_SUCCESS)
02171       return status;
02172 
02173    length = NAME_LENGTH;
02174    status = db_get_data(hDB, hKey, client_name, &length, TID_STRING);
02175    if (status != DB_SUCCESS)
02176       return status;
02177 
02178    return CM_SUCCESS;
02179 }
02180 
02181 /********************************************************************/
02182 /**
02183 Returns MIDAS environment variables. 
02184 @attention This function can be used to evaluate the standard MIDAS
02185            environment variables before connecting to an experiment
02186            (see @ref Environment_variables).
02187            The usual way is that the host name and experiment name are first derived
02188            from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
02189            They can then be superseded by command line parameters with -h and -e flags.
02190 \code
02191 #include <stdio.h>
02192 #include <midas.h>
02193 main(int argc, char *argv[])
02194 {
02195   INT  status, i;
02196   char host_name[256],exp_name[32];
02197 
02198   // get default values from environment
02199   cm_get_environment(host_name, exp_name);
02200 
02201   // parse command line parameters
02202   for (i=1 ; i<argc ; i++)
02203     {
02204     if (argv[i][0] == '-')
02205       {
02206       if (i+1 >= argc || argv[i+1][0] == '-')
02207         goto usage;
02208       if (argv[i][1] == 'e')
02209         strcpy(exp_name, argv[++i]);
02210       else if (argv[i][1] == 'h')
02211         strcpy(host_name, argv[++i]);
02212       else
02213         {
02214 usage:
02215         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02216         return 1;
02217         }
02218       }
02219     }
02220   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02221   if (status != CM_SUCCESS)
02222     return 1;
02223     ...do anyting...
02224   cm_disconnect_experiment();
02225 }
02226 \endcode
02227 @param host_name           Contents of MIDAS_SERVER_HOST environment variable.
02228 @param host_name_size     string length
02229 @param exp_name           Contents of MIDAS_EXPT_NAME environment variable.
02230 @param exp_name_size      string length
02231 @return CM_SUCCESS
02232 */
02233 INT cm_get_environment(char *host_name, int host_name_size, char *exp_name,
02234                        int exp_name_size)
02235 {
02236    host_name[0] = exp_name[0] = 0;
02237 
02238    if (getenv("MIDAS_SERVER_HOST"))
02239       strlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
02240 
02241    if (getenv("MIDAS_EXPT_NAME"))
02242       strlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
02243 
02244    return CM_SUCCESS;
02245 }
02246 
02247 
02248 /**dox***************************************************************/
02249 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02250 
02251 /********************************************************************/
02252 void cm_check_connect(void)
02253 {
02254    if (_hKeyClient)
02255       cm_msg(MERROR, "", "cm_disconnect_experiment not called at end of program");
02256 }
02257 
02258 /**dox***************************************************************/
02259 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02260 
02261 /********************************************************************/
02262 /**
02263 This function connects to an existing MIDAS experiment.
02264 This must be the first call in a MIDAS application.
02265 It opens three TCP connection to the remote host (one for RPC calls,
02266 one to send events and one for hot-link notifications from the remote host)
02267 and writes client information into the ODB under /System/Clients.
02268 @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
02269 and MIDAS_EXPT_NAME environment variables as defaults to the host name and
02270 experiment name (see @ref Environment_variables).
02271 For that purpose, the function cm_get_environment()
02272 should be called prior to cm_connect_experiment(). If command line
02273 parameters -h and -e are used, the evaluation should be done between
02274 cm_get_environment() and cm_connect_experiment(). The function
02275 cm_disconnect_experiment() must be called before a MIDAS application exits.
02276 \code
02277 #include <stdio.h>
02278 #include <midas.h>
02279 main(int argc, char *argv[])
02280 {
02281   INT  status, i;
02282   char host_name[256],exp_name[32];
02283 
02284   // get default values from environment
02285   cm_get_environment(host_name, exp_name);
02286 
02287   // parse command line parameters
02288   for (i=1 ; i<argc ; i++)
02289     {
02290     if (argv[i][0] == '-')
02291       {
02292       if (i+1 >= argc || argv[i+1][0] == '-')
02293         goto usage;
02294       if (argv[i][1] == 'e')
02295         strcpy(exp_name, argv[++i]);
02296       else if (argv[i][1] == 'h')
02297         strcpy(host_name, argv[++i]);
02298       else
02299         {
02300 usage:
02301         printf("usage: test [-h Hostname] [-e Experiment]\n\n");
02302         return 1;
02303         }
02304       }
02305     }
02306   status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
02307   if (status != CM_SUCCESS)
02308     return 1;
02309   ...do operations...
02310   cm_disconnect_experiment();
02311 }
02312 \endcode
02313 @param host_name Specifies host to connect to. Must be a valid IP host name.
02314   The string can be empty ("") if to connect to the local computer.
02315 @param exp_name Specifies the experiment to connect to.
02316   If this string is empty, the number of defined experiments in exptab is checked.
02317   If only one experiment is defined, the function automatically connects to this
02318   one. If more than one experiment is defined, a list is presented and the user
02319   can interactively select one experiment.
02320 @param client_name Client name of the calling program as it can be seen by
02321   others (like the scl command in ODBEdit).
02322 @param func Callback function to read in a password if security has
02323   been enabled. In all command line applications this function is NULL which
02324   invokes an internal ss_gets() function to read in a password.
02325   In windows environments (MS Windows, X Windows) a function can be supplied to
02326   open a dialog box and read in the password. The argument of this function must
02327   be the returned password.
02328 @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br> 
02329 CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
02330 */
02331 INT cm_connect_experiment(char *host_name, char *exp_name,
02332                           char *client_name, void (*func) (char *))
02333 {
02334    INT status;
02335    char str[256];
02336 
02337    status = cm_connect_experiment1(host_name, exp_name, client_name,
02338                                    func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
02339    if (status != CM_SUCCESS) {
02340       cm_get_error(status, str);
02341       puts(str);
02342    }
02343 
02344    return status;
02345 }
02346 
02347 /********************************************************************/
02348 /**
02349 Connect to a MIDAS experiment (to the online database) on
02350            a specific host.
02351 @internal
02352 */
02353 INT cm_connect_experiment1(char *host_name, char *exp_name,
02354                            char *client_name, void (*func) (char *),
02355                            INT odb_size, DWORD watchdog_timeout)
02356 {
02357    INT status, i, mutex_elog, mutex_alarm, size;
02358    char local_host_name[HOST_NAME_LENGTH];
02359    char client_name1[NAME_LENGTH];
02360    char password[NAME_LENGTH], str[256], exp_name1[NAME_LENGTH];
02361    HNDLE hDB, hKeyClient;
02362    BOOL call_watchdog;
02363    RUNINFO_STR(runinfo_str);
02364 
02365    if (_hKeyClient)
02366       cm_disconnect_experiment();
02367 
02368    rpc_set_name(client_name);
02369 
02370    /* check for local host */
02371    if (equal_ustring(host_name, "local"))
02372       host_name[0] = 0;
02373 
02374 #ifdef OS_WINNT
02375    {
02376       WSADATA WSAData;
02377 
02378       /* Start windows sockets */
02379       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02380          return RPC_NET_ERROR;
02381    }
02382 #endif
02383 
02384    /* search for experiment name in exptab */
02385    if (exp_name == NULL)
02386       exp_name = "";
02387 
02388    strcpy(exp_name1, exp_name);
02389    if (exp_name1[0] == 0) {
02390       status = cm_select_experiment(host_name, exp_name1);
02391       if (status != CM_SUCCESS)
02392          return status;
02393    }
02394 
02395    /* connect to MIDAS server */
02396    if (host_name[0]) {
02397       status = rpc_server_connect(host_name, exp_name1);
02398       if (status != RPC_SUCCESS)
02399          return status;
02400 
02401       /* register MIDAS library functions */
02402       status = rpc_register_functions(rpc_get_internal_list(1), NULL);
02403       if (status != RPC_SUCCESS)
02404          return status;
02405    } else {
02406       /* lookup path for *SHM files and save it */
02407       status = cm_scan_experiments();
02408       if (status != CM_SUCCESS)
02409          return status;
02410 
02411       for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++)
02412          if (equal_ustring(exp_name1, exptab[i].name))
02413             break;
02414 
02415       /* return if experiment not defined */
02416       if (i == MAX_EXPERIMENT || exptab[i].name[0] == 0) {
02417          /* message should be displayed by application
02418             sprintf(str, "Experiment %s not defined in exptab\r", exp_name1);
02419             cm_msg(MERROR, str);
02420           */
02421          return CM_UNDEF_EXP;
02422       }
02423 
02424       cm_set_path(exptab[i].directory);
02425 
02426       /* create alarm and elog mutexes */
02427       status = ss_mutex_create("ALARM", &mutex_alarm);
02428       if (status != SS_CREATED && status != SS_SUCCESS) {
02429          cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm mutex");
02430          return status;
02431       }
02432       status = ss_mutex_create("ELOG", &mutex_elog);
02433       if (status != SS_CREATED && status != SS_SUCCESS) {
02434          cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog mutex");
02435          return status;
02436       }
02437       cm_set_experiment_mutex(mutex_alarm, mutex_elog);
02438    }
02439 
02440    /* open ODB */
02441    if (odb_size == 0)
02442       odb_size = DEFAULT_ODB_SIZE;
02443 
02444    status = db_open_database("ODB", odb_size, &hDB, client_name);
02445    if (status != DB_SUCCESS && status != DB_CREATED) {
02446       cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02447       return status;
02448    }
02449 
02450    /* now setup client info */
02451    gethostname(local_host_name, sizeof(local_host_name));
02452 
02453    /* check watchdog timeout */
02454    if (watchdog_timeout == 0)
02455       watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
02456 
02457    strcpy(client_name1, client_name);
02458    password[0] = 0;
02459    status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02460                                client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02461                                password, watchdog_timeout);
02462 
02463    if (status == CM_WRONG_PASSWORD) {
02464       if (func == NULL)
02465          strcpy(str, ss_getpass("Password: "));
02466       else
02467          func(str);
02468 
02469       /* re-open database */
02470       status = db_open_database("ODB", odb_size, &hDB, client_name);
02471       if (status != DB_SUCCESS && status != DB_CREATED) {
02472          cm_msg(MERROR, "cm_connect_experiment1", "cannot open database");
02473          return status;
02474       }
02475 
02476       strcpy(password, ss_crypt(str, "mi"));
02477       status = cm_set_client_info(hDB, &hKeyClient, local_host_name,
02478                                   client_name1, rpc_get_option(0, RPC_OHW_TYPE),
02479                                   password, watchdog_timeout);
02480       if (status != CM_SUCCESS) {
02481          /* disconnect */
02482          if (rpc_is_remote())
02483             rpc_server_disconnect();
02484 
02485          return status;
02486       }
02487    }
02488 
02489    cm_set_experiment_database(hDB, hKeyClient);
02490 
02491    /* set experiment name in ODB */
02492    db_set_value(hDB, 0, "/Experiment/Name", exp_name1, NAME_LENGTH, 1, TID_STRING);
02493 
02494    /* set data dir in ODB */
02495    cm_get_path(str);
02496    size = sizeof(str);
02497    db_get_value(hDB, 0, "/Logger/Data dir", str, &size, TID_STRING, TRUE);
02498 
02499    /* check /runinfo structure */
02500    status = db_check_record(hDB, 0, "/Runinfo", strcomb(runinfo_str), FALSE);
02501    if (status == DB_STRUCT_MISMATCH) {
02502       cm_msg(MERROR, "cm_connect_experiment1",
02503              "Aborting on mismatching /Runinfo structure");
02504       cm_disconnect_experiment();
02505       abort();
02506    }
02507 
02508    /* register server to be able to be called by other clients */
02509    status = cm_register_server();
02510    if (status != CM_SUCCESS)
02511       return status;
02512 
02513    /* set watchdog timeout */
02514    cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
02515    size = sizeof(watchdog_timeout);
02516    sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
02517    db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT, TRUE);
02518    cm_set_watchdog_params(call_watchdog, watchdog_timeout);
02519 
02520    /* send startup notification */
02521    if (strchr(local_host_name, '.'))
02522       *strchr(local_host_name, '.') = 0;
02523 
02524    /* startup message is not displayed */
02525    _message_print = NULL;
02526 
02527    cm_msg(MINFO, "cm_connect_experiment", "Program %s on host %s started",
02528           client_name, local_host_name);
02529 
02530    /* enable system and user messages to stdout as default */
02531    cm_set_msg_print(MT_ALL, MT_ALL, puts);
02532 
02533    /* call cm_check_connect when exiting */
02534    atexit((void (*)(void)) cm_check_connect);
02535 
02536    /* register ctrl-c handler */
02537    ss_ctrlc_handler(cm_ctrlc_handler);
02538 
02539    return CM_SUCCESS;
02540 }
02541 
02542 /********************************************************************/
02543 /** 
02544 Connect to a MIDAS server and return all defined
02545            experiments in *exp_name[MAX_EXPERIMENTS]
02546 @param  host_name         Internet host name.
02547 @param  exp_name          list of experiment names
02548 @return CM_SUCCESS, RPC_NET_ERROR
02549 */
02550 INT cm_list_experiments(char *host_name, char exp_name[MAX_EXPERIMENT][NAME_LENGTH])
02551 {
02552    INT i, status;
02553    struct sockaddr_in bind_addr;
02554    INT sock;
02555    char str[MAX_EXPERIMENT * NAME_LENGTH];
02556    struct hostent *phe;
02557 
02558    if (host_name[0] == 0 || equal_ustring(host_name, "local")) {
02559       status = cm_scan_experiments();
02560       if (status != CM_SUCCESS)
02561          return status;
02562 
02563       for (i = 0; i < MAX_EXPERIMENT; i++)
02564          strcpy(exp_name[i], exptab[i].name);
02565 
02566       return CM_SUCCESS;
02567    }
02568 #ifdef OS_WINNT
02569    {
02570       WSADATA WSAData;
02571 
02572       /* Start windows sockets */
02573       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
02574          return RPC_NET_ERROR;
02575    }
02576 #endif
02577 
02578    /* create a new socket for connecting to remote server */
02579    sock = socket(AF_INET, SOCK_STREAM, 0);
02580    if (sock == -1) {
02581       cm_msg(MERROR, "cm_list_experiments", "cannot create socket");
02582       return RPC_NET_ERROR;
02583    }
02584 
02585    /* connect to remote node */
02586    memset(&bind_addr, 0, sizeof(bind_addr));
02587    bind_addr.sin_family = AF_INET;
02588    bind_addr.sin_addr.s_addr = 0;
02589    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
02590 
02591 #ifdef OS_VXWORKS
02592    {
02593       INT host_addr;
02594 
02595       host_addr = hostGetByName(host_name);
02596       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
02597    }
02598 #else
02599    phe = gethostbyname(host_name);
02600    if (phe == NULL) {
02601       cm_msg(MERROR, "cm_list_experiments", "cannot get host name");
02602       return RPC_NET_ERROR;
02603    }
02604    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
02605 #endif
02606 
02607 #ifdef OS_UNIX
02608    do {
02609       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
02610 
02611       /* don't return if an alarm signal was cought */
02612    } while (status == -1 && errno == EINTR);
02613 #else
02614    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
02615 #endif
02616 
02617    if (status != 0) {
02618 /*    cm_msg(MERROR, "cannot connect"); message should be displayed by application */
02619       return RPC_NET_ERROR;
02620    }
02621 
02622    /* request experiment list */
02623    send(sock, "I", 2, 0);
02624 
02625    for (i = 0; i < MAX_EXPERIMENT; i++) {
02626       exp_name[i][0] = 0;
02627       status = recv_string(sock, str, sizeof(str), 1000);
02628 
02629       if (status < 0)
02630          return RPC_NET_ERROR;
02631 
02632       if (status == 0)
02633          break;
02634 
02635       strcpy(exp_name[i], str);
02636    }
02637 
02638    exp_name[i][0] = 0;
02639    closesocket(sock);
02640 
02641    return CM_SUCCESS;
02642 }
02643 
02644 /********************************************************************/
02645 /**
02646 Connect to a MIDAS server and select an experiment
02647            from the experiments available on this server
02648 @internal
02649 @param  host_name         Internet host name.
02650 @param  exp_name          list of experiment names
02651 @return CM_SUCCESS, RPC_NET_ERROR
02652 */
02653 INT cm_select_experiment(char *host_name, char *exp_name)
02654 {
02655    INT status, i;
02656    char expts[MAX_EXPERIMENT][NAME_LENGTH];
02657    char str[32];
02658 
02659    /* retrieve list of experiments and make selection */
02660    status = cm_list_experiments(host_name, expts);
02661    if (status != CM_SUCCESS)
02662       return status;
02663 
02664    if (expts[1][0]) {
02665       if (host_name[0])
02666          printf("Available experiments on server %s:\n", host_name);
02667       else
02668          printf("Available experiments on local computer:\n");
02669 
02670       for (i = 0; expts[i][0]; i++)
02671          printf("%d : %s\n", i, expts[i]);
02672       printf("Select number: ");
02673       ss_gets(str, 32);
02674       i = atoi(str);
02675       strcpy(exp_name, expts[i]);
02676    } else
02677       strcpy(exp_name, expts[0]);
02678 
02679    return CM_SUCCESS;
02680 }
02681 
02682 /********************************************************************/
02683 /**
02684 Connect to a MIDAS client of the current experiment
02685 @internal
02686 @param  client_name       Name of client to connect to. This name
02687                             is set by the other client via the
02688                             cm_connect_experiment call.
02689 @param  hConn            Connection handle
02690 @return CM_SUCCESS, CM_NO_CLIENT
02691 */
02692 INT cm_connect_client(char *client_name, HNDLE * hConn)
02693 {
02694    HNDLE hDB, hKeyRoot, hSubkey, hKey;
02695    INT status, i, length, port;
02696    char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
02697 
02698    /* find client entry in ODB */
02699    cm_get_experiment_database(&hDB, &hKey);
02700 
02701    status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
02702    if (status != DB_SUCCESS)
02703       return status;
02704 
02705    i = 0;
02706    do {
02707       /* search for client with specific name */
02708       status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
02709       if (status == DB_NO_MORE_SUBKEYS)
02710          return CM_NO_CLIENT;
02711 
02712       status = db_find_key(hDB, hSubkey, "Name", &hKey);
02713       if (status != DB_SUCCESS)
02714          return status;
02715 
02716       length = NAME_LENGTH;
02717       status = db_get_data(hDB, hKey, name, &length, TID_STRING);
02718       if (status != DB_SUCCESS)
02719          return status;
02720 
02721       if (equal_ustring(name, client_name)) {
02722          status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
02723          if (status != DB_SUCCESS)
02724             return status;
02725 
02726          length = sizeof(INT);
02727          status = db_get_data(hDB, hKey, &port, &length, TID_INT);
02728          if (status != DB_SUCCESS)
02729             return status;
02730 
02731          status = db_find_key(hDB, hSubkey, "Host", &hKey);
02732          if (status != DB_SUCCESS)
02733             return status;
02734 
02735          length = sizeof(host_name);
02736          status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
02737          if (status != DB_SUCCESS)
02738             return status;
02739 
02740          /* client found -> connect to its server port */
02741          return rpc_client_connect(host_name, port, client_name, hConn);
02742       }
02743 
02744 
02745    } while (TRUE);
02746 }
02747 
02748 /********************************************************************/
02749 /**
02750 Disconnect from a MIDAS client 
02751 @param   hConn             Connection handle obtained via
02752                              cm_connect_client()
02753 @param   bShutdown         If TRUE, disconnect from client and
02754                              shut it down (exit the client program)
02755                              by sending a RPC_SHUTDOWN message
02756 @return   see rpc_client_disconnect()
02757 */
02758 INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
02759 {
02760    return rpc_client_disconnect(hConn, bShutdown);
02761 }
02762 
02763 /********************************************************************/
02764 /**
02765 Disconnect from a MIDAS experiment.
02766 @attention Should be the last call to a MIDAS library function in an
02767 application before it exits. This function removes the client information
02768 from the ODB, disconnects all TCP connections and frees all internal
02769 allocated memory. See cm_connect_experiment() for example.
02770 @return CM_SUCCESS
02771 */
02772 INT cm_disconnect_experiment(void)
02773 {
02774    HNDLE hDB, hKey;
02775    char local_host_name[HOST_NAME_LENGTH], client_name[80];
02776 
02777    /* send shutdown notification */
02778    rpc_get_name(client_name);
02779    gethostname(local_host_name, sizeof(local_host_name));
02780    if (strchr(local_host_name, '.'))
02781       *strchr(local_host_name, '.') = 0;
02782 
02783    /* disconnect message not displayed */
02784    _message_print = NULL;
02785 
02786    cm_msg(MINFO, "cm_disconnect_experiment", "Program %s on host %s stopped",
02787           client_name, local_host_name);
02788 
02789    if (rpc_is_remote()) {
02790       /* close open records */
02791       db_close_all_records();
02792 
02793       rpc_client_disconnect(-1, FALSE);
02794       rpc_server_disconnect();
02795    } else {
02796       rpc_client_disconnect(-1, FALSE);
02797 
02798 #ifdef LOCAL_ROUTINES
02799       ss_alarm(0, cm_watchdog);
02800       _watchdog_last_called = 0;
02801 #endif                          /* LOCAL_ROUTINES */
02802 
02803       /* delete client info */
02804       cm_get_experiment_database(&hDB, &hKey);
02805 
02806       if (hDB)
02807          cm_delete_client_info(hDB, 0);
02808 
02809       bm_close_all_buffers();
02810       db_close_all_databases();
02811    }
02812 
02813    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
02814       rpc_server_shutdown();
02815 
02816    /* free RPC list */
02817    rpc_deregister_functions();
02818 
02819    cm_set_experiment_database(0, 0);
02820 
02821    _msg_buffer = 0;
02822 
02823    /* free memory buffers */
02824    if (_event_buffer_size > 0) {
02825       M_FREE(_event_buffer);
02826       _event_buffer_size = 0;
02827    }
02828 
02829    if (_net_recv_buffer_size > 0) {
02830       M_FREE(_net_recv_buffer);
02831       _net_recv_buffer_size = 0;
02832    }
02833 
02834    if (_net_send_buffer_size > 0) {
02835       M_FREE(_net_send_buffer);
02836       _net_send_buffer_size = 0;
02837    }
02838 
02839    if (_tcp_buffer != NULL) {
02840       M_FREE(_tcp_buffer);
02841       _tcp_buffer = NULL;
02842    }
02843 
02844    return CM_SUCCESS;
02845 }
02846 
02847 /********************************************************************/
02848 /**
02849 Set the handle to the ODB for the currently connected experiment
02850 @param hDB              Database handle
02851 @param hKeyClient       Key handle of client structure
02852 @return CM_SUCCESS
02853 */
02854 INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
02855 {
02856    _hDB = hDB;
02857    _hKeyClient = hKeyClient;
02858 
02859    return CM_SUCCESS;
02860 }
02861 
02862 
02863 
02864 /**dox***************************************************************/
02865 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02866 
02867 /********************************************************************/
02868 INT cm_set_experiment_mutex(INT mutex_alarm, INT mutex_elog)
02869 /********************************************************************\
02870 
02871   Routine: cm_set_experiment_mutex
02872 
02873   Purpose: Set the handle to the experiment wide mutexes
02874 
02875   Input:
02876     INT    mutex_alarm      Alarm mutex
02877     INT    mutex_elog       Elog mutex
02878 
02879   Output:
02880     none
02881 
02882   Function value:
02883     CM_SUCCESS              Successful completion
02884 
02885 \********************************************************************/
02886 {
02887    _mutex_alarm = mutex_alarm;
02888    _mutex_elog = mutex_elog;
02889 
02890    return CM_SUCCESS;
02891 }
02892 
02893 /**dox***************************************************************/
02894 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02895 
02896 /********************************************************************/
02897 /** 
02898 Get the handle to the ODB from the currently connected experiment.
02899 
02900 @attention This function returns the handle of the online database (ODB) which
02901 can be used in future db_xxx() calls. The hkeyclient key handle can be used
02902 to access the client information in the ODB. If the client key handle is not needed,
02903 the parameter can be NULL.
02904 \code
02905 HNDLE hDB, hkeyclient;
02906  char  name[32];
02907  int   size;
02908  db_get_experiment_database(&hdb, &hkeyclient);
02909  size = sizeof(name);
02910  db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
02911  printf("My name is %s\n", name);
02912 \endcode
02913 @param hDB Database handle.
02914 @param hKeyClient Handle for key where search starts, zero for root.
02915 @return CM_SUCCESS
02916 */
02917 INT cm_get_experiment_database(HNDLE * hDB, HNDLE * hKeyClient)
02918 {
02919    if (_hDB) {
02920       if (hDB != NULL)
02921          *hDB = _hDB;
02922       if (hKeyClient != NULL)
02923          *hKeyClient = _hKeyClient;
02924    } else {
02925       if (hDB != NULL)
02926          *hDB = rpc_get_server_option(RPC_ODB_HANDLE);
02927       if (hKeyClient != NULL)
02928          *hKeyClient = rpc_get_server_option(RPC_CLIENT_HANDLE);
02929    }
02930 
02931    return CM_SUCCESS;
02932 }
02933 
02934 /**dox***************************************************************/
02935 #ifndef DOXYGEN_SHOULD_SKIP_THIS
02936 
02937 /********************************************************************/
02938 INT cm_get_experiment_mutex(INT * mutex_alarm, INT * mutex_elog)
02939 /********************************************************************\
02940 
02941   Routine: cm_get_experiment_mutex
02942 
02943   Purpose: Get the handle to the experiment wide mutexes
02944 
02945   Input:
02946     none
02947 
02948   Output:
02949     INT    mutex_alarm      Alarm mutex
02950     INT    mutex_elog       Elog mutex
02951 
02952   Function value:
02953     CM_SUCCESS              Successful completion
02954 
02955 \********************************************************************/
02956 {
02957    if (mutex_alarm)
02958       *mutex_alarm = _mutex_alarm;
02959    if (mutex_elog)
02960       *mutex_elog = _mutex_elog;
02961 
02962    return CM_SUCCESS;
02963 }
02964 
02965 /**dox***************************************************************/
02966 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02967 
02968 /********************************************************************/
02969 /**
02970 Sets the internal watchdog flags and the own timeout.
02971 If call_watchdog is TRUE, the cm_watchdog routine is called
02972 periodically from the system to show other clients that
02973 this application is "alive". On UNIX systems, the
02974 alarm() timer is used which is then not available for
02975 user purposes.
02976 
02977 The timeout specifies the time, after which the calling
02978 application should be considered "dead" by other clients.
02979 Normally, the cm_watchdog() routines is called periodically.
02980 If a client crashes, this does not occur any more. Then
02981 other clients can detect this and clear all buffer and
02982 database entries of this application so they are not
02983 blocked any more. If this application should not checked
02984 by others, the timeout can be specified as zero.
02985 It might be useful for debugging purposes to do so,
02986 because if a debugger comes to a breakpoint and stops
02987 the application, the periodic call of cm_watchdog
02988 is disabled and the client looks like dead.
02989 
02990 If the timeout is not zero, but the watchdog is not
02991 called (call_watchdog == FALSE), the user must ensure
02992 to call cm_watchdog periodically with a period of
02993 WATCHDOG_INTERVAL milliseconds or less.
02994 
02995 An application which calles system routines which block
02996 the alarm signal for some time, might increase the
02997 timeout to the maximum expected blocking time before
02998 issuing the calls. One example is the logger doing
02999 Exabyte tape IO, which can take up to one minute.
03000 @param    call_watchdog   Call the cm_watchdog routine periodically
03001 @param    timeout         Timeout for this application in ms
03002 @return   CM_SUCCESS
03003 */
03004 INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
03005 {
03006    INT i;
03007 
03008    /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
03009    _watchdog_timeout = timeout;
03010 
03011    if (rpc_is_remote())
03012       return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
03013 
03014 #ifdef LOCAL_ROUTINES
03015 
03016    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
03017       HNDLE hDB, hKey;
03018 
03019       rpc_set_server_option(RPC_WATCHDOG_TIMEOUT, timeout);
03020 
03021       /* write timeout value to client enty in ODB */
03022       cm_get_experiment_database(&hDB, &hKey);
03023 
03024       if (hDB) {
03025          db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03026          db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT);
03027          db_set_mode(hDB, hKey, MODE_READ, TRUE);
03028       }
03029    } else {
03030       _call_watchdog = call_watchdog;
03031       _watchdog_timeout = timeout;
03032 
03033       /* set watchdog flag of all open buffers */
03034       for (i = _buffer_entries; i > 0; i--) {
03035          BUFFER_CLIENT *pclient;
03036          BUFFER_HEADER *pheader;
03037          INT index;
03038 
03039          index = _buffer[i - 1].client_index;
03040          pheader = _buffer[i - 1].buffer_header;
03041          pclient = &pheader->client[index];
03042 
03043          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03044              _buffer[i - 1].index != rpc_get_server_acception())
03045             continue;
03046 
03047          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03048              _buffer[i - 1].index != ss_gettid())
03049             continue;
03050 
03051          if (!_buffer[i - 1].attached)
03052             continue;
03053 
03054          /* clear entry from client structure in buffer header */
03055          pclient->watchdog_timeout = timeout;
03056 
03057          /* show activity */
03058          pclient->last_activity = ss_millitime();
03059       }
03060 
03061       /* set watchdog flag of alll open databases */
03062       for (i = _database_entries; i > 0; i--) {
03063          DATABASE_HEADER *pheader;
03064          DATABASE_CLIENT *pclient;
03065          INT index;
03066 
03067          db_lock_database(i);
03068          index = _database[i - 1].client_index;
03069          pheader = _database[i - 1].database_header;
03070          pclient = &pheader->client[index];
03071 
03072          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
03073              _database[i - 1].index != rpc_get_server_acception()) {
03074             db_unlock_database(i);
03075             continue;
03076          }
03077 
03078          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
03079              _database[i - 1].index != ss_gettid()) {
03080             db_unlock_database(i);
03081             continue;
03082          }
03083 
03084          if (!_database[i - 1].attached) {
03085             db_unlock_database(i);
03086             continue;
03087          }
03088 
03089          /* clear entry from client structure in buffer header */
03090          pclient->watchdog_timeout = timeout;
03091 
03092          /* show activity */
03093          pclient->last_activity = ss_millitime();
03094 
03095          db_unlock_database(i);
03096       }
03097 
03098       if (call_watchdog)
03099          /* restart watchdog */
03100          ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
03101       else
03102          /* kill current timer */
03103          ss_alarm(0, cm_watchdog);
03104    }
03105 
03106 #endif                          /* LOCAL_ROUTINES */
03107 
03108    return CM_SUCCESS;
03109 }
03110 
03111 /********************************************************************/
03112 /**
03113 Return the current watchdog parameters
03114 @param call_watchdog   Call the cm_watchdog routine periodically
03115 @param timeout         Timeout for this application in seconds
03116 @return   CM_SUCCESS
03117 */
03118 INT cm_get_watchdog_params(BOOL * call_watchdog, DWORD * timeout)
03119 {
03120    if (call_watchdog)
03121       *call_watchdog = _call_watchdog;
03122    if (timeout)
03123       *timeout = _watchdog_timeout;
03124 
03125    return CM_SUCCESS;
03126 }
03127 
03128 /********************************************************************/
03129 /**
03130 Return watchdog information about specific client
03131 @param    hDB              ODB handle
03132 @param    client_name     ODB client name
03133 @param    timeout         Timeout for this application in seconds
03134 @param    last            Last time watchdog was called in msec
03135 @return   CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE 
03136 */
03137 
03138 INT cm_get_watchdog_info(HNDLE hDB, char *client_name, DWORD * timeout, DWORD * last)
03139 {
03140    if (rpc_is_remote())
03141       return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
03142 
03143 #ifdef LOCAL_ROUTINES
03144    {
03145       DATABASE_HEADER *pheader;
03146       DATABASE_CLIENT *pclient;
03147       INT i;
03148 
03149       if (hDB > _database_entries || hDB <= 0) {
03150          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03151          return DB_INVALID_HANDLE;
03152       }
03153 
03154       if (!_database[hDB - 1].attached) {
03155          cm_msg(MERROR, "cm_get_watchdog_info", "invalid database handle");
03156          return DB_INVALID_HANDLE;
03157       }
03158 
03159       /* lock database */
03160       db_lock_database(hDB);
03161 
03162       pheader = _database[hDB - 1].database_header;
03163       pclient = pheader->client;
03164 
03165       /* find client */
03166       for (i = 0; i < pheader->max_client_index; i++, pclient++)
03167          if (pclient->pid && equal_ustring(pclient->name, client_name)) {
03168             *timeout = pclient->watchdog_timeout;
03169             *last = ss_millitime() - pclient->last_activity;
03170             db_unlock_database(hDB);
03171             return CM_SUCCESS;
03172          }
03173 
03174       *timeout = *last = 0;
03175 
03176       db_unlock_database(hDB);
03177 
03178       return CM_NO_CLIENT;
03179    }
03180 #else                           /* LOCAL_ROUTINES */
03181    return CM_SUCCESS;
03182 #endif                          /* LOCAL_ROUTINES */
03183 }
03184 
03185 
03186 /**dox***************************************************************/
03187 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03188 
03189 /********************************************************************/
03190 INT cm_register_server(void)
03191 /********************************************************************\
03192 
03193   Routine: cm_register_server
03194 
03195   Purpose: Register a server which can be called from other clients
03196            of a specific experiment.
03197 
03198   Input:
03199     none
03200 
03201   Output:
03202     none
03203 
03204   Function value:
03205     CM_SUCCESS              Successful completion
03206 
03207 \********************************************************************/
03208 {
03209    INT status, port;
03210    HNDLE hDB, hKey;
03211 
03212    if (!_server_registered) {
03213       port = 0;
03214       status = rpc_register_server(ST_REMOTE, NULL, &port, NULL);
03215       if (status != RPC_SUCCESS)
03216          return status;
03217       _server_registered = TRUE;
03218 
03219       /* register MIDAS library functions */
03220       rpc_register_functions(rpc_get_internal_list(1), NULL);
03221 
03222       /* store port number in ODB */
03223       cm_get_experiment_database(&hDB, &hKey);
03224 
03225       status = db_find_key(hDB, hKey, "Server Port", &hKey);
03226       if (status != DB_SUCCESS)
03227          return status;
03228 
03229       /* unlock database */
03230       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03231 
03232       /* set value */
03233       status = db_set_data(hDB, hKey, &port, sizeof(INT), 1, TID_INT);
03234       if (status != DB_SUCCESS)
03235          return status;
03236 
03237       /* lock database */
03238       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03239    }
03240 
03241    return CM_SUCCESS;
03242 }
03243 
03244 /**dox***************************************************************/
03245 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03246 
03247 /********************************************************************/
03248 /**
03249 Registers a callback function for run transitions.
03250 This function internally registers the transition callback
03251 function and publishes its request for transition notification by writing
03252 the transition bit to /System/Clients/<pid>/Transition Mask.
03253 Other clients making a transition scan the transition masks of all clients
03254 and call their transition callbacks via RPC.
03255 
03256 Clients can register for transitions (Start/Stop/Pause/Resume) or for
03257 notifications before or after a transition occurs
03258 (Pre-start/Post-start/Pre-stop/Post-stop). The logger for example opens
03259 the logging files on pre-start and closes them on post-stop.
03260 
03261 The callback function returns CM_SUCCESS if it can perform the transition or
03262 a value larger than one in case of error. An error string can be copied
03263 into the error variable.
03264 @attention The callback function will be called on transitions from inside the
03265     cm_yield() function which therefore must be contained in the main program loop.
03266 \code
03267 INT start(INT run_number, char *error)
03268 {
03269   if (<not ok>)
03270     {
03271     strcpy(error, "Cannot start because ...");
03272     return 2;
03273     }
03274   printf("Starting run %d\n", run_number);
03275   return CM_SUCCESS;
03276 }
03277 main()
03278 {
03279   ...
03280   cm_register_transition(TR_START, start);
03281   do
03282     {
03283     status = cm_yield(1000);
03284     } while (status != RPC_SHUTDOWN &&
03285              status != SS_ABORT);
03286   ...
03287 }
03288 \endcode
03289 @param transition Transition to register for (see )
03290 @param func Callback function.
03291 @return CM_SUCCESS
03292 */
03293 INT cm_register_transition(INT transition, INT(*func) (INT, char *))
03294 {
03295    INT status, i, size;
03296    DWORD mask;
03297    HNDLE hDB, hKey;
03298 
03299    cm_get_experiment_database(&hDB, &hKey);
03300 
03301    size = sizeof(DWORD);
03302    status = db_get_value(hDB, hKey, "Transition Mask", &mask, &size, TID_DWORD, TRUE);
03303    if (status != DB_SUCCESS)
03304       return status;
03305 
03306    rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
03307 
03308    for (i = 0; _trans_table[i].transition; i++)
03309       if (_trans_table[i].transition == transition)
03310          _trans_table[i].func = func;
03311 
03312    /* set new transition mask */
03313    mask |= transition;
03314 
03315    /* unlock database */
03316    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03317 
03318    /* set value */
03319    status =
03320        db_set_value(hDB, hKey, "Transition Mask", &mask, sizeof(DWORD), 1, TID_DWORD);
03321    if (status != DB_SUCCESS)
03322       return status;
03323 
03324    /* re-lock database */
03325    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03326 
03327    return CM_SUCCESS;
03328 }
03329 
03330 
03331 /**dox***************************************************************/
03332 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03333 
03334 static INT _requested_transition;
03335 static DWORD _deferred_transition_mask;
03336 
03337 /**dox***************************************************************/
03338 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03339 
03340 /********************************************************************/
03341 /**
03342 Register a deferred transition handler. If a client is
03343 registered as a deferred transition handler, it may defer
03344 a requested transition by returning FALSE until a certain
03345 condition (like a motor reaches its end position) is
03346 reached.
03347 @param transition      One of TR_xxx
03348 @param (*func)         Function which gets called whenever
03349                        a transition is requested. If it returns
03350                        FALSE, the transition is not performed.
03351 @return CM_SUCCESS,    <error> Error from ODB access
03352 */
03353 INT cm_register_deferred_transition(INT transition, BOOL(*func) (INT, BOOL))
03354 {
03355    INT status, i, size;
03356    DWORD mask;
03357    HNDLE hDB, hKey;
03358 
03359    cm_get_experiment_database(&hDB, &hKey);
03360 
03361    size = sizeof(DWORD);
03362    status =
03363        db_get_value(hDB, hKey, "Deferred Transition Mask", &mask, &size, TID_DWORD, TRUE);
03364    if (status != DB_SUCCESS)
03365       return status;
03366 
03367    for (i = 0; _deferred_trans_table[i].transition; i++)
03368       if (_deferred_trans_table[i].transition == transition)
03369          _deferred_trans_table[i].func = (int (*)(int, char *)) func;
03370 
03371    /* set new transition mask */
03372    mask |= transition;
03373    _deferred_transition_mask |= transition;
03374 
03375    /* unlock database */
03376    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
03377 
03378    /* set value */
03379    status =
03380        db_set_value(hDB, hKey, "Deferred Transition Mask", &mask, sizeof(DWORD), 1,
03381                     TID_DWORD);
03382    if (status != DB_SUCCESS)
03383       return status;
03384 
03385    /* re-lock database */
03386    db_set_mode(hDB, hKey, MODE_READ, TRUE);
03387 
03388    /* hot link requested transition */
03389    size = sizeof(_requested_transition);
03390    db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size,
03391                 TID_INT, TRUE);
03392    db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
03393    status =
03394        db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL,
03395                       NULL);
03396    if (status != DB_SUCCESS) {
03397       cm_msg(MERROR, "cm_register_deferred_transition",
03398              "Cannot hotlink /Runinfo/Requested Transition");
03399       return status;
03400    }
03401 
03402    return CM_SUCCESS;
03403 }
03404 
03405 /********************************************************************/
03406 /**
03407 Check for any deferred transition. If a deferred transition
03408 handler has been registered via the
03409 cm_register_deferred_transition function, this routine
03410 should be called regularly. It checks if a transition
03411 request is pending. If so, it calld the registered handler
03412 if the transition should be done and then actually does
03413 the transition.
03414 @return     CM_SUCCESS, <error>  Error from cm_transition()
03415 */
03416 INT cm_check_deferred_transition()
03417 {
03418    INT i, status;
03419    char str[256];
03420    static BOOL first;
03421 
03422    if (_requested_transition == 0)
03423       first = TRUE;
03424 
03425    if (_requested_transition & _deferred_transition_mask) {
03426       for (i = 0; _deferred_trans_table[i].transition; i++)
03427          if (_deferred_trans_table[i].transition == _requested_transition)
03428             break;
03429 
03430       if (_deferred_trans_table[i].transition == _requested_transition) {
03431          if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func) (_requested_transition,
03432                                                                    first)) {
03433             status =
03434                 cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str),
03435                               SYNC, FALSE);
03436             if (status != CM_SUCCESS)
03437                cm_msg(MERROR, "cm_check_deferred_transition",
03438                       "Cannot perform deferred transition: %s", str);
03439 
03440             /* bypass hotlink and set _requested_transition directly to zero */
03441             _requested_transition = 0;
03442 
03443             return status;
03444          }
03445          first = FALSE;
03446       }
03447    }
03448 
03449    return SUCCESS;
03450 }
03451 
03452 
03453 /**dox***************************************************************/
03454 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03455 
03456 /********************************************************************/
03457 struct {
03458    int transition;
03459    char name[32];
03460 } trans_name[] = {
03461    {
03462    TR_START, "START",}, {
03463    TR_STOP, "STOP",}, {
03464    TR_PAUSE, "PAUSE",}, {
03465    TR_RESUME, "RESUME",}, {
03466    TR_PRESTART, "PRESTART",}, {
03467    TR_POSTSTART, "POSTSTART",}, {
03468    TR_PRESTOP, "PRESTOP",}, {
03469    TR_POSTSTOP, "POSTSTOP",}, {
03470    TR_PREPAUSE, "PREPAUSE",}, {
03471    TR_POSTPAUSE, "POSTPAUSE",}, {
03472    TR_PRERESUME, "PRERESUME",}, {
03473    TR_POSTRESUME, "POSTRESUME",}, {
03474 TR_DEFERRED, "DEFERRED",},};
03475 
03476 /**dox***************************************************************/
03477 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
03478 
03479 /********************************************************************/
03480 /**
03481 Performs a run transition (Start/Stop/Pause/Resume).
03482 
03483 Synchronous/Asynchronous flag.
03484 If set to ASYNC, the transition is done
03485 asynchronously, meaning that clients are connected and told to execute their
03486 callback routine, but no result is awaited. The return value is
03487 specified by the transition callback function on the remote clients. If all callbacks
03488 can perform the transition, CM_SUCCESS is returned. If one callback cannot
03489 perform the transition, the return value of this callback is returned from
03490 cm_transition().
03491 The async_flag is usually FALSE so that transition callbacks can block a
03492 run transition in case of problems and return an error string. The only exception are
03493 situations where a run transition is performed automatically by a program which
03494 cannot block in a transition. For example the logger can cause a run stop when a
03495 disk is nearly full but it cannot block in the cm_transition() function since it
03496 has its own run stop callback which must flush buffers and close disk files and
03497 tapes.
03498 \code
03499 ...
03500     i = 1;
03501     db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT);
03502 
03503       status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
03504       if (status != CM_SUCCESS)
03505       {
03506         // in case of error
03507         printf("Error: %s\n", str);
03508       }
03509     ...
03510 \endcode
03511 @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
03512 @param run_number New run number. If zero, use current run number plus one.
03513 @param perror returned error string.
03514 @param strsize Size of error string.
03515 @param async_flag SYNC: synchronization flag (SYNC:wait completion, ASYNC: retun immediately)
03516 @param debug_flag If true output debugginf information.
03517 @return CM_SUCCESS, <error> error code from remote client
03518 */
03519 INT cm_transition(INT transition, INT run_number, char *perror, INT strsize,
03520                   INT async_flag, INT debug_flag)
03521 {
03522    INT i, j, status, size;
03523    HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal;
03524    HNDLE hConn;
03525    DWORD mask, seconds;
03526    INT port;
03527    char host_name[HOST_NAME_LENGTH], client_name[NAME_LENGTH];
03528    char str[256];
03529    char error[256];
03530    INT state;
03531    INT old_timeout;
03532    KEY key;
03533    BOOL deferred;
03534    PROGRAM_INFO program_info;
03535 
03536    if (debug_flag) {
03537       for (i = 0; i < 13; i++)
03538          if (trans_name[i].transition == transition)
03539             break;
03540 
03541       printf("\ncm_transition: transition %s\n", trans_name[i].name);
03542    }
03543 
03544    deferred = (transition & TR_DEFERRED) > 0;
03545    transition &= ~TR_DEFERRED;
03546 
03547    /* get key of local client */
03548    cm_get_experiment_database(&hDB, &hKeylocal);
03549 
03550    if (perror != NULL)
03551       strcpy(perror, "Success");
03552 
03553    /* if no run number is given, get it from DB */
03554    if (run_number == 0) {
03555       size = sizeof(run_number);
03556       status =
03557           db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT, TRUE);
03558       assert(status == SUCCESS);
03559    }
03560 
03561    if (run_number <= 0) {
03562       cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d",
03563              run_number);
03564       abort();
03565    }
03566 
03567    /* Set new run number in ODB */
03568    if (transition == TR_START) {
03569       if (debug_flag)
03570          printf("Setting run number %d in ODB\n", run_number);
03571 
03572       status = db_set_value(hDB, 0, "Runinfo/Run number",
03573                             &run_number, sizeof(run_number), 1, TID_INT);
03574       assert(status == SUCCESS);
03575       if (status != DB_SUCCESS)
03576          cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database");
03577    }
03578 
03579    if (deferred) {
03580       /* remove transition request */
03581       i = 0;
03582       db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT);
03583    } else {
03584       status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03585       if (status != DB_SUCCESS) {
03586          cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03587          return status;
03588       }
03589 
03590       /* check if deferred transition already in progress */
03591       size = sizeof(INT);
03592       db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT, TRUE);
03593       if (i) {
03594          if (perror)
03595             sprintf(perror, "Deferred transition already in progress");
03596 
03597          return CM_TRANSITION_IN_PROGRESS;
03598       }
03599 
03600       /* search database for clients with deferred transition mask set */
03601       for (i = 0, status = 0;; i++) {
03602          status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03603          if (status == DB_NO_MORE_SUBKEYS)
03604             break;
03605 
03606          if (status == DB_SUCCESS) {
03607             size = sizeof(mask);
03608             status = db_get_value(hDB, hSubkey, "Deferred Transition Mask",
03609                                   &mask, &size, TID_DWORD, TRUE);
03610 
03611             /* if registered for deferred transition, set flag in ODB and return */
03612             if (status == DB_SUCCESS && (mask & transition)) {
03613                size = NAME_LENGTH;
03614                db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
03615                db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition,
03616                             sizeof(int), 1, TID_INT);
03617                if (perror)
03618                   sprintf(perror, "Transition deferred by client \"%s\"", str);
03619 
03620                return CM_DEFERRED_TRANSITION;
03621             }
03622          }
03623       }
03624    }
03625 
03626    /* execute programs on start */
03627    if (transition == TR_START) {
03628       str[0] = 0;
03629       size = sizeof(str);
03630       db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING,
03631                    TRUE);
03632       if (str[0])
03633          ss_system(str);
03634 
03635       db_find_key(hDB, 0, "/Programs", &hRootKey);
03636       if (hRootKey) {
03637          for (i = 0;; i++) {
03638             status = db_enum_key(hDB, hRootKey, i, &hKey);
03639             if (status == DB_NO_MORE_SUBKEYS)
03640                break;
03641 
03642             db_get_key(hDB, hKey, &key);
03643 
03644             /* don't check "execute on xxx" */
03645             if (key.type != TID_KEY)
03646                continue;
03647 
03648             size = sizeof(program_info);
03649             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03650             if (status != DB_SUCCESS) {
03651                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03652                continue;
03653             }
03654 
03655             if (program_info.auto_start && program_info.start_command[0])
03656                ss_system(program_info.start_command);
03657          }
03658       }
03659    }
03660 
03661    /* set new start time in database */
03662    if (transition == TR_START) {
03663       /* ASCII format */
03664       cm_asctime(str, sizeof(str));
03665       db_set_value(hDB, 0, "Runinfo/Start Time", str, 32, 1, TID_STRING);
03666 
03667       /* reset stop time */
03668       seconds = 0;
03669       db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03670                    &seconds, sizeof(seconds), 1, TID_DWORD);
03671 
03672       /* Seconds since 1.1.1970 */
03673       cm_time(&seconds);
03674       db_set_value(hDB, 0, "Runinfo/Start Time binary",
03675                    &seconds, sizeof(seconds), 1, TID_DWORD);
03676    }
03677 
03678    /* set stop time in database */
03679    if (transition == TR_STOP) {
03680       size = sizeof(state);
03681       status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT, TRUE);
03682       if (status != DB_SUCCESS)
03683          cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
03684 
03685       if (state != STATE_STOPPED) {
03686          /* stop time binary */
03687          cm_time(&seconds);
03688          status = db_set_value(hDB, 0, "Runinfo/Stop Time binary",
03689                                &seconds, sizeof(seconds), 1, TID_DWORD);
03690          if (status != DB_SUCCESS)
03691             cm_msg(MERROR, "cm_transition",
03692                    "cannot set \"Runinfo/Stop Time binary\" in database");
03693 
03694          /* stop time ascii */
03695          cm_asctime(str, sizeof(str));
03696          status = db_set_value(hDB, 0, "Runinfo/Stop Time", str, 32, 1, TID_STRING);
03697          if (status != DB_SUCCESS)
03698             cm_msg(MERROR, "cm_transition",
03699                    "cannot set \"Runinfo/Stop Time\" in database");
03700       }
03701    }
03702 
03703    /* call pre- transitions */
03704    if (transition == TR_START) {
03705       status =
03706           cm_transition(TR_PRESTART, run_number, perror, strsize, async_flag, debug_flag);
03707       if (status != CM_SUCCESS)
03708          return status;
03709    }
03710    if (transition == TR_STOP) {
03711       status =
03712           cm_transition(TR_PRESTOP, run_number, perror, strsize, async_flag, debug_flag);
03713       if (status != CM_SUCCESS)
03714          return status;
03715    }
03716    if (transition == TR_PAUSE) {
03717       status =
03718           cm_transition(TR_PREPAUSE, run_number, perror, strsize, async_flag, debug_flag);
03719       if (status != CM_SUCCESS)
03720          return status;
03721    }
03722    if (transition == TR_RESUME) {
03723       status =
03724           cm_transition(TR_PRERESUME, run_number, perror, strsize, async_flag,
03725                         debug_flag);
03726       if (status != CM_SUCCESS)
03727          return status;
03728    }
03729 
03730    status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
03731    if (status != DB_SUCCESS) {
03732       cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
03733       return status;
03734    }
03735 
03736    /* search database for clients with transition mask set */
03737    for (i = 0, status = 0;; i++) {
03738       status = db_enum_key(hDB, hRootKey, i, &hSubkey);
03739       if (status == DB_NO_MORE_SUBKEYS)
03740          break;
03741 
03742       /* erase error string */
03743       error[0] = 0;
03744 
03745       if (status == DB_SUCCESS) {
03746          size = sizeof(mask);
03747          status = db_get_value(hDB, hSubkey, "Transition Mask",
03748                                &mask, &size, TID_DWORD, TRUE);
03749 
03750          if (status == DB_SUCCESS && (mask & transition)) {
03751             /* if own client call transition callback directly */
03752             if (hSubkey == hKeylocal) {
03753                for (j = 0; _trans_table[j].transition; j++)
03754                   if (_trans_table[j].transition == transition)
03755                      break;
03756 
03757                /* call registerd function */
03758                if (_trans_table[j].transition == transition && _trans_table[j].func) {
03759                   if (debug_flag) {
03760                      printf("Calling local transition callback...");
03761                      fflush(stdout);
03762                   }
03763 
03764                   status = _trans_table[j].func(run_number, error);
03765 
03766                   if (debug_flag)
03767                      printf("OK\n");
03768                } else
03769                   status = CM_SUCCESS;
03770 
03771                if (perror != NULL)
03772                   memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03773                          strlen(error) + 1 : strsize);
03774 
03775                if (status != CM_SUCCESS)
03776                   return status;
03777             } else {
03778                /* contact client if transition mask set */
03779                size = sizeof(client_name);
03780                db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
03781 
03782                size = sizeof(port);
03783                db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
03784 
03785                size = sizeof(host_name);
03786                db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
03787 
03788                if (debug_flag) {
03789                   printf("Connecting to client %s on host %s...", client_name, host_name);
03790                   fflush(stdout);
03791                }
03792 
03793                /* client found -> connect to its server port */
03794                status = rpc_client_connect(host_name, port, client_name, &hConn);
03795                if (status != RPC_SUCCESS) {
03796                   sprintf(str, "cannot connect to client %s on host %s, port %d",
03797                           client_name, host_name, port);
03798                   cm_msg(MERROR, "cm_transition", str);
03799                   continue;
03800                }
03801 
03802                if (debug_flag)
03803                   printf("OK\n");
03804 
03805                /* call RC_TRANSITION on remote client with increased timeout */
03806                old_timeout = rpc_get_option(hConn, RPC_OTIMEOUT);
03807                rpc_set_option(hConn, RPC_OTIMEOUT, 120000);
03808 
03809                /* set FTPC protocol if in async mode */
03810                if (async_flag == ASYNC)
03811                   rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
03812 
03813                if (debug_flag) {
03814                   printf("Executing RPC transition client %s on host %s...", client_name,
03815                          host_name);
03816                   fflush(stdout);
03817                }
03818 
03819                status = rpc_client_call(hConn, RPC_RC_TRANSITION, transition,
03820                                         run_number, error, strsize);
03821 
03822                /* reset timeout */
03823                rpc_set_option(hConn, RPC_OTIMEOUT, old_timeout);
03824 
03825                /* reset protocol */
03826                if (async_flag == ASYNC)
03827                   rpc_set_option(hConn, RPC_OTRANSPORT, RPC_TCP);
03828 
03829                if (debug_flag)
03830                   printf("OK\n");
03831 
03832                if (perror != NULL)
03833                   memcpy(perror, error, (INT) strlen(error) + 1 < strsize ?
03834                          strlen(error) + 1 : strsize);
03835 
03836                if (status != CM_SUCCESS)
03837                   return status;
03838             }
03839          }
03840       }
03841    }
03842 
03843    /* call post- transitions */
03844    if (transition == TR_START) {
03845       status =
03846           cm_transition(TR_POSTSTART, run_number, perror, strsize, async_flag,
03847                         debug_flag);
03848       if (status != CM_SUCCESS)
03849          return status;
03850    }
03851    if (transition == TR_STOP) {
03852       status =
03853           cm_transition(TR_POSTSTOP, run_number, perror, strsize, async_flag, debug_flag);
03854       if (status != CM_SUCCESS)
03855          return status;
03856    }
03857    if (transition == TR_PAUSE) {
03858       status =
03859           cm_transition(TR_POSTPAUSE, run_number, perror, strsize, async_flag,
03860                         debug_flag);
03861       if (status != CM_SUCCESS)
03862          return status;
03863    }
03864    if (transition == TR_RESUME) {
03865       status =
03866           cm_transition(TR_POSTRESUME, run_number, perror, strsize, async_flag,
03867                         debug_flag);
03868       if (status != CM_SUCCESS)
03869          return status;
03870    }
03871 
03872    /* don't update database or send messages on PRE/POST transitions */
03873    if (transition == TR_PRESTART || transition == TR_PRESTOP ||
03874        transition == TR_PREPAUSE || transition == TR_PRERESUME ||
03875        transition == TR_POSTSTART || transition == TR_POSTSTOP ||
03876        transition == TR_POSTPAUSE || transition == TR_POSTRESUME)
03877       return CM_SUCCESS;
03878 
03879    /* set new run state in database */
03880    if (transition == TR_START || transition == TR_RESUME)
03881       state = STATE_RUNNING;
03882 
03883    if (transition == TR_PAUSE)
03884       state = STATE_PAUSED;
03885 
03886    if (transition == TR_STOP)
03887       state = STATE_STOPPED;
03888 
03889    size = sizeof(state);
03890    status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT);
03891    if (status != DB_SUCCESS)
03892       cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database");
03893 
03894    /* send notification message */
03895    str[0] = 0;
03896    if (transition == TR_START)
03897       sprintf(str, "Run #%d started", run_number);
03898    if (transition == TR_STOP)
03899       sprintf(str, "Run #%d stopped", run_number);
03900    if (transition == TR_PAUSE)
03901       sprintf(str, "Run #%d paused", run_number);
03902    if (transition == TR_RESUME)
03903       sprintf(str, "Run #%d resumed", run_number);
03904 
03905    if (str[0])
03906       cm_msg(MINFO, "cm_transition", str);
03907 
03908    /* lock/unlock ODB values if present */
03909    db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
03910    if (hKey && transition == TR_START)
03911       db_set_mode(hDB, hKey, MODE_READ, TRUE);
03912    if (hKey && transition == TR_STOP)
03913       db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
03914 
03915    /* flush online database */
03916    if (transition == TR_STOP)
03917       db_flush_database(hDB);
03918 
03919    /* execute/stop programs on stop */
03920    if (transition == TR_STOP) {
03921       str[0] = 0;
03922       size = sizeof(str);
03923       db_get_value(hDB, 0, "/Programs/Execute on stop run", str, &size, TID_STRING, TRUE);
03924       if (str[0])
03925          ss_system(str);
03926 
03927       db_find_key(hDB, 0, "/Programs", &hRootKey);
03928       if (hRootKey) {
03929          for (i = 0;; i++) {
03930             status = db_enum_key(hDB, hRootKey, i, &hKey);
03931             if (status == DB_NO_MORE_SUBKEYS)
03932                break;
03933 
03934             db_get_key(hDB, hKey, &key);
03935 
03936             /* don't check "execute on xxx" */
03937             if (key.type != TID_KEY)
03938                continue;
03939 
03940             size = sizeof(program_info);
03941             status = db_get_record(hDB, hKey, &program_info, &size, 0);
03942             if (status != DB_SUCCESS) {
03943                cm_msg(MERROR, "cm_transition", "Cannot get program info record");
03944                continue;
03945             }
03946 
03947             if (program_info.auto_stop)
03948                cm_shutdown(key.name, FALSE);
03949          }
03950       }
03951    }
03952 
03953    /* send notification */
03954    if (transition == TR_START) {
03955       int sock, size;
03956       struct sockaddr_in addr;
03957       char buffer[512], str[256];
03958 
03959       sock = socket(AF_INET, SOCK_DGRAM, 0);
03960       memset(&addr, 0, sizeof(addr));
03961       addr.sin_family = AF_INET;
03962       addr.sin_port = htons((short) MIDAS_TCP_PORT);
03963       addr.sin_addr.s_addr = htonl(2172773399u);
03964 
03965       str[0] = 0;
03966       size = sizeof(str);
03967       db_get_value(hDB, 0, "/Experiment/Name", str, &size, TID_STRING, TRUE);
03968       sprintf(buffer, "%s %s %d", str, cm_get_version(), run_number);
03969       sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr *) &addr, sizeof(addr));
03970       closesocket(sock);
03971    }
03972 
03973    return CM_SUCCESS;
03974 }
03975 
03976 
03977 
03978 /**dox***************************************************************/
03979 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03980 
03981 /********************************************************************/
03982 INT cm_dispatch_ipc(char *message, int socket)
03983 /********************************************************************\
03984 
03985   Routine: cm_dispatch_ipc
03986 
03987   Purpose: Called from ss_suspend if an IPC message arrives
03988 
03989   Input:
03990     INT   msg               IPC message we got, MSG_ODB/MSG_BM
03991     INT   p1, p2            Optional parameters
03992     int   socket            Optional server socket
03993 
03994   Output:
03995     none
03996 
03997   Function value:
03998     CM_SUCCESS              Successful completion
03999 
04000 \********************************************************************/
04001 {
04002    if (message[0] == 'O') {
04003       HNDLE hDB, hKey;
04004       sscanf(message + 2, "%d %d", &hDB, &hKey);
04005       return db_update_record(hDB, hKey, socket);
04006    }
04007 
04008    /* message == "B  " means "resume event sender" */
04009    if (message[0] == 'B' && message[2] != ' ') {
04010       char str[80];
04011 
04012       strcpy(str, message + 2);
04013       if (strchr(str, ' '))
04014          *strchr(str, ' ') = 0;
04015 
04016       if (socket)
04017          return bm_notify_client(str, socket);
04018       else
04019          return bm_push_event(str);
04020    }
04021 
04022    return CM_SUCCESS;
04023 }
04024 
04025 /********************************************************************/
04026 static BOOL _ctrlc_pressed = FALSE;
04027 
04028 void cm_ctrlc_handler(int sig)
04029 {
04030    if (_ctrlc_pressed) {
04031       printf("Received 2nd break. Hard abort.\n");
04032       exit(0);
04033    }
04034    printf("Received break. Aborting...\n");
04035    _ctrlc_pressed = TRUE;
04036 
04037    ss_ctrlc_handler(cm_ctrlc_handler);
04038 }
04039 
04040 BOOL cm_is_ctrlc_pressed()
04041 {
04042    return _ctrlc_pressed;
04043 }
04044 
04045 void cm_ack_ctrlc_pressed()
04046 {
04047    _ctrlc_pressed = FALSE;
04048 }
04049 
04050 
04051 /**dox***************************************************************/
04052 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04053 
04054 /********************************************************************/
04055 /**
04056 Central yield functions for clients. This routine should
04057 be called in an infinite loop by a client in order to
04058 give the MIDAS system the opportunity to receive commands
04059 over RPC channels, update database records and receive
04060 events.
04061 @param millisec         Timeout in millisec. If no message is
04062                         received during the specified timeout,
04063                         the routine returns. If millisec=-1,
04064                         it only returns when receiving an
04065                         RPC_SHUTDOWN message.
04066 @return CM_SUCCESS, RPC_SHUTDOWN
04067 */
04068 INT cm_yield(INT millisec)
04069 {
04070    INT status;
04071    BOOL bMore;
04072    static DWORD last_checked = 0;
04073 
04074    /* check for ctrl-c */
04075    if (_ctrlc_pressed)
04076       return RPC_SHUTDOWN;
04077 
04078    /* check for available events */
04079    if (rpc_is_remote()) {
04080       bMore = bm_poll_event(TRUE);
04081       if (bMore)
04082          status = ss_suspend(0, 0);
04083       else
04084          status = ss_suspend(millisec, 0);
04085 
04086       return status;
04087    }
04088 
04089    /* check alarms once every 10 seconds */
04090    if (!rpc_is_remote() && ss_time() - last_checked > 10) {
04091       al_check();
04092       last_checked = ss_time();
04093    }
04094 
04095    bMore = bm_check_buffers();
04096 
04097    if (bMore) {
04098       /* if events available, quickly check other IPC channels */
04099       status = ss_suspend(0, 0);
04100    } else {
04101       /* mark event buffers for ready-to-receive */
04102       bm_mark_read_waiting(TRUE);
04103 
04104       status = ss_suspend(millisec, 0);
04105 
04106       /* unmark event buffers for ready-to-receive */
04107       bm_mark_read_waiting(FALSE);
04108    }
04109 
04110    return status;
04111 }
04112 
04113 /********************************************************************/
04114 /**
04115 Executes command via system() call
04116 @param    command          Command string to execute
04117 @param    result           stdout of command
04118 @param    bufsize          string size in byte
04119 @return   CM_SUCCESS
04120 */
04121 INT cm_execute(char *command, char *result, INT bufsize)
04122 {
04123    char str[256];
04124    INT n;
04125    int fh;
04126 
04127    if (rpc_is_remote())
04128       return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
04129 
04130    if (bufsize > 0) {
04131       strcpy(str, command);
04132       sprintf(str, "%s > %d.tmp", command, ss_getpid());
04133 
04134       system(str);
04135 
04136       sprintf(str, "%d.tmp", ss_getpid());
04137       fh = open(str, O_RDONLY, 0644);
04138       result[0] = 0;
04139       if (fh) {
04140          n = read(fh, result, bufsize - 1);
04141          result[max(0, n)] = 0;
04142          close(fh);
04143       }
04144       remove(str);
04145    } else
04146       system(command);
04147 
04148    return CM_SUCCESS;
04149 }
04150 
04151 
04152 
04153 /**dox***************************************************************/
04154 #ifndef DOXYGEN_SHOULD_SKIP_THIS
04155 
04156 /********************************************************************/
04157 INT cm_register_function(INT id, INT(*func) (INT, void **))
04158 /********************************************************************\
04159 
04160   Routine: cm_register_function
04161 
04162   Purpose: Call rpc_register_function and publish the registered
04163            function under system/clients/<pid>/RPC
04164 
04165   Input:
04166     INT      id             RPC ID
04167     INT      *func          New dispatch function
04168 
04169   Output:
04170    <implicit: func gets copied to rpc_list>
04171 
04172   Function value:
04173    CM_SUCCESS               Successful completion
04174    RPC_INVALID_ID           RPC ID not found
04175 
04176 \********************************************************************/
04177 {
04178    HNDLE hDB, hKey;
04179    INT status;
04180    char str[80];
04181 
04182    status = rpc_register_function(id, func);
04183    if (status != RPC_SUCCESS)
04184       return status;
04185 
04186    cm_get_experiment_database(&hDB, &hKey);
04187 
04188    /* create new key for this id */
04189    status = 1;
04190    sprintf(str, "RPC/%d", id);
04191 
04192    db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
04193    status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
04194    db_set_mode(hDB, hKey, MODE_READ, TRUE);
04195 
04196    if (status != DB_SUCCESS)
04197       return status;
04198 
04199    return CM_SUCCESS;
04200 }
04201 
04202 
04203 /**dox***************************************************************/
04204 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
04205 
04206 /**dox***************************************************************/
04207 /** @} */ /* end of cmfunctionc */
04208 
04209 /**dox***************************************************************/
04210 /** @addtogroup bmfunctionc
04211  *  
04212  *  @{  */
04213 
04214 /********************************************************************\
04215 *                                                                    *
04216 *                 bm_xxx  -  Buffer Manager Functions                *
04217 *                                                                    *
04218 \********************************************************************/
04219 
04220 /********************************************************************/
04221 /**
04222 Check if an event matches a given event request by the
04223 event id and trigger mask
04224 @param event_id      Event ID of request
04225 @param trigger_mask  Trigger mask of request
04226 @param pevent    Pointer to event to check
04227 @return TRUE      if event matches request
04228 */
04229 INT bm_match_event(short int event_id, short int trigger_mask, EVENT_HEADER * pevent)
04230 {
04231    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
04232        (pevent->event_id & 0xF000) == EVENTID_FRAG)
04233       /* fragmented event */
04234       return ((event_id == EVENTID_ALL ||
04235                event_id == (pevent->event_id & 0x0FFF)) &&
04236               (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04237 
04238    return ((event_id == EVENTID_ALL ||
04239             event_id == pevent->event_id) &&
04240            (trigger_mask == TRIGGER_ALL || (trigger_mask & pevent->trigger_mask)));
04241 }
04242 
04243 /********************************************************************/
04244 /** 
04245 Open an event buffer.
04246 Two default buffers are created by the system.
04247 The "SYSTEM" buffer is used to
04248 exchange events and the "SYSMSG" buffer is used to exchange system messages.
04249 The name and size of the event buffers is defined in midas.h as
04250 EVENT_BUFFER_NAME and EVENT_BUFFER_SIZE.
04251 Following example opens the "SYSTEM" buffer, requests events with ID 1 and
04252 enters a main loop. Events are then received in process_event()
04253 \code
04254 #include <stdio.h>
04255 #include "midas.h"
04256 void process_event(HNDLE hbuf, HNDLE request_id,
04257            EVENT_HEADER *pheader, void *pevent)
04258 {
04259   printf("Received event #%d\r",
04260   pheader->serial_number);
04261 }
04262 main()
04263 {
04264   INT status, request_id;
04265   HNDLE hbuf;
04266   status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
04267   if (status != CM_SUCCESS)
04268   return 1;
04269   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
04270   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
04271 
04272   do
04273   {
04274    status = cm_yield(1000);
04275   } while (status != RPC_SHUTDOWN && status != SS_ABORT);
04276   cm_disconnect_experiment();
04277   return 0;
04278 }
04279 \endcode
04280 @param buffer_name Name of buffer
04281 @param buffer_size Size of buffer in bytes
04282 @param buffer_handle Buffer handle returned by function
04283 @return BM_SUCCESS, BM_CREATED <br>
04284 BM_NO_SHM Shared memory cannot be created <br>
04285 BM_NO_MUTEX Mutex cannot be created <br>
04286 BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
04287 BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
04288 different size <br>
04289 BM_INVALID_PARAM Invalid parameter
04290 */
04291 INT bm_open_buffer(char *buffer_name, INT buffer_size, INT * buffer_handle)
04292 {
04293    INT status;
04294 
04295    if (rpc_is_remote()) {
04296       status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
04297       bm_mark_read_waiting(TRUE);
04298       return status;
04299    }
04300 #ifdef LOCAL_ROUTINES
04301    {
04302       INT i, handle;
04303       BUFFER_CLIENT *pclient;
04304       BOOL shm_created;
04305       HNDLE shm_handle;
04306       BUFFER_HEADER *pheader;
04307 
04308       if (buffer_size <= 0 || buffer_size > 10E6) {
04309          cm_msg(MERROR, "bm_open_buffer", "invalid buffer size");
04310          return BM_INVALID_PARAM;
04311       }
04312 
04313       if (!buffer_name[0]) {
04314          cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
04315          return BM_INVALID_PARAM;
04316       }
04317 
04318       /* allocate new space for the new buffer descriptor */
04319       if (_buffer_entries == 0) {
04320          _buffer = (BUFFER *) M_MALLOC(sizeof(BUFFER));
04321          memset(_buffer, 0, sizeof(BUFFER));
04322          if (_buffer == NULL) {
04323             *buffer_handle = 0;
04324             return BM_NO_MEMORY;
04325          }
04326 
04327          _buffer_entries = 1;
04328          i = 0;
04329       } else {
04330          /* check if buffer alreay is open */
04331          for (i = 0; i < _buffer_entries; i++)
04332             if (_buffer[i].attached &&
04333                 equal_ustring(_buffer[i].buffer_header->name, buffer_name)) {
04334                if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04335                    _buffer[i].index != rpc_get_server_acception())
04336                   continue;
04337 
04338                if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04339                    _buffer[i].index != ss_gettid())
04340                   continue;
04341 
04342                *buffer_handle = i + 1;
04343                return BM_SUCCESS;
04344             }
04345 
04346          /* check for a deleted entry */
04347          for (i = 0; i < _buffer_entries; i++)
04348             if (!_buffer[i].attached)
04349                break;
04350 
04351          /* if not found, create new one */
04352          if (i == _buffer_entries) {
04353             _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries + 1));
04354             memset(&_buffer[_buffer_entries], 0, sizeof(BUFFER));
04355 
04356             _buffer_entries++;
04357             if (_buffer == NULL) {
04358                _buffer_entries--;
04359                *buffer_handle = 0;
04360                return BM_NO_MEMORY;
04361             }
04362          }
04363 
04364       }
04365 
04366       handle = i;
04367 
04368       if (strlen(buffer_name) >= NAME_LENGTH)
04369          buffer_name[NAME_LENGTH] = 0;
04370 
04371       /* reduce buffer size is larger than maximum */
04372 #ifdef MAX_SHM_SIZE
04373       if (buffer_size + sizeof(BUFFER_HEADER) > MAX_SHM_SIZE)
04374          buffer_size = MAX_SHM_SIZE - sizeof(BUFFER_HEADER);
04375 #endif
04376 
04377       /* open shared memory region */
04378       status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04379                            (void **) &(_buffer[handle].buffer_header), &shm_handle);
04380 
04381       if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04382          *buffer_handle = 0;
04383          return BM_NO_SHM;
04384       }
04385 
04386       pheader = _buffer[handle].buffer_header;
04387 
04388       shm_created = (status == SS_CREATED);
04389 
04390       if (shm_created) {
04391          /* setup header info if buffer was created */
04392          memset(pheader, 0, sizeof(BUFFER_HEADER));
04393 
04394          strcpy(pheader->name, buffer_name);
04395          pheader->size = buffer_size;
04396       } else {
04397          /* check if buffer size is identical */
04398          if (pheader->size != buffer_size) {
04399             buffer_size = pheader->size;
04400 
04401             /* re-open shared memory with proper size */
04402 
04403             status = ss_shm_close(buffer_name, _buffer[handle].buffer_header,
04404                                   shm_handle, FALSE);
04405             if (status != BM_SUCCESS)
04406                return BM_MEMSIZE_MISMATCH;
04407 
04408             status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size,
04409                                  (void **) &(_buffer[handle].buffer_header), &shm_handle);
04410 
04411             if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
04412                *buffer_handle = 0;
04413                return BM_INVALID_NAME;
04414             }
04415 
04416             pheader = _buffer[handle].buffer_header;
04417          }
04418       }
04419 
04420       /* create mutex for the buffer */
04421       status = ss_mutex_create(buffer_name, &(_buffer[handle].mutex));
04422       if (status != SS_CREATED && status != SS_SUCCESS) {
04423          *buffer_handle = 0;
04424          return BM_NO_MUTEX;
04425       }
04426 
04427       /* first lock buffer */
04428       bm_lock_buffer(handle + 1);
04429 
04430       /*
04431          Now we have a BUFFER_HEADER, so let's setup a CLIENT
04432          structure in that buffer. The information there can also
04433          be seen by other processes.
04434        */
04435 
04436       for (i = 0; i < MAX_CLIENTS; i++)
04437          if (pheader->client[i].pid == 0)
04438             break;
04439 
04440       if (i == MAX_CLIENTS) {
04441          bm_unlock_buffer(handle + 1);
04442          *buffer_handle = 0;
04443          cm_msg(MERROR, "bm_open_buffer", "maximum number of clients exceeded");
04444          return BM_NO_SLOT;
04445       }
04446 
04447       /* store slot index in _buffer structure */
04448       _buffer[handle].client_index = i;
04449 
04450       /*
04451          Save the index of the last client of that buffer so that later only
04452          the clients 0..max_client_index-1 have to be searched through.
04453        */
04454       pheader->num_clients++;
04455       if (i + 1 > pheader->max_client_index)
04456          pheader->max_client_index = i + 1;
04457 
04458       /* setup buffer header and client structure */
04459       pclient = &pheader->client[i];
04460 
04461       memset(pclient, 0, sizeof(BUFFER_CLIENT));
04462       /* use client name previously set by bm_set_name */
04463       cm_get_client_info(pclient->name);
04464       if (pclient->name[0] == 0)
04465          strcpy(pclient->name, "unknown");
04466       pclient->pid = ss_getpid();
04467       pclient->tid = ss_gettid();
04468       pclient->thandle = ss_getthandle();
04469 
04470       ss_suspend_get_port(&pclient->port);
04471 
04472       pclient->read_pointer = pheader->write_pointer;
04473       pclient->last_activity = ss_millitime();
04474 
04475       cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
04476 
04477       bm_unlock_buffer(handle + 1);
04478 
04479       /* setup _buffer entry */
04480       _buffer[handle].buffer_data = _buffer[handle].buffer_header + 1;
04481       _buffer[handle].attached = TRUE;
04482       _buffer[handle].shm_handle = shm_handle;
04483       _buffer[handle].callback = FALSE;
04484 
04485       /* remember to which connection acutal buffer belongs */
04486       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE)
04487          _buffer[handle].index = rpc_get_server_acception();
04488       else
04489          _buffer[handle].index = ss_gettid();
04490 
04491       *buffer_handle = (handle + 1);
04492 
04493       /* initialize buffer counters */
04494       bm_init_buffer_counters(handle + 1);
04495 
04496       /* setup dispatcher for receive events */
04497       ss_suspend_set_dispatch(CH_IPC, 0, (int (*)(void)) cm_dispatch_ipc);
04498 
04499       if (shm_created)
04500          return BM_CREATED;
04501    }
04502 #endif                          /* LOCAL_ROUTINES */
04503 
04504    return BM_SUCCESS;
04505 }
04506 
04507 /********************************************************************/
04508 /** 
04509 Closes an event buffer previously opened with bm_open_buffer().
04510 @param buffer_handle buffer handle
04511 @return BM_SUCCESS, BM_INVALID_HANDLE
04512 */
04513 INT bm_close_buffer(INT buffer_handle)
04514 {
04515    if (rpc_is_remote())
04516       return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
04517 
04518 #ifdef LOCAL_ROUTINES
04519    {
04520       BUFFER_CLIENT *pclient;
04521       BUFFER_HEADER *pheader;
04522       INT i, j, index, destroy_flag;
04523 
04524       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
04525          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04526          return BM_INVALID_HANDLE;
04527       }
04528 
04529       /*
04530          Check if buffer was opened by current thread. This is necessary
04531          in the server process where one thread may not close the buffer
04532          of other threads.
04533        */
04534 
04535       index = _buffer[buffer_handle - 1].client_index;
04536       pheader = _buffer[buffer_handle - 1].buffer_header;
04537 
04538       if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
04539           _buffer[buffer_handle - 1].index != rpc_get_server_acception())
04540          return BM_INVALID_HANDLE;
04541 
04542       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
04543           _buffer[buffer_handle - 1].index != ss_gettid())
04544          return BM_INVALID_HANDLE;
04545 
04546       if (!_buffer[buffer_handle - 1].attached) {
04547          cm_msg(MERROR, "bm_close_buffer", "invalid buffer handle %d", buffer_handle);
04548          return BM_INVALID_HANDLE;
04549       }
04550 
04551       /* delete all requests for this buffer */
04552       for (i = 0; i < _request_list_entries; i++)
04553          if (_request_list[i].buffer_handle == buffer_handle)
04554             bm_delete_request(i);
04555 
04556       /* first lock buffer */
04557       bm_lock_buffer(buffer_handle);
04558 
04559       /* mark entry in _buffer as empty */
04560       _buffer[buffer_handle - 1].attached = FALSE;
04561 
04562       /* clear entry from client structure in buffer header */
04563       memset(&(pheader->client[index]), 0, sizeof(BUFFER_CLIENT));
04564 
04565       /* calculate new max_client_index entry */
04566       for (i = MAX_CLIENTS - 1; i >= 0; i--)
04567          if (pheader->client[i].pid != 0)
04568             break;
04569       pheader->max_client_index = i + 1;
04570 
04571       /* count new number of clients */
04572       for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
04573          if (pheader->client[i].pid != 0)
04574             j++;
04575       pheader->num_clients = j;
04576 
04577       destroy_flag = (pheader->num_clients == 0);
04578 
04579       /* free cache */
04580       if (_buffer[buffer_handle - 1].read_cache_size > 0)
04581          M_FREE(_buffer[buffer_handle - 1].read_cache);
04582       if (_buffer[buffer_handle - 1].write_cache_size > 0)
04583          M_FREE(_buffer[buffer_handle - 1].write_cache);
04584 
04585       /* check if anyone is waiting and wake him up */
04586       pclient = pheader->client;
04587 
04588       for (i = 0; i < pheader->max_client_index; i++, pclient++)
04589          if (pclient->pid && (pclient->write_wait || pclient->read_wait))
04590             ss_resume(pclient->port, "B  ");
04591 
04592       /* unmap shared memory, delete it if we are the last */
04593       ss_shm_close(pheader->name, _buffer[buffer_handle - 1].buffer_header,
04594                    _buffer[buffer_handle - 1].shm_handle, destroy_flag);
04595 
04596       /* unlock buffer */
04597       bm_unlock_buffer(buffer_handle);
04598 
04599       /* delete mutex */
04600       ss_mutex_delete(_buffer[buffer_handle - 1].mutex, destroy_flag);
04601 
04602       /* update _buffer_entries */
04603       if (buffer_handle == _buffer_entries)
04604          _buffer_entries--;
04605 
04606       if (_buffer_entries > 0)
04607          _buffer = (BUFFER *) realloc(_buffer, sizeof(BUFFER) * (_buffer_entries));
04608       else {
04609          M_FREE(_buffer);
04610          _buffer = NULL;
04611       }
04612    }
04613 #endif                          /* LOCAL_ROUTINES */
04614 
04615    return BM_SUCCESS;
04616 }
04617 
04618 /********************************************************************/
04619 /**
04620 Close all open buffers
04621 @return BM_SUCCESS
04622 */
04623 INT bm_close_all_buffers(void)
04624 {
04625    if (rpc_is_remote())
04626       return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
04627 
04628 #ifdef LOCAL_ROUTINES
04629    {
04630       INT i;
04631 
04632       for (i = _buffer_entries; i > 0; i--)
04633          bm_close_buffer(i);
04634    }
04635 #endif                          /* LOCAL_ROUTINES */
04636 
04637    return BM_SUCCESS;
04638 }
04639 
04640 /**dox***************************************************************/
04641 /** @} */ /* end of bmfunctionc */
04642 
04643 /**dox***************************************************************/
04644 /** @addtogroup cmfunctionc
04645  *  
04646  *  @{  */
04647 
04648 /*-- Watchdog routines ---------------------------------------------*/
04649 #ifdef LOCAL_ROUTINES
04650 
04651 /********************************************************************/
04652 /**
04653 Called at periodic intervals, checks if all clients are
04654 alive. If one process died, its client entries are cleaned up.
04655 @param dummy unused!
04656 */
04657 void cm_watchdog(int dummy)
04658 {
04659    BUFFER_HEADER *pheader;
04660    BUFFER_CLIENT *pbclient, *pbctmp;
04661    DATABASE_HEADER *pdbheader;
04662    DATABASE_CLIENT *pdbclient;
04663    KEY *pkey;
04664    DWORD actual_time, interval;
04665    INT client_pid;
04666    INT i, j, k, nc, status;
04667    BOOL bDeleted, time_changed, wrong_interval;
04668    char str[256];
04669 
04670    /* return immediately if watchdog has been disabled in meantime */
04671    if (!_call_watchdog)
04672       return;
04673 
04674    /* tell system services that we are in async mode ... */
04675    ss_set_async_flag(TRUE);
04676 
04677    /* Calculate the time since last watchdog call. Kill clients if they
04678       are inactive for more than the timeout they specified */
04679    actual_time = ss_millitime();
04680    if (_watchdog_last_called == 0)
04681       _watchdog_last_called = actual_time - WATCHDOG_INTERVAL;
04682    interval = actual_time - _watchdog_last_called;
04683 
04684    /* check if system time has been changed more than 10 min */
04685    time_changed = interval < 0 || interval > 600000;
04686    wrong_interval = interval < 0.8 * WATCHDOG_INTERVAL
04687        || interval > 1.2 * WATCHDOG_INTERVAL;
04688 
04689    if (time_changed)
04690       cm_msg(MINFO, "cm_watchdog",
04691              "System time has been changed! last:%dms  now:%dms  delta:%dms",
04692              _watchdog_last_called, actual_time, interval);
04693 
04694    /* check buffers */
04695    for (i = 0; i < _buffer_entries; i++)
04696       if (_buffer[i].attached) {
04697          /* update the last_activity entry to show that we are alive */
04698          pheader = _buffer[i].buffer_header;
04699          pbclient = pheader->client;
04700          pbclient[_buffer[i].client_index].last_activity = actual_time;
04701 
04702          /* don't check other clients if interval is stange */
04703          if (wrong_interval)
04704             continue;
04705 
04706          /* now check other clients */
04707          for (j = 0; j < pheader->max_client_index; j++, pbclient++)
04708             /* If client process has no activity, clear its buffer entry. */
04709             if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04710                 actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04711                bm_lock_buffer(i + 1);
04712                str[0] = 0;
04713 
04714                /* now make again the check with the buffer locked */
04715                actual_time = ss_millitime();
04716                if (pbclient->pid && pbclient->watchdog_timeout > 0 &&
04717                    actual_time > pbclient->last_activity &&
04718                    actual_time - pbclient->last_activity > pbclient->watchdog_timeout) {
04719                   sprintf(str, "Client %s on %s removed (idle %1.1lfs,TO %1.0lfs)",
04720                           pbclient->name, pheader->name,
04721                           (actual_time - pbclient->last_activity) / 1000.0,
04722                           pbclient->watchdog_timeout / 1000.0);
04723 
04724                   /* clear entry from client structure in buffer header */
04725                   memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
04726 
04727                   /* calculate new max_client_index entry */
04728                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04729                      if (pheader->client[k].pid != 0)
04730                         break;
04731                   pheader->max_client_index = k + 1;
04732 
04733                   /* count new number of clients */
04734                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04735                      if (pheader->client[k].pid != 0)
04736                         nc++;
04737                   pheader->num_clients = nc;
04738 
04739                   /* check if anyone is wating and wake him up */
04740                   pbctmp = pheader->client;
04741 
04742                   for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
04743                      if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
04744                         ss_resume(pbctmp->port, "B  ");
04745 
04746                }
04747 
04748                bm_unlock_buffer(i + 1);
04749 
04750                /* display info message after unlocking buffer */
04751                if (str[0])
04752                   cm_msg(MINFO, "cm_watchdog", str);
04753             }
04754       }
04755 
04756    /* check online databases */
04757    for (i = 0; i < _database_entries; i++)
04758       if (_database[i].attached) {
04759          /* update the last_activity entry to show that we are alive */
04760          pdbheader = _database[i].database_header;
04761          pdbclient = pdbheader->client;
04762          pdbclient[_database[i].client_index].last_activity = actual_time;
04763 
04764          /* don't check other clients if interval is stange */
04765          if (wrong_interval)
04766             continue;
04767 
04768          /* now check other clients */
04769          for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
04770             /* If client process has no activity, clear its buffer entry. */
04771             if (pdbclient->pid && pdbclient->watchdog_timeout > 0 &&
04772                 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04773                client_pid = pdbclient->tid;
04774                bDeleted = FALSE;
04775                db_lock_database(i + 1);
04776                str[0] = 0;
04777 
04778                /* now make again the check with the buffer locked */
04779                actual_time = ss_millitime();
04780                if (pdbclient->pid && pdbclient->watchdog_timeout &&
04781                    actual_time > pdbclient->last_activity &&
04782                    actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout) {
04783                   sprintf(str,
04784                           "Client %s (PID %d) on %s removed (idle %1.1lfs,TO %1.0lfs)",
04785                           pdbclient->name, client_pid, pdbheader->name,
04786                           (actual_time - pdbclient->last_activity) / 1000.0,
04787                           pdbclient->watchdog_timeout / 1000.0);
04788 
04789                   /* decrement notify_count for open records and clear exclusive mode */
04790                   for (k = 0; k < pdbclient->max_index; k++)
04791                      if (pdbclient->open_record[k].handle) {
04792                         pkey = (KEY *) ((char *) pdbheader +
04793                                         pdbclient->open_record[k].handle);
04794                         if (pkey->notify_count > 0)
04795                            pkey->notify_count--;
04796 
04797                         if (pdbclient->open_record[k].access_mode & MODE_WRITE)
04798                            db_set_mode(i + 1, pdbclient->open_record[k].handle,
04799                                        (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2);
04800                      }
04801 
04802                   /* clear entry from client structure in buffer header */
04803                   memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
04804 
04805                   /* calculate new max_client_index entry */
04806                   for (k = MAX_CLIENTS - 1; k >= 0; k--)
04807                      if (pdbheader->client[k].pid != 0)
04808                         break;
04809                   pdbheader->max_client_index = k + 1;
04810 
04811                   /* count new number of clients */
04812                   for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
04813                      if (pdbheader->client[k].pid != 0)
04814                         nc++;
04815                   pdbheader->num_clients = nc;
04816                   bDeleted = TRUE;
04817                }
04818 
04819                db_unlock_database(i + 1);
04820 
04821                /* display info message after unlocking db */
04822                if (str[0])
04823                   cm_msg(MINFO, "cm_watchdog", str);
04824 
04825                /* delete client entry after unlocking db */
04826                if (bDeleted) {
04827                   status = cm_delete_client_info(i + 1, client_pid);
04828                   if (status != CM_SUCCESS)
04829                      cm_msg(MERROR, "cm_watchdog", "cannot delete client info");
04830                }
04831             }
04832       }
04833 
04834    _watchdog_last_called = actual_time;
04835 
04836    ss_set_async_flag(FALSE);
04837 
04838    /* Schedule next watchdog call */
04839    if (_call_watchdog)
04840       ss_alarm(WATCHDOG_INTERVAL, cm_watchdog);
04841 }
04842 
04843 /********************************************************************/
04844 /**
04845 Temporarily disable watchdog calling. Used for tape IO
04846 not to interrupt lengthy operations like mount.
04847 @param flag FALSE for disable, TRUE for re-enable
04848 @return CM_SUCCESS
04849 */
04850 INT cm_enable_watchdog(BOOL flag)
04851 {
04852    static INT timeout = DEFAULT_WATCHDOG_TIMEOUT;
04853    static BOOL call_flag = FALSE;
04854 
04855    if (flag) {
04856       if (call_flag)
04857          cm_set_watchdog_params(TRUE, timeout);
04858    } else {
04859       call_flag = _call_watchdog;
04860       timeout = _watchdog_timeout;
04861       if (call_flag)
04862          cm_set_watchdog_params(FALSE, 0);
04863    }
04864 
04865    return CM_SUCCESS;
04866 }
04867 
04868 #endif                          /* local routines */
04869 
04870 /********************************************************************/
04871 /**
04872 Shutdown (exit) other MIDAS client
04873 @param name           Client name or "all" for all clients
04874 @param bUnique        If true, look for the exact client name.
04875                       If false, look for namexxx where xxx is
04876                       a any number.
04877 
04878 @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY 
04879 */
04880 INT cm_shutdown(char *name, BOOL bUnique)
04881 {
04882    INT status, return_status, i, size;
04883    HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
04884    KEY key;
04885    char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH], str[256];
04886    INT port;
04887    DWORD start_time;
04888 
04889    cm_get_experiment_database(&hDB, &hKeyClient);
04890 
04891    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04892    if (status != DB_SUCCESS)
04893       return DB_NO_KEY;
04894 
04895    return_status = CM_NO_CLIENT;
04896 
04897    /* loop over all clients */
04898    for (i = 0;; i++) {
04899       status = db_enum_key(hDB, hKey, i, &hSubkey);
04900       if (status == DB_NO_MORE_SUBKEYS)
04901          break;
04902 
04903       /* don't shutdown ourselves */
04904       if (hSubkey == hKeyClient)
04905          continue;
04906 
04907       if (status == DB_SUCCESS) {
04908          db_get_key(hDB, hSubkey, &key);
04909 
04910          /* contact client */
04911          size = sizeof(client_name);
04912          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04913 
04914          if (!bUnique)
04915             client_name[strlen(name)] = 0;      /* strip number */
04916 
04917          /* check if individual client */
04918          if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
04919             continue;
04920 
04921          size = sizeof(port);
04922          db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT, TRUE);
04923 
04924          size = sizeof(remote_host);
04925          db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
04926 
04927          /* client found -> connect to its server port */
04928          status = rpc_client_connect(remote_host, port, client_name, &hConn);
04929          if (status != RPC_SUCCESS) {
04930             return_status = CM_NO_CLIENT;
04931             sprintf(str, "cannot connect to client %s on host %s, port %d",
04932                     client_name, remote_host, port);
04933             cm_msg(MERROR, "cm_shutdown", str);
04934          } else {
04935             /* call disconnect with shutdown=TRUE */
04936             rpc_client_disconnect(hConn, TRUE);
04937 
04938             /* wait until client has shut down */
04939             start_time = ss_millitime();
04940             do {
04941                ss_sleep(100);
04942                status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
04943             } while (status == DB_SUCCESS && (ss_millitime() - start_time < 5000));
04944 
04945             if (status == DB_SUCCESS) {
04946                cm_msg(MINFO, "cm_shutdown",
04947                       "Cannot shutdown client \"%s\", please kill manually and do an ODB cleanup",
04948                       client_name);
04949                return_status = CM_NO_CLIENT;
04950             } else {
04951                return_status = CM_SUCCESS;
04952                i--;
04953             }
04954          }
04955       }
04956    }
04957 
04958    return return_status;
04959 }
04960 
04961 /********************************************************************/
04962 /**
04963 Check if a MIDAS client exists in current experiment
04964 @param    name            Client name
04965 @param    bUnique         If true, look for the exact client name.
04966                           If false, look for namexxx where xxx is
04967                           a any number
04968 @return   CM_SUCCESS, CM_NO_CLIENT 
04969 */
04970 INT cm_exist(char *name, BOOL bUnique)
04971 {
04972    INT status, i, size;
04973    HNDLE hDB, hKeyClient, hKey, hSubkey;
04974    char client_name[NAME_LENGTH];
04975 
04976    if (rpc_is_remote())
04977       return rpc_call(RPC_CM_EXIST, name, bUnique);
04978 
04979    cm_get_experiment_database(&hDB, &hKeyClient);
04980 
04981    status = db_find_key(hDB, 0, "System/Clients", &hKey);
04982    if (status != DB_SUCCESS)
04983       return DB_NO_KEY;
04984 
04985    /* loop over all clients */
04986    for (i = 0;; i++) {
04987       status = db_enum_key(hDB, hKey, i, &hSubkey);
04988       if (status == DB_NO_MORE_SUBKEYS)
04989          break;
04990 
04991       if (hSubkey == hKeyClient)
04992          continue;
04993 
04994       if (status == DB_SUCCESS) {
04995          /* get client name */
04996          size = sizeof(client_name);
04997          db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
04998 
04999          if (equal_ustring(client_name, name))
05000             return CM_SUCCESS;
05001 
05002          if (!bUnique) {
05003             client_name[strlen(name)] = 0;      /* strip number */
05004             if (equal_ustring(client_name, name))
05005                return CM_SUCCESS;
05006          }
05007       }
05008    }
05009 
05010    return CM_NO_CLIENT;
05011 }
05012 
05013 /********************************************************************/
05014 /**
05015 Remove hanging clients independent of their watchdog
05016            timeout.
05017 
05018 Since this function does not obey the client watchdog
05019 timeout, it should be only called to remove clients which
05020 have their watchdog checking turned off or which are
05021 known to be dead. The normal client removement is done
05022 via cm_watchdog().
05023 
05024 Currently (Sept. 02) there are two applications for that:
05025 -# The ODBEdit command "cleanup", which can be used to
05026 remove clients which have their watchdog checking off,
05027 like the analyzer started with the "-d" flag for a
05028 debugging session.
05029 -# The frontend init code to remove previous frontends.
05030 This can be helpful if a frontend dies. Normally,
05031 one would have to wait 60 sec. for a crashed frontend
05032 to be removed. Only then one can start again the
05033 frontend. Since the frontend init code contains a
05034 call to cm_cleanup(<frontend_name>), one can restart
05035 a frontend immediately.
05036 
05037 Added ignore_timeout on Nov.03. A logger might have an
05038 increased tiemout of up to 60 sec. because of tape
05039 operations. If ignore_timeout is FALSE, the logger is
05040 then not killed if its inactivity is less than 60 sec., 
05041 while in the previous implementation it was always
05042 killed after 2*WATCHDOG_INTERVAL.
05043 @param    client_name      Client name, if zero check all clients
05044 @param    ignore_timeout   If TRUE, ignore a possible increased
05045                            timeout defined by each client.
05046 @return   CM_SUCCESS
05047 */
05048 INT cm_cleanup(char *client_name, BOOL ignore_timeout)
05049 {
05050    if (rpc_is_remote())
05051       return rpc_call(RPC_CM_CLEANUP, client_name);
05052 
05053 #ifdef LOCAL_ROUTINES
05054    {
05055       BUFFER_HEADER *pheader = NULL;
05056       BUFFER_CLIENT *pbclient, *pbctmp;
05057       DATABASE_HEADER *pdbheader;
05058       DATABASE_CLIENT *pdbclient;
05059       KEY *pkey;
05060       INT client_pid;
05061       INT i, j, k, status, nc;
05062       BOOL bDeleted;
05063       char str[256];
05064       DWORD interval;
05065 
05066       /* check buffers */
05067       for (i = 0; i < _buffer_entries; i++)
05068          if (_buffer[i].attached) {
05069             /* update the last_activity entry to show that we are alive */
05070             pheader = _buffer[i].buffer_header;
05071             pbclient = pheader->client;
05072             pbclient[_buffer[i].client_index].last_activity = ss_millitime();
05073 
05074             /* now check other clients */
05075             for (j = 0; j < pheader->max_client_index; j++, pbclient++)
05076                if (j != _buffer[i].client_index && pbclient->pid &&
05077                    (client_name[0] == 0
05078                     || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
05079                   if (ignore_timeout)
05080                      interval = 2 * WATCHDOG_INTERVAL;
05081                   else
05082                      interval = pbclient->watchdog_timeout;
05083 
05084                   /* If client process has no activity, clear its buffer entry. */
05085                   if (ss_millitime() - pbclient->last_activity > interval) {
05086                      bm_lock_buffer(i + 1);
05087                      str[0] = 0;
05088 
05089                      /* now make again the check with the buffer locked */
05090                      if (ss_millitime() - pbclient->last_activity > interval) {
05091                         sprintf(str,
05092                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05093                                 pbclient->name, pheader->name,
05094                                 (ss_millitime() - pbclient->last_activity) / 1000.0,
05095                                 interval / 1000.0);
05096 
05097                         /* clear entry from client structure in buffer header */
05098                         memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
05099 
05100                         /* calculate new max_client_index entry */
05101                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05102                            if (pheader->client[k].pid != 0)
05103                               break;
05104                         pheader->max_client_index = k + 1;
05105 
05106                         /* count new number of clients */
05107                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05108                            if (pheader->client[k].pid != 0)
05109                               nc++;
05110                         pheader->num_clients = nc;
05111 
05112                         /* check if anyone is wating and wake him up */
05113                         pbctmp = pheader->client;
05114 
05115                         for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
05116                            if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
05117                               ss_resume(pbctmp->port, "B  ");
05118 
05119                      }
05120 
05121                      bm_unlock_buffer(i + 1);
05122 
05123                      /* display info message after unlocking buffer */
05124                      if (str[0])
05125                         cm_msg(MINFO, "cm_cleanup", str);
05126 
05127                      /* go again through whole list */
05128                      j = 0;
05129                   }
05130                }
05131          }
05132 
05133       /* check online databases */
05134       for (i = 0; i < _database_entries; i++)
05135          if (_database[i].attached) {
05136             /* update the last_activity entry to show that we are alive */
05137             db_lock_database(i + 1);
05138 
05139             pdbheader = _database[i].database_header;
05140             pdbclient = pdbheader->client;
05141             pdbclient[_database[i].client_index].last_activity = ss_millitime();
05142 
05143             /* now check other clients */
05144             for (j = 0; j < pdbheader->max_client_index; j++, pdbclient++)
05145                if (j != _database[i].client_index && pdbclient->pid &&
05146                    (client_name[0] == 0
05147                     || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
05148                   client_pid = pdbclient->tid;
05149                   if (ignore_timeout)
05150                      interval = 2 * WATCHDOG_INTERVAL;
05151                   else
05152                      interval = pdbclient->watchdog_timeout;
05153 
05154                   /* If client process has no activity, clear its buffer entry. */
05155 
05156                   if (ss_millitime() - pdbclient->last_activity > interval) {
05157                      bDeleted = FALSE;
05158                      str[0] = 0;
05159 
05160                      /* now make again the check with the buffer locked */
05161                      if (ss_millitime() - pdbclient->last_activity > interval) {
05162                         sprintf(str,
05163                                 "Client %s on %s removed (via cleanup) (idle %1.1lfs,TO %1.0lfs)",
05164                                 pdbclient->name, pdbheader->name,
05165                                 (ss_millitime() - pdbclient->last_activity) / 1000.0,
05166                                 interval / 1000.0);
05167 
05168                         /* decrement notify_count for open records and clear exclusive mode */
05169                         for (k = 0; k < pdbclient->max_index; k++)
05170                            if (pdbclient->open_record[k].handle) {
05171                               pkey = (KEY *) ((char *) pdbheader +
05172                                               pdbclient->open_record[k].handle);
05173                               if (pkey->notify_count > 0)
05174                                  pkey->notify_count--;
05175 
05176                               if (pdbclient->open_record[k].access_mode & MODE_WRITE)
05177                                  db_set_mode(i + 1, pdbclient->open_record[k].handle,
05178                                              (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE),
05179                                              2);
05180                            }
05181 
05182                         /* clear entry from client structure in buffer header */
05183                         memset(&(pdbheader->client[j]), 0, sizeof(DATABASE_CLIENT));
05184 
05185                         /* calculate new max_client_index entry */
05186                         for (k = MAX_CLIENTS - 1; k >= 0; k--)
05187                            if (pdbheader->client[k].pid != 0)
05188                               break;
05189                         pdbheader->max_client_index = k + 1;
05190 
05191                         /* count new number of clients */
05192                         for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
05193                            if (pheader->client[k].pid != 0)
05194                               nc++;
05195                         pdbheader->num_clients = nc;
05196 
05197                         bDeleted = TRUE;
05198                      }
05199 
05200 
05201                      /* delete client entry after unlocking db */
05202                      if (bDeleted) {
05203                         db_unlock_database(i + 1);
05204 
05205                         /* display info message after unlocking buffer */
05206                         cm_msg(MINFO, "cm_cleanup", str);
05207 
05208                         status = cm_delete_client_info(i + 1, client_pid);
05209                         if (status != CM_SUCCESS)
05210                            cm_msg(MERROR, "cm_cleanup", "cannot delete client info");
05211 
05212                         /* re-lock database */
05213                         db_lock_database(i + 1);
05214                         pdbheader = _database[i].database_header;
05215                         pdbclient = pdbheader->client;
05216 
05217                         /* go again though whole list */
05218                         j = 0;
05219                      }
05220                   }
05221                }
05222 
05223             db_unlock_database(i + 1);
05224          }
05225 
05226    }
05227 #endif                          /* LOCAL_ROUTINES */
05228 
05229    return CM_SUCCESS;
05230 }
05231 
05232 /**dox***************************************************************/
05233 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05234 
05235 /********************************************************************/
05236 INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER * buffer_header)
05237 /********************************************************************\
05238 
05239   Routine: bm_buffer_info
05240 
05241   Purpose: Copies the current buffer header referenced by buffer_handle
05242            into the *buffer_header structure which must be supplied
05243            by the calling routine.
05244 
05245   Input:
05246     INT buffer_handle       Handle of the buffer to get the header from
05247 
05248   Output:
05249     BUFFER_HEADER *buffer_header   Destination address which gets a copy
05250                                    of the buffer header structure.
05251 
05252   Function value:
05253     BM_SUCCESS              Successful completion
05254     BM_INVALID_HANDLE       Buffer handle is invalid
05255     RPC_NET_ERROR           Network error
05256 
05257 \********************************************************************/
05258 {
05259    if (rpc_is_remote())
05260       return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
05261 
05262 #ifdef LOCAL_ROUTINES
05263 
05264    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05265       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05266       return BM_INVALID_HANDLE;
05267    }
05268 
05269    if (!_buffer[buffer_handle - 1].attached) {
05270       cm_msg(MERROR, "bm_get_buffer_info", "invalid buffer handle %d", buffer_handle);
05271       return BM_INVALID_HANDLE;
05272    }
05273 
05274    bm_lock_buffer(buffer_handle);
05275 
05276    memcpy(buffer_header, _buffer[buffer_handle - 1].buffer_header, sizeof(BUFFER_HEADER));
05277 
05278    bm_unlock_buffer(buffer_handle);
05279 
05280 #endif                          /* LOCAL_ROUTINES */
05281 
05282    return BM_SUCCESS;
05283 }
05284 
05285 /********************************************************************/
05286 INT bm_get_buffer_level(INT buffer_handle, INT * n_bytes)
05287 /********************************************************************\
05288 
05289   Routine: bm_get_buffer_level
05290 
05291   Purpose: Return number of bytes in buffer or in cache
05292 
05293   Input:
05294     INT buffer_handle       Handle of the buffer to get the info
05295 
05296   Output:
05297     INT *n_bytes              Number of bytes in buffer
05298 
05299   Function value:
05300     BM_SUCCESS              Successful completion
05301     BM_INVALID_HANDLE       Buffer handle is invalid
05302     RPC_NET_ERROR           Network error
05303 
05304 \********************************************************************/
05305 {
05306    if (rpc_is_remote())
05307       return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
05308 
05309 #ifdef LOCAL_ROUTINES
05310    {
05311       BUFFER *pbuf;
05312       BUFFER_HEADER *pheader;
05313       BUFFER_CLIENT *pclient;
05314 
05315       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05316          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05317          return BM_INVALID_HANDLE;
05318       }
05319 
05320       pbuf = &_buffer[buffer_handle - 1];
05321       pheader = pbuf->buffer_header;
05322 
05323       if (!pbuf->attached) {
05324          cm_msg(MERROR, "bm_get_buffer_level", "invalid buffer handle %d", buffer_handle);
05325          return BM_INVALID_HANDLE;
05326       }
05327 
05328       bm_lock_buffer(buffer_handle);
05329 
05330       pclient = &(pheader->client[_buffer[buffer_handle - 1].client_index]);
05331 
05332       *n_bytes = pheader->write_pointer - pclient->read_pointer;
05333       if (*n_bytes < 0)
05334          *n_bytes += pheader->size;
05335 
05336       bm_unlock_buffer(buffer_handle);
05337 
05338       /* add bytes in cache */
05339       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
05340          *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
05341    }
05342 #endif                          /* LOCAL_ROUTINES */
05343 
05344    return BM_SUCCESS;
05345 }
05346 
05347 
05348 
05349 #ifdef LOCAL_ROUTINES
05350 
05351 /********************************************************************/
05352 INT bm_lock_buffer(INT buffer_handle)
05353 /********************************************************************\
05354 
05355   Routine: bm_lock_buffer
05356 
05357   Purpose: Lock a buffer for exclusive access via system mutex calls.
05358 
05359   Input:
05360     INT    bufer_handle     Handle to the buffer to lock
05361   Output:
05362     none
05363 
05364   Function value:
05365     BM_SUCCESS              Successful completion
05366     BM_INVALID_HANDLE       Buffer handle is invalid
05367 
05368 \********************************************************************/
05369 {
05370    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05371       cm_msg(MERROR, "bm_lock_buffer", "invalid buffer handle %d", buffer_handle);
05372       return BM_INVALID_HANDLE;
05373    }
05374 
05375    ss_mutex_wait_for(_buffer[buffer_handle - 1].mutex, 0);
05376    return BM_SUCCESS;
05377 }
05378 
05379 /********************************************************************/
05380 INT bm_unlock_buffer(INT buffer_handle)
05381 /********************************************************************\
05382 
05383   Routine: bm_unlock_buffer
05384 
05385   Purpose: Unlock a buffer via system mutex calls.
05386 
05387   Input:
05388     INT    bufer_handle     Handle to the buffer to lock
05389   Output:
05390     none
05391 
05392   Function value:
05393     BM_SUCCESS              Successful completion
05394     BM_INVALID_HANDLE       Buffer handle is invalid
05395 
05396 \********************************************************************/
05397 {
05398    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05399       cm_msg(MERROR, "bm_unlock_buffer", "invalid buffer handle %d", buffer_handle);
05400       return BM_INVALID_HANDLE;
05401    }
05402 
05403    ss_mutex_release(_buffer[buffer_handle - 1].mutex);
05404    return BM_SUCCESS;
05405 }
05406 
05407 #endif                          /* LOCAL_ROUTINES */
05408 
05409 /********************************************************************/
05410 INT bm_init_buffer_counters(INT buffer_handle)
05411 /********************************************************************\
05412 
05413   Routine: bm_init_event_counters
05414 
05415   Purpose: Initialize counters for a specific buffer. This routine
05416            should be called at the beginning of a run.
05417 
05418   Input:
05419     INT    buffer_handle    Handle to the buffer to be
05420                             initialized.
05421   Output:
05422     none
05423 
05424   Function value:
05425     BM_SUCCESS              Successful completion
05426     BM_INVALID_HANDLE       Buffer handle is invalid
05427 
05428 \********************************************************************/
05429 {
05430    if (rpc_is_remote())
05431       return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
05432 
05433 #ifdef LOCAL_ROUTINES
05434 
05435    if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05436       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05437              buffer_handle);
05438       return BM_INVALID_HANDLE;
05439    }
05440 
05441    if (!_buffer[buffer_handle - 1].attached) {
05442       cm_msg(MERROR, "bm_init_buffer_counters", "invalid buffer handle %d",
05443              buffer_handle);
05444       return BM_INVALID_HANDLE;
05445    }
05446 
05447    _buffer[buffer_handle - 1].buffer_header->num_in_events = 0;
05448    _buffer[buffer_handle - 1].buffer_header->num_out_events = 0;
05449 
05450 #endif                          /* LOCAL_ROUTINES */
05451 
05452    return BM_SUCCESS;
05453 }
05454 
05455 /**dox***************************************************************/
05456 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05457 
05458 /**dox***************************************************************/
05459 /** @} */ /* end of cmfunctionc */
05460 
05461 /**dox***************************************************************/
05462 /** @addtogroup bmfunctionc
05463  *  
05464  *  @{  */
05465 
05466 /********************************************************************/
05467 /**
05468 Modifies buffer cache size.
05469 Without a buffer cache, events are copied to/from the shared
05470 memory event by event.
05471 
05472 To protect processed from accessing the shared memory simultaneously,
05473 semaphores are used. Since semaphore operations are CPU consuming (typically
05474 50-100us) this can slow down the data transfer especially for small events.
05475 By using a cache the number of semaphore operations is reduced dramatically.
05476 Instead writing directly to the shared memory, the events are copied to a
05477 local cache buffer. When this buffer is full, it is copied to the shared
05478 memory in one operation. The same technique can be used when receiving events.
05479 
05480 The drawback of this method is that the events have to be copied twice, once to the
05481 cache and once from the cache to the shared memory. Therefore it can happen that the
05482 usage of a cache even slows down data throughput on a given environment (computer
05483 type, OS type, event size).
05484 The cache size has therefore be optimized manually to maximize data throughput.
05485 @param buffer_handle buffer handle obtained via bm_open_buffer()
05486 @param read_size cache size for reading events in bytes, zero for no cache
05487 @param write_size cache size for writing events in bytes, zero for no cache
05488 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
05489 */
05490 INT bm_set_cache_size(INT buffer_handle, INT read_size, INT write_size)
05491 /*------------------------------------------------------------------*/
05492 {
05493    if (rpc_is_remote())
05494       return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
05495 
05496 #ifdef LOCAL_ROUTINES
05497    {
05498       BUFFER *pbuf;
05499 
05500       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05501          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05502          return BM_INVALID_HANDLE;
05503       }
05504 
05505       if (!_buffer[buffer_handle - 1].attached) {
05506          cm_msg(MERROR, "bm_set_cache_size", "invalid buffer handle %d", buffer_handle);
05507          return BM_INVALID_HANDLE;
05508       }
05509 
05510       if (read_size < 0 || read_size > 1E6) {
05511          cm_msg(MERROR, "bm_set_cache_size", "invalid read chache size");
05512          return BM_INVALID_PARAM;
05513       }
05514 
05515       if (write_size < 0 || write_size > 1E6) {
05516          cm_msg(MERROR, "bm_set_cache_size", "invalid write chache size");
05517          return BM_INVALID_PARAM;
05518       }
05519 
05520       /* manage read cache */
05521       pbuf = &_buffer[buffer_handle - 1];
05522 
05523       if (pbuf->read_cache_size > 0)
05524          M_FREE(pbuf->read_cache);
05525 
05526       if (read_size > 0) {
05527          pbuf->read_cache = (char *) M_MALLOC(read_size);
05528          if (pbuf->read_cache == NULL) {
05529             cm_msg(MERROR, "bm_set_cache_size",
05530                    "not enough memory to allocate cache buffer");
05531             return BM_NO_MEMORY;
05532          }
05533       }
05534 
05535       pbuf->read_cache_size = read_size;
05536       pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
05537 
05538       /* manage write cache */
05539       if (pbuf->write_cache_size > 0)
05540          M_FREE(pbuf->write_cache);
05541 
05542       if (write_size > 0) {
05543          pbuf->write_cache = (char *) M_MALLOC(write_size);
05544          if (pbuf->write_cache == NULL) {
05545             cm_msg(MERROR, "bm_set_cache_size",
05546                    "not enough memory to allocate cache buffer");
05547             return BM_NO_MEMORY;
05548          }
05549       }
05550 
05551       pbuf->write_cache_size = write_size;
05552       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
05553 
05554    }
05555 #endif                          /* LOCAL_ROUTINES */
05556 
05557    return BM_SUCCESS;
05558 }
05559 
05560 /********************************************************************/
05561 /**
05562 Compose a Midas event header.
05563 An event header can usually be set-up manually or
05564 through this routine. If the data size of the event is not known when
05565 the header is composed, it can be set later with event_header->data-size = <...>
05566 Following structure is created at the beginning of an event
05567 \code
05568 typedef struct {
05569  short int     event_id;
05570  short int     trigger_mask;
05571  DWORD         serial_number;
05572  DWORD         time_stamp;
05573  DWORD         data_size;
05574 } EVENT_HEADER;
05575 
05576 char event[1000];
05577  bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
05578  *(event+sizeof(EVENT_HEADER)) = <...>
05579 \endcode
05580 @param event_header pointer to the event header
05581 @param event_id event ID of the event
05582 @param trigger_mask trigger mask of the event
05583 @param size size if the data part of the event in bytes
05584 @param serial serial number
05585 @return BM_SUCCESS
05586 */
05587 INT bm_compose_event(EVENT_HEADER * event_header,
05588                      short int event_id, short int trigger_mask, DWORD size, DWORD serial)
05589 {
05590    event_header->event_id = event_id;
05591    event_header->trigger_mask = trigger_mask;
05592    event_header->data_size = size;
05593    event_header->time_stamp = ss_time();
05594    event_header->serial_number = serial;
05595 
05596    return BM_SUCCESS;
05597 }
05598 
05599 
05600 /**dox***************************************************************/
05601 #ifndef DOXYGEN_SHOULD_SKIP_THIS
05602 
05603 /********************************************************************/
05604 INT bm_add_event_request(INT buffer_handle, short int event_id,
05605                          short int trigger_mask,
05606                          INT sampling_type,
05607                          void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *),
05608                          INT request_id)
05609 /********************************************************************\
05610 
05611   Routine:  bm_add_event_request
05612 
05613   Purpose:  Place a request for a specific event type in the client
05614             structure of the buffer refereced by buffer_handle.
05615 
05616   Input:
05617     INT          buffer_handle  Handle to the buffer where the re-
05618                                 quest should be placed in
05619 
05620     short int    event_id       Event ID      \
05621     short int    trigger_mask   Trigger mask  / Event specification
05622 
05623     INT          sampling_type  One of GET_ALL, GET_SOME or GET_FARM
05624 
05625 
05626                  Note: to request all types of events, use
05627                    event_id = 0 (all others should be !=0 !)
05628                    trigger_mask = TRIGGER_ALL
05629                    sampling_typ = GET_ALL
05630 
05631 
05632     void         *func          Callback function
05633     INT          request_id     Request id (unique number assigned
05634                                 by bm_request_event)
05635 
05636   Output:
05637     none
05638 
05639   Function value:
05640     BM_SUCCESS              Successful completion
05641     BM_NO_MEMORY            Too much request. MAX_EVENT_REQUESTS in
05642                             MIDAS.H should be increased.
05643     BM_INVALID_HANDLE       Buffer handle is invalid
05644     RPC_NET_ERROR           Network error
05645 
05646 \********************************************************************/
05647 {
05648    if (rpc_is_remote())
05649       return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
05650                       trigger_mask, sampling_type, (INT) func, request_id);
05651 
05652 #ifdef LOCAL_ROUTINES
05653    {
05654       INT i;
05655       BUFFER_CLIENT *pclient;
05656 
05657       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05658          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05659                 buffer_handle);
05660          return BM_INVALID_HANDLE;
05661       }
05662 
05663       if (!_buffer[buffer_handle - 1].attached) {
05664          cm_msg(MERROR, "bm_add_event_request", "invalid buffer handle %d",
05665                 buffer_handle);
05666          return BM_INVALID_HANDLE;
05667       }
05668 
05669       /* avoid callback/non callback requests */
05670       if (func == NULL && _buffer[buffer_handle - 1].callback) {
05671          cm_msg(MERROR, "bm_add_event_request",
05672                 "mixing callback/non callback requests not possible");
05673          return BM_INVALID_MIXING;
05674       }
05675 
05676       /* get a pointer to the proper client structure */
05677       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05678                   client[_buffer[buffer_handle - 1].client_index]);
05679 
05680       /* lock buffer */
05681       bm_lock_buffer(buffer_handle);
05682 
05683       /* look for a empty request entry */
05684       for (i = 0; i < MAX_EVENT_REQUESTS; i++)
05685          if (!pclient->event_request[i].valid)
05686             break;
05687 
05688       if (i == MAX_EVENT_REQUESTS) {
05689          bm_unlock_buffer(buffer_handle);
05690          return BM_NO_MEMORY;
05691       }
05692 
05693       /* setup event_request structure */
05694       pclient->event_request[i].id = request_id;
05695       pclient->event_request[i].valid = TRUE;
05696       pclient->event_request[i].event_id = event_id;
05697       pclient->event_request[i].trigger_mask = trigger_mask;
05698       pclient->event_request[i].sampling_type = sampling_type;
05699       pclient->event_request[i].dispatch = func;
05700 
05701       pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
05702 
05703       /* set callback flag in buffer structure */
05704       if (func != NULL)
05705          _buffer[buffer_handle - 1].callback = TRUE;
05706 
05707       /*
05708          Save the index of the last request in the list so that later only the
05709          requests 0..max_request_index-1 have to be searched through.
05710        */
05711 
05712       if (i + 1 > pclient->max_request_index)
05713          pclient->max_request_index = i + 1;
05714 
05715       bm_unlock_buffer(buffer_handle);
05716    }
05717 #endif                          /* LOCAL_ROUTINES */
05718 
05719    return BM_SUCCESS;
05720 }
05721 
05722 /**dox***************************************************************/
05723 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
05724 
05725 /********************************************************************/
05726 /**
05727 Place an event request based on certain characteristics.
05728 Multiple event requests can be placed for each buffer, which
05729 are later identified by their request ID. They can contain different callback
05730 routines. Example see bm_open_buffer() and bm_receive_event()
05731 @param buffer_handle buffer handle obtained via bm_open_buffer()
05732 @param event_id event ID for requested events. Use EVENTID_ALL
05733 to receive events with any ID.
05734 @param trigger_mask trigger mask for requested events.
05735 The requested events must have at least one bit in its
05736 trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
05737 receive events with any trigger mask.
05738 @param sampling_type specifies how many events to receive.
05739 A value of GET_ALL receives all events which
05740 match the specified event ID and trigger mask. If the events are consumed slower
05741 than produced, the producer is automatically slowed down. A value of GET_SOME
05742 receives as much events as possible without slowing down the producer. GET_ALL is
05743 typically used by the logger, while GET_SOME is typically used by analyzers.
05744 @param request_id request ID returned by the function.
05745 This ID is passed to the callback routine and must
05746 be used in the bm_delete_request() routine.
05747 @param func allback routine which gets called when an event of the
05748 specified type is received.
05749 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
05750 BM_NO_MEMORY  too many requests. The value MAX_EVENT_REQUESTS in midas.h
05751 should be increased.
05752 */
05753 INT bm_request_event(HNDLE buffer_handle, short int event_id,
05754                      short int trigger_mask,
05755                      INT sampling_type, HNDLE * request_id,
05756                      void (*func) (HNDLE, HNDLE, EVENT_HEADER *, void *))
05757 {
05758    INT index, status;
05759 
05760    /* allocate new space for the local request list */
05761    if (_request_list_entries == 0) {
05762       _request_list = (REQUEST_LIST *) M_MALLOC(sizeof(REQUEST_LIST));
05763       memset(_request_list, 0, sizeof(REQUEST_LIST));
05764       if (_request_list == NULL) {
05765          cm_msg(MERROR, "bm_request_event",
05766                 "not enough memory to allocate request list buffer");
05767          return BM_NO_MEMORY;
05768       }
05769 
05770       _request_list_entries = 1;
05771       index = 0;
05772    } else {
05773       /* check for a deleted entry */
05774       for (index = 0; index < _request_list_entries; index++)
05775          if (!_request_list[index].buffer_handle)
05776             break;
05777 
05778       /* if not found, create new one */
05779       if (index == _request_list_entries) {
05780          _request_list = (REQUEST_LIST *) realloc(_request_list,
05781                                                   sizeof(REQUEST_LIST) *
05782                                                   (_request_list_entries + 1));
05783          if (_request_list == NULL) {
05784             cm_msg(MERROR, "bm_request_event",
05785                    "not enough memory to allocate request list buffer");
05786             return BM_NO_MEMORY;
05787          }
05788 
05789          memset(&_request_list[_request_list_entries], 0, sizeof(REQUEST_LIST));
05790 
05791          _request_list_entries++;
05792       }
05793    }
05794 
05795    /* initialize request list */
05796    _request_list[index].buffer_handle = buffer_handle;
05797    _request_list[index].event_id = event_id;
05798    _request_list[index].trigger_mask = trigger_mask;
05799    _request_list[index].dispatcher = func;
05800 
05801    *request_id = index;
05802 
05803    /* add request in buffer structure */
05804    status = bm_add_event_request(buffer_handle, event_id, trigger_mask,
05805                                  sampling_type, func, index);
05806    if (status != BM_SUCCESS)
05807       return status;
05808 
05809    return BM_SUCCESS;
05810 }
05811 
05812 /********************************************************************/
05813 /**
05814 Delete a previously placed request for a specific event
05815 type in the client structure of the buffer refereced by buffer_handle.
05816 @param buffer_handle  Handle to the buffer where the re-
05817                                 quest should be placed in
05818 @param request_id     Request id returned by bm_request_event
05819 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR 
05820 */
05821 INT bm_remove_event_request(INT buffer_handle, INT request_id)
05822 {
05823    if (rpc_is_remote())
05824       return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
05825 
05826 #ifdef LOCAL_ROUTINES
05827    {
05828       INT i, deleted;
05829       BUFFER_CLIENT *pclient;
05830 
05831       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
05832          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05833                 buffer_handle);
05834          return BM_INVALID_HANDLE;
05835       }
05836 
05837       if (!_buffer[buffer_handle - 1].attached) {
05838          cm_msg(MERROR, "bm_remove_event_request", "invalid buffer handle %d",
05839                 buffer_handle);
05840          return BM_INVALID_HANDLE;
05841       }
05842 
05843       /* get a pointer to the proper client structure */
05844       pclient = &(_buffer[buffer_handle - 1].buffer_header->
05845                   client[_buffer[buffer_handle - 1].client_index]);
05846 
05847       /* lock buffer */
05848       bm_lock_buffer(buffer_handle);
05849 
05850       /* check all requests and set to zero if matching */
05851       for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
05852          if (pclient->event_request[i].valid &&
05853              pclient->event_request[i].id == request_id) {
05854             memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
05855             deleted++;
05856          }
05857 
05858       /* calculate new max_request_index entry */
05859       for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
05860          if (pclient->event_request[i].valid)
05861             break;
05862 
05863       pclient->max_request_index = i + 1;
05864 
05865       /* caluclate new all_flag */
05866       pclient->all_flag = FALSE;
05867 
05868       for (i = 0; i < pclient->max_request_index; i++)
05869          if (pclient->event_request[i].valid &&
05870              (pclient->event_request[i].sampling_type & GET_ALL)) {
05871             pclient->all_flag = TRUE;
05872             break;
05873          }
05874 
05875       bm_unlock_buffer(buffer_handle);
05876 
05877       if (!deleted)
05878          return BM_NOT_FOUND;
05879    }
05880 #endif                          /* LOCAL_ROUTINES */
05881 
05882    return BM_SUCCESS;
05883 }
05884 
05885 /********************************************************************/
05886 /** 
05887 Deletes an event request previously done with bm_request_event().
05888 When an event request gets deleted, events of that requested type are
05889 not received any more. When a buffer is closed via bm_close_buffer(), all
05890 event requests from that buffer are deleted automatically
05891 @param request_id request identifier given by bm_request_event()
05892 @return BM_SUCCESS, BM_INVALID_HANDLE
05893 */
05894 INT bm_delete_request(INT request_id)
05895 {
05896    if (request_id < 0 || request_id >= _request_list_entries)
05897       return BM_INVALID_HANDLE;
05898 
05899    /* remove request entry from buffer */
05900    bm_remove_event_request(_request_list[request_id].buffer_handle, request_id);
05901 
05902    memset(&_request_list[request_id], 0, sizeof(REQUEST_LIST));
05903 
05904    return BM_SUCCESS;
05905 }
05906 
05907 /********************************************************************/
05908 /** 
05909 Sends an event to a buffer.
05910 This function check if the buffer has enough space for the
05911 event, then copies the event to the buffer in shared memory.
05912 If clients have requests for the event, they are notified via an UDP packet.
05913 \code
05914 char event[1000];
05915 // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05916 bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05917 
05918 // set first byte of event
05919 *(event+sizeof(EVENT_HEADER)) = <...>
05920 #include <stdio.h>
05921 #include "midas.h"
05922 main()
05923 {
05924  INT status, i;
05925  HNDLE hbuf;
05926  char event[1000];
05927  status = cm_connect_experiment("", "Sample", "Producer", NULL);
05928  if (status != CM_SUCCESS)
05929  return 1;
05930  bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
05931 
05932  // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
05933  bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
05934 
05935  // set event data
05936  for (i=0 ; i<100 ; i++)
05937  *(event+sizeof(EVENT_HEADER)+i) = i;
05938  // send event
05939  bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), SYNC);
05940  cm_disconnect_experiment();
05941  return 0;
05942 }
05943 \endcode
05944 @param buffer_handle Buffer handle obtained via bm_open_buffer()
05945 @param source Address of event buffer
05946 @param buf_size Size of event including event header in bytes
05947 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
05948 blocks if the buffer has not enough free space to receive the event.
05949 If TRUE, the function returns immediately with a
05950 value of BM_ASYNC_RETURN without writing the event to the buffer
05951 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
05952 BM_ASYNC_RETURN Routine called with async_flag == TRUE and
05953 buffer has not enough space to receive event<br>
05954 BM_NO_MEMORY   Event is too large for network buffer or event buffer.
05955 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h and
05956 recompile.
05957 */
05958 INT bm_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
05959 {
05960    EVENT_HEADER *pevent;
05961 
05962    /* check if event size defined in header matches buf_size */
05963    if (ALIGN8(buf_size) != (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05964                                       sizeof(EVENT_HEADER))) {
05965       cm_msg(MERROR, "bm_send_event", "event size (%d) mismatch in header (%d)",
05966              ALIGN8(buf_size), (INT) ALIGN8(((EVENT_HEADER *) source)->data_size +
05967                                           sizeof(EVENT_HEADER)));
05968       return BM_INVALID_PARAM;
05969    }
05970 
05971    /* check for maximal event size */
05972    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
05973       cm_msg(MERROR, "bm_send_event",
05974              "event size (%d) larger than maximum event size (%d)",
05975              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
05976       return BM_NO_MEMORY;
05977    }
05978 
05979    if (rpc_is_remote())
05980       return rpc_call(RPC_BM_SEND_EVENT, buffer_handle, source, buf_size, async_flag);
05981 
05982 #ifdef LOCAL_ROUTINES
05983    {
05984       BUFFER *pbuf;
05985       BUFFER_HEADER *pheader;
05986       BUFFER_CLIENT *pclient, *pc;
05987       EVENT_REQUEST *prequest;
05988       EVENT_HEADER *pevent_test;
05989       INT i, j, min_wp, size, total_size, status;
05990       INT increment;
05991       INT my_client_index;
05992       INT old_write_pointer;
05993       INT old_read_pointer, new_read_pointer;
05994       INT num_requests_client;
05995       char *pdata;
05996       BOOL blocking;
05997       INT n_blocking;
05998       INT request_id;
05999       char str[80];
06000 
06001       pbuf = &_buffer[buffer_handle - 1];
06002 
06003       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06004          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06005          return BM_INVALID_HANDLE;
06006       }
06007 
06008       if (!pbuf->attached) {
06009          cm_msg(MERROR, "bm_send_event", "invalid buffer handle %d", buffer_handle);
06010          return BM_INVALID_HANDLE;
06011       }
06012 
06013       pevent = (EVENT_HEADER *) source;
06014       total_size = buf_size;
06015 
06016       /* round up total_size to next DWORD boundary */
06017       total_size = ALIGN8(total_size);
06018 
06019       /* look if there is space in the cache */
06020       if (pbuf->write_cache_size) {
06021          status = BM_SUCCESS;
06022 
06023          if (pbuf->write_cache_size - pbuf->write_cache_wp < total_size)
06024             status = bm_flush_cache(buffer_handle, async_flag);
06025 
06026          if (status != BM_SUCCESS)
06027             return status;
06028 
06029          if (total_size < pbuf->write_cache_size) {
06030             memcpy(pbuf->write_cache + pbuf->write_cache_wp, source, total_size);
06031 
06032             pbuf->write_cache_wp += total_size;
06033             return BM_SUCCESS;
06034          }
06035       }
06036 
06037       /* calculate some shorthands */
06038       pheader = _buffer[buffer_handle - 1].buffer_header;
06039       pdata = (char *) (pheader + 1);
06040       my_client_index = _buffer[buffer_handle - 1].client_index;
06041       pclient = pheader->client;
06042 
06043       /* check if buffer is large enough */
06044       if (total_size >= pheader->size) {
06045          cm_msg(MERROR, "bm_send_event",
06046                 "total event size (%d) larger than buffer size (%d)", total_size,
06047                 pheader->size);
06048          return BM_NO_MEMORY;
06049       }
06050 
06051       /* lock the buffer */
06052       bm_lock_buffer(buffer_handle);
06053 
06054       /* check if enough space in buffer left */
06055       do {
06056          size = pheader->read_pointer - pheader->write_pointer;
06057          if (size <= 0)
06058             size += pheader->size;
06059 
06060          if (size <= total_size) {      /* note the '<=' to avoid 100% filling */
06061             /* if not enough space, find out who's blocking */
06062             n_blocking = 0;
06063 
06064             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06065                if (pc->pid) {
06066                   if (pc->read_pointer == pheader->read_pointer) {
06067                      /*
06068                         First assume that the client with the "minimum" read pointer
06069                         is not really blocking due to a GET_ALL request.
06070                       */
06071                      blocking = FALSE;
06072                      request_id = -1;
06073 
06074                      /* check if this request blocks */
06075 
06076                      prequest = pc->event_request;
06077                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06078 
06079                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06080                         if (prequest->valid &&
06081                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06082                                            pevent_test)) {
06083                            request_id = prequest->id;
06084                            if (prequest->sampling_type & GET_ALL) {
06085                               blocking = TRUE;
06086                               break;
06087                            }
06088                         }
06089 
06090                      if (!blocking) {
06091                         /*
06092                            The blocking guy has no GET_ALL request for this event
06093                            -> shift its read pointer.
06094                          */
06095 
06096                         old_read_pointer = pc->read_pointer;
06097 
06098                         increment = sizeof(EVENT_HEADER) +
06099                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06100 
06101                         /* correct increment for DWORD boundary */
06102                         increment = ALIGN8(increment);
06103 
06104                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06105 
06106                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06107                            new_read_pointer = 0;
06108 
06109                         pc->read_pointer = new_read_pointer;
06110                      } else {
06111                         n_blocking++;
06112                      }
06113 
06114                      /* wake that client if it has a request */
06115                      if (pc->read_wait && request_id != -1) {
06116 #ifdef DEBUG_MSG
06117                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06118                                pheader->read_pointer, pheader->write_pointer);
06119 #endif
06120                         sprintf(str, "B %s %d", pheader->name, request_id);
06121                         ss_resume(pc->port, str);
06122                      }
06123 
06124 
06125                   }             /* read_pointer blocks */
06126                }
06127             /* client loop */
06128             if (n_blocking > 0) {
06129                /* at least one client is blocking */
06130 
06131                bm_unlock_buffer(buffer_handle);
06132 
06133                /* return now in ASYNC mode */
06134                if (async_flag)
06135                   return BM_ASYNC_RETURN;
06136 
06137 #ifdef DEBUG_MSG
06138                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06139                       pheader->read_pointer,
06140                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06141 #endif
06142 
06143                /* has the read pointer moved in between ? */
06144                size = pheader->read_pointer - pheader->write_pointer;
06145                if (size <= 0)
06146                   size += pheader->size;
06147 
06148                /* suspend process */
06149                if (size <= total_size) {
06150                   /* signal other clients wait mode */
06151                   pclient[my_client_index].write_wait = total_size;
06152 
06153                   status = ss_suspend(1000, MSG_BM);
06154 
06155                   pclient[my_client_index].write_wait = 0;
06156 
06157                   /* return if TCP connection broken */
06158                   if (status == SS_ABORT)
06159                      return SS_ABORT;
06160                }
06161 #ifdef DEBUG_MSG
06162                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06163                       pheader->read_pointer,
06164                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06165 #endif
06166 
06167                bm_lock_buffer(buffer_handle);
06168 
06169                /* has the write pointer moved in between ? */
06170                size = pheader->read_pointer - pheader->write_pointer;
06171                if (size <= 0)
06172                   size += pheader->size;
06173             } else {
06174                /*
06175                   calculate new global read pointer as "minimum" of
06176                   client read pointers
06177                 */
06178                min_wp = pheader->write_pointer;
06179 
06180                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06181                   if (pc->pid) {
06182                      if (pc->read_pointer < min_wp)
06183                         min_wp = pc->read_pointer;
06184 
06185                      if (pc->read_pointer > pheader->write_pointer &&
06186                          pc->read_pointer - pheader->size < min_wp)
06187                         min_wp = pc->read_pointer - pheader->size;
06188                   }
06189 
06190                if (min_wp < 0)
06191                   min_wp += pheader->size;
06192 
06193                pheader->read_pointer = min_wp;
06194             }
06195 
06196          }
06197          /* if (size <= total_size) */
06198       } while (size <= total_size);
06199 
06200       /* we have space, so let's copy the event */
06201       old_write_pointer = pheader->write_pointer;
06202 
06203       if (pheader->write_pointer + total_size <= pheader->size) {
06204          memcpy(pdata + pheader->write_pointer, pevent, total_size);
06205          pheader->write_pointer = (pheader->write_pointer + total_size) % pheader->size;
06206          if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06207             pheader->write_pointer = 0;
06208       } else {
06209          /* split event */
06210          size = pheader->size - pheader->write_pointer;
06211 
06212          memcpy(pdata + pheader->write_pointer, pevent, size);
06213          memcpy(pdata, (char *) pevent + size, total_size - size);
06214 
06215          pheader->write_pointer = total_size - size;
06216       }
06217 
06218       /* check which clients have a request for this event */
06219       for (i = 0; i < pheader->max_client_index; i++)
06220          if (pclient[i].pid) {
06221             prequest = pclient[i].event_request;
06222             num_requests_client = 0;
06223             request_id = -1;
06224 
06225             for (j = 0; j < pclient[i].max_request_index; j++, prequest++)
06226                if (prequest->valid &&
06227                    bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06228                   if (prequest->sampling_type & GET_ALL)
06229                      pclient[i].num_waiting_events++;
06230 
06231                   num_requests_client++;
06232                   request_id = prequest->id;
06233                }
06234 
06235             /* if that client has a request and is suspended, wake it up */
06236             if (num_requests_client && pclient[i].read_wait) {
06237 #ifdef DEBUG_MSG
06238                cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06239                       pheader->write_pointer);
06240 #endif
06241                sprintf(str, "B %s %d", pheader->name, request_id);
06242                ss_resume(pclient[i].port, str);
06243             }
06244 
06245             /* if that client has no request, shift its read pointer */
06246             if (num_requests_client == 0 && pclient[i].read_pointer == old_write_pointer)
06247                pclient[i].read_pointer = pheader->write_pointer;
06248          }
06249 
06250       /* shift read pointer of own client */
06251 /* 16.4.99 SR, outcommented to receive own messages
06252 
06253   if (pclient[my_client_index].read_pointer == old_write_pointer)
06254     pclient[my_client_index].read_pointer = pheader->write_pointer;
06255 */
06256 
06257       /* calculate global read pointer as "minimum" of client read pointers */
06258       min_wp = pheader->write_pointer;
06259 
06260       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06261          if (pc->pid) {
06262             if (pc->read_pointer < min_wp)
06263                min_wp = pc->read_pointer;
06264 
06265             if (pc->read_pointer > pheader->write_pointer &&
06266                 pc->read_pointer - pheader->size < min_wp)
06267                min_wp = pc->read_pointer - pheader->size;
06268          }
06269 
06270       if (min_wp < 0)
06271          min_wp += pheader->size;
06272 
06273 #ifdef DEBUG_MSG
06274       if (min_wp == pheader->read_pointer)
06275          cm_msg(MDEBUG, "bm_send_event -> wp=%d", pheader->write_pointer);
06276       else
06277          cm_msg(MDEBUG, "bm_send_event -> wp=%d, rp %d -> %d, size=%d",
06278                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06279 #endif
06280 
06281       pheader->read_pointer = min_wp;
06282 
06283       /* update statistics */
06284       pheader->num_in_events++;
06285 
06286       /* unlock the buffer */
06287       bm_unlock_buffer(buffer_handle);
06288    }
06289 #endif                          /* LOCAL_ROUTINES */
06290 
06291    return BM_SUCCESS;
06292 }
06293 
06294 /********************************************************************/
06295 /**
06296 Empty write cache.
06297 This function should be used if events in the write cache
06298 should be visible to the consumers immediately. It should be called at the
06299 end of each run, otherwise events could be kept in the write buffer and will
06300 flow to the data of the next run.
06301 @param buffer_handle Buffer handle obtained via bm_open_buffer()
06302 @param async_flag Synchronous/asynchronous flag.
06303 If FALSE, the function blocks if the buffer has not
06304 enough free space to receive the full cache. If TRUE, the function returns
06305 immediately with a value of BM_ASYNC_RETURN without writing the cache.
06306 @return BM_SUCCESS, BM_INVALID_HANDLE<br>
06307 BM_ASYNC_RETURN Routine called with async_flag == TRUE
06308 and buffer has not enough space to receive cache<br>
06309 BM_NO_MEMORY Event is too large for network buffer or event buffer.
06310 One has to increase MAX_EVENT_SIZE or EVENT_BUFFER_SIZE in midas.h
06311 and recompile.
06312 */
06313 INT bm_flush_cache(INT buffer_handle, INT async_flag)
06314 {
06315    if (rpc_is_remote())
06316       return rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, async_flag);
06317 
06318 #ifdef LOCAL_ROUTINES
06319    {
06320       BUFFER *pbuf;
06321       BUFFER_HEADER *pheader;
06322       BUFFER_CLIENT *pclient, *pc;
06323       EVENT_REQUEST *prequest;
06324       EVENT_HEADER *pevent, *pevent_test;
06325       INT i, j, min_wp, size, total_size, status;
06326       INT increment;
06327       INT my_client_index;
06328       INT old_write_pointer;
06329       INT old_read_pointer, new_read_pointer;
06330       char *pdata;
06331       BOOL blocking;
06332       INT n_blocking;
06333       INT request_id;
06334       char str[80];
06335 
06336       pbuf = &_buffer[buffer_handle - 1];
06337 
06338       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06339          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06340          return BM_INVALID_HANDLE;
06341       }
06342 
06343       if (!pbuf->attached) {
06344          cm_msg(MERROR, "bm_flush_cache", "invalid buffer handle %d", buffer_handle);
06345          return BM_INVALID_HANDLE;
06346       }
06347 
06348       if (pbuf->write_cache_size == 0)
06349          return BM_SUCCESS;
06350 
06351       /* check if anything needs to be flushed */
06352       if (pbuf->write_cache_rp == pbuf->write_cache_wp)
06353          return BM_SUCCESS;
06354 
06355       /* calculate some shorthands */
06356       pheader = _buffer[buffer_handle - 1].buffer_header;
06357       pdata = (char *) (pheader + 1);
06358       my_client_index = _buffer[buffer_handle - 1].client_index;
06359       pclient = pheader->client;
06360       pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06361 
06362       /* lock the buffer */
06363       bm_lock_buffer(buffer_handle);
06364 
06365 #ifdef DEBUG_MSG
06366       cm_msg(MDEBUG, "bm_flush_cache initial: rp=%d, wp=%d", pheader->read_pointer,
06367              pheader->write_pointer);
06368 #endif
06369 
06370       /* check if enough space in buffer left */
06371       do {
06372          size = pheader->read_pointer - pheader->write_pointer;
06373          if (size <= 0)
06374             size += pheader->size;
06375 
06376          if (size <= pbuf->write_cache_wp) {
06377             /* if not enough space, find out who's blocking */
06378             n_blocking = 0;
06379 
06380             for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06381                if (pc->pid) {
06382                   if (pc->read_pointer == pheader->read_pointer) {
06383                      /*
06384                         First assume that the client with the "minimum" read pointer
06385                         is not really blocking due to a GET_ALL request.
06386                       */
06387                      blocking = FALSE;
06388                      request_id = -1;
06389 
06390                      /* check if this request blocks. */
06391                      prequest = pc->event_request;
06392                      pevent_test = (EVENT_HEADER *) (pdata + pc->read_pointer);
06393 
06394                      for (j = 0; j < pc->max_request_index; j++, prequest++)
06395                         if (prequest->valid &&
06396                             bm_match_event(prequest->event_id, prequest->trigger_mask,
06397                                            pevent_test)) {
06398                            request_id = prequest->id;
06399                            if (prequest->sampling_type & GET_ALL) {
06400                               blocking = TRUE;
06401                               break;
06402                            }
06403                         }
06404 
06405                      if (!blocking) {
06406                         /*
06407                            The blocking guy has no GET_ALL request for this event
06408                            -> shift its read pointer.
06409                          */
06410 
06411                         old_read_pointer = pc->read_pointer;
06412 
06413                         increment = sizeof(EVENT_HEADER) +
06414                             ((EVENT_HEADER *) (pdata + pc->read_pointer))->data_size;
06415 
06416                         /* correct increment for DWORD boundary */
06417                         increment = ALIGN8(increment);
06418 
06419                         new_read_pointer = (pc->read_pointer + increment) % pheader->size;
06420 
06421                         if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06422                            new_read_pointer = 0;
06423 
06424 #ifdef DEBUG_MSG
06425                         cm_msg(MDEBUG, "bm_flush_cache: shift client %d rp=%d -> =%d", i,
06426                                pc->read_pointer, new_read_pointer);
06427 #endif
06428 
06429                         pc->read_pointer = new_read_pointer;
06430                      } else {
06431                         n_blocking++;
06432                      }
06433 
06434                      /* wake that client if it has a request */
06435                      if (pc->read_wait && request_id != -1) {
06436 #ifdef DEBUG_MSG
06437                         cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d",
06438                                pheader->read_pointer, pheader->write_pointer);
06439 #endif
06440                         sprintf(str, "B %s %d", pheader->name, request_id);
06441                         ss_resume(pc->port, str);
06442                      }
06443 
06444 
06445                   }             /* read_pointer blocks */
06446                }
06447             /* client loop */
06448             if (n_blocking > 0) {
06449                /* at least one client is blocking */
06450 
06451                bm_unlock_buffer(buffer_handle);
06452 
06453                /* return now in ASYNC mode */
06454                if (async_flag)
06455                   return BM_ASYNC_RETURN;
06456 
06457 #ifdef DEBUG_MSG
06458                cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf",
06459                       pheader->read_pointer,
06460                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06461 #endif
06462 
06463                /* has the read pointer moved in between ? */
06464                size = pheader->read_pointer - pheader->write_pointer;
06465                if (size <= 0)
06466                   size += pheader->size;
06467 
06468                /* suspend process */
06469                if (size <= pbuf->write_cache_wp) {
06470                   /* signal other clients wait mode */
06471                   pclient[my_client_index].write_wait = pbuf->write_cache_wp;
06472 
06473                   status = ss_suspend(1000, MSG_BM);
06474 
06475                   pclient[my_client_index].write_wait = 0;
06476 
06477                   /* return if TCP connection broken */
06478                   if (status == SS_ABORT)
06479                      return SS_ABORT;
06480                }
06481 #ifdef DEBUG_MSG
06482                cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf",
06483                       pheader->read_pointer,
06484                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06485 #endif
06486 
06487                bm_lock_buffer(buffer_handle);
06488 
06489                /* has the write pointer moved in between ? */
06490                size = pheader->read_pointer - pheader->write_pointer;
06491                if (size <= 0)
06492                   size += pheader->size;
06493             } else {
06494                /*
06495                   calculate new global read pointer as "minimum" of
06496                   client read pointers
06497                 */
06498                min_wp = pheader->write_pointer;
06499 
06500                for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06501                   if (pc->pid) {
06502                      if (pc->read_pointer < min_wp)
06503                         min_wp = pc->read_pointer;
06504 
06505                      if (pc->read_pointer > pheader->write_pointer &&
06506                          pc->read_pointer - pheader->size < min_wp)
06507                         min_wp = pc->read_pointer - pheader->size;
06508                   }
06509 
06510                if (min_wp < 0)
06511                   min_wp += pheader->size;
06512 
06513                pheader->read_pointer = min_wp;
06514             }
06515 
06516          }
06517          /* if (size <= total_size) */
06518       } while (size <= pbuf->write_cache_wp);
06519 
06520 
06521       /* we have space, so let's copy the event */
06522       old_write_pointer = pheader->write_pointer;
06523 
06524 #ifdef DEBUG_MSG
06525       cm_msg(MDEBUG, "bm_flush_cache: found space rp=%d, wp=%d", pheader->read_pointer,
06526              pheader->write_pointer);
06527 #endif
06528 
06529       while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
06530          /* loop over all events in cache */
06531 
06532          pevent = (EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
06533          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06534 
06535          /* correct size for DWORD boundary */
06536          total_size = ALIGN8(total_size);
06537 
06538          if (pheader->write_pointer + total_size <= pheader->size) {
06539             memcpy(pdata + pheader->write_pointer, pevent, total_size);
06540             pheader->write_pointer = (pheader->write_pointer + total_size) %
06541                 pheader->size;
06542             if (pheader->write_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06543                pheader->write_pointer = 0;
06544          } else {
06545             /* split event */
06546             size = pheader->size - pheader->write_pointer;
06547 
06548             memcpy(pdata + pheader->write_pointer, pevent, size);
06549             memcpy(pdata, (char *) pevent + size, total_size - size);
06550 
06551             pheader->write_pointer = total_size - size;
06552          }
06553 
06554          pbuf->write_cache_rp += total_size;
06555       }
06556 
06557       pbuf->write_cache_rp = pbuf->write_cache_wp = 0;
06558 
06559       /* check which clients are waiting */
06560       for (i = 0; i < pheader->max_client_index; i++)
06561          if (pclient[i].pid && pclient[i].read_wait) {
06562 #ifdef DEBUG_MSG
06563             cm_msg(MDEBUG, "Send wake: rp=%d, wp=%d", pheader->read_pointer,
06564                    pheader->write_pointer);
06565 #endif
06566             sprintf(str, "B %s %d", pheader->name, -1);
06567             ss_resume(pclient[i].port, str);
06568          }
06569 
06570       /* shift read pointer of own client */
06571 /* 16.4.99 SR, outcommented to receive own messages
06572 
06573   if (pclient[my_client_index].read_pointer == old_write_pointer)
06574     pclient[my_client_index].read_pointer = pheader->write_pointer;
06575 */
06576 
06577       /* calculate global read pointer as "minimum" of client read pointers */
06578       min_wp = pheader->write_pointer;
06579 
06580       for (i = 0, pc = pclient; i < pheader->max_client_index; i++, pc++)
06581          if (pc->pid) {
06582 #ifdef DEBUG_MSG
06583             cm_msg(MDEBUG, "bm_flush_cache: client %d rp=%d", i, pc->read_pointer);
06584 #endif
06585             if (pc->read_pointer < min_wp)
06586                min_wp = pc->read_pointer;
06587 
06588             if (pc->read_pointer > pheader->write_pointer &&
06589                 pc->read_pointer - pheader->size < min_wp)
06590                min_wp = pc->read_pointer - pheader->size;
06591          }
06592 
06593       if (min_wp < 0)
06594          min_wp += pheader->size;
06595 
06596 #ifdef DEBUG_MSG
06597       if (min_wp == pheader->read_pointer)
06598          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d", pheader->write_pointer);
06599       else
06600          cm_msg(MDEBUG, "bm_flush_cache -> wp=%d, rp %d -> %d, size=%d",
06601                 pheader->write_pointer, pheader->read_pointer, min_wp, size);
06602 #endif
06603 
06604       pheader->read_pointer = min_wp;
06605 
06606       /* update statistics */
06607       pheader->num_in_events++;
06608 
06609       /* unlock the buffer */
06610       bm_unlock_buffer(buffer_handle);
06611    }
06612 #endif                          /* LOCAL_ROUTINES */
06613 
06614    return BM_SUCCESS;
06615 }
06616 
06617 /********************************************************************/
06618 /**
06619 Receives events directly.
06620 This function is an alternative way to receive events without
06621 a main loop.
06622 
06623 It can be used in analysis systems which actively receive events,
06624 rather than using callbacks. A analysis package could for example contain its own
06625 command line interface. A command
06626 like "receive 1000 events" could make it necessary to call bm_receive_event()
06627 1000 times in a row to receive these events and then return back to the
06628 command line prompt.
06629 The according bm_request_event() call contains NULL as the
06630 callback routine to indicate that bm_receive_event() is called to receive
06631 events.
06632 \code
06633 #include <stdio.h>
06634 #include "midas.h"
06635 void process_event(EVENT_HEADER *pheader)
06636 {
06637  printf("Received event #%d\r",
06638  pheader->serial_number);
06639 }
06640 main()
06641 {
06642   INT status, request_id;
06643   HNDLE hbuf;
06644   char event_buffer[1000];
06645   status = cm_connect_experiment("", "Sample",
06646   "Simple Analyzer", NULL);
06647   if (status != CM_SUCCESS)
06648    return 1;
06649   bm_open_buffer(EVENT_BUFFER_NAME, EVENT_BUFFER_SIZE, &hbuf);
06650   bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
06651 
06652   do
06653   {
06654    size = sizeof(event_buffer);
06655    status = bm_receive_event(hbuf, event_buffer, &size, ASYNC);
06656   if (status == CM_SUCCESS)
06657    process_event((EVENT_HEADER *) event_buffer);
06658    <...do something else...>
06659    status = cm_yield(0);
06660   } while (status != RPC_SHUTDOWN &&
06661   status != SS_ABORT);
06662   cm_disconnect_experiment();
06663   return 0;
06664 }
06665 \endcode
06666 @param buffer_handle buffer handle
06667 @param destination destination address where event is written to
06668 @param buf_size size of destination buffer on input, size of event plus
06669 header on return.
06670 @param async_flag Synchronous/asynchronous flag. If FALSE, the function
06671 blocks if no event is available. If TRUE, the function returns immediately
06672 with a value of BM_ASYNC_RETURN without receiving any event.
06673 @return BM_SUCCESS, BM_INVALID_HANDLE <br>
06674 BM_TRUNCATED   The event is larger than the destination buffer and was
06675                therefore truncated <br>
06676 BM_ASYNC_RETURN No event available
06677 */
06678 INT bm_receive_event(INT buffer_handle, void *destination, INT * buf_size, INT async_flag)
06679 {
06680    if (rpc_is_remote()) {
06681       if (*buf_size > NET_BUFFER_SIZE) {
06682          cm_msg(MERROR, "bm_receive_event",
06683                 "max. event size larger than NET_BUFFER_SIZE");
06684          return RPC_NET_ERROR;
06685       }
06686 
06687       return rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle,
06688                       destination, buf_size, async_flag);
06689    }
06690 #ifdef LOCAL_ROUTINES
06691    {
06692       BUFFER *pbuf;
06693       BUFFER_HEADER *pheader;
06694       BUFFER_CLIENT *pclient, *pc, *pctmp;
06695       EVENT_REQUEST *prequest;
06696       EVENT_HEADER *pevent;
06697       char *pdata;
06698       INT convert_flags;
06699       INT i, min_wp, size, max_size, total_size, status = 0;
06700       INT my_client_index;
06701       BOOL found;
06702       INT old_read_pointer, new_read_pointer;
06703 
06704       pbuf = &_buffer[buffer_handle - 1];
06705 
06706       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
06707          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06708          return BM_INVALID_HANDLE;
06709       }
06710 
06711       if (!pbuf->attached) {
06712          cm_msg(MERROR, "bm_receive_event", "invalid buffer handle %d", buffer_handle);
06713          return BM_INVALID_HANDLE;
06714       }
06715 
06716       max_size = *buf_size;
06717       *buf_size = 0;
06718 
06719       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
06720          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
06721       else
06722          convert_flags = 0;
06723 
06724     CACHE_READ:
06725 
06726       /* look if there is anything in the cache */
06727       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
06728          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
06729          size = pevent->data_size + sizeof(EVENT_HEADER);
06730 
06731          if (size > max_size) {
06732             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, max_size);
06733             cm_msg(MERROR, "bm_receive_event", "event size larger than buffer size");
06734             *buf_size = max_size;
06735             status = BM_TRUNCATED;
06736          } else {
06737             memcpy(destination, pbuf->read_cache + pbuf->read_cache_rp, size);
06738             *buf_size = size;
06739             status = BM_SUCCESS;
06740          }
06741 
06742          /* now convert event header */
06743          if (convert_flags) {
06744             pevent = (EVENT_HEADER *) destination;
06745             rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING, convert_flags);
06746             rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06747                                convert_flags);
06748             rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06749                                convert_flags);
06750             rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06751                                convert_flags);
06752             rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06753                                convert_flags);
06754          }
06755 
06756          /* correct size for DWORD boundary */
06757          size = ALIGN8(size);
06758 
06759          pbuf->read_cache_rp += size;
06760 
06761          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
06762             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
06763 
06764          return status;
06765       }
06766 
06767       /* calculate some shorthands */
06768       pheader = pbuf->buffer_header;
06769       pdata = (char *) (pheader + 1);
06770       my_client_index = pbuf->client_index;
06771       pclient = pheader->client;
06772       pc = pheader->client + my_client_index;
06773 
06774       /* first do a quick check without locking the buffer */
06775       if (async_flag == ASYNC && pheader->write_pointer == pc->read_pointer)
06776          return BM_ASYNC_RETURN;
06777 
06778       /* lock the buffer */
06779       bm_lock_buffer(buffer_handle);
06780 
06781     LOOP:
06782 
06783       while (pheader->write_pointer == pc->read_pointer) {
06784          bm_unlock_buffer(buffer_handle);
06785 
06786          /* return now in ASYNC mode */
06787          if (async_flag == ASYNC)
06788             return BM_ASYNC_RETURN;
06789 
06790          pc->read_wait = TRUE;
06791 
06792          /* check again pointers (may have moved in between) */
06793          if (pheader->write_pointer == pc->read_pointer) {
06794 #ifdef DEBUG_MSG
06795             cm_msg(MDEBUG, "Receive sleep: grp=%d, rp=%d wp=%d",
06796                    pheader->read_pointer, pc->read_pointer, pheader->write_pointer);
06797 #endif
06798 
06799             status = ss_suspend(1000, MSG_BM);
06800 
06801 #ifdef DEBUG_MSG
06802             cm_msg(MDEBUG, "Receive woke up: rp=%d, wp=%d",
06803                    pheader->read_pointer, pheader->write_pointer);
06804 #endif
06805 
06806             /* return if TCP connection broken */
06807             if (status == SS_ABORT)
06808                return SS_ABORT;
06809          }
06810 
06811          pc->read_wait = FALSE;
06812 
06813          bm_lock_buffer(buffer_handle);
06814       }
06815 
06816       /* check if event at current read pointer matches a request */
06817       found = FALSE;
06818 
06819       do {
06820          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
06821 
06822          total_size = pevent->data_size + sizeof(EVENT_HEADER);
06823          total_size = ALIGN8(total_size);
06824 
06825          prequest = pc->event_request;
06826 
06827          for (i = 0; i < pc->max_request_index; i++, prequest++)
06828             if (prequest->valid &&
06829                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
06830                /* we found one, so copy it */
06831 
06832                if (pbuf->read_cache_size > 0 &&
06833                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06834                   if (pbuf->read_cache_size - pbuf->read_cache_wp < total_size)
06835                      goto CACHE_FULL;
06836 
06837                   if (pc->read_pointer + total_size <= pheader->size) {
06838                      /* copy event to cache */
06839                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
06840                   } else {
06841                      /* event is splitted */
06842                      size = pheader->size - pc->read_pointer;
06843                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
06844                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
06845                             pdata, total_size - size);
06846                   }
06847                } else {
06848                   if (pc->read_pointer + total_size <= pheader->size) {
06849                      /* event is not splitted */
06850                      if (total_size > max_size)
06851                         memcpy(destination, pevent, max_size);
06852                      else
06853                         memcpy(destination, pevent, total_size);
06854                   } else {
06855                      /* event is splitted */
06856                      size = pheader->size - pc->read_pointer;
06857 
06858                      if (size > max_size)
06859                         memcpy(destination, pevent, max_size);
06860                      else
06861                         memcpy(destination, pevent, size);
06862 
06863                      if (total_size > max_size) {
06864                         if (size <= max_size)
06865                            memcpy((char *) destination + size, pdata, max_size - size);
06866                      } else
06867                         memcpy((char *) destination + size, pdata, total_size - size);
06868                   }
06869 
06870                   if (total_size < max_size)
06871                      *buf_size = total_size;
06872                   else
06873                      *buf_size = max_size;
06874 
06875                   /* now convert event header */
06876                   if (convert_flags) {
06877                      pevent = (EVENT_HEADER *) destination;
06878                      rpc_convert_single(&pevent->event_id, TID_SHORT, RPC_OUTGOING,
06879                                         convert_flags);
06880                      rpc_convert_single(&pevent->trigger_mask, TID_SHORT, RPC_OUTGOING,
06881                                         convert_flags);
06882                      rpc_convert_single(&pevent->serial_number, TID_DWORD, RPC_OUTGOING,
06883                                         convert_flags);
06884                      rpc_convert_single(&pevent->time_stamp, TID_DWORD, RPC_OUTGOING,
06885                                         convert_flags);
06886                      rpc_convert_single(&pevent->data_size, TID_DWORD, RPC_OUTGOING,
06887                                         convert_flags);
06888                   }
06889                }
06890 
06891                if (pbuf->read_cache_size > 0 &&
06892                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
06893                   pbuf->read_cache_wp += total_size;
06894                } else {
06895                   if (total_size > max_size) {
06896                      cm_msg(MERROR, "bm_receive_event",
06897                             "event size larger than buffer size");
06898                      status = BM_TRUNCATED;
06899                   } else
06900                      status = BM_SUCCESS;
06901                }
06902 
06903                /* update statistics */
06904                found = TRUE;
06905                pheader->num_out_events++;
06906                break;
06907             }
06908 
06909          old_read_pointer = pc->read_pointer;
06910 
06911          /* shift read pointer */
06912          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
06913 
06914          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
06915             new_read_pointer = 0;
06916 
06917 #ifdef DEBUG_MSG
06918          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
06919                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
06920                 total_size);
06921 #endif
06922 
06923          pc->read_pointer = new_read_pointer;
06924 
06925          /*
06926             Repeat until a requested event is found or no more events
06927             are available.
06928           */
06929 
06930          if (pbuf->read_cache_size == 0 && found)
06931             break;
06932 
06933          /* break if event has bypassed read cache */
06934          if (pbuf->read_cache_size > 0 &&
06935              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
06936             break;
06937 
06938       } while (pheader->write_pointer != pc->read_pointer);
06939 
06940     CACHE_FULL:
06941 
06942       /* calculate global read pointer as "minimum" of client read pointers */
06943       min_wp = pheader->write_pointer;
06944 
06945       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06946          if (pctmp->pid) {
06947             if (pctmp->read_pointer < min_wp)
06948                min_wp = pctmp->read_pointer;
06949 
06950             if (pctmp->read_pointer > pheader->write_pointer &&
06951                 pctmp->read_pointer - pheader->size < min_wp)
06952                min_wp = pctmp->read_pointer - pheader->size;
06953          }
06954 
06955       if (min_wp < 0)
06956          min_wp += pheader->size;
06957 
06958       pheader->read_pointer = min_wp;
06959 
06960       /*
06961          If read pointer has been changed, it may have freed up some space
06962          for waiting producers. So check if free space is now more than 50%
06963          of the buffer size and wake waiting producers.
06964        */
06965       size = pc->read_pointer - pheader->write_pointer;
06966       if (size <= 0)
06967          size += pheader->size;
06968 
06969       if (size >= pheader->size * 0.5)
06970          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
06971             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
06972                                                              (pctmp->pid == ss_getpid()
06973                                                               && pctmp->tid !=
06974                                                               ss_gettid()))) {
06975 #ifdef DEBUG_MSG
06976                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
06977                       pheader->read_pointer,
06978                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
06979 #endif
06980                ss_resume(pctmp->port, "B  ");
06981             }
06982 
06983       /* if no matching event found, start again */
06984       if (!found)
06985          goto LOOP;
06986 
06987       bm_unlock_buffer(buffer_handle);
06988 
06989       if (pbuf->read_cache_size > 0 &&
06990           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
06991          goto CACHE_READ;
06992 
06993       return status;
06994    }
06995 #else                           /* LOCAL_ROUTINES */
06996 
06997    return SS_SUCCESS;
06998 #endif
06999 }
07000 
07001 /********************************************************************/
07002 /**
07003 Skip all events in current buffer.
07004 
07005 Useful for single event displays to see the newest events
07006 @param buffer_handle      Handle of the buffer. Must be obtained
07007                           via bm_open_buffer.
07008 @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR 
07009 */
07010 INT bm_skip_event(INT buffer_handle)
07011 {
07012    if (rpc_is_remote())
07013       return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
07014 
07015 #ifdef LOCAL_ROUTINES
07016    {
07017       BUFFER *pbuf;
07018       BUFFER_HEADER *pheader;
07019       BUFFER_CLIENT *pclient;
07020 
07021       if (buffer_handle > _buffer_entries || buffer_handle <= 0) {
07022          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07023          return BM_INVALID_HANDLE;
07024       }
07025 
07026       pbuf = &_buffer[buffer_handle - 1];
07027       pheader = pbuf->buffer_header;
07028 
07029       if (!pbuf->attached) {
07030          cm_msg(MERROR, "bm_skip_event", "invalid buffer handle %d", buffer_handle);
07031          return BM_INVALID_HANDLE;
07032       }
07033 
07034       /* clear cache */
07035       if (pbuf->read_cache_wp > pbuf->read_cache_rp)
07036          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07037 
07038       bm_lock_buffer(buffer_handle);
07039 
07040       /* forward read pointer to global write pointer */
07041       pclient = pheader->client + pbuf->client_index;
07042       pclient->read_pointer = pheader->write_pointer;
07043 
07044       bm_unlock_buffer(buffer_handle);
07045    }
07046 #endif
07047 
07048    return BM_SUCCESS;
07049 }
07050 
07051 /********************************************************************/
07052 /**
07053 Check a buffer if an event is available and call the dispatch function if found.
07054 @param buffer_name       Name of buffer
07055 @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, 
07056                     RPC_NET_ERROR
07057 */
07058 INT bm_push_event(char *buffer_name)
07059 {
07060 #ifdef LOCAL_ROUTINES
07061    {
07062       BUFFER *pbuf;
07063       BUFFER_HEADER *pheader;
07064       BUFFER_CLIENT *pclient, *pc, *pctmp;
07065       EVENT_REQUEST *prequest;
07066       EVENT_HEADER *pevent;
07067       char *pdata;
07068       INT i, min_wp, size, total_size, buffer_handle;
07069       INT my_client_index;
07070       BOOL found;
07071       INT old_read_pointer, new_read_pointer;
07072 
07073       for (i = 0; i < _buffer_entries; i++)
07074          if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07075             break;
07076       if (i == _buffer_entries)
07077          return BM_INVALID_HANDLE;
07078 
07079       buffer_handle = i + 1;
07080       pbuf = &_buffer[buffer_handle - 1];
07081 
07082       if (!pbuf->attached)
07083          return BM_INVALID_HANDLE;
07084 
07085       /* return immediately if no callback routine is defined */
07086       if (!pbuf->callback)
07087          return BM_SUCCESS;
07088 
07089       if (_event_buffer_size == 0) {
07090          _event_buffer = (EVENT_HEADER *) M_MALLOC(1000);
07091          if (_event_buffer == NULL) {
07092             cm_msg(MERROR, "bm_push_event", "not enough memory to allocate cache buffer");
07093             return BM_NO_MEMORY;
07094          }
07095          _event_buffer_size = 1000;
07096       }
07097 
07098     CACHE_READ:
07099 
07100       /* look if there is anything in the cache */
07101       if (pbuf->read_cache_wp > pbuf->read_cache_rp) {
07102          pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
07103          size = pevent->data_size + sizeof(EVENT_HEADER);
07104 
07105          /* correct size for DWORD boundary */
07106          size = ALIGN8(size);
07107 
07108          /* increment read pointer */
07109          pbuf->read_cache_rp += size;
07110          if (pbuf->read_cache_rp == pbuf->read_cache_wp)
07111             pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07112 
07113          /* call dispatcher */
07114          for (i = 0; i < _request_list_entries; i++)
07115             if (_request_list[i].buffer_handle == buffer_handle &&
07116                 bm_match_event(_request_list[i].event_id,
07117                                _request_list[i].trigger_mask, pevent)) {
07118                /* if event is fragmented, call defragmenter */
07119                if ((pevent->event_id & 0xF000) == EVENTID_FRAG1 ||
07120                    (pevent->event_id & 0xF000) == EVENTID_FRAG)
07121                   bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1),
07122                                       _request_list[i].dispatcher);
07123                else
07124                   _request_list[i].dispatcher(buffer_handle, i, pevent,
07125                                               (void *) (pevent + 1));
07126             }
07127 
07128          return BM_MORE_EVENTS;
07129       }
07130 
07131       /* calculate some shorthands */
07132       pheader = pbuf->buffer_header;
07133       pdata = (char *) (pheader + 1);
07134       my_client_index = pbuf->client_index;
07135       pclient = pheader->client;
07136       pc = pheader->client + my_client_index;
07137 
07138       /* first do a quick check without locking the buffer */
07139       if (pheader->write_pointer == pc->read_pointer)
07140          return BM_SUCCESS;
07141 
07142       /* lock the buffer */
07143       bm_lock_buffer(buffer_handle);
07144 
07145     LOOP:
07146 
07147       if (pheader->write_pointer == pc->read_pointer) {
07148          bm_unlock_buffer(buffer_handle);
07149 
07150          /* return if no event available */
07151          return BM_SUCCESS;
07152       }
07153 
07154       /* check if event at current read pointer matches a request */
07155       found = FALSE;
07156 
07157       do {
07158          pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
07159 
07160          total_size = pevent->data_size + sizeof(EVENT_HEADER);
07161          total_size = ALIGN8(total_size);
07162 
07163          prequest = pc->event_request;
07164 
07165          for (i = 0; i < pc->max_request_index; i++, prequest++)
07166             if (prequest->valid &&
07167                 bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
07168                /* we found one, so copy it */
07169 
07170                if (pbuf->read_cache_size > 0 &&
07171                    !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)) {
07172                   /* copy dispatch function and event to cache */
07173 
07174                   if (pbuf->read_cache_size - pbuf->read_cache_wp <
07175                       total_size + (INT) sizeof(void *) + (INT) sizeof(INT))
07176                      goto CACHE_FULL;
07177 
07178                   if (pc->read_pointer + total_size <= pheader->size) {
07179                      /* copy event to cache */
07180                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, total_size);
07181                   } else {
07182                      /* event is splitted */
07183 
07184                      size = pheader->size - pc->read_pointer;
07185                      memcpy(pbuf->read_cache + pbuf->read_cache_wp, pevent, size);
07186                      memcpy((char *) pbuf->read_cache + pbuf->read_cache_wp + size,
07187                             pdata, total_size - size);
07188                   }
07189 
07190                   pbuf->read_cache_wp += total_size;
07191                } else {
07192                   /* copy event to copy buffer, save dispatcher */
07193 
07194                   if (total_size > _event_buffer_size) {
07195                      _event_buffer = (EVENT_HEADER *) realloc(_event_buffer, total_size);
07196                      _event_buffer_size = total_size;
07197                   }
07198 
07199                   if (pc->read_pointer + total_size <= pheader->size) {
07200                      memcpy(_event_buffer, pevent, total_size);
07201                   } else {
07202                      /* event is splitted */
07203                      size = pheader->size - pc->read_pointer;
07204 
07205                      memcpy(_event_buffer, pevent, size);
07206                      memcpy((char *) _event_buffer + size, pdata, total_size - size);
07207                   }
07208                }
07209 
07210                /* update statistics */
07211                found = TRUE;
07212                pheader->num_out_events++;
07213                break;
07214             }
07215 
07216          old_read_pointer = pc->read_pointer;
07217 
07218          /* shift read pointer */
07219          new_read_pointer = (pc->read_pointer + total_size) % pheader->size;
07220 
07221          if (new_read_pointer > pheader->size - (int) sizeof(EVENT_HEADER))
07222             new_read_pointer = 0;
07223 
07224 #ifdef DEBUG_MSG
07225          cm_msg(MDEBUG, "bm_receive_event -> wp=%d, rp %d -> %d (found=%d,size=%d)",
07226                 pheader->write_pointer, pc->read_pointer, new_read_pointer, found,
07227                 total_size);
07228 #endif
07229 
07230          pc->read_pointer = new_read_pointer;
07231 
07232          /*
07233             Repeat until a requested event is found or no more events
07234             are available or large event received.
07235           */
07236 
07237          if (pbuf->read_cache_size == 0 && found)
07238             break;
07239 
07240          /* break if event has bypassed read cache */
07241          if (pbuf->read_cache_size > 0 &&
07242              total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0)
07243             break;
07244 
07245       } while (pheader->write_pointer != pc->read_pointer);
07246 
07247     CACHE_FULL:
07248 
07249       /* calculate global read pointer as "minimum" of client read pointers */
07250       min_wp = pheader->write_pointer;
07251 
07252       for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07253          if (pctmp->pid) {
07254             if (pctmp->read_pointer < min_wp)
07255                min_wp = pctmp->read_pointer;
07256 
07257             if (pctmp->read_pointer > pheader->write_pointer &&
07258                 pctmp->read_pointer - pheader->size < min_wp)
07259                min_wp = pctmp->read_pointer - pheader->size;
07260          }
07261 
07262       if (min_wp < 0)
07263          min_wp += pheader->size;
07264 
07265       pheader->read_pointer = min_wp;
07266 
07267       /*
07268          If read pointer has been changed, it may have freed up some space
07269          for waiting producers. So check if free space is now more than 50%
07270          of the buffer size and wake waiting producers.
07271        */
07272       size = pc->read_pointer - pheader->write_pointer;
07273       if (size <= 0)
07274          size += pheader->size;
07275 
07276       if (size >= pheader->size * 0.5)
07277          for (i = 0, pctmp = pclient; i < pheader->max_client_index; i++, pctmp++)
07278             if (pctmp->pid && (pctmp->write_wait < size) && (pctmp->pid != ss_getpid() ||       /* check if not own thread */
07279                                                              (pctmp->pid == ss_getpid()
07280                                                               && pctmp->tid !=
07281                                                               ss_gettid()))) {
07282 #ifdef DEBUG_MSG
07283                cm_msg(MDEBUG, "Receive wake: rp=%d, wp=%d, level=%1.1lf",
07284                       pheader->read_pointer,
07285                       pheader->write_pointer, 100 - 100.0 * size / pheader->size);
07286 #endif
07287                ss_resume(pctmp->port, "B  ");
07288             }
07289 
07290       /* if no matching event found, start again */
07291       if (!found)
07292          goto LOOP;
07293 
07294       bm_unlock_buffer(buffer_handle);
07295 
07296       if (pbuf->read_cache_size > 0 &&
07297           !(total_size > pbuf->read_cache_size && pbuf->read_cache_wp == 0))
07298          goto CACHE_READ;
07299 
07300       /* call dispatcher */
07301       for (i = 0; i < _request_list_entries; i++)
07302          if (_request_list[i].buffer_handle == buffer_handle &&
07303              bm_match_event(_request_list[i].event_id,
07304                             _request_list[i].trigger_mask, _event_buffer)) {
07305             if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07306                 (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07307                bm_defragment_event(buffer_handle, i, _event_buffer,
07308                                    (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07309                                    _request_list[i].dispatcher);
07310             else
07311                _request_list[i].dispatcher(buffer_handle, i, _event_buffer,
07312                                            (void *) (((EVENT_HEADER *) _event_buffer) +
07313                                                      1));
07314          }
07315 
07316       return BM_MORE_EVENTS;
07317    }
07318 #else                           /* LOCAL_ROUTINES */
07319 
07320    return BM_SUCCESS;
07321 #endif
07322 }
07323 
07324 /********************************************************************/
07325 /**
07326 Check if any requested event is waiting in a buffer
07327 @return TRUE             More events are waiting<br>
07328         FALSE            No more events are waiting
07329 */
07330 INT bm_check_buffers()
07331 {
07332 #ifdef LOCAL_ROUTINES
07333    {
07334       INT index, status;
07335       INT server_type, server_conn, tid;
07336       BOOL bMore;
07337       DWORD start_time;
07338 
07339       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07340       server_conn = rpc_get_server_acception();
07341       tid = ss_gettid();
07342 
07343       /* if running as a server, buffer checking is done by client
07344          via ASYNC bm_receive_event */
07345       if (server_type == ST_SUBPROCESS || server_type == ST_MTHREAD)
07346          return FALSE;
07347 
07348       bMore = FALSE;
07349       start_time = ss_millitime();
07350 
07351       /* go through all buffers */
07352       for (index = 0; index < _buffer_entries; index++) {
07353          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07354             continue;
07355 
07356          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07357             continue;
07358 
07359          if (!_buffer[index].attached)
07360             continue;
07361 
07362          do {
07363             status = bm_push_event(_buffer[index].buffer_header->name);
07364 
07365             if (status != BM_MORE_EVENTS)
07366                break;
07367 
07368             /* stop after one second */
07369             if (ss_millitime() - start_time > 1000) {
07370                bMore = TRUE;
07371                break;
07372             }
07373 
07374          } while (TRUE);
07375       }
07376 
07377       return bMore;
07378 
07379    }
07380 #else                           /* LOCAL_ROUTINES */
07381 
07382    return FALSE;
07383 
07384 #endif
07385 }
07386 
07387 /**dox***************************************************************/
07388 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07389 
07390 /********************************************************************/
07391 INT bm_mark_read_waiting(BOOL flag)
07392 /********************************************************************\
07393 
07394   Routine: bm_mark_read_waiting
07395 
07396   Purpose: Mark all open buffers ready for receiving events.
07397            Called internally by ss_suspend
07398 
07399 
07400   Input:
07401     BOOL flag               TRUE for waiting, FALSE for not waiting
07402 
07403   Output:
07404     none
07405 
07406   Function value:
07407     BM_SUCCESS              Successful completion
07408 
07409 \********************************************************************/
07410 {
07411    if (rpc_is_remote())
07412       return rpc_call(RPC_BM_MARK_READ_WAITING, flag);
07413 
07414 #ifdef LOCAL_ROUTINES
07415    {
07416       INT i;
07417       BUFFER_HEADER *pheader;
07418       BUFFER_CLIENT *pclient;
07419 
07420       /* Mark all buffer for read waiting */
07421       for (i = 0; i < _buffer_entries; i++) {
07422          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE &&
07423              _buffer[i].index != rpc_get_server_acception())
07424             continue;
07425 
07426          if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_SINGLE &&
07427              _buffer[i].index != ss_gettid())
07428             continue;
07429 
07430          if (!_buffer[i].attached)
07431             continue;
07432 
07433          pheader = _buffer[i].buffer_header;
07434          pclient = pheader->client + _buffer[i].client_index;
07435          pclient->read_wait = flag;
07436       }
07437    }
07438 #endif                          /* LOCAL_ROUTINES */
07439 
07440    return BM_SUCCESS;
07441 }
07442 
07443 /********************************************************************/
07444 INT bm_notify_client(char *buffer_name, int socket)
07445 /********************************************************************\
07446 
07447   Routine: bm_notify_client
07448 
07449   Purpose: Called by cm_dispatch_ipc. Send an event notification to
07450            the connected client
07451 
07452   Input:
07453     char  *buffer_name      Name of buffer
07454     int   socket            Network socket to client
07455 
07456   Output:
07457     none
07458 
07459   Function value:
07460     BM_SUCCESS              Successful completion
07461 
07462 \********************************************************************/
07463 {
07464    char buffer[32];
07465    NET_COMMAND *nc;
07466    INT i, convert_flags;
07467    static DWORD last_time = 0;
07468 
07469    for (i = 0; i < _buffer_entries; i++)
07470       if (strcmp(buffer_name, _buffer[i].buffer_header->name) == 0)
07471          break;
07472    if (i == _buffer_entries)
07473       return BM_INVALID_HANDLE;
07474 
07475    /* don't send notification if client has no callback defined
07476       to receive events -> client calls bm_receive_event manually */
07477    if (!_buffer[i].callback)
07478       return DB_SUCCESS;
07479 
07480    convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
07481 
07482    /* only send notification once each 500ms */
07483    if (ss_millitime() - last_time < 500 && !(convert_flags & CF_ASCII))
07484       return DB_SUCCESS;
07485 
07486    last_time = ss_millitime();
07487 
07488    if (convert_flags & CF_ASCII) {
07489       sprintf(buffer, "MSG_BM&%s", buffer_name);
07490       send_tcp(socket, buffer, strlen(buffer) + 1, 0);
07491    } else {
07492       nc = (NET_COMMAND *) buffer;
07493 
07494       nc->header.routine_id = MSG_BM;
07495       nc->header.param_size = 0;
07496 
07497       if (convert_flags) {
07498          rpc_convert_single(&nc->header.routine_id, TID_DWORD,
07499                             RPC_OUTGOING, convert_flags);
07500          rpc_convert_single(&nc->header.param_size, TID_DWORD,
07501                             RPC_OUTGOING, convert_flags);
07502       }
07503 
07504       /* send the update notification to the client */
07505       send_tcp(socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
07506    }
07507 
07508    return BM_SUCCESS;
07509 }
07510 
07511 /********************************************************************/
07512 INT bm_poll_event(INT flag)
07513 /********************************************************************\
07514 
07515   Routine: bm_poll_event
07516 
07517   Purpose: Poll an event from a remote server. Gets called by
07518            rpc_client_dispatch
07519 
07520   Input:
07521     INT flag         TRUE if called from cm_yield
07522 
07523   Output:
07524     none
07525 
07526   Function value:
07527     TRUE             More events are waiting
07528     FALSE            No more events are waiting
07529     SS_ABORT         Network connection broken
07530 
07531 \********************************************************************/
07532 {
07533    INT status, size, i, request_id;
07534    DWORD start_time;
07535    BOOL bMore;
07536    static BOOL bMoreLast = FALSE;
07537 
07538    if (_event_buffer_size == 0) {
07539       _event_buffer = (EVENT_HEADER *) M_MALLOC(MAX_EVENT_SIZE + sizeof(EVENT_HEADER));
07540       if (!_event_buffer) {
07541          cm_msg(MERROR, "bm_poll_event", "not enough memory to allocate event buffer");
07542          return SS_ABORT;
07543       }
07544       _event_buffer_size = MAX_EVENT_SIZE + sizeof(EVENT_HEADER);
07545    }
07546 
07547    start_time = ss_millitime();
07548 
07549    /* if we got event notification, turn off read_wait */
07550    if (!flag)
07551       bm_mark_read_waiting(FALSE);
07552 
07553    /* if called from yield, return if no more events */
07554    if (flag) {
07555       if (!bMoreLast)
07556          return FALSE;
07557    }
07558 
07559    bMore = FALSE;
07560 
07561    /* loop over all requests */
07562    for (request_id = 0; request_id < _request_list_entries; request_id++) {
07563       /* continue if no dispatcher set (manual bm_receive_event) */
07564       if (_request_list[request_id].dispatcher == NULL)
07565          continue;
07566 
07567       do {
07568          /* receive event */
07569          size = _event_buffer_size;
07570          status = bm_receive_event(_request_list[request_id].buffer_handle,
07571                                    _event_buffer, &size, ASYNC);
07572 
07573          /* call user function if successful */
07574          if (status == BM_SUCCESS)
07575             /* search proper request for this event */
07576             for (i = 0; i < _request_list_entries; i++)
07577                if ((_request_list[i].buffer_handle ==
07578                     _request_list[request_id].buffer_handle) &&
07579                    bm_match_event(_request_list[i].event_id,
07580                                   _request_list[i].trigger_mask, _event_buffer)) {
07581                   if ((_event_buffer->event_id & 0xF000) == EVENTID_FRAG1 ||
07582                       (_event_buffer->event_id & 0xF000) == EVENTID_FRAG)
07583                      bm_defragment_event(_request_list[i].buffer_handle, i, _event_buffer,
07584                                          (void *) (((EVENT_HEADER *) _event_buffer) + 1),
07585                                          _request_list[i].dispatcher);
07586                   else
07587                      _request_list[i].dispatcher(_request_list[i].buffer_handle, i,
07588                                                  _event_buffer,
07589                                                  (void
07590                                                   *) (((EVENT_HEADER *) _event_buffer) +
07591                                                       1));
07592                }
07593 
07594          /* break if no more events */
07595          if (status == BM_ASYNC_RETURN)
07596             break;
07597 
07598          /* break if server died */
07599          if (status == RPC_NET_ERROR)
07600             return SS_ABORT;
07601 
07602          /* stop after one second */
07603          if (ss_millitime() - start_time > 1000) {
07604             bMore = TRUE;
07605             break;
07606          }
07607 
07608       } while (TRUE);
07609    }
07610 
07611    if (!bMore)
07612       bm_mark_read_waiting(TRUE);
07613 
07614    bMoreLast = bMore;
07615 
07616    return bMore;
07617 }
07618 
07619 /**dox***************************************************************/
07620 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07621 
07622 /********************************************************************/
07623 /** 
07624 Clears event buffer and cache.
07625 If an event buffer is large and a consumer is slow in analyzing
07626 events, events are usually received some time after they are produced.
07627 This effect is even more experienced if a read cache is used
07628 (via bm_set_cache_size()).
07629 When changes to the hardware are made in the experience, the consumer will then
07630 still analyze old events before any new event which reflects the hardware change.
07631 Users can be fooled by looking at histograms which reflect the hardware change
07632 many seconds after they have been made.
07633 
07634 To overcome this potential problem, the analyzer can call
07635 bm_empty_buffers() just after the hardware change has been made which
07636 skips all old events contained in event buffers and read caches.
07637 Technically this is done by forwarding the read pointer of the client.
07638 No events are really deleted, they are still visible to other clients like
07639 the logger.
07640 
07641 Note that the front-end also contains write buffers which can delay the
07642 delivery of events.
07643 The standard front-end framework mfe.c reduces this effect by flushing
07644 all buffers once every second.
07645 @return BM_SUCCESS
07646 */
07647 INT bm_empty_buffers()
07648 {
07649    if (rpc_is_remote())
07650       return rpc_call(RPC_BM_EMPTY_BUFFERS);
07651 
07652 #ifdef LOCAL_ROUTINES
07653    {
07654       INT index, server_type, server_conn, tid;
07655       BUFFER *pbuf;
07656       BUFFER_CLIENT *pclient;
07657 
07658       server_type = rpc_get_server_option(RPC_OSERVER_TYPE);
07659       server_conn = rpc_get_server_acception();
07660       tid = ss_gettid();
07661 
07662       /* go through all buffers */
07663       for (index = 0; index < _buffer_entries; index++) {
07664          if (server_type == ST_SINGLE && _buffer[index].index != server_conn)
07665             continue;
07666 
07667          if (server_type != ST_SINGLE && _buffer[index].index != tid)
07668             continue;
07669 
07670          if (!_buffer[index].attached)
07671             continue;
07672 
07673          pbuf = &_buffer[index];
07674 
07675          /* empty cache */
07676          pbuf->read_cache_rp = pbuf->read_cache_wp = 0;
07677 
07678          /* set read pointer to write pointer */
07679          pclient = (pbuf->buffer_header)->client + pbuf->client_index;
07680          bm_lock_buffer(index + 1);
07681          pclient->read_pointer = (pbuf->buffer_header)->write_pointer;
07682          bm_unlock_buffer(index + 1);
07683       }
07684 
07685    }
07686 #endif                          /* LOCAL_ROUTINES */
07687 
07688    return BM_SUCCESS;
07689 }
07690 
07691 /**dox***************************************************************/
07692 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07693 
07694 #define MAX_DEFRAG_EVENTS 10
07695 
07696 typedef struct {
07697    WORD event_id;
07698    DWORD data_size;
07699    DWORD received;
07700    EVENT_HEADER *pevent;
07701 } EVENT_DEFRAG_BUFFER;
07702 
07703 EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
07704 
07705 /********************************************************************/
07706 void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
07707                          EVENT_HEADER * pevent, void *pdata,
07708                          void (*dispatcher) (HNDLE, HNDLE, EVENT_HEADER *, void *))
07709 /********************************************************************\
07710 
07711   Routine: bm_defragment_event
07712 
07713   Purpose: Called internally from the event receiving routines
07714            bm_push_event and bm_poll_event to recombine event
07715            fragments and call the user callback routine upon
07716            completion.
07717 
07718   Input:
07719     HNDLE buffer_handle  Handle for the buffer containing event
07720     HNDLE request_id     Handle for event request
07721     EVENT_HEADER *pevent Pointer to event header
07722     void *pata           Pointer to event data
07723     dispatcher()         User callback routine
07724 
07725   Output:
07726     <calls dispatcher() after successfull recombination of event>
07727 
07728   Function value:
07729     void
07730 
07731 \********************************************************************/
07732 {
07733    INT i;
07734 
07735    if ((pevent->event_id & 0xF000) == EVENTID_FRAG1) {
07736     /*---- start new event ----*/
07737 
07738       /* check if fragments already stored */
07739       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07740          if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
07741             break;
07742 
07743       if (i < MAX_DEFRAG_EVENTS) {
07744          free(defrag_buffer[i].pevent);
07745          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07746          cm_msg(MERROR, "bm_defragement_event",
07747                 "Received new event with ID %d while old fragments were not completed",
07748                 (pevent->event_id & 0x0FFF));
07749       }
07750 
07751       /* search new slot */
07752       for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07753          if (defrag_buffer[i].event_id == 0)
07754             break;
07755 
07756       if (i == MAX_DEFRAG_EVENTS) {
07757          cm_msg(MERROR, "bm_defragment_evnet",
07758                 "Not eough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
07759          return;
07760       }
07761 
07762       /* check event size */
07763       if (pevent->data_size != sizeof(DWORD)) {
07764          cm_msg(MERROR, "bm_defragment_evnet",
07765                 "Received first event fragment with %s bytes instead of %d bytes, event ignored",
07766                 pevent->data_size, sizeof(DWORD));
07767          return;
07768       }
07769 
07770       /* setup defragment buffer */
07771       defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
07772       defrag_buffer[i].data_size = *(DWORD *) pdata;
07773       defrag_buffer[i].received = 0;
07774       defrag_buffer[i].pevent =
07775           (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
07776 
07777       if (defrag_buffer[i].pevent == NULL) {
07778          memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07779          cm_msg(MERROR, "bm_defragement_event",
07780                 "Not enough memory to allocate event defragment buffer");
07781          return;
07782       }
07783 
07784       memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
07785       defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
07786       defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
07787 
07788       return;
07789    }
07790 
07791    /* search buffer for that event */
07792    for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
07793       if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
07794          break;
07795 
07796    if (i == MAX_DEFRAG_EVENTS) {
07797       /* no buffer available -> no first fragment received */
07798       free(defrag_buffer[i].pevent);
07799       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07800       cm_msg(MERROR, "bm_defragement_event",
07801              "Received fragment with no first fragment (ID %d)",
07802              pevent->event_id & 0x0FFF);
07803       return;
07804    }
07805 
07806    /* add fragment to buffer */
07807    if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
07808       free(defrag_buffer[i].pevent);
07809       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07810       cm_msg(MERROR, "bm_defragement_event",
07811              "Received fragments with more data (%d) than event size (%d)",
07812              pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
07813       return;
07814    }
07815 
07816    memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
07817           defrag_buffer[i].received, pdata, pevent->data_size);
07818 
07819    defrag_buffer[i].received += pevent->data_size;
07820 
07821    if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
07822       /* event complete */
07823       dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent,
07824                  defrag_buffer[i].pevent + 1);
07825       free(defrag_buffer[i].pevent);
07826       memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
07827    }
07828 }
07829 
07830 /**dox***************************************************************/
07831 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
07832 
07833 /**dox***************************************************************/
07834 /** @} */ /* end of bmfunctionc */
07835 
07836 /**dox***************************************************************/
07837 /** @addtogroup rpcfunctionc
07838  *  
07839  *  @{  */
07840 
07841 /**dox***************************************************************/
07842 #ifndef DOXYGEN_SHOULD_SKIP_THIS
07843 
07844 /********************************************************************\
07845 *                                                                    *
07846 *                         RPC functions                              *
07847 *                                                                    *
07848 \********************************************************************/
07849 
07850 /* globals */
07851 
07852 RPC_CLIENT_CONNECTION _client_connection[MAX_RPC_CONNECTION];
07853 RPC_SERVER_CONNECTION _server_connection;
07854 
07855 static int _lsock;
07856 RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
07857 static INT _server_acception_index = 0;
07858 static INT _server_type;
07859 static char _server_name[256];
07860 
07861 static RPC_LIST *rpc_list = NULL;
07862 
07863 int _opt_tcp_size = OPT_TCP_SIZE;
07864 
07865 
07866 /********************************************************************\
07867 *                       conversion functions                         *
07868 \********************************************************************/
07869 
07870 void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT * convert_flags)
07871 {
07872    *convert_flags = 0;
07873 
07874    /* big/little endian conversion */
07875    if (((remote_hw_type & DRI_BIG_ENDIAN) &&
07876         (hw_type & DRI_LITTLE_ENDIAN)) ||
07877        ((remote_hw_type & DRI_LITTLE_ENDIAN) && (hw_type & DRI_BIG_ENDIAN)))
07878       *convert_flags |= CF_ENDIAN;
07879 
07880    /* float conversion between IEEE and VAX G */
07881    if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
07882       *convert_flags |= CF_VAX2IEEE;
07883 
07884    /* float conversion between VAX G and IEEE */
07885    if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
07886       *convert_flags |= CF_IEEE2VAX;
07887 
07888    /* ASCII format */
07889    if (remote_hw_type & DR_ASCII)
07890       *convert_flags |= CF_ASCII;
07891 }
07892 
07893 /********************************************************************/
07894 void rpc_get_convert_flags(INT * convert_flags)
07895 {
07896    rpc_calc_convert_flags(rpc_get_option(0, RPC_OHW_TYPE),
07897                           _server_connection.remote_hw_type, convert_flags);
07898 }
07899 
07900 /********************************************************************/
07901 void rpc_ieee2vax_float(float *var)
07902 {
07903    unsigned short int lo, hi;
07904 
07905    /* swap hi and lo word */
07906    lo = *((short int *) (var) + 1);
07907    hi = *((short int *) (var));
07908 
07909    /* correct exponent */
07910    if (lo != 0)
07911       lo += 0x100;
07912 
07913    *((short int *) (var) + 1) = hi;
07914    *((short int *) (var)) = lo;
07915 }
07916 
07917 void rpc_vax2ieee_float(float *var)
07918 {
07919    unsigned short int lo, hi;
07920 
07921    /* swap hi and lo word */
07922    lo = *((short int *) (var) + 1);
07923    hi = *((short int *) (var));
07924 
07925    /* correct exponent */
07926    if (hi != 0)
07927       hi -= 0x100;
07928 
07929    *((short int *) (var) + 1) = hi;
07930    *((short int *) (var)) = lo;
07931 
07932 }
07933 
07934 void rpc_vax2ieee_double(double *var)
07935 {
07936    unsigned short int i1, i2, i3, i4;
07937 
07938    /* swap words */
07939    i1 = *((short int *) (var) + 3);
07940    i2 = *((short int *) (var) + 2);
07941    i3 = *((short int *) (var) + 1);
07942    i4 = *((short int *) (var));
07943 
07944    /* correct exponent */
07945    if (i4 != 0)
07946       i4 -= 0x20;
07947 
07948    *((short int *) (var) + 3) = i4;
07949    *((short int *) (var) + 2) = i3;
07950    *((short int *) (var) + 1) = i2;
07951    *((short int *) (var)) = i1;
07952 }
07953 
07954 void rpc_ieee2vax_double(double *var)
07955 {
07956    unsigned short int i1, i2, i3, i4;
07957 
07958    /* swap words */
07959    i1 = *((short int *) (var) + 3);
07960    i2 = *((short int *) (var) + 2);
07961    i3 = *((short int *) (var) + 1);
07962    i4 = *((short int *) (var));
07963 
07964    /* correct exponent */
07965    if (i1 != 0)
07966       i1 += 0x20;
07967 
07968    *((short int *) (var) + 3) = i4;
07969    *((short int *) (var) + 2) = i3;
07970    *((short int *) (var) + 1) = i2;
07971    *((short int *) (var)) = i1;
07972 }
07973 
07974 /********************************************************************/
07975 void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
07976 {
07977 
07978    if (convert_flags & CF_ENDIAN) {
07979       if (tid == TID_WORD || tid == TID_SHORT)
07980          WORD_SWAP(data);
07981       if (tid == TID_DWORD || tid == TID_INT || tid == TID_BOOL || tid == TID_FLOAT)
07982          DWORD_SWAP(data);
07983       if (tid == TID_DOUBLE)
07984          QWORD_SWAP(data);
07985    }
07986 
07987    if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
07988        ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
07989       if (tid == TID_FLOAT)
07990          rpc_ieee2vax_float((float *) data);
07991       if (tid == TID_DOUBLE)
07992          rpc_ieee2vax_double((double *) data);
07993    }
07994 
07995    if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
07996        ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
07997       if (tid == TID_FLOAT)
07998          rpc_vax2ieee_float((float *) data);
07999       if (tid == TID_DOUBLE)
08000          rpc_vax2ieee_double((double *) data);
08001    }
08002 }
08003 
08004 void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
08005 /********************************************************************\
08006 
08007   Routine: rpc_convert_data
08008 
08009   Purpose: Convert data format between differenct computers
08010 
08011   Input:
08012     void   *data            Pointer to data
08013     INT    tid              Type ID of data, one of TID_xxx
08014     INT    flags            Combination of following flags:
08015                               RPC_IN: data is input parameter
08016                               RPC_OUT: data is output variable
08017                               RPC_FIXARRAY, RPC_VARARRAY: data is array
08018                                 of "size" bytes (see next param.)
08019                               RPC_OUTGOING: data is outgoing
08020     INT    total_size       Size of bytes of data. Used for variable
08021                             length arrays.
08022     INT    convert_flags    Flags for data conversion
08023 
08024   Output:
08025     void   *data            Is converted according to _convert_flag
08026                             value
08027 
08028   Function value:
08029     RPC_SUCCESS             Successful completion
08030 
08031 \********************************************************************/
08032 {
08033    INT i, n, single_size;
08034    char *p;
08035 
08036    /* convert array */
08037    if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
08038       single_size = tid_size[tid];
08039       /* don't convert TID_ARRAY & TID_STRUCT */
08040       if (single_size == 0)
08041          return;
08042 
08043       n = total_size / single_size;
08044 
08045       for (i = 0; i < n; i++) {
08046          p = (char *) data + (i * single_size);
08047          rpc_convert_single(p, tid, flags, convert_flags);
08048       }
08049    } else {
08050       rpc_convert_single(data, tid, flags, convert_flags);
08051    }
08052 }
08053 
08054 /********************************************************************\
08055 *                       type ID functions                            *
08056 \********************************************************************/
08057 
08058 INT rpc_tid_size(INT id)
08059 {
08060    if (id < TID_LAST)
08061       return tid_size[id];
08062 
08063    return 0;
08064 }
08065 
08066 char *rpc_tid_name(INT id)
08067 {
08068    if (id < TID_LAST)
08069       return tid_name[id];
08070    else
08071       return "<unknown>";
08072 }
08073 
08074 
08075 /**dox***************************************************************/
08076 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
08077 
08078 /********************************************************************\
08079 *                        client functions                            *
08080 \********************************************************************/
08081 
08082 /********************************************************************/
08083 /**
08084 Register RPC client for standalone mode (without standard
08085            midas server)
08086 @param list           Array of RPC_LIST structures containing
08087                             function IDs and parameter definitions.
08088                             The end of the list must be indicated by
08089                             a function ID of zero.
08090 @param name          Name of this client
08091 @return RPC_SUCCESS
08092 */
08093 INT rpc_register_client(char *name, RPC_LIST * list)
08094 {
08095    rpc_set_name(name);
08096    rpc_register_functions(rpc_get_internal_list(0), NULL);
08097    rpc_register_functions(list, NULL);
08098 
08099    return RPC_SUCCESS;
08100 }
08101 
08102 /********************************************************************/
08103 /**
08104 Register a set of RPC functions (both as clients or servers)
08105 @param new_list       Array of RPC_LIST structures containing
08106                             function IDs and parameter definitions.
08107                             The end of the list must be indicated by
08108                             a function ID of zero.
08109 @param func          Default dispatch function
08110 
08111 @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
08112 */
08113 INT rpc_register_functions(RPC_LIST * new_list, INT(*func) (INT, void **))
08114 {
08115    INT i, j, iold, inew;
08116 
08117    /* count number of new functions */
08118    for (i = 0; new_list[i].id != 0; i++) {
08119       /* check double defined functions */
08120       for (j = 0; rpc_list != NULL && rpc_list[j].id != 0; j++)
08121          if (rpc_list[j].id == new_list[i].id)
08122             return RPC_DOUBLE_DEFINED;
08123    }
08124    inew = i;
08125 
08126    /* count number of existing functions */
08127    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++);
08128    iold = i;
08129 
08130    /* allocate new memory for rpc_list */
08131    if (rpc_list == NULL)
08132       rpc_list = (RPC_LIST *) M_MALLOC(sizeof(RPC_LIST) * (inew + 1));
08133    else
08134       rpc_list = (RPC_LIST *) realloc(rpc_list, sizeof(RPC_LIST) * (iold + inew + 1));
08135 
08136    if (rpc_list == NULL) {
08137       cm_msg(MERROR, "rpc_register_functions", "out of memory");
08138       return RPC_NO_MEMORY;
08139    }
08140 
08141    /* append new functions */
08142    for (i = iold; i < iold + inew; i++) {
08143       memcpy(rpc_list + i, new_list + i - iold, sizeof(RPC_LIST));
08144 
08145       /* set default dispatcher */
08146       if (rpc_list[i].dispatch == NULL)
08147          rpc_list[i].dispatch = func;
08148 
08149       /* check valid ID for user functions */
08150       if (new_list != rpc_get_internal_list(0) &&
08151           new_list != rpc_get_internal_list(1) &&
08152           (rpc_list[i].id < RPC_MIN_ID || rpc_list[i].id > RPC_MAX_ID))
08153          cm_msg(MERROR, "rpc_register_functions",
08154                 "registered RPC function with invalid ID");
08155    }
08156 
08157    /* mark end of list */
08158    rpc_list[i].id = 0;
08159 
08160    return RPC_SUCCESS;
08161 }
08162 
08163 
08164 
08165 /**dox***************************************************************/
08166 #ifndef DOXYGEN_SHOULD_SKIP_THIS
08167 
08168 /********************************************************************/
08169 INT rpc_deregister_functions()
08170 /********************************************************************\
08171 
08172   Routine: rpc_deregister_functions
08173 
08174   Purpose: Free memory of previously registered functions
08175 
08176   Input:
08177     none
08178 
08179   Output:
08180     none
08181 
08182   Function value:
08183     RPC_SUCCESS              Successful completion
08184 
08185 \********************************************************************/
08186 {
08187    if (rpc_list)
08188       M_FREE(rpc_list);
08189    rpc_list = NULL;
08190 
08191    return RPC_SUCCESS;
08192 }
08193 
08194 
08195 /********************************************************************/
08196 INT rpc_register_function(INT id, INT(*func) (INT, void **))
08197 /********************************************************************\
08198 
08199   Routine: rpc_register_function
08200 
08201   Purpose: Replace a dispatch function for a specific rpc routine
08202 
08203   Input:
08204     INT      id             RPC ID
08205     INT      *func          New dispatch function
08206 
08207   Output:
08208    <implicit: func gets copied to rpc_list>
08209 
08210   Function value:
08211    RPC_SUCCESS              Successful completion
08212    RPC_INVALID_ID           RPC ID not found
08213 
08214 \********************************************************************/
08215 {
08216    INT i;
08217 
08218    for (i = 0; rpc_list != NULL && rpc_list[i].id != 0; i++)
08219       if (rpc_list[i].id == id)
08220          break;
08221 
08222    if (rpc_list[i].id == id)
08223       rpc_list[i].dispatch = func;
08224    else
08225       return RPC_INVALID_ID;
08226 
08227    return RPC_SUCCESS;
08228 }
08229 
08230 
08231 /********************************************************************/
08232 INT rpc_client_dispatch(int sock)
08233 /********************************************************************\
08234 
08235   Routine: rpc_client_dispatch
08236 
08237   Purpose: Gets called whenever a client receives data from the
08238            server. Get set via rpc_connect. Internal use only.
08239 
08240 \********************************************************************/
08241 {
08242    INT hDB, hKey, n;
08243    NET_COMMAND *nc;
08244    INT status = 0;
08245    char net_buffer[256];
08246 
08247    nc = (NET_COMMAND *) net_buffer;
08248 
08249    n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08250    if (n <= 0)
08251       return SS_ABORT;
08252 
08253    if (nc->header.routine_id == MSG_ODB) {
08254       /* update a changed record */
08255       hDB = *((INT *) nc->param);
08256       hKey = *((INT *) nc->param + 1);
08257       status = db_update_record(hDB, hKey, 0);
08258    }
08259 
08260    else if (nc->header.routine_id == MSG_WATCHDOG) {
08261       nc->header.routine_id = 1;
08262       nc->header.param_size = 0;
08263       send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08264       status = RPC_SUCCESS;
08265    }
08266 
08267    else if (nc->header.routine_id == MSG_BM) {
08268       fd_set readfds;
08269       struct timeval timeout;
08270 
08271       /* receive further messages to empty TCP queue */
08272       do {
08273          FD_ZERO(&readfds);
08274          FD_SET(sock, &readfds);
08275 
08276          timeout.tv_sec = 0;
08277          timeout.tv_usec = 0;
08278 
08279          select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08280 
08281          if (FD_ISSET(sock, &readfds)) {
08282             n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
08283             if (n <= 0)
08284                return SS_ABORT;
08285 
08286             if (nc->header.routine_id == MSG_ODB) {
08287                /* update a changed record */
08288                hDB = *((INT *) nc->param);
08289                hKey = *((INT *) nc->param + 1);
08290                status = db_update_record(hDB, hKey, 0);
08291             }
08292 
08293             else if (nc->header.routine_id == MSG_WATCHDOG) {
08294                nc->header.routine_id = 1;
08295                nc->header.param_size = 0;
08296                send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
08297                status = RPC_SUCCESS;
08298             }
08299          }
08300 
08301       } while (FD_ISSET(sock, &readfds));
08302 
08303       /* poll event from server */
08304       status = bm_poll_event(FALSE);
08305    }
08306 
08307    return status;
08308 }
08309 
08310 
08311 /********************************************************************/
08312 INT rpc_client_connect(char *host_name, INT port, char *client_name, HNDLE * hConnection)
08313 /********************************************************************\
08314 
08315   Routine: rpc_client_connect
08316 
08317   Purpose: Establish a network connection to a remote client
08318 
08319   Input:
08320     char *host_name          IP address of host to connect to.
08321     INT  port                TPC port to connect to.
08322     char *clinet_name        Client program name
08323 
08324   Output:
08325     HNDLE *hConnection       Handle for new connection which can be used
08326                              in future rpc_call(hConnection....) calls
08327 
08328   Function value:
08329     RPC_SUCCESS              Successful completion
08330     RPC_NET_ERROR            Error in socket call
08331     RPC_NO_CONNECTION        Maximum number of connections reached
08332     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08333 
08334 \********************************************************************/
08335 {
08336    INT i, status, index;
08337    struct sockaddr_in bind_addr;
08338    INT sock;
08339    INT remote_hw_type, hw_type;
08340    char str[200];
08341    char version[32], v1[32];
08342    char local_prog_name[NAME_LENGTH];
08343    char local_host_name[HOST_NAME_LENGTH];
08344    struct hostent *phe;
08345 
08346 #ifdef OS_WINNT
08347    {
08348       WSADATA WSAData;
08349 
08350       /* Start windows sockets */
08351       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08352          return RPC_NET_ERROR;
08353    }
08354 #endif
08355 
08356    /* check if cm_connect_experiment was called */
08357    if (_client_name[0] == 0) {
08358       cm_msg(MERROR, "rpc_client_connect",
08359              "cm_connect_experiment/rpc_set_name not called");
08360       return RPC_NOT_REGISTERED;
08361    }
08362 
08363    /* check for broken connections */
08364    rpc_client_check();
08365 
08366    /* check if connection already exists */
08367    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08368       if (_client_connection[i].send_sock != 0 &&
08369           strcmp(_client_connection[i].host_name, host_name) == 0 &&
08370           _client_connection[i].port == port) {
08371          *hConnection = i + 1;
08372          return RPC_SUCCESS;
08373       }
08374 
08375    /* search for free entry */
08376    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08377       if (_client_connection[i].send_sock == 0)
08378          break;
08379 
08380    /* open new network connection */
08381    if (i == MAX_RPC_CONNECTION) {
08382       cm_msg(MERROR, "rpc_client_connect", "maximum number of connections exceeded");
08383       return RPC_NO_CONNECTION;
08384    }
08385 
08386    /* create a new socket for connecting to remote server */
08387    sock = socket(AF_INET, SOCK_STREAM, 0);
08388    if (sock == -1) {
08389       cm_msg(MERROR, "rpc_client_connect", "cannot create socket");
08390       return RPC_NET_ERROR;
08391    }
08392 
08393    index = i;
08394    strcpy(_client_connection[index].host_name, host_name);
08395    strcpy(_client_connection[index].client_name, client_name);
08396    _client_connection[index].port = port;
08397    _client_connection[index].exp_name[0] = 0;
08398    _client_connection[index].transport = RPC_TCP;
08399    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08400    _client_connection[index].rpc_timeout = DEFAULT_RPC_TIMEOUT;
08401 
08402    /* connect to remote node */
08403    memset(&bind_addr, 0, sizeof(bind_addr));
08404    bind_addr.sin_family = AF_INET;
08405    bind_addr.sin_addr.s_addr = 0;
08406    bind_addr.sin_port = htons((short) port);
08407 
08408 #ifdef OS_VXWORKS
08409    {
08410       INT host_addr;
08411 
08412       host_addr = hostGetByName(host_name);
08413       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08414    }
08415 #else
08416    phe = gethostbyname(host_name);
08417    if (phe == NULL) {
08418       cm_msg(MERROR, "rpc_client_connect", "cannot get host name");
08419       return RPC_NET_ERROR;
08420    }
08421    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08422 #endif
08423 
08424 #ifdef OS_UNIX
08425    do {
08426       status = connect(sock, (void *) &bind_addr, sizeof(bind_addr));
08427 
08428       /* don't return if an alarm signal was cought */
08429    } while (status == -1 && errno == EINTR);
08430 #else
08431    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08432 #endif
08433 
08434    if (status != 0) {
08435       /* cm_msg(MERROR, "rpc_client_connect", "cannot connect");
08436          message should be displayed by application */
08437       return RPC_NET_ERROR;
08438    }
08439 
08440    /* set TCP_NODELAY option for better performance */
08441 #ifdef OS_VXWORKS
08442    i = 1;
08443    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
08444 #endif
08445 
08446    /* send local computer info */
08447    rpc_get_name(local_prog_name);
08448    gethostname(local_host_name, sizeof(local_host_name));
08449 
08450    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08451    sprintf(str, "%d %s %s %s", hw_type, cm_get_version(), local_prog_name,
08452            local_host_name);
08453 
08454    send(sock, str, strlen(str) + 1, 0);
08455 
08456    /* receive remote computer info */
08457    i = recv_string(sock, str, sizeof(str), 10000);
08458    if (i <= 0) {
08459       cm_msg(MERROR, "rpc_client_connect", "timeout on receive remote computer info: %s",
08460              str);
08461       return RPC_NET_ERROR;
08462    }
08463 
08464    remote_hw_type = version[0] = 0;
08465    sscanf(str, "%d %s", &remote_hw_type, version);
08466    _client_connection[index].remote_hw_type = remote_hw_type;
08467    _client_connection[index].send_sock = sock;
08468 
08469    /* print warning if version patch level doesn't agree */
08470    strcpy(v1, version);
08471    if (strchr(v1, '.'))
08472       if (strchr(strchr(v1, '.') + 1, '.'))
08473          *strchr(strchr(v1, '.') + 1, '.') = 0;
08474 
08475    strcpy(str, cm_get_version());
08476    if (strchr(str, '.'))
08477       if (strchr(strchr(str, '.') + 1, '.'))
08478          *strchr(strchr(str, '.') + 1, '.') = 0;
08479 
08480    if (strcmp(v1, str) != 0) {
08481       sprintf(str, "remote MIDAS version %s differs from local version %s",
08482               version, cm_get_version());
08483       cm_msg(MERROR, "rpc_client_connect", str);
08484    }
08485 
08486    *hConnection = index + 1;
08487 
08488    return RPC_SUCCESS;
08489 }
08490 
08491 
08492 /********************************************************************/
08493 void rpc_client_check()
08494 /********************************************************************\
08495 
08496   Routine: rpc_client_check
08497 
08498   Purpose: Check all client connections if remote client closed link
08499 
08500   Function value:
08501     RPC_SUCCESS              Successful completion
08502     RPC_NET_ERROR            Error in socket call
08503     RPC_NO_CONNECTION        Maximum number of connections reached
08504     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08505 
08506 \********************************************************************/
08507 {
08508    INT i, status;
08509 
08510    /* check for broken connections */
08511    for (i = 0; i < MAX_RPC_CONNECTION; i++)
08512       if (_client_connection[i].send_sock != 0) {
08513          int sock;
08514          fd_set readfds;
08515          struct timeval timeout;
08516          char buffer[64];
08517 
08518          sock = _client_connection[i].send_sock;
08519          FD_ZERO(&readfds);
08520          FD_SET(sock, &readfds);
08521 
08522          timeout.tv_sec = 0;
08523          timeout.tv_usec = 0;
08524 
08525          do {
08526             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
08527          } while (status == -1);        /* dont return if an alarm signal was cought */
08528 
08529          if (FD_ISSET(sock, &readfds)) {
08530             status = recv(sock, (char *) buffer, sizeof(buffer), 0);
08531 
08532             if (equal_ustring(buffer, "EXIT")) {
08533                /* normal exit */
08534                closesocket(sock);
08535                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08536             }
08537 
08538             if (status <= 0) {
08539                cm_msg(MERROR, "rpc_client_check",
08540                       "Connection broken to \"%s\" on host %s",
08541                       _client_connection[i].client_name, _client_connection[i].host_name);
08542 
08543                /* connection broken -> reset */
08544                closesocket(sock);
08545                memset(&_client_connection[i], 0, sizeof(RPC_CLIENT_CONNECTION));
08546             }
08547          }
08548       }
08549 }
08550 
08551 
08552 /********************************************************************/
08553 INT rpc_server_connect(char *host_name, char *exp_name)
08554 /********************************************************************\
08555 
08556   Routine: rpc_server_connect
08557 
08558   Purpose: Extablish a network connection to a remote MIDAS
08559            server using a callback scheme.
08560 
08561   Input:
08562     char *host_name         IP address of host to connect to.
08563 
08564     INT  port               TPC port to connect to.
08565 
08566     char *exp_name          Name of experiment to connect to. By using
08567                             this name, several experiments (e.g. online
08568                             DAQ and offline analysis) can run simultan-
08569                             eously on the same host.
08570 
08571   Output:
08572     none
08573 
08574   Function value:
08575     RPC_SUCCESS              Successful completion
08576     RPC_NET_ERROR            Error in socket call
08577     RPC_NOT_REGISTERED       cm_connect_experiment was not called properly
08578     CM_UNDEF_EXP             Undefined experiment on server
08579 
08580 \********************************************************************/
08581 {
08582    INT i, status, flag;
08583    struct sockaddr_in bind_addr;
08584    INT sock, lsock1, lsock2, lsock3;
08585    INT listen_port1, listen_port2, listen_port3;
08586    INT remote_hw_type, hw_type;
08587    int size;
08588    char str[200], version[32], v1[32];
08589    char local_prog_name[NAME_LENGTH];
08590    struct hostent *phe;
08591 
08592 #ifdef OS_WINNT
08593    {
08594       WSADATA WSAData;
08595 
08596       /* Start windows sockets */
08597       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
08598          return RPC_NET_ERROR;
08599    }
08600 #endif
08601 
08602    /* check if local connection */
08603    if (host_name[0] == 0)
08604       return RPC_SUCCESS;
08605 
08606    /* register system functions */
08607    rpc_register_functions(rpc_get_internal_list(0), NULL);
08608 
08609    /* check if cm_connect_experiment was called */
08610    if (_client_name[0] == 0) {
08611       cm_msg(MERROR, "rpc_server_connect",
08612              "cm_connect_experiment/rpc_set_name not called");
08613       return RPC_NOT_REGISTERED;
08614    }
08615 
08616    /* check if connection already exists */
08617    if (_server_connection.send_sock != 0)
08618       return RPC_SUCCESS;
08619 
08620    strcpy(_server_connection.host_name, host_name);
08621    strcpy(_server_connection.exp_name, exp_name);
08622    _server_connection.transport = RPC_TCP;
08623    _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
08624 
08625    /* create new TCP sockets for listening */
08626    lsock1 = socket(AF_INET, SOCK_STREAM, 0);
08627    lsock2 = socket(AF_INET, SOCK_STREAM, 0);
08628    lsock3 = socket(AF_INET, SOCK_STREAM, 0);
08629    if (lsock3 == -1) {
08630       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08631       return RPC_NET_ERROR;
08632    }
08633 
08634    flag = 1;
08635    setsockopt(lsock1, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08636    setsockopt(lsock2, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08637    setsockopt(lsock3, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
08638 
08639    /* let OS choose any port number */
08640    memset(&bind_addr, 0, sizeof(bind_addr));
08641    bind_addr.sin_family = AF_INET;
08642    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
08643    bind_addr.sin_port = 0;
08644 
08645    status = bind(lsock1, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08646    bind_addr.sin_port = 0;
08647    status = bind(lsock2, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08648    bind_addr.sin_port = 0;
08649    status = bind(lsock3, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08650    if (status < 0) {
08651       cm_msg(MERROR, "rpc_server_connect", "cannot bind");
08652       return RPC_NET_ERROR;
08653    }
08654 
08655    /* listen for connection */
08656    status = listen(lsock1, 1);
08657    status = listen(lsock2, 1);
08658    status = listen(lsock3, 1);
08659    if (status < 0) {
08660       cm_msg(MERROR, "rpc_server_connect", "cannot listen");
08661       return RPC_NET_ERROR;
08662    }
08663 
08664    /* find out which port OS has chosen */
08665    size = sizeof(bind_addr);
08666    getsockname(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08667    listen_port1 = ntohs(bind_addr.sin_port);
08668    getsockname(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08669    listen_port2 = ntohs(bind_addr.sin_port);
08670    getsockname(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08671    listen_port3 = ntohs(bind_addr.sin_port);
08672 
08673    /* create a new socket for connecting to remote server */
08674    sock = socket(AF_INET, SOCK_STREAM, 0);
08675    if (sock == -1) {
08676       cm_msg(MERROR, "rpc_server_connect", "cannot create socket");
08677       return RPC_NET_ERROR;
08678    }
08679 
08680    /* connect to remote node */
08681    memset(&bind_addr, 0, sizeof(bind_addr));
08682    bind_addr.sin_family = AF_INET;
08683    bind_addr.sin_addr.s_addr = 0;
08684    bind_addr.sin_port = htons((short) MIDAS_TCP_PORT);
08685 
08686 #ifdef OS_VXWORKS
08687    {
08688       INT host_addr;
08689 
08690       host_addr = hostGetByName(host_name);
08691       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
08692    }
08693 #else
08694    phe = gethostbyname(host_name);
08695    if (phe == NULL) {
08696       cm_msg(MERROR, "rpc_server_connect", "cannot get host name");
08697       return RPC_NET_ERROR;
08698    }
08699    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
08700 #endif
08701 
08702 #ifdef OS_UNIX
08703    do {
08704       status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08705 
08706       /* don't return if an alarm signal was cought */
08707    } while (status == -1 && errno == EINTR);
08708 #else
08709    status = connect(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
08710 #endif
08711 
08712    if (status != 0) {
08713 /*    cm_msg(MERROR, "rpc_server_connect", "cannot connect"); message should be displayed by application */
08714       return RPC_NET_ERROR;
08715    }
08716 
08717    /* connect to experiment */
08718    if (exp_name[0] == 0)
08719       sprintf(str, "C %d %d %d %s Default",
08720               listen_port1, listen_port2, listen_port3, cm_get_version());
08721    else
08722       sprintf(str, "C %d %d %d %s %s",
08723               listen_port1, listen_port2, listen_port3, cm_get_version(), exp_name);
08724 
08725    send(sock, str, strlen(str) + 1, 0);
08726    i = recv_string(sock, str, sizeof(str), 10000);
08727    closesocket(sock);
08728    if (i <= 0) {
08729       cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
08730       return RPC_NET_ERROR;
08731    }
08732 
08733    status = version[0] = 0;
08734    sscanf(str, "%d %s", &status, version);
08735 
08736    if (status == 2) {
08737 /*  message "undefined experiment" should be displayed by application */
08738       return CM_UNDEF_EXP;
08739    }
08740 
08741    /* print warning if version patch level doesn't agree */
08742    strcpy(v1, version);
08743    if (strchr(v1, '.'))
08744       if (strchr(strchr(v1, '.') + 1, '.'))
08745          *strchr(strchr(v1, '.') + 1, '.') = 0;
08746 
08747    strcpy(str, cm_get_version());
08748    if (strchr(str, '.'))
08749       if (strchr(strchr(str, '.') + 1, '.'))
08750          *strchr(strchr(str, '.') + 1, '.') = 0;
08751 
08752    if (strcmp(v1, str) != 0) {
08753       sprintf(str, "remote MIDAS version %s differs from local version %s",
08754               version, cm_get_version());
08755       cm_msg(MERROR, "rpc_server_connect", str);
08756    }
08757 
08758    /* wait for callback on send and recv socket */
08759    size = sizeof(bind_addr);
08760    _server_connection.send_sock =
08761        accept(lsock1, (struct sockaddr *) &bind_addr, (int *) &size);
08762 
08763    _server_connection.recv_sock =
08764        accept(lsock2, (struct sockaddr *) &bind_addr, (int *) &size);
08765 
08766    _server_connection.event_sock =
08767        accept(lsock3, (struct sockaddr *) &bind_addr, (int *) &size);
08768 
08769    if (_server_connection.send_sock == -1 ||
08770        _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
08771       cm_msg(MERROR, "rpc_server_connect", "accept() failed");
08772       return RPC_NET_ERROR;
08773    }
08774 
08775    closesocket(lsock1);
08776    closesocket(lsock2);
08777    closesocket(lsock3);
08778 
08779    /* set TCP_NODELAY option for better performance */
08780 #ifdef OS_VXWORKS
08781    flag = 1;
08782    setsockopt(_server_connection.send_sock,
08783               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08784    setsockopt(_server_connection.event_sock,
08785               IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
08786 #endif
08787 
08788    /* increase send buffer size to 64kB */
08789    flag = 0x10000;
08790    setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF,
08791               (char *) &flag, sizeof(flag));
08792 
08793    /* send local computer info */
08794    rpc_get_name(local_prog_name);
08795    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
08796    sprintf(str, "%d %s", hw_type, local_prog_name);
08797 
08798    send(_server_connection.send_sock, str, strlen(str) + 1, 0);
08799 
08800    /* receive remote computer info */
08801    i = recv_string(_server_connection.send_sock, str, sizeof(str), 10000);
08802    if (i <= 0) {
08803       cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
08804       return RPC_NET_ERROR;
08805    }
08806 
08807    sscanf(str, "%d", &remote_hw_type);
08808    _server_connection.remote_hw_type = remote_hw_type;
08809 
08810    /* set dispatcher which receives database updates */
08811    ss_suspend_set_dispatch(CH_CLIENT, &_server_connection,
08812                            (int (*)(void)) rpc_client_dispatch);
08813 
08814    return RPC_SUCCESS;
08815 }
08816 
08817 
08818 /********************************************************************/
08819 INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
08820 /********************************************************************\
08821 
08822   Routine: rpc_client_disconnect
08823 
08824   Purpose: Close a rpc connection to a MIDAS client
08825 
08826   Input:
08827     HNDLE  hConn           Handle of connection
08828     BOOL   bShutdown       Shut down remote server if TRUE
08829 
08830   Output:
08831     none
08832 
08833   Function value:
08834    RPC_SUCCESS             Successful completion
08835 
08836 \********************************************************************/
08837 {
08838    INT i;
08839 
08840    if (hConn == -1) {
08841       /* close all open connections */
08842       for (i = MAX_RPC_CONNECTION - 1; i >= 0; i--)
08843          if (_client_connection[i].send_sock != 0)
08844             rpc_client_disconnect(i + 1, FALSE);
08845 
08846       /* close server connection from other clients */
08847       for (i = 0; i < MAX_RPC_CONNECTION; i++)
08848          if (_server_acception[i].recv_sock) {
08849             send(_server_acception[i].recv_sock, "EXIT", 5, 0);
08850             closesocket(_server_acception[i].recv_sock);
08851          }
08852    } else {
08853       /* notify server about exit */
08854 
08855       /* set FTCP mode (helps for rebooted VxWorks nodes) */
08856       rpc_set_option(hConn, RPC_OTRANSPORT, RPC_FTCP);
08857       rpc_client_call(hConn, bShutdown ? RPC_ID_SHUTDOWN : RPC_ID_EXIT);
08858 
08859       /* close socket */
08860       if (_client_connection[hConn - 1].send_sock)
08861          closesocket(_client_connection[hConn - 1].send_sock);
08862 
08863       memset(&_client_connection[hConn - 1], 0, sizeof(RPC_CLIENT_CONNECTION));
08864    }
08865 
08866    return RPC_SUCCESS;
08867 }
08868 
08869 
08870 /********************************************************************/
08871 INT rpc_server_disconnect()
08872 /********************************************************************\
08873 
08874   Routine: rpc_server_disconnect
08875 
08876   Purpose: Close a rpc connection to a MIDAS server and close all
08877            server connections from other clients
08878 
08879   Input:
08880     none
08881 
08882   Output:
08883     none
08884 
08885   Function value:
08886    RPC_SUCCESS             Successful completion
08887    RPC_NET_ERROR           Error in socket call
08888    RPC_NO_CONNECTION       Maximum number of connections reached
08889 
08890 \********************************************************************/
08891 {
08892    static int rpc_server_disconnect_recursion_level = 0;
08893 
08894    if (rpc_server_disconnect_recursion_level)
08895       return RPC_SUCCESS;
08896 
08897    rpc_server_disconnect_recursion_level = 1;
08898 
08899    /* flush remaining events */
08900    rpc_flush_event();
08901 
08902    /* notify server about exit */
08903    rpc_call(RPC_ID_EXIT);
08904 
08905    /* close sockets */
08906    closesocket(_server_connection.send_sock);
08907    closesocket(_server_connection.recv_sock);
08908    closesocket(_server_connection.event_sock);
08909 
08910    memset(&_server_connection, 0, sizeof(RPC_SERVER_CONNECTION));
08911 
08912    rpc_server_disconnect_recursion_level = 0;
08913    return RPC_SUCCESS;
08914 }
08915 
08916 
08917 /********************************************************************/
08918 INT rpc_is_remote(void)
08919 /********************************************************************\
08920 
08921   Routine: rpc_is_remote
08922 
08923   Purpose: Return true if program is connected to a remote server
08924 
08925   Input:
08926    none
08927 
08928   Output:
08929     none
08930 
08931   Function value:
08932     INT    RPC connection index
08933 
08934 \********************************************************************/
08935 {
08936    return _server_connection.send_sock != 0;
08937 }
08938 
08939 
08940 /********************************************************************/
08941 INT rpc_get_server_acception(void)
08942 /********************************************************************\
08943 
08944   Routine: rpc_get_server_acception
08945 
08946   Purpose: Return actual RPC server connection index
08947 
08948   Input:
08949    none
08950 
08951   Output:
08952     none
08953 
08954   Function value:
08955     INT    RPC server connection index
08956 
08957 \********************************************************************/
08958 {
08959    return _server_acception_index;
08960 }
08961 
08962 
08963 /********************************************************************/
08964 INT rpc_set_server_acception(INT index)
08965 /********************************************************************\
08966 
08967   Routine: rpc_set_server_acception
08968 
08969   Purpose: Set actual RPC server connection index
08970 
08971   Input:
08972     INT  index              Server index
08973 
08974   Output:
08975     none
08976 
08977   Function value:
08978     RPC_SUCCESS             Successful completion
08979 
08980 \********************************************************************/
08981 {
08982    _server_acception_index = index;
08983    return RPC_SUCCESS;
08984 }
08985 
08986 
08987 /********************************************************************/
08988 INT rpc_get_option(HNDLE hConn, INT item)
08989 /********************************************************************\
08990 
08991   Routine: rpc_get_option
08992 
08993   Purpose: Get actual RPC option
08994 
08995   Input:
08996     HNDLE hConn             RPC connection handle
08997     INT   item              One of RPC_Oxxx
08998 
08999   Output:
09000     none
09001 
09002   Function value:
09003     INT                     Actual option
09004 
09005 \********************************************************************/
09006 {
09007    switch (item) {
09008    case RPC_OTIMEOUT:
09009       if (hConn == -1)
09010          return _server_connection.rpc_timeout;
09011       return _client_connection[hConn - 1].rpc_timeout;
09012 
09013    case RPC_OTRANSPORT:
09014       if (hConn == -1)
09015          return _server_connection.transport;
09016       return _client_connection[hConn - 1].transport;
09017 
09018    case RPC_OHW_TYPE:
09019       {
09020          INT tmp_type, size;
09021          DWORD dummy;
09022          unsigned char *p;
09023          float f;
09024          double d;
09025 
09026          tmp_type = 0;
09027 
09028          /* test pointer size */
09029          size = sizeof(p);
09030          if (size == 2)
09031             tmp_type |= DRI_16;
09032          if (size == 4)
09033             tmp_type |= DRI_32;
09034          if (size == 8)
09035             tmp_type |= DRI_64;
09036 
09037          /* test if little or big endian machine */
09038          dummy = 0x12345678;
09039          p = (unsigned char *) &dummy;
09040          if (*p == 0x78)
09041             tmp_type |= DRI_LITTLE_ENDIAN;
09042          else if (*p == 0x12)
09043             tmp_type |= DRI_BIG_ENDIAN;
09044          else
09045             cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
09046 
09047          /* floating point format */
09048          f = (float) 1.2345;
09049          dummy = 0;
09050          memcpy(&dummy, &f, sizeof(f));
09051          if ((dummy & 0xFF) == 0x19 &&
09052              ((dummy >> 8) & 0xFF) == 0x04 &&
09053              ((dummy >> 16) & 0xFF) == 0x9E && ((dummy >> 24) & 0xFF) == 0x3F)
09054             tmp_type |= DRF_IEEE;
09055          else if ((dummy & 0xFF) == 0x9E &&
09056                   ((dummy >> 8) & 0xFF) == 0x40 &&
09057                   ((dummy >> 16) & 0xFF) == 0x19 && ((dummy >> 24) & 0xFF) == 0x04)
09058             tmp_type |= DRF_G_FLOAT;
09059          else
09060             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09061 
09062          d = (double) 1.2345;
09063          dummy = 0;
09064          memcpy(&dummy, &d, sizeof(f));
09065          if ((dummy & 0xFF) == 0x8D &&  /* little endian */
09066              ((dummy >> 8) & 0xFF) == 0x97 &&
09067              ((dummy >> 16) & 0xFF) == 0x6E && ((dummy >> 24) & 0xFF) == 0x12)
09068             tmp_type |= DRF_IEEE;
09069          else if ((dummy & 0xFF) == 0x83 &&     /* big endian */
09070                   ((dummy >> 8) & 0xFF) == 0xC0 &&
09071                   ((dummy >> 16) & 0xFF) == 0xF3 && ((dummy >> 24) & 0xFF) == 0x3F)
09072             tmp_type |= DRF_IEEE;
09073          else if ((dummy & 0xFF) == 0x13 &&
09074                   ((dummy >> 8) & 0xFF) == 0x40 &&
09075                   ((dummy >> 16) & 0xFF) == 0x83 && ((dummy >> 24) & 0xFF) == 0xC0)
09076             tmp_type |= DRF_G_FLOAT;
09077          else if ((dummy & 0xFF) == 0x9E &&
09078                   ((dummy >> 8) & 0xFF) == 0x40 &&
09079                   ((dummy >> 16) & 0xFF) == 0x18 && ((dummy >> 24) & 0xFF) == 0x04)
09080             cm_msg(MERROR, "rpc_get_option",
09081                    "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
09082          else
09083             cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
09084 
09085          return tmp_type;
09086       }
09087 
09088    default:
09089       cm_msg(MERROR, "rpc_get_option", "invalid argument");
09090       break;
09091    }
09092 
09093    return 0;
09094 }
09095 
09096 /**dox***************************************************************/
09097 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09098 
09099 /********************************************************************/
09100 /**
09101 Set RPC option
09102 @param hConn              RPC connection handle
09103 @param item               One of RPC_Oxxx
09104 @param value              Value to set
09105 @return RPC_SUCCESS
09106 */
09107 INT rpc_set_option(HNDLE hConn, INT item, INT value)
09108 {
09109    switch (item) {
09110    case RPC_OTIMEOUT:
09111       if (hConn == -1)
09112          _server_connection.rpc_timeout = value;
09113       else
09114          _client_connection[hConn - 1].rpc_timeout = value;
09115       break;
09116 
09117    case RPC_OTRANSPORT:
09118       if (hConn == -1)
09119          _server_connection.transport = value;
09120       else
09121          _client_connection[hConn - 1].transport = value;
09122       break;
09123 
09124    case RPC_NODELAY:
09125       if (hConn == -1)
09126          setsockopt(_server_connection.send_sock, IPPROTO_TCP,
09127                     TCP_NODELAY, (char *) &value, sizeof(value));
09128       else
09129          setsockopt(_client_connection[hConn - 1].send_sock, IPPROTO_TCP,
09130                     TCP_NODELAY, (char *) &value, sizeof(value));
09131       break;
09132 
09133    default:
09134       cm_msg(MERROR, "rpc_set_option", "invalid argument");
09135       break;
09136    }
09137 
09138    return 0;
09139 }
09140 
09141 
09142 /**dox***************************************************************/
09143 #ifndef DOXYGEN_SHOULD_SKIP_THIS
09144 
09145 /********************************************************************/
09146 PTYPE rpc_get_server_option(INT item)
09147 /********************************************************************\
09148 
09149   Routine: rpc_get_server_option
09150 
09151   Purpose: Get actual RPC option for server connection
09152 
09153   Input:
09154     INT  item               One of RPC_Oxxx
09155 
09156   Output:
09157     none
09158 
09159   Function value:
09160     INT                     Actual option
09161 
09162 \********************************************************************/
09163 {
09164    INT i;
09165 
09166    if (item == RPC_OSERVER_TYPE)
09167       return _server_type;
09168 
09169    if (item == RPC_OSERVER_NAME)
09170       return (PTYPE) _server_name;
09171 
09172    /* return 0 for local calls */
09173    if (_server_type == ST_NONE)
09174       return 0;
09175 
09176    /* check which connections belongs to caller */
09177    if (_server_type == ST_MTHREAD) {
09178       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09179          if (_server_acception[i].tid == ss_gettid())
09180             break;
09181    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09182       i = max(0, _server_acception_index - 1);
09183    else
09184       i = 0;
09185 
09186    switch (item) {
09187    case RPC_CONVERT_FLAGS:
09188       return _server_acception[i].convert_flags;
09189    case RPC_ODB_HANDLE:
09190       return _server_acception[i].odb_handle;
09191    case RPC_CLIENT_HANDLE:
09192       return _server_acception[i].client_handle;
09193    case RPC_SEND_SOCK:
09194       return _server_acception[i].send_sock;
09195    case RPC_WATCHDOG_TIMEOUT:
09196       return _server_acception[i].watchdog_timeout;
09197    }
09198 
09199    return 0;
09200 }
09201 
09202 
09203 /********************************************************************/
09204 INT rpc_set_server_option(INT item, PTYPE value)
09205 /********************************************************************\
09206 
09207   Routine: rpc_set_server_option
09208 
09209   Purpose: Set RPC option for server connection
09210 
09211   Input:
09212    INT  item               One of RPC_Oxxx
09213    INT  value              Value to set
09214 
09215   Output:
09216     none
09217 
09218   Function value:
09219     RPC_SUCCESS             Successful completion
09220 
09221 \********************************************************************/
09222 {
09223    INT i;
09224 
09225    if (item == RPC_OSERVER_TYPE) {
09226       _server_type = value;
09227       return RPC_SUCCESS;
09228    }
09229    if (item == RPC_OSERVER_NAME) {
09230       strcpy(_server_name, (char *) value);
09231       return RPC_SUCCESS;
09232    }
09233 
09234    /* check which connections belongs to caller */
09235    if (_server_type == ST_MTHREAD) {
09236       for (i = 0; i < MAX_RPC_CONNECTION; i++)
09237          if (_server_acception[i].tid == ss_gettid())
09238             break;
09239    } else if (_server_type == ST_SINGLE || _server_type == ST_REMOTE)
09240       i = max(0, _server_acception_index - 1);
09241    else
09242       i = 0;
09243 
09244    switch (item) {
09245    case RPC_CONVERT_FLAGS:
09246       _server_acception[i].convert_flags = value;
09247       break;
09248    case RPC_ODB_HANDLE:
09249       _server_acception[i].odb_handle = value;
09250       break;
09251    case RPC_CLIENT_HANDLE:
09252       _server_acception[i].client_handle = value;
09253       break;
09254    case RPC_WATCHDOG_TIMEOUT:
09255       _server_acception[i].watchdog_timeout = value;
09256       break;
09257    }
09258 
09259    return RPC_SUCCESS;
09260 }
09261 
09262 
09263 /********************************************************************/
09264 INT rpc_get_name(char *name)
09265 /********************************************************************\
09266 
09267   Routine: rpc_get_name
09268 
09269   Purpose: Get name set by rpc_set_name
09270 
09271   Input:
09272     none
09273 
09274   Output:
09275     char*  name             The location pointed by *name receives a
09276                             copy of the _prog_name
09277 
09278   Function value:
09279     RPC_SUCCESS             Successful completion
09280 
09281 \********************************************************************/
09282 {
09283    strcpy(name, _client_name);
09284 
09285    return RPC_SUCCESS;
09286 }
09287 
09288 
09289 /********************************************************************/
09290 INT rpc_set_name(char *name)
09291 /********************************************************************\
09292 
09293   Routine: rpc_set_name
09294 
09295   Purpose: Set name of actual program for further rpc connections
09296 
09297   Input:
09298    char *name               Program name, up to NAME_LENGTH chars,
09299                             no blanks
09300 
09301   Output:
09302     none
09303 
09304   Function value:
09305     RPC_SUCCESS             Successful completion
09306 
09307 \********************************************************************/
09308 {
09309    strcpy(_client_name, name);
09310 
09311    return RPC_SUCCESS;
09312 }
09313 
09314 
09315 /********************************************************************/
09316 INT rpc_set_debug(void (*func) (char *), INT mode)
09317 /********************************************************************\
09318 
09319   Routine: rpc_set_debug
09320 
09321   Purpose: Set a function which is called on every RPC call to
09322            display the function name and parameters of the RPC
09323            call.
09324 
09325   Input:
09326    void *func(char*)        Pointer to function.
09327    INT  mode                Debug mode
09328 
09329   Output:
09330     none
09331 
09332   Function value:
09333     RPC_SUCCESS             Successful completion
09334 
09335 \********************************************************************/
09336 {
09337    _debug_print = func;
09338    _debug_mode = mode;
09339    return RPC_SUCCESS;
09340 }
09341 
09342 
09343 /********************************************************************/
09344 void rpc_va_arg(va_list * arg_ptr, INT arg_type, void *arg)
09345 {
09346    switch (arg_type) {
09347       /* On the stack, the minimum parameter size is sizeof(int).
09348          To avoid problems on little endian systems, treat all
09349          smaller parameters as int's */
09350    case TID_BYTE:
09351    case TID_SBYTE:
09352    case TID_CHAR:
09353    case TID_WORD:
09354    case TID_SHORT:
09355       *((int *) arg) = va_arg(*arg_ptr, int);
09356       break;
09357 
09358    case TID_INT:
09359    case TID_BOOL:
09360       *((INT *) arg) = va_arg(*arg_ptr, INT);
09361       break;
09362 
09363    case TID_DWORD:
09364       *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
09365       break;
09366 
09367       /* float variables are passed as double by the compiler */
09368    case TID_FLOAT:
09369       *((float *) arg) = (float) va_arg(*arg_ptr, double);
09370       break;
09371 
09372    case TID_DOUBLE:
09373       *((double *) arg) = va_arg(*arg_ptr, double);
09374       break;
09375 
09376    case TID_ARRAY:
09377       *((char **) arg) = va_arg(*arg_ptr, char *);
09378       break;
09379    }
09380 }
09381 
09382 
09383 /********************************************************************/
09384 INT rpc_client_call(HNDLE hConn, const INT routine_id, ...)
09385 /********************************************************************\
09386 
09387   Routine: rpc_client_call
09388 
09389   Purpose: Call a function on a MIDAS client
09390 
09391   Input:
09392     INT  hConn              Client connection
09393     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09394 
09395     ...                     variable argument list
09396 
09397   Output:
09398     (depends on argument list)
09399 
09400   Function value:
09401     RPC_SUCCESS             Successful completion
09402     RPC_NET_ERROR           Error in socket call
09403     RPC_NO_CONNECTION       No active connection
09404     RPC_TIMEOUT             Timeout in RPC call
09405     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09406     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09407 
09408 \********************************************************************/
09409 {
09410    va_list ap, aptmp;
09411    char arg[8], arg_tmp[8];
09412    INT arg_type, transport, rpc_timeout;
09413    INT i, index, status, rpc_index;
09414    INT param_size, arg_size, send_size;
09415    INT tid, flags;
09416    fd_set readfds;
09417    struct timeval timeout;
09418    char *param_ptr, str[80];
09419    BOOL bpointer, bbig;
09420    NET_COMMAND *nc;
09421    int send_sock;
09422 
09423    index = hConn - 1;
09424 
09425    if (_client_connection[index].send_sock == 0) {
09426       cm_msg(MERROR, "rpc_client_call", "no rpc connection");
09427       return RPC_NO_CONNECTION;
09428    }
09429 
09430    send_sock = _client_connection[index].send_sock;
09431    rpc_timeout = _client_connection[index].rpc_timeout;
09432    transport = _client_connection[index].transport;
09433 
09434    /* init network buffer */
09435    if (_net_send_buffer_size == 0) {
09436       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09437       if (_net_send_buffer == NULL) {
09438          cm_msg(MERROR, "rpc_client_call",
09439                 "not enough memory to allocate network buffer");
09440          return RPC_EXCEED_BUFFER;
09441       }
09442       _net_send_buffer_size = NET_BUFFER_SIZE;
09443    }
09444 
09445    nc = (NET_COMMAND *) _net_send_buffer;
09446    nc->header.routine_id = routine_id;
09447 
09448    if (transport == RPC_FTCP)
09449       nc->header.routine_id |= TCP_FAST;
09450 
09451    for (i = 0;; i++)
09452       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09453          break;
09454    rpc_index = i;
09455    if (rpc_list[i].id == 0) {
09456       sprintf(str, "invalid rpc ID (%d)", routine_id);
09457       cm_msg(MERROR, "rpc_client_call", str);
09458       return RPC_INVALID_ID;
09459    }
09460 
09461    /* examine variable argument list and convert it to parameter array */
09462    va_start(ap, routine_id);
09463 
09464    /* find out if we are on a big endian system */
09465    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09466 
09467    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09468       tid = rpc_list[rpc_index].param[i].tid;
09469       flags = rpc_list[rpc_index].param[i].flags;
09470 
09471       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09472           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09473           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09474 
09475       if (bpointer)
09476          arg_type = TID_ARRAY;
09477       else
09478          arg_type = tid;
09479 
09480       /* floats are passed as doubles, at least under NT */
09481       if (tid == TID_FLOAT && !bpointer)
09482          arg_type = TID_DOUBLE;
09483 
09484       /* get pointer to argument */
09485       rpc_va_arg(&ap, arg_type, arg);
09486 
09487       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09488       if (bbig) {
09489          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09490             arg[0] = arg[3];
09491          }
09492          if (tid == TID_WORD || tid == TID_SHORT) {
09493             arg[0] = arg[2];
09494             arg[1] = arg[3];
09495          }
09496       }
09497 
09498       if (flags & RPC_IN) {
09499          if (bpointer)
09500             arg_size = tid_size[tid];
09501          else
09502             arg_size = tid_size[arg_type];
09503 
09504          /* for strings, the argument size depends on the string length */
09505          if (tid == TID_STRING || tid == TID_LINK)
09506             arg_size = 1 + strlen((char *) *((char **) arg));
09507 
09508          /* for varibale length arrays, the size is given by
09509             the next parameter on the stack */
09510          if (flags & RPC_VARARRAY) {
09511             memcpy(&aptmp, &ap, sizeof(ap));
09512             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09513 
09514             if (flags & RPC_OUT)
09515                arg_size = *((INT *) * ((void **) arg_tmp));
09516             else
09517                arg_size = *((INT *) arg_tmp);
09518 
09519             *((INT *) param_ptr) = ALIGN8(arg_size);
09520             param_ptr += ALIGN8(sizeof(INT));
09521          }
09522 
09523          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09524             arg_size = rpc_list[rpc_index].param[i].n;
09525 
09526          /* always align parameter size */
09527          param_size = ALIGN8(arg_size);
09528 
09529          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09530             cm_msg(MERROR, "rpc_client_call",
09531                    "parameters (%d) too large for network buffer (%d)",
09532                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09533             return RPC_EXCEED_BUFFER;
09534          }
09535 
09536          if (bpointer)
09537             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09538          else {
09539             /* floats are passed as doubles on most systems */
09540             if (tid != TID_FLOAT)
09541                memcpy(param_ptr, arg, arg_size);
09542             else
09543                *((float *) param_ptr) = (float) *((double *) arg);
09544          }
09545 
09546          param_ptr += param_size;
09547 
09548       }
09549    }
09550 
09551    va_end(ap);
09552 
09553    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09554 
09555    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09556 
09557    /* in FAST TCP mode, only send call and return immediately */
09558    if (transport == RPC_FTCP) {
09559       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09560 
09561       if (i != send_size) {
09562          cm_msg(MERROR, "rpc_client_call", "send_tcp() failed");
09563          return RPC_NET_ERROR;
09564       }
09565 
09566       return RPC_SUCCESS;
09567    }
09568 
09569    /* in TCP mode, send and wait for reply on send socket */
09570    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09571    if (i != send_size) {
09572       cm_msg(MERROR, "rpc_client_call",
09573              "send_tcp() failed, routine = \"%s\", host = \"%s\"",
09574              rpc_list[rpc_index].name, _client_connection[index].host_name);
09575       return RPC_NET_ERROR;
09576    }
09577 
09578    /* make some timeout checking */
09579    if (rpc_timeout > 0) {
09580       FD_ZERO(&readfds);
09581       FD_SET(send_sock, &readfds);
09582 
09583       timeout.tv_sec = rpc_timeout / 1000;
09584       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09585 
09586       do {
09587          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09588 
09589          /* if an alarm signal was cought, restart select with reduced timeout */
09590          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09591             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09592 
09593       } while (status == -1);   /* dont return if an alarm signal was cought */
09594 
09595       if (!FD_ISSET(send_sock, &readfds)) {
09596          cm_msg(MERROR, "rpc_client_call", "rpc timeout, routine = \"%s\", host = \"%s\"",
09597                 rpc_list[rpc_index].name, _client_connection[index].host_name);
09598 
09599          /* disconnect to avoid that the reply to this rpc_call comes at
09600             the next rpc_call */
09601          rpc_client_disconnect(hConn, FALSE);
09602 
09603          return RPC_TIMEOUT;
09604       }
09605    }
09606 
09607    /* receive result on send socket */
09608    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09609 
09610    if (i <= 0) {
09611       cm_msg(MERROR, "rpc_client_call",
09612              "recv_tcp() failed, routine = \"%s\", host = \"%s\"",
09613              rpc_list[rpc_index].name, _client_connection[index].host_name);
09614       return RPC_NET_ERROR;
09615    }
09616 
09617    /* extract result variables and place it to argument list */
09618    status = nc->header.routine_id;
09619 
09620    va_start(ap, routine_id);
09621 
09622    for (i = 0, param_ptr = nc->param; rpc_list[rpc_index].param[i].tid != 0; i++) {
09623       tid = rpc_list[rpc_index].param[i].tid;
09624       flags = rpc_list[rpc_index].param[i].flags;
09625 
09626       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09627           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09628           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09629 
09630       if (bpointer)
09631          arg_type = TID_ARRAY;
09632       else
09633          arg_type = rpc_list[rpc_index].param[i].tid;
09634 
09635       if (tid == TID_FLOAT && !bpointer)
09636          arg_type = TID_DOUBLE;
09637 
09638       rpc_va_arg(&ap, arg_type, arg);
09639 
09640       if (rpc_list[rpc_index].param[i].flags & RPC_OUT) {
09641          tid = rpc_list[rpc_index].param[i].tid;
09642          flags = rpc_list[rpc_index].param[i].flags;
09643 
09644          arg_size = tid_size[tid];
09645 
09646          if (tid == TID_STRING || tid == TID_LINK)
09647             arg_size = strlen((char *) (param_ptr)) + 1;
09648 
09649          if (flags & RPC_VARARRAY) {
09650             arg_size = *((INT *) param_ptr);
09651             param_ptr += ALIGN8(sizeof(INT));
09652          }
09653 
09654          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09655             arg_size = rpc_list[rpc_index].param[i].n;
09656 
09657          /* return parameters are always pointers */
09658          if (*((char **) arg))
09659             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09660 
09661          /* parameter size is always aligned */
09662          param_size = ALIGN8(arg_size);
09663 
09664          param_ptr += param_size;
09665       }
09666    }
09667 
09668    va_end(ap);
09669 
09670    return status;
09671 }
09672 
09673 
09674 /********************************************************************/
09675 INT rpc_call(const INT routine_id, ...)
09676 /********************************************************************\
09677 
09678   Routine: rpc_call
09679 
09680   Purpose: Call a function on a MIDAS server
09681 
09682   Input:
09683     INT  routine_id         routine ID as defined in RPC.H (RPC_xxx)
09684 
09685     ...                     variable argument list
09686 
09687   Output:
09688     (depends on argument list)
09689 
09690   Function value:
09691     RPC_SUCCESS             Successful completion
09692     RPC_NET_ERROR           Error in socket call
09693     RPC_NO_CONNECTION       No active connection
09694     RPC_TIMEOUT             Timeout in RPC call
09695     RPC_INVALID_ID          Invalid routine_id (not in rpc_list)
09696     RPC_EXCEED_BUFFER       Paramters don't fit in network buffer
09697 
09698 \********************************************************************/
09699 {
09700    va_list ap, aptmp;
09701    char arg[8], arg_tmp[8];
09702    INT arg_type, transport, rpc_timeout;
09703    INT i, index, status;
09704    INT param_size, arg_size, send_size;
09705    INT tid, flags;
09706    fd_set readfds;
09707    struct timeval timeout;
09708    char *param_ptr, str[80];
09709    BOOL bpointer, bbig;
09710    NET_COMMAND *nc;
09711    int send_sock;
09712 
09713    send_sock = _server_connection.send_sock;
09714    transport = _server_connection.transport;
09715    rpc_timeout = _server_connection.rpc_timeout;
09716 
09717    /* init network buffer */
09718    if (_net_send_buffer_size == 0) {
09719       _net_send_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
09720       if (_net_send_buffer == NULL) {
09721          cm_msg(MERROR, "rpc_call", "not enough memory to allocate network buffer");
09722          return RPC_EXCEED_BUFFER;
09723       }
09724       _net_send_buffer_size = NET_BUFFER_SIZE;
09725    }
09726 
09727    nc = (NET_COMMAND *) _net_send_buffer;
09728    nc->header.routine_id = routine_id;
09729 
09730    if (transport == RPC_FTCP)
09731       nc->header.routine_id |= TCP_FAST;
09732 
09733    for (i = 0;; i++)
09734       if (rpc_list[i].id == routine_id || rpc_list[i].id == 0)
09735          break;
09736    index = i;
09737    if (rpc_list[i].id == 0) {
09738       sprintf(str, "invalid rpc ID (%d)", routine_id);
09739       cm_msg(MERROR, "rpc_call", str);
09740       return RPC_INVALID_ID;
09741    }
09742 
09743    /* examine variable argument list and convert it to parameter array */
09744    va_start(ap, routine_id);
09745 
09746    /* find out if we are on a big endian system */
09747    bbig = ((rpc_get_option(0, RPC_OHW_TYPE) & DRI_BIG_ENDIAN) > 0);
09748 
09749    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09750       tid = rpc_list[index].param[i].tid;
09751       flags = rpc_list[index].param[i].flags;
09752 
09753       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09754           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09755           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09756 
09757       if (bpointer)
09758          arg_type = TID_ARRAY;
09759       else
09760          arg_type = tid;
09761 
09762       /* floats are passed as doubles, at least under NT */
09763       if (tid == TID_FLOAT && !bpointer)
09764          arg_type = TID_DOUBLE;
09765 
09766       /* get pointer to argument */
09767       rpc_va_arg(&ap, arg_type, arg);
09768 
09769       /* shift 1- and 2-byte parameters to the LSB on big endian systems */
09770       if (bbig) {
09771          if (tid == TID_BYTE || tid == TID_CHAR || tid == TID_SBYTE) {
09772             arg[0] = arg[3];
09773          }
09774          if (tid == TID_WORD || tid == TID_SHORT) {
09775             arg[0] = arg[2];
09776             arg[1] = arg[3];
09777          }
09778       }
09779 
09780       if (flags & RPC_IN) {
09781          if (bpointer)
09782             arg_size = tid_size[tid];
09783          else
09784             arg_size = tid_size[arg_type];
09785 
09786          /* for strings, the argument size depends on the string length */
09787          if (tid == TID_STRING || tid == TID_LINK)
09788             arg_size = 1 + strlen((char *) *((char **) arg));
09789 
09790          /* for varibale length arrays, the size is given by
09791             the next parameter on the stack */
09792          if (flags & RPC_VARARRAY) {
09793             memcpy(&aptmp, &ap, sizeof(ap));
09794             rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
09795 
09796             if (flags & RPC_OUT)
09797                arg_size = *((INT *) * ((void **) arg_tmp));
09798             else
09799                arg_size = *((INT *) arg_tmp);
09800 
09801             *((INT *) param_ptr) = ALIGN8(arg_size);
09802             param_ptr += ALIGN8(sizeof(INT));
09803          }
09804 
09805          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09806             arg_size = rpc_list[index].param[i].n;
09807 
09808          /* always align parameter size */
09809          param_size = ALIGN8(arg_size);
09810 
09811          if ((PTYPE) param_ptr - (PTYPE) nc + param_size > NET_BUFFER_SIZE) {
09812             cm_msg(MERROR, "rpc_call",
09813                    "parameters (%d) too large for network buffer (%d)",
09814                    (PTYPE) param_ptr - (PTYPE) nc + param_size, NET_BUFFER_SIZE);
09815             return RPC_EXCEED_BUFFER;
09816          }
09817 
09818          if (bpointer)
09819             memcpy(param_ptr, (void *) *((void **) arg), arg_size);
09820          else {
09821             /* floats are passed as doubles on most systems */
09822             if (tid != TID_FLOAT)
09823                memcpy(param_ptr, arg, arg_size);
09824             else
09825                *((float *) param_ptr) = (float) *((double *) arg);
09826          }
09827 
09828          param_ptr += param_size;
09829 
09830       }
09831    }
09832 
09833    va_end(ap);
09834 
09835    nc->header.param_size = (PTYPE) param_ptr - (PTYPE) nc->param;
09836 
09837    send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
09838 
09839    /* in FAST TCP mode, only send call and return immediately */
09840    if (transport == RPC_FTCP) {
09841       i = send_tcp(send_sock, (char *) nc, send_size, 0);
09842 
09843       if (i != send_size) {
09844          cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09845          return RPC_NET_ERROR;
09846       }
09847 
09848       return RPC_SUCCESS;
09849    }
09850 
09851    /* in TCP mode, send and wait for reply on send socket */
09852    i = send_tcp(send_sock, (char *) nc, send_size, 0);
09853    if (i != send_size) {
09854       cm_msg(MERROR, "rpc_call", "send_tcp() failed");
09855       return RPC_NET_ERROR;
09856    }
09857 
09858    /* make some timeout checking */
09859    if (rpc_timeout > 0) {
09860       FD_ZERO(&readfds);
09861       FD_SET(send_sock, &readfds);
09862 
09863       timeout.tv_sec = rpc_timeout / 1000;
09864       timeout.tv_usec = (rpc_timeout % 1000) * 1000;
09865 
09866       do {
09867          status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
09868 
09869          /* if an alarm signal was cought, restart select with reduced timeout */
09870          if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
09871             timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
09872 
09873       } while (status == -1);   /* dont return if an alarm signal was cought */
09874 
09875       if (!FD_ISSET(send_sock, &readfds)) {
09876          cm_msg(MERROR, "rpc_call", "rpc timeout, routine = \"%s\"",
09877                 rpc_list[index].name);
09878 
09879          /* disconnect to avoid that the reply to this rpc_call comes at
09880             the next rpc_call */
09881          rpc_server_disconnect();
09882 
09883          return RPC_TIMEOUT;
09884       }
09885    }
09886 
09887    /* receive result on send socket */
09888    i = recv_tcp(send_sock, _net_send_buffer, NET_BUFFER_SIZE, 0);
09889 
09890    if (i <= 0) {
09891       cm_msg(MERROR, "rpc_call", "recv_tcp() failed, routine = \"%s\"",
09892              rpc_list[index].name);
09893       return RPC_NET_ERROR;
09894    }
09895 
09896    /* extract result variables and place it to argument list */
09897    status = nc->header.routine_id;
09898 
09899    va_start(ap, routine_id);
09900 
09901    for (i = 0, param_ptr = nc->param; rpc_list[index].param[i].tid != 0; i++) {
09902       tid = rpc_list[index].param[i].tid;
09903       flags = rpc_list[index].param[i].flags;
09904 
09905       bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
09906           (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
09907           tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
09908 
09909       if (bpointer)
09910          arg_type = TID_ARRAY;
09911       else
09912          arg_type = rpc_list[index].param[i].tid;
09913 
09914       if (tid == TID_FLOAT && !bpointer)
09915          arg_type = TID_DOUBLE;
09916 
09917       rpc_va_arg(&ap, arg_type, arg);
09918 
09919       if (rpc_list[index].param[i].flags & RPC_OUT) {
09920          tid = rpc_list[index].param[i].tid;
09921          arg_size = tid_size[tid];
09922 
09923          if (tid == TID_STRING || tid == TID_LINK)
09924             arg_size = strlen((char *) (param_ptr)) + 1;
09925 
09926          if (flags & RPC_VARARRAY) {
09927             arg_size = *((INT *) param_ptr);
09928             param_ptr += ALIGN8(sizeof(INT));
09929          }
09930 
09931          if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
09932             arg_size = rpc_list[index].param[i].n;
09933 
09934          /* return parameters are always pointers */
09935          if (*((char **) arg))
09936             memcpy((void *) *((char **) arg), param_ptr, arg_size);
09937 
09938          /* parameter size is always aligned */
09939          param_size = ALIGN8(arg_size);
09940 
09941          param_ptr += param_size;
09942       }
09943    }
09944 
09945    va_end(ap);
09946 
09947    return status;
09948 }
09949 
09950 
09951 /********************************************************************/
09952 INT rpc_set_opt_tcp_size(INT tcp_size)
09953 {
09954    INT old;
09955 
09956    old = _opt_tcp_size;
09957    _opt_tcp_size = tcp_size;
09958    return old;
09959 }
09960 
09961 INT rpc_get_opt_tcp_size()
09962 {
09963    return _opt_tcp_size;
09964 }
09965 
09966 /**dox***************************************************************/
09967 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
09968 
09969 /********************************************************************/
09970 /**
09971 Fast send_event routine which bypasses the RPC layer and
09972            sends the event directly at the TCP level.
09973 @param buffer_handle      Handle of the buffer to send the event to.
09974                             Must be obtained via bm_open_buffer.
09975 @param source            Address of the event to send. It must have
09976                             a proper event header.
09977 @param buf_size           Size of event in bytes with header.
09978 @param async_flag         SYNC / ASYNC flag. In ASYNC mode, the
09979                             function returns immediately if it cannot
09980                             send the event over the network. In SYNC
09981                             mode, it waits until the packet is sent
09982                             (blocking).
09983 
09984 @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR, 
09985         RPC_NO_CONNECTION, RPC_EXCEED_BUFFER       
09986 */
09987 INT rpc_send_event(INT buffer_handle, void *source, INT buf_size, INT async_flag)
09988 {
09989    INT i;
09990    NET_COMMAND *nc;
09991    unsigned long flag;
09992    BOOL would_block = 0;
09993    DWORD aligned_buf_size;
09994 
09995    aligned_buf_size = ALIGN8(buf_size);
09996 
09997    if (aligned_buf_size !=
09998        (INT) ALIGN8(((EVENT_HEADER *) source)->data_size + sizeof(EVENT_HEADER))) {
09999       cm_msg(MERROR, "rpc_send_event", "event size mismatch");
10000       return BM_INVALID_PARAM;
10001    }
10002    if (((EVENT_HEADER *) source)->data_size > MAX_EVENT_SIZE) {
10003       cm_msg(MERROR, "rpc_send_event",
10004              "event size (%d) larger than maximum event size (%d)",
10005              ((EVENT_HEADER *) source)->data_size, MAX_EVENT_SIZE);
10006       return RPC_EXCEED_BUFFER;
10007    }
10008 
10009    if (!rpc_is_remote())
10010       return bm_send_event(buffer_handle, source, buf_size, async_flag);
10011 
10012    /* init network buffer */
10013    if (!_tcp_buffer)
10014       _tcp_buffer = (char *) M_MALLOC(NET_TCP_SIZE);
10015    if (!_tcp_buffer) {
10016       cm_msg(MERROR, "rpc_send_event", "not enough memory to allocate network buffer");
10017       return RPC_EXCEED_BUFFER;
10018    }
10019 
10020    /* check if not enough space in TCP buffer */
10021    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >=
10022        (DWORD) (_opt_tcp_size - _tcp_wp) && _tcp_wp != _tcp_rp) {
10023       /* set socket to nonblocking IO */
10024       if (async_flag == ASYNC) {
10025          flag = 1;
10026 #ifdef OS_VXWORKS
10027          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10028 #else
10029          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10030 #endif
10031       }
10032 
10033       i = send_tcp(_server_connection.send_sock,
10034                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10035 
10036       if (i < 0)
10037 #ifdef OS_WINNT
10038          would_block = (WSAGetLastError() == WSAEWOULDBLOCK);
10039 #else
10040          would_block = (errno == EWOULDBLOCK);
10041 #endif
10042 
10043       /* set socket back to blocking IO */
10044       if (async_flag == ASYNC) {
10045          flag = 0;
10046 #ifdef OS_VXWORKS
10047          ioctlsocket(_server_connection.send_sock, FIONBIO, (int) &flag);
10048 #else
10049          ioctlsocket(_server_connection.send_sock, FIONBIO, &flag);
10050 #endif
10051       }
10052 
10053       /* increment read pointer */
10054       if (i > 0)
10055          _tcp_rp += i;
10056 
10057       /* check if whole buffer is sent */
10058       if (_tcp_rp == _tcp_wp)
10059          _tcp_rp = _tcp_wp = 0;
10060 
10061       if (i < 0 && !would_block) {
10062          printf("send_tcp() returned %d\n", i);
10063          cm_msg(MERROR, "rpc_send_event", "send_tcp() failed");
10064          return RPC_NET_ERROR;
10065       }
10066 
10067       /* return if buffer is not emptied */
10068       if (_tcp_wp > 0)
10069          return BM_ASYNC_RETURN;
10070    }
10071 
10072    nc = (NET_COMMAND *) (_tcp_buffer + _tcp_wp);
10073    nc->header.routine_id = RPC_BM_SEND_EVENT | TCP_FAST;
10074    nc->header.param_size = 4 * 8 + aligned_buf_size;
10075 
10076    /* assemble parameters manually */
10077    *((INT *) (&nc->param[0])) = buffer_handle;
10078    *((INT *) (&nc->param[8])) = buf_size;
10079 
10080    /* send events larger than optimal buffer size directly */
10081    if (aligned_buf_size + 4 * 8 + sizeof(NET_COMMAND_HEADER) >= (DWORD) _opt_tcp_size) {
10082       /* send header */
10083       send_tcp(_server_connection.send_sock,
10084                _tcp_buffer + _tcp_wp, sizeof(NET_COMMAND_HEADER) + 16, 0);
10085 
10086       /* send data */
10087       send_tcp(_server_connection.send_sock, (char *) source, aligned_buf_size, 0);
10088 
10089       /* send last two parameters */
10090       *((INT *) (&nc->param[0])) = buf_size;
10091       *((INT *) (&nc->param[8])) = 0;
10092       send_tcp(_server_connection.send_sock, &nc->param[0], 16, 0);
10093    } else {
10094       /* copy event */
10095       memcpy(&nc->param[16], source, buf_size);
10096 
10097       /* last two parameters (buf_size and async_flag */
10098       *((INT *) (&nc->param[16 + aligned_buf_size])) = buf_size;
10099       *((INT *) (&nc->param[24 + aligned_buf_size])) = 0;
10100 
10101       _tcp_wp += nc->header.param_size + sizeof(NET_COMMAND_HEADER);
10102    }
10103 
10104    return RPC_SUCCESS;
10105 }
10106 
10107 
10108 
10109 /**dox***************************************************************/
10110 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10111 
10112 /********************************************************************/
10113 int rpc_get_send_sock()
10114 /********************************************************************\
10115 
10116   Routine: rpc_get_send_sock
10117 
10118   Purpose: Return send socket to MIDAS server. Used by MFE.C for
10119            optimized event sending.
10120 
10121   Input:
10122     none
10123 
10124   Output:
10125     none
10126 
10127   Function value:
10128     int    socket
10129 
10130 \********************************************************************/
10131 {
10132    return _server_connection.send_sock;
10133 }
10134 
10135 
10136 /********************************************************************/
10137 int rpc_get_event_sock()
10138 /********************************************************************\
10139 
10140   Routine: rpc_get_event_sock
10141 
10142   Purpose: Return event send socket to MIDAS server. Used by MFE.C for
10143            optimized event sending.
10144 
10145   Input:
10146     none
10147 
10148   Output:
10149     none
10150 
10151   Function value:
10152     int    socket
10153 
10154 \********************************************************************/
10155 {
10156    return _server_connection.event_sock;
10157 }
10158 
10159 /**dox***************************************************************/
10160 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
10161 
10162 /********************************************************************/
10163 /**
10164 Send event residing in the TCP cache buffer filled by
10165            rpc_send_event. This routine should be called when a
10166            run is stopped.
10167 
10168 @return RPC_SUCCESS, RPC_NET_ERROR
10169 */
10170 INT rpc_flush_event()
10171 {
10172    INT i;
10173 
10174    if (!rpc_is_remote())
10175       return RPC_SUCCESS;
10176 
10177    /* return if rpc_send_event was not called */
10178    if (!_tcp_buffer || _tcp_wp == 0)
10179       return RPC_SUCCESS;
10180 
10181    /* empty TCP buffer */
10182    if (_tcp_wp > 0) {
10183       i = send_tcp(_server_connection.send_sock,
10184                    _tcp_buffer + _tcp_rp, _tcp_wp - _tcp_rp, 0);
10185 
10186       if (i != _tcp_wp - _tcp_rp) {
10187          cm_msg(MERROR, "rpc_flush_event", "send_tcp() failed");
10188          return RPC_NET_ERROR;
10189       }
10190    }
10191 
10192    _tcp_rp = _tcp_wp = 0;
10193 
10194    return RPC_SUCCESS;
10195 }
10196 
10197 /**dox***************************************************************/
10198 #ifndef DOXYGEN_SHOULD_SKIP_THIS
10199 
10200 /********************************************************************/
10201 static INT rpc_transition_dispatch(INT index, void *prpc_param[])
10202 /********************************************************************\
10203 
10204   Routine: rpc_transition_dispatch
10205 
10206   Purpose: Gets called when a transition function was registered and
10207            a transition occured. Internal use only.
10208 
10209   Input:
10210     INT    index            RPC function ID
10211     void   *prpc_param      RPC parameters
10212 
10213   Output:
10214     none
10215 
10216   Function value:
10217     INT    return value from called user routine
10218 
10219 \********************************************************************/
10220 {
10221    INT status, i;
10222 
10223    if (index == RPC_RC_TRANSITION) {
10224       for (i = 0; _trans_table[i].transition; i++)
10225          if (_trans_table[i].transition == CINT(0))
10226             break;
10227 
10228       /* erase error string */
10229       *(CSTRING(2)) = 0;
10230 
10231       /* call registerd function */
10232       if (_trans_table[i].transition == CINT(0) && _trans_table[i].func)
10233          status = _trans_table[i].func(CINT(1), CSTRING(2));
10234       else
10235          status = RPC_SUCCESS;
10236    } else {
10237       cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command");
10238       status = RPC_INVALID_ID;
10239    }
10240 
10241    return status;
10242 }
10243 
10244 
10245 /********************************************************************\
10246 *                        server functions                            *
10247 \********************************************************************/
10248 
10249 
10250 /********************************************************************/
10251 INT recv_tcp_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10252                     INT * remaining)
10253 /********************************************************************\
10254 
10255   Routine: recv_tcp_server
10256 
10257   Purpose: TCP receive routine with local cache. To speed up network
10258            performance, a 64k buffer is read in at once and split into
10259            several RPC command on successive calls to recv_tcp_server.
10260            Therefore, the number of recv() calls is minimized.
10261 
10262            This routine is ment to be called by the server process.
10263            Clients should call recv_tcp instead.
10264 
10265   Input:
10266     INT   index              Index of server connection
10267     DWORD buffer_size        Size of the buffer in bytes.
10268     INT   flags              Flags passed to recv()
10269     INT   convert_flags      Convert flags needed for big/little
10270                              endian conversion
10271 
10272   Output:
10273     char  *buffer            Network receive buffer.
10274     INT   *remaining         Remaining data in cache
10275 
10276   Function value:
10277     INT                      Same as recv()
10278 
10279 \********************************************************************/
10280 {
10281    INT size, param_size;
10282    NET_COMMAND *nc;
10283    INT write_ptr, read_ptr, misalign;
10284    char *net_buffer;
10285    INT copied, status;
10286    INT sock;
10287 
10288    sock = _server_acception[index].recv_sock;
10289 
10290    if (flags & MSG_PEEK) {
10291       status = recv(sock, buffer, buffer_size, flags);
10292       if (status == -1)
10293          cm_msg(MERROR, "recv_tcp_server",
10294                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10295                 errno, strerror(errno));
10296       return status;
10297    }
10298 
10299    if (!_server_acception[index].net_buffer) {
10300       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10301          _server_acception[index].net_buffer_size = NET_TCP_SIZE;
10302       else
10303          _server_acception[index].net_buffer_size = NET_BUFFER_SIZE;
10304 
10305       _server_acception[index].net_buffer =
10306           (char *) M_MALLOC(_server_acception[index].net_buffer_size);
10307       _server_acception[index].write_ptr = 0;
10308       _server_acception[index].read_ptr = 0;
10309       _server_acception[index].misalign = 0;
10310    }
10311    if (!_server_acception[index].net_buffer) {
10312       cm_msg(MERROR, "recv_tcp_server", "not enough memory to allocate network buffer");
10313       return -1;
10314    }
10315 
10316    if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
10317       cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10318       return -1;
10319    }
10320 
10321    copied = 0;
10322    param_size = -1;
10323 
10324    write_ptr = _server_acception[index].write_ptr;
10325    read_ptr = _server_acception[index].read_ptr;
10326    misalign = _server_acception[index].misalign;
10327    net_buffer = _server_acception[index].net_buffer;
10328 
10329    do {
10330       if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
10331          if (param_size == -1) {
10332             if (copied > 0) {
10333                /* assemble split header */
10334                memcpy(buffer + copied, net_buffer + read_ptr,
10335                       (INT) sizeof(NET_COMMAND_HEADER) - copied);
10336                nc = (NET_COMMAND *) (buffer);
10337             } else
10338                nc = (NET_COMMAND *) (net_buffer + read_ptr);
10339 
10340             param_size = (INT) nc->header.param_size;
10341 
10342             if (_server_acception[index].convert_flags)
10343                rpc_convert_single(&param_size, TID_DWORD, 0,
10344                                   _server_acception[index].convert_flags);
10345          }
10346 
10347          /* check if parameters fit in buffer */
10348          if (buffer_size < param_size + sizeof(NET_COMMAND_HEADER)) {
10349             cm_msg(MERROR, "recv_tcp_server", "parameters too large for network buffer");
10350             _server_acception[index].read_ptr = _server_acception[index].write_ptr = 0;
10351             return -1;
10352          }
10353 
10354          /* check if we have all parameters in buffer */
10355          if (write_ptr - read_ptr >=
10356              param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
10357             break;
10358       }
10359 
10360       /* not enough data, so copy partially and get new */
10361       size = write_ptr - read_ptr;
10362 
10363       if (size > 0) {
10364          memcpy(buffer + copied, net_buffer + read_ptr, size);
10365          copied += size;
10366          read_ptr = write_ptr;
10367       }
10368 #ifdef OS_UNIX
10369       do {
10370          write_ptr =
10371              recv(sock, net_buffer + misalign,
10372                   _server_acception[index].net_buffer_size - 8, flags);
10373 
10374          /* don't return if an alarm signal was cought */
10375       } while (write_ptr == -1 && errno == EINTR);
10376 #else
10377       write_ptr =
10378           recv(sock, net_buffer + misalign, _server_acception[index].net_buffer_size - 8,
10379                flags);
10380 #endif
10381 
10382       /* abort if connection broken */
10383       if (write_ptr <= 0) {
10384          cm_msg(MERROR, "recv_tcp_server", "recv() returned %d, errno: %d (%s)",
10385                 write_ptr, errno, strerror(errno));
10386 
10387          if (remaining)
10388             *remaining = 0;
10389 
10390          return write_ptr;
10391       }
10392 
10393       read_ptr = misalign;
10394       write_ptr += misalign;
10395 
10396       misalign = write_ptr % 8;
10397    } while (TRUE);
10398 
10399    /* copy rest of parameters */
10400    size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
10401    memcpy(buffer + copied, net_buffer + read_ptr, size);
10402    read_ptr += size;
10403 
10404    if (remaining) {
10405       /* don't keep rpc_server_receive in an infinite loop */
10406       if (write_ptr - read_ptr < param_size)
10407          *remaining = 0;
10408       else
10409          *remaining = write_ptr - read_ptr;
10410    }
10411 
10412    _server_acception[index].write_ptr = write_ptr;
10413    _server_acception[index].read_ptr = read_ptr;
10414    _server_acception[index].misalign = misalign;
10415 
10416    return size + copied;
10417 }
10418 
10419 
10420 /********************************************************************/
10421 INT recv_tcp_check(int sock)
10422 /********************************************************************\
10423 
10424   Routine: recv_tcp_check
10425 
10426   Purpose: Check if in TCP receive buffer associated with sock is
10427            some data. Called by ss_suspend.
10428 
10429   Input:
10430     INT   sock               TCP receive socket
10431 
10432   Output:
10433     none
10434 
10435   Function value:
10436     INT   count              Number of bytes remaining in TCP buffer
10437 
10438 \********************************************************************/
10439 {
10440    INT index;
10441 
10442    /* figure out to which connection socket belongs */
10443    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10444       if (_server_acception[index].recv_sock == sock)
10445          break;
10446 
10447    return _server_acception[index].write_ptr - _server_acception[index].read_ptr;
10448 }
10449 
10450 
10451 /********************************************************************/
10452 INT recv_event_server(INT index, char *buffer, DWORD buffer_size, INT flags,
10453                       INT * remaining)
10454 /********************************************************************\
10455 
10456   Routine: recv_event_server
10457 
10458   Purpose: TCP event receive routine with local cache. To speed up
10459            network performance, a 64k buffer is read in at once and
10460            split into several RPC command on successive calls to
10461            recv_event_server. Therefore, the number of recv() calls
10462            is minimized.
10463 
10464            This routine is ment to be called by the server process.
10465            Clients should call recv_tcp instead.
10466 
10467   Input:
10468     INT   index              Index of server connection
10469     DWORD buffer_size        Size of the buffer in bytes.
10470     INT   flags              Flags passed to recv()
10471     INT   convert_flags      Convert flags needed for big/little
10472                              endian conversion
10473 
10474   Output:
10475     char  *buffer            Network receive buffer.
10476     INT   *remaining         Remaining data in cache
10477 
10478   Function value:
10479     INT                      Same as recv()
10480 
10481 \********************************************************************/
10482 {
10483    INT size, event_size, aligned_event_size = 0, *pbh, header_size;
10484    EVENT_HEADER *pevent;
10485    INT write_ptr, read_ptr, misalign;
10486    char *net_buffer;
10487    INT copied, status;
10488    INT sock;
10489    RPC_SERVER_ACCEPTION *psa;
10490 
10491    psa = &_server_acception[index];
10492    sock = psa->event_sock;
10493 
10494    if (flags & MSG_PEEK) {
10495       status = recv(sock, buffer, buffer_size, flags);
10496       if (status == -1)
10497          cm_msg(MERROR, "recv_event_server",
10498                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", buffer_size, status,
10499                 errno, strerror(errno));
10500       return status;
10501    }
10502 
10503    if (!psa->ev_net_buffer) {
10504       if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
10505          psa->net_buffer_size = NET_TCP_SIZE;
10506       else
10507          psa->net_buffer_size = NET_BUFFER_SIZE;
10508 
10509       psa->ev_net_buffer = (char *) M_MALLOC(psa->net_buffer_size);
10510       psa->ev_write_ptr = 0;
10511       psa->ev_read_ptr = 0;
10512       psa->ev_misalign = 0;
10513    }
10514    if (!psa->ev_net_buffer) {
10515       cm_msg(MERROR, "recv_event_server", "not enough memory to allocate network buffer");
10516       return -1;
10517    }
10518 
10519    header_size = (INT) (sizeof(EVENT_HEADER) + sizeof(INT));
10520 
10521    if ((INT) buffer_size < header_size) {
10522       cm_msg(MERROR, "recv_event_server", "parameters too large for network buffer");
10523       return -1;
10524    }
10525 
10526    copied = 0;
10527    event_size = -1;
10528 
10529    write_ptr = psa->ev_write_ptr;
10530    read_ptr = psa->ev_read_ptr;
10531    misalign = psa->ev_misalign;
10532    net_buffer = psa->ev_net_buffer;
10533 
10534    do {
10535       if (write_ptr - read_ptr >= header_size - copied) {
10536          if (event_size == -1) {
10537             if (copied > 0) {
10538                /* assemble split header */
10539                memcpy(buffer + copied, net_buffer + read_ptr, header_size - copied);
10540                pbh = (INT *) buffer;
10541             } else
10542                pbh = (INT *) (net_buffer + read_ptr);
10543 
10544             pevent = (EVENT_HEADER *) (pbh + 1);
10545 
10546             event_size = pevent->data_size;
10547             if (psa->convert_flags)
10548                rpc_convert_single(&event_size, TID_DWORD, 0, psa->convert_flags);
10549 
10550             aligned_event_size = ALIGN8(event_size);
10551          }
10552 
10553          /* check if data part fits in buffer */
10554          if ((INT) buffer_size < aligned_event_size + header_size) {
10555             cm_msg(MERROR, "recv_event_server",
10556                    "parameters too large for network buffer");
10557             psa->ev_read_ptr = psa->ev_write_ptr = 0;
10558             return -1;
10559          }
10560 
10561          /* check if we have whole event in buffer */
10562          if (write_ptr - read_ptr >= aligned_event_size + header_size - copied)
10563             break;
10564       }
10565 
10566       /* not enough data, so copy partially and get new */
10567       size = write_ptr - read_ptr;
10568 
10569       if (size > 0) {
10570          memcpy(buffer + copied, net_buffer + read_ptr, size);
10571          copied += size;
10572          read_ptr = write_ptr;
10573       }
10574 #ifdef OS_UNIX
10575       do {
10576          write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10577 
10578          /* don't return if an alarm signal was cought */
10579       } while (write_ptr == -1 && errno == EINTR);
10580 #else
10581       write_ptr = recv(sock, net_buffer + misalign, psa->net_buffer_size - 8, flags);
10582 #endif
10583 
10584       /* abort if connection broken */
10585       if (write_ptr <= 0) {
10586          cm_msg(MERROR, "recv_event_server", "recv() returned %d, errno: %d (%s)",
10587                 write_ptr, errno, strerror(errno));
10588 
10589          if (remaining)
10590             *remaining = 0;
10591 
10592          return write_ptr;
10593       }
10594 
10595       read_ptr = misalign;
10596       write_ptr += misalign;
10597 
10598       misalign = write_ptr % 8;
10599    } while (TRUE);
10600 
10601    /* copy rest of event */
10602    size = aligned_event_size + header_size - copied;
10603    if (size > 0) {
10604       memcpy(buffer + copied, net_buffer + read_ptr, size);
10605       read_ptr += size;
10606    }
10607 
10608    if (remaining)
10609       *remaining = write_ptr - read_ptr;
10610 
10611    psa->ev_write_ptr = write_ptr;
10612    psa->ev_read_ptr = read_ptr;
10613    psa->ev_misalign = misalign;
10614 
10615    /* convert header little endian/big endian */
10616    if (psa->convert_flags) {
10617       pevent = (EVENT_HEADER *) (((INT *) buffer) + 1);
10618 
10619       rpc_convert_single(buffer, TID_INT, 0, psa->convert_flags);
10620       rpc_convert_single(&pevent->event_id, TID_SHORT, 0, psa->convert_flags);
10621       rpc_convert_single(&pevent->trigger_mask, TID_SHORT, 0, psa->convert_flags);
10622       rpc_convert_single(&pevent->serial_number, TID_DWORD, 0, psa->convert_flags);
10623       rpc_convert_single(&pevent->time_stamp, TID_DWORD, 0, psa->convert_flags);
10624       rpc_convert_single(&pevent->data_size, TID_DWORD, 0, psa->convert_flags);
10625    }
10626 
10627    return header_size + event_size;
10628 }
10629 
10630 
10631 /********************************************************************/
10632 INT recv_event_check(int sock)
10633 /********************************************************************\
10634 
10635   Routine: recv_event_check
10636 
10637   Purpose: Check if in TCP event receive buffer associated with sock
10638            is some data. Called by ss_suspend.
10639 
10640   Input:
10641     INT   sock               TCP receive socket
10642 
10643   Output:
10644     none
10645 
10646   Function value:
10647     INT   count              Number of bytes remaining in TCP buffer
10648 
10649 \********************************************************************/
10650 {
10651    INT index;
10652 
10653    /* figure out to which connection socket belongs */
10654    for (index = 0; index < MAX_RPC_CONNECTION; index++)
10655       if (_server_acception[index].event_sock == sock)
10656          break;
10657 
10658    return _server_acception[index].ev_write_ptr - _server_acception[index].ev_read_ptr;
10659 }
10660 
10661 
10662 /********************************************************************/
10663 INT rpc_register_server(INT server_type, char *name, INT * port,
10664                         INT(*func) (INT, void **))
10665 /********************************************************************\
10666 
10667   Routine: rpc_register_server
10668 
10669   Purpose: Register the calling process as a MIDAS RPC server. Note
10670            that cm_connnect_experiment must be called prior to any call of
10671            rpc_register_server.
10672 
10673   Input:
10674     INT   server_type       One of the following constants:
10675                             ST_SINGLE: register a single process server
10676                             ST_MTHREAD: for each connection, start
10677                                         a new thread to serve it
10678                             ST_MPROCESS: for each connection, start
10679                                          a new process to server it
10680                             ST_SUBPROCESS: the routine was called from
10681                                            a multi process server
10682                             ST_REMOTE: register a client program server
10683                                        connected to the ODB
10684     char  *name             Name of .EXE file to start in MPROCESS mode
10685     INT   *port             TCP port for listen. NULL if listen as main
10686                             server (MIDAS_TCP_PORT is then used). If *port=0,
10687                             the OS chooses a free port and returns it. If
10688                             *port != 0, this port is used.
10689     INT   *func             Default dispatch function
10690 
10691   Output:
10692     INT   *port             Port under which server is listening.
10693 
10694   Function value:
10695     RPC_SUCCESS             Successful completion
10696     RPC_NET_ERROR           Error in socket call
10697     RPC_NOT_REGISTERED      cm_connect_experiment was not called
10698 
10699 \********************************************************************/
10700 {
10701    struct sockaddr_in bind_addr;
10702    INT status, flag;
10703    int size;
10704 
10705 #ifdef OS_WINNT
10706    {
10707       WSADATA WSAData;
10708 
10709       /* Start windows sockets */
10710       if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
10711          return RPC_NET_ERROR;
10712    }
10713 #endif
10714 
10715    rpc_set_server_option(RPC_OSERVER_TYPE, server_type);
10716 
10717    /* register system functions */
10718    rpc_register_functions(rpc_get_internal_list(0), func);
10719 
10720    if (name != NULL)
10721       rpc_set_server_option(RPC_OSERVER_NAME, (PTYPE) name);
10722 
10723    /* in subprocess mode, don't start listener */
10724    if (server_type == ST_SUBPROCESS)
10725       return RPC_SUCCESS;
10726 
10727    /* create a socket for listening */
10728    _lsock = socket(AF_INET, SOCK_STREAM, 0);
10729    if (_lsock == -1) {
10730       cm_msg(MERROR, "rpc_register_server", "socket() failed");
10731       return RPC_NET_ERROR;
10732    }
10733 
10734    /* reuse address, needed if previous server stopped (30s timeout!) */
10735    flag = 1;
10736    status = setsockopt(_lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(INT));
10737    if (status < 0) {
10738       cm_msg(MERROR, "rpc_register_server", "setsockopt() failed");
10739       return RPC_NET_ERROR;
10740    }
10741 
10742    /* bind local node name and port to socket */
10743    memset(&bind_addr, 0, sizeof(bind_addr));
10744    bind_addr.sin_family = AF_INET;
10745    bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
10746 
10747    if (!port)
10748       bind_addr.sin_port = htons(MIDAS_TCP_PORT);
10749    else
10750       bind_addr.sin_port = htons((short) (*port));
10751 
10752    status = bind(_lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
10753    if (status < 0) {
10754       cm_msg(MERROR, "rpc_register_server", "bind() failed: %s", strerror(errno));
10755       return RPC_NET_ERROR;
10756    }
10757 
10758    /* listen for connection */
10759 #ifdef OS_MSDOS
10760    status = listen(_lsock, 1);
10761 #else
10762    status = listen(_lsock, SOMAXCONN);
10763 #endif
10764    if (status < 0) {
10765       cm_msg(MERROR, "rpc_register_server", "listen() failed");
10766       return RPC_NET_ERROR;
10767    }
10768 
10769    /* return port wich OS has choosen */
10770    if (port && *port == 0) {
10771       size = sizeof(bind_addr);
10772       getsockname(_lsock, (struct sockaddr *) &bind_addr, (int *) &size);
10773       *port = ntohs(bind_addr.sin_port);
10774    }
10775 
10776    /* define callbacks for ss_suspend */
10777    if (server_type == ST_REMOTE)
10778       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_client_accept);
10779    else
10780       ss_suspend_set_dispatch(CH_LISTEN, &_lsock, (int (*)(void)) rpc_server_accept);
10781 
10782    return RPC_SUCCESS;
10783 }
10784 
10785 
10786 /********************************************************************/
10787 INT rpc_execute(INT sock, char *buffer, INT convert_flags)
10788 /********************************************************************\
10789 
10790   Routine: rpc_execute
10791 
10792   Purpose: Execute a RPC command received over the network
10793 
10794   Input:
10795     INT  sock               TCP socket to which the result should be
10796                             send back
10797 
10798     char *buffer            Command buffer
10799     INT  convert_flags      Flags for data conversion
10800 
10801   Output:
10802     none
10803 
10804   Function value:
10805     RPC_SUCCESS             Successful completion
10806     RPC_INVALID_ID          Invalid routine_id received
10807     RPC_NET_ERROR           Error in socket call
10808     RPC_EXCEED_BUFFER       Not enough memory for network buffer
10809     RPC_SHUTDOWN            Shutdown requested
10810     SS_ABORT                TCP connection broken
10811     SS_EXIT                 TCP connection closed
10812 
10813 \********************************************************************/
10814 {
10815    INT i, index, routine_id, status;
10816    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
10817    INT tid, flags;
10818    NET_COMMAND *nc_in, *nc_out;
10819    INT param_size, max_size;
10820    void *prpc_param[20];
10821    char str[1024], debug_line[1024];
10822 
10823 /* return buffer must be auto for multi-thread servers */
10824    char return_buffer[NET_BUFFER_SIZE];
10825 
10826 
10827    /* extract pointer array to parameters */
10828    nc_in = (NET_COMMAND *) buffer;
10829    nc_out = (NET_COMMAND *) return_buffer;
10830 
10831    /* convert header format (byte swapping) */
10832    if (convert_flags) {
10833       rpc_convert_single(&nc_in->header.routine_id, TID_DWORD, 0, convert_flags);
10834       rpc_convert_single(&nc_in->header.param_size, TID_DWORD, 0, convert_flags);
10835    }
10836 
10837    /* no result return in FAST TCP mode */
10838    if (nc_in->header.routine_id & TCP_FAST)
10839       sock = 0;
10840 
10841    /* find entry in rpc_list */
10842    routine_id = nc_in->header.routine_id & ~TCP_FAST;
10843 
10844    for (i = 0;; i++)
10845       if (rpc_list[i].id == 0 || rpc_list[i].id == routine_id)
10846          break;
10847    index = i;
10848    if (rpc_list[i].id == 0) {
10849       cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
10850       return RPC_INVALID_ID;
10851    }
10852 
10853    in_param_ptr = nc_in->param;
10854    out_param_ptr = nc_out->param;
10855 
10856    if (_debug_print)
10857       sprintf(debug_line, "%s(", rpc_list[index].name);
10858 
10859    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
10860       tid = rpc_list[index].param[i].tid;
10861       flags = rpc_list[index].param[i].flags;
10862 
10863       if (flags & RPC_IN) {
10864          param_size = ALIGN8(tid_size[tid]);
10865 
10866          if (tid == TID_STRING || tid == TID_LINK)
10867             param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
10868 
10869          if (flags & RPC_VARARRAY) {
10870             /* for arrays, the size is stored as a INT in front of the array */
10871             param_size = *((INT *) in_param_ptr);
10872             if (convert_flags)
10873                rpc_convert_single(&param_size, TID_INT, 0, convert_flags);
10874             param_size = ALIGN8(param_size);
10875 
10876             in_param_ptr += ALIGN8(sizeof(INT));
10877          }
10878 
10879          if (tid == TID_STRUCT)
10880             param_size = ALIGN8(rpc_list[index].param[i].n);
10881 
10882          prpc_param[i] = in_param_ptr;
10883 
10884          /* convert data format */
10885          if (convert_flags) {
10886             if (flags & RPC_VARARRAY)
10887                rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
10888             else
10889                rpc_convert_data(in_param_ptr, tid, flags,
10890                                 rpc_list[index].param[i].n * tid_size[tid],
10891                                 convert_flags);
10892          }
10893 
10894          if (_debug_print) {
10895             db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
10896             if (rpc_list[index].param[i].tid == TID_STRING) {
10897                /* check for long strings (db_create_record...) */
10898                if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
10899                   strcat(debug_line, "\"");
10900                   strcat(debug_line, str);
10901                   strcat(debug_line, "\"");
10902                } else
10903                   strcat(debug_line, "...");
10904             } else
10905                strcat(debug_line, str);
10906          }
10907 
10908          in_param_ptr += param_size;
10909       }
10910 
10911       if (flags & RPC_OUT) {
10912          param_size = ALIGN8(tid_size[tid]);
10913 
10914          if (flags & RPC_VARARRAY || tid == TID_STRING) {
10915             /* save maximum array length */
10916             max_size = *((INT *) in_param_ptr);
10917             if (convert_flags)
10918                rpc_convert_single(&max_size, TID_INT, 0, convert_flags);
10919             max_size = ALIGN8(max_size);
10920 
10921             *((INT *) out_param_ptr) = max_size;
10922 
10923             /* save space for return array length */
10924             out_param_ptr += ALIGN8(sizeof(INT));
10925 
10926             /* use maximum array length from input */
10927             param_size += max_size;
10928          }
10929 
10930          if (rpc_list[index].param[i].tid == TID_STRUCT)
10931             param_size = ALIGN8(rpc_list[index].param[i].n);
10932 
10933          if ((PTYPE) out_param_ptr - (PTYPE) nc_out + param_size > NET_BUFFER_SIZE) {
10934             cm_msg(MERROR, "rpc_execute",
10935                    "return parameters (%d) too large for network buffer (%d)",
10936                    (PTYPE) out_param_ptr - (PTYPE) nc_out + param_size, NET_BUFFER_SIZE);
10937             return RPC_EXCEED_BUFFER;
10938          }
10939 
10940          /* if parameter goes both directions, copy input to output */
10941          if (rpc_list[index].param[i].flags & RPC_IN)
10942             memcpy(out_param_ptr, prpc_param[i], param_size);
10943 
10944          if (_debug_print && !(flags & RPC_IN))
10945             strcat(debug_line, "-");
10946 
10947          prpc_param[i] = out_param_ptr;
10948          out_param_ptr += param_size;
10949       }
10950 
10951       if (_debug_print)
10952          if (rpc_list[index].param[i + 1].tid)
10953             strcat(debug_line, ", ");
10954    }
10955 
10956    if (_debug_print) {
10957       strcat(debug_line, ")");
10958       _debug_print(debug_line);
10959    }
10960 
10961    last_param_ptr = out_param_ptr;
10962 
10963   /*********************************\
10964   *   call dispatch function        *
10965   \*********************************/
10966    if (rpc_list[index].dispatch)
10967       status = rpc_list[index].dispatch(routine_id, prpc_param);
10968    else
10969       status = RPC_INVALID_ID;
10970 
10971    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
10972        routine_id == RPC_ID_WATCHDOG)
10973       status = RPC_SUCCESS;
10974 
10975    /* return immediately for closed down client connections */
10976    if (!sock && routine_id == RPC_ID_EXIT)
10977       return SS_EXIT;
10978 
10979    if (!sock && routine_id == RPC_ID_SHUTDOWN)
10980       return RPC_SHUTDOWN;
10981 
10982    /* Return if TCP connection broken */
10983    if (status == SS_ABORT)
10984       return SS_ABORT;
10985 
10986    /* if sock == 0, we are in FTCP mode and may not sent results */
10987    if (!sock)
10988       return RPC_SUCCESS;
10989 
10990    /* compress variable length arrays */
10991    out_param_ptr = nc_out->param;
10992    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
10993       if (rpc_list[index].param[i].flags & RPC_OUT) {
10994          tid = rpc_list[index].param[i].tid;
10995          flags = rpc_list[index].param[i].flags;
10996          param_size = ALIGN8(tid_size[tid]);
10997 
10998          if (tid == TID_STRING) {
10999             max_size = *((INT *) out_param_ptr);
11000             param_size = strlen((char *) prpc_param[i]) + 1;
11001             param_size = ALIGN8(param_size);
11002 
11003             /* move string ALIGN8(sizeof(INT)) left */
11004             memcpy(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
11005 
11006             /* move remaining parameters to end of string */
11007             memcpy(out_param_ptr + param_size,
11008                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11009                    (PTYPE) last_param_ptr -
11010                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11011          }
11012 
11013          if (flags & RPC_VARARRAY) {
11014             /* store array length at current out_param_ptr */
11015             max_size = *((INT *) out_param_ptr);
11016             param_size = *((INT *) prpc_param[i + 1]);
11017             *((INT *) out_param_ptr) = param_size;
11018             if (convert_flags)
11019                rpc_convert_single(out_param_ptr, TID_INT, RPC_OUTGOING, convert_flags);
11020 
11021             out_param_ptr += ALIGN8(sizeof(INT));
11022 
11023             param_size = ALIGN8(param_size);
11024 
11025             /* move remaining parameters to end of array */
11026             memcpy(out_param_ptr + param_size,
11027                    out_param_ptr + max_size + ALIGN8(sizeof(INT)),
11028                    (PTYPE) last_param_ptr -
11029                    ((PTYPE) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
11030          }
11031 
11032          if (tid == TID_STRUCT)
11033             param_size = ALIGN8(rpc_list[index].param[i].n);
11034 
11035          /* convert data format */
11036          if (convert_flags) {
11037             if (flags & RPC_VARARRAY)
11038                rpc_convert_data(out_param_ptr, tid,
11039                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11040                                 param_size, convert_flags);
11041             else
11042                rpc_convert_data(out_param_ptr, tid,
11043                                 rpc_list[index].param[i].flags | RPC_OUTGOING,
11044                                 rpc_list[index].param[i].n * tid_size[tid],
11045                                 convert_flags);
11046          }
11047 
11048          out_param_ptr += param_size;
11049       }
11050 
11051    /* send return parameters */
11052    param_size = (PTYPE) out_param_ptr - (PTYPE) nc_out->param;
11053    nc_out->header.routine_id = status;
11054    nc_out->header.param_size = param_size;
11055 
11056    /* convert header format (byte swapping) if necessary */
11057    if (convert_flags) {
11058       rpc_convert_single(&nc_out->header.routine_id, TID_DWORD,
11059                          RPC_OUTGOING, convert_flags);
11060       rpc_convert_single(&nc_out->header.param_size, TID_DWORD,
11061                          RPC_OUTGOING, convert_flags);
11062    }
11063 
11064    status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
11065 
11066    if (status < 0) {
11067       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11068       return RPC_NET_ERROR;
11069    }
11070 
11071    /* print return buffer */
11072 /*
11073   printf("Return buffer, ID %d:\n", routine_id);
11074   for (i=0; i<param_size ; i++)
11075     {
11076     status = (char) nc_out->param[i];
11077     printf("%02X ", status);
11078     if (i%8 == 7)
11079       printf("\n");
11080     }
11081 */
11082    /* return SS_EXIT if RPC_EXIT is called */
11083    if (routine_id == RPC_ID_EXIT)
11084       return SS_EXIT;
11085 
11086    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11087    if (routine_id == RPC_ID_SHUTDOWN)
11088       return RPC_SHUTDOWN;
11089 
11090    return RPC_SUCCESS;
11091 }
11092 
11093 
11094 /********************************************************************/
11095 INT rpc_execute_ascii(INT sock, char *buffer)
11096 /********************************************************************\
11097 
11098   Routine: rpc_execute_ascii
11099 
11100   Purpose: Execute a RPC command received over the network in ASCII
11101            mode
11102 
11103   Input:
11104     INT  sock               TCP socket to which the result should be
11105                             send back
11106 
11107     char *buffer            Command buffer
11108 
11109   Output:
11110     none
11111 
11112   Function value:
11113     RPC_SUCCESS             Successful completion
11114     RPC_INVALID_ID          Invalid routine_id received
11115     RPC_NET_ERROR           Error in socket call
11116     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11117     RPC_SHUTDOWN            Shutdown requested
11118     SS_ABORT                TCP connection broken
11119     SS_EXIT                 TCP connection closed
11120 
11121 \********************************************************************/
11122 {
11123 #define ASCII_BUFFER_SIZE 64500
11124 #define N_APARAM           1024
11125 
11126    INT i, j, index, status, index_in;
11127    char *in_param_ptr, *out_param_ptr, *last_param_ptr;
11128    INT routine_id, tid, flags, array_tid, n_param;
11129    INT param_size, item_size, num_values;
11130    void *prpc_param[20];
11131    char *arpc_param[N_APARAM], *pc;
11132    char str[1024], debug_line[1024];
11133    char buffer1[ASCII_BUFFER_SIZE];     /* binary in */
11134    char buffer2[ASCII_BUFFER_SIZE];     /* binary out */
11135    char return_buffer[ASCII_BUFFER_SIZE];       /* ASCII out */
11136 
11137    /* parse arguments */
11138    arpc_param[0] = buffer;
11139    for (i = 1; i < N_APARAM; i++) {
11140       arpc_param[i] = strchr(arpc_param[i - 1], '&');
11141       if (arpc_param[i] == NULL)
11142          break;
11143       *arpc_param[i] = 0;
11144       arpc_param[i]++;
11145    }
11146 
11147    /* decode '%' */
11148    for (i = 0; i < N_APARAM && arpc_param[i]; i++)
11149       while ((pc = strchr(arpc_param[i], '%')) != NULL) {
11150          if (isxdigit(pc[1]) && isxdigit(pc[2])) {
11151             str[0] = pc[1];
11152             str[1] = pc[2];
11153             str[2] = 0;
11154             sscanf(str, "%02X", &i);
11155 
11156             *pc++ = i;
11157             while (pc[2]) {
11158                pc[0] = pc[2];
11159                pc++;
11160             }
11161          }
11162       }
11163 
11164    /* find entry in rpc_list */
11165    for (i = 0;; i++)
11166       if (rpc_list[i].id == 0 || strcmp(arpc_param[0], rpc_list[i].name) == 0)
11167          break;
11168    index = i;
11169    routine_id = rpc_list[i].id;
11170    if (rpc_list[i].id == 0) {
11171       cm_msg(MERROR, "rpc_execute", "Invalid rpc name (%s)", arpc_param[0]);
11172       return RPC_INVALID_ID;
11173    }
11174 
11175    in_param_ptr = buffer1;
11176    out_param_ptr = buffer2;
11177    index_in = 1;
11178 
11179    if (_debug_print)
11180       sprintf(debug_line, "%s(", rpc_list[index].name);
11181 
11182    for (i = 0; rpc_list[index].param[i].tid != 0; i++) {
11183       tid = rpc_list[index].param[i].tid;
11184       flags = rpc_list[index].param[i].flags;
11185 
11186       if (flags & RPC_IN) {
11187          if (flags & RPC_VARARRAY) {
11188             sscanf(arpc_param[index_in++], "%d %d", &n_param, &array_tid);
11189 
11190             prpc_param[i] = in_param_ptr;
11191             for (j = 0; j < n_param; j++) {
11192                db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, array_tid);
11193                in_param_ptr += param_size;
11194             }
11195             in_param_ptr = (char *) ALIGN8(((PTYPE) in_param_ptr));
11196 
11197             if (_debug_print)
11198                strcat(debug_line, "<array>");
11199          } else {
11200             db_sscanf(arpc_param[index_in++], in_param_ptr, &param_size, 0, tid);
11201             param_size = ALIGN8(param_size);
11202 
11203             if (tid == TID_STRING || tid == TID_LINK)
11204                param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
11205 
11206             /*
11207                if (tid == TID_STRUCT)
11208                param_size = ALIGN8( rpc_list[index].param[i].n );
11209              */
11210             prpc_param[i] = in_param_ptr;
11211 
11212             if (_debug_print) {
11213                db_sprintf(str, in_param_ptr, param_size, 0, rpc_list[index].param[i].tid);
11214                if (rpc_list[index].param[i].tid == TID_STRING) {
11215                   /* check for long strings (db_create_record...) */
11216                   if (strlen(debug_line) + strlen(str) + 2 < sizeof(debug_line)) {
11217                      strcat(debug_line, "\"");
11218                      strcat(debug_line, str);
11219                      strcat(debug_line, "\"");
11220                   } else
11221                      strcat(debug_line, "...");
11222                } else
11223                   strcat(debug_line, str);
11224             }
11225 
11226             in_param_ptr += param_size;
11227          }
11228 
11229          if ((PTYPE) in_param_ptr - (PTYPE) buffer1 > ASCII_BUFFER_SIZE) {
11230             cm_msg(MERROR, "rpc_ascii_execute",
11231                    "parameters (%d) too large for network buffer (%d)", param_size,
11232                    ASCII_BUFFER_SIZE);
11233             return RPC_EXCEED_BUFFER;
11234          }
11235 
11236       }
11237 
11238       if (flags & RPC_OUT) {
11239          param_size = ALIGN8(tid_size[tid]);
11240 
11241          if (flags & RPC_VARARRAY || tid == TID_STRING) {
11242             /* reserve maximum array length */
11243             param_size = atoi(arpc_param[index_in]);
11244             param_size = ALIGN8(param_size);
11245          }
11246 
11247 /*
11248       if (rpc_list[index].param[i].tid == TID_STRUCT)
11249         param_size = ALIGN8( rpc_list[index].param[i].n );
11250 */
11251          if ((PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size > ASCII_BUFFER_SIZE) {
11252             cm_msg(MERROR, "rpc_execute",
11253                    "return parameters (%d) too large for network buffer (%d)",
11254                    (PTYPE) out_param_ptr - (PTYPE) buffer2 + param_size,
11255                    ASCII_BUFFER_SIZE);
11256             return RPC_EXCEED_BUFFER;
11257          }
11258 
11259          /* if parameter goes both directions, copy input to output */
11260          if (rpc_list[index].param[i].flags & RPC_IN)
11261             memcpy(out_param_ptr, prpc_param[i], param_size);
11262 
11263          if (_debug_print && !(flags & RPC_IN))
11264             strcat(debug_line, "-");
11265 
11266          prpc_param[i] = out_param_ptr;
11267          out_param_ptr += param_size;
11268       }
11269 
11270       if (_debug_print)
11271          if (rpc_list[index].param[i + 1].tid)
11272             strcat(debug_line, ", ");
11273    }
11274 
11275    if (_debug_print) {
11276       strcat(debug_line, ")");
11277       _debug_print(debug_line);
11278    }
11279 
11280    last_param_ptr = out_param_ptr;
11281 
11282   /*********************************\
11283   *   call dispatch function        *
11284   \*********************************/
11285    if (rpc_list[index].dispatch)
11286       status = rpc_list[index].dispatch(routine_id, prpc_param);
11287    else
11288       status = RPC_INVALID_ID;
11289 
11290    if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN ||
11291        routine_id == RPC_ID_WATCHDOG)
11292       status = RPC_SUCCESS;
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    /* send return status */
11303    out_param_ptr = return_buffer;
11304    sprintf(out_param_ptr, "%d", status);
11305    out_param_ptr += strlen(out_param_ptr);
11306 
11307    /* convert return parameters */
11308    for (i = 0; rpc_list[index].param[i].tid != 0; i++)
11309       if (rpc_list[index].param[i].flags & RPC_OUT) {
11310          *out_param_ptr++ = '&';
11311 
11312          tid = rpc_list[index].param[i].tid;
11313          flags = rpc_list[index].param[i].flags;
11314          param_size = ALIGN8(tid_size[tid]);
11315 
11316          if (tid == TID_STRING && !(flags & RPC_VARARRAY)) {
11317             strcpy(out_param_ptr, (char *) prpc_param[i]);
11318             param_size = strlen((char *) prpc_param[i]);
11319          }
11320 
11321          else if (flags & RPC_VARARRAY) {
11322             if (rpc_list[index].id == RPC_BM_RECEIVE_EVENT) {
11323                param_size = *((INT *) prpc_param[i + 1]);
11324                /* write number of bytes to output */
11325                sprintf(out_param_ptr, "%d", param_size);
11326                out_param_ptr += strlen(out_param_ptr) + 1;      /* '0' finishes param */
11327                memcpy(out_param_ptr, prpc_param[i], param_size);
11328                out_param_ptr += param_size;
11329                *out_param_ptr = 0;
11330             } else {
11331                if (rpc_list[index].id == RPC_DB_GET_DATA1) {
11332                   param_size = *((INT *) prpc_param[i + 1]);
11333                   array_tid = *((INT *) prpc_param[i + 2]);
11334                   num_values = *((INT *) prpc_param[i + 3]);
11335                } else if (rpc_list[index].id == RPC_DB_GET_DATA_INDEX) {
11336                   param_size = *((INT *) prpc_param[i + 1]);
11337                   array_tid = *((INT *) prpc_param[i + 3]);
11338                   num_values = 1;
11339                } else if (rpc_list[index].id == RPC_HS_READ) {
11340                   param_size = *((INT *) prpc_param[i + 1]);
11341                   if (i == 6) {
11342                      array_tid = TID_DWORD;
11343                      num_values = param_size / sizeof(DWORD);
11344                   } else {
11345                      array_tid = *((INT *) prpc_param[10]);
11346                      num_values = *((INT *) prpc_param[11]);
11347                   }
11348                } else {         /* variable arrays of fixed type like hs_enum_events, hs_enum_vars */
11349 
11350                   param_size = *((INT *) prpc_param[i + 1]);
11351                   array_tid = tid;
11352                   if (tid == TID_STRING)
11353                      num_values = param_size / NAME_LENGTH;
11354                   else
11355                      num_values = param_size / tid_size[tid];
11356                }
11357 
11358                /* derive size of individual item */
11359                if (array_tid == TID_STRING)
11360                   item_size = param_size / num_values;
11361                else
11362                   item_size = tid_size[array_tid];
11363 
11364                /* write number of elements to output */
11365                sprintf(out_param_ptr, "%d", num_values);
11366                out_param_ptr += strlen(out_param_ptr);
11367 
11368                /* write array of values to output */
11369                for (j = 0; j < num_values; j++) {
11370                   *out_param_ptr++ = '&';
11371                   db_sprintf(out_param_ptr, prpc_param[i], item_size, j, array_tid);
11372                   out_param_ptr += strlen(out_param_ptr);
11373                }
11374             }
11375          }
11376 
11377 /*
11378       else if (tid == TID_STRUCT)
11379         param_size = ALIGN8( rpc_list[index].param[i].n );
11380 */
11381          else
11382             db_sprintf(out_param_ptr, prpc_param[i], param_size, 0, tid);
11383 
11384          out_param_ptr += strlen(out_param_ptr);
11385 
11386          if ((PTYPE) out_param_ptr - (PTYPE) return_buffer > ASCII_BUFFER_SIZE) {
11387             cm_msg(MERROR, "rpc_execute",
11388                    "return parameter (%d) too large for network buffer (%d)", param_size,
11389                    ASCII_BUFFER_SIZE);
11390             return RPC_EXCEED_BUFFER;
11391          }
11392       }
11393 
11394    /* send return parameters */
11395    param_size = (PTYPE) out_param_ptr - (PTYPE) return_buffer + 1;
11396 
11397    status = send_tcp(sock, return_buffer, param_size, 0);
11398 
11399    if (status < 0) {
11400       cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
11401       return RPC_NET_ERROR;
11402    }
11403 
11404    /* print return buffer */
11405    if (_debug_print) {
11406       if (strlen(return_buffer) > sizeof(debug_line)) {
11407          memcpy(debug_line, return_buffer, sizeof(debug_line) - 10);
11408          strcat(debug_line, "...");
11409       } else
11410          sprintf(debug_line, "-> %s", return_buffer);
11411       _debug_print(debug_line);
11412    }
11413 
11414    /* return SS_EXIT if RPC_EXIT is called */
11415    if (routine_id == RPC_ID_EXIT)
11416       return SS_EXIT;
11417 
11418    /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
11419    if (routine_id == RPC_ID_SHUTDOWN)
11420       return RPC_SHUTDOWN;
11421 
11422    return RPC_SUCCESS;
11423 }
11424 
11425 
11426 /********************************************************************/
11427 INT rpc_server_accept(int lsock)
11428 /********************************************************************\
11429 
11430   Routine: rpc_server_accept
11431 
11432   Purpose: Accept new incoming connections
11433 
11434   Input:
11435     INT    lscok            Listen socket
11436 
11437   Output:
11438     none
11439 
11440   Function value:
11441     RPC_SUCCESS             Successful completion
11442     RPC_NET_ERROR           Error in socket call
11443     RPC_CONNCLOSED          Connection was closed
11444     RPC_SHUTDOWN            Listener shutdown
11445     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11446 
11447 \********************************************************************/
11448 {
11449    INT index, i;
11450    int size, status;
11451    char command, version[32], v1[32];
11452    INT sock, port1, port2, port3;
11453    struct sockaddr_in acc_addr;
11454    struct hostent *phe;
11455    char str[100];
11456    char host_port1_str[30], host_port2_str[30], host_port3_str[30];
11457    char debug_str[30];
11458    char *argv[10];
11459    char net_buffer[256];
11460    struct linger ling;
11461 
11462    static struct callback_addr callback;
11463 
11464    if (lsock > 0) {
11465       size = sizeof(acc_addr);
11466       sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11467 
11468       if (sock == -1)
11469          return RPC_NET_ERROR;
11470    } else {
11471       /* lsock is stdin -> already connected from inetd */
11472 
11473       size = sizeof(acc_addr);
11474       sock = lsock;
11475       getpeername(sock, (struct sockaddr *) &acc_addr, (int *) &size);
11476    }
11477 
11478    /* receive string with timeout */
11479    i = recv_string(sock, net_buffer, 256, 10000);
11480    if (i > 0) {
11481       command = (char) toupper(net_buffer[0]);
11482 
11483       switch (command) {
11484       case 'S':
11485                /*----------- shutdown listener ----------------------*/
11486          closesocket(sock);
11487          return RPC_SHUTDOWN;
11488 
11489       case 7:
11490          ss_shell(sock);
11491          closesocket(sock);
11492          break;
11493 
11494       case 'I':
11495                /*----------- return available experiments -----------*/
11496          cm_scan_experiments();
11497          for (i = 0; i < MAX_EXPERIMENT && exptab[i].name[0]; i++) {
11498             sprintf(str, "%s", exptab[i].name);
11499             send(sock, str, strlen(str) + 1, 0);
11500          }
11501          send(sock, "", 1, 0);
11502          closesocket(sock);
11503          break;
11504 
11505       case 'C':
11506                /*----------- connect to experiment -----------*/
11507 
11508          /* get callback information */
11509          callback.experiment[0] = 0;
11510          port1 = port2 = version[0] = 0;
11511          sscanf(net_buffer + 2, "%d %d %d %s", &port1, &port2, &port3, version);
11512          strcpy(callback.experiment,
11513                 strchr(strchr(strchr(strchr(net_buffer + 2, ' ') + 1, ' ') + 1, ' ') + 1,
11514                        ' ') + 1);
11515 
11516          /* print warning if version patch level doesn't agree */
11517          strcpy(v1, version);
11518          if (strchr(v1, '.'))
11519             if (strchr(strchr(v1, '.') + 1, '.'))
11520                *strchr(strchr(v1, '.') + 1, '.') = 0;
11521 
11522          strcpy(str, cm_get_version());
11523          if (strchr(str, '.'))
11524             if (strchr(strchr(str, '.') + 1, '.'))
11525                *strchr(strchr(str, '.') + 1, '.') = 0;
11526 
11527          if (strcmp(v1, str) != 0) {
11528             sprintf(str, "client MIDAS version %s differs from local version %s",
11529                     version, cm_get_version());
11530             cm_msg(MERROR, "rpc_server_accept", str);
11531 
11532             sprintf(str, "received string: %s", net_buffer + 2);
11533             cm_msg(MERROR, "rpc_server_accept", str);
11534          }
11535 
11536          callback.host_port1 = (short) port1;
11537          callback.host_port2 = (short) port2;
11538          callback.host_port3 = (short) port3;
11539          callback.debug = _debug_mode;
11540 
11541          /* get the name of the remote host */
11542 #ifdef OS_VXWORKS
11543          {
11544             INT status;
11545             status = hostGetByAddr(acc_addr.sin_addr.s_addr, callback.host_name);
11546             if (status != 0) {
11547                cm_msg(MERROR, "rpc_server_accept", "cannot get host name");
11548                break;
11549             }
11550          }
11551 #else
11552          phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11553          if (phe == NULL) {
11554             /* use IP number instead */
11555             strcpy(callback.host_name, (char *) inet_ntoa(acc_addr.sin_addr));
11556          } else
11557             strcpy(callback.host_name, phe->h_name);
11558 #endif
11559 
11560          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MPROCESS) {
11561             /* update experiment definition */
11562             cm_scan_experiments();
11563 
11564             /* lookup experiment */
11565             if (equal_ustring(callback.experiment, "Default"))
11566                index = 0;
11567             else
11568                for (index = 0; index < MAX_EXPERIMENT && exptab[index].name[0]; index++)
11569                   if (equal_ustring(callback.experiment, exptab[index].name))
11570                      break;
11571 
11572             if (index == MAX_EXPERIMENT || exptab[index].name[0] == 0) {
11573                sprintf(str, "experiment %s not defined in exptab\r", callback.experiment);
11574                cm_msg(MERROR, "rpc_server_accept", str);
11575 
11576                send(sock, "2", 2, 0);   /* 2 means exp. not found */
11577                closesocket(sock);
11578                break;
11579             }
11580 
11581             strcpy(callback.directory, exptab[index].directory);
11582             strcpy(callback.user, exptab[index].user);
11583 
11584             /* create a new process */
11585             sprintf(host_port1_str, "%d", callback.host_port1);
11586             sprintf(host_port2_str, "%d", callback.host_port2);
11587             sprintf(host_port3_str, "%d", callback.host_port3);
11588             sprintf(debug_str, "%d", callback.debug);
11589 
11590             argv[0] = (char *) rpc_get_server_option(RPC_OSERVER_NAME);
11591             argv[1] = callback.host_name;
11592             argv[2] = host_port1_str;
11593             argv[3] = host_port2_str;
11594             argv[4] = host_port3_str;
11595             argv[5] = debug_str;
11596             argv[6] = callback.experiment;
11597             argv[7] = callback.directory;
11598             argv[8] = callback.user;
11599             argv[9] = NULL;
11600 
11601 /*
11602           cm_msg(MINFO, "", "%s %s %s %s %s %s %s %s %s %s",
11603             argv[0], argv[1], argv[2], argv[3], argv[4],
11604             argv[5], argv[6], argv[7], argv[8], argv[9]);
11605 */
11606             status =
11607                 ss_spawnv(P_NOWAIT, (char *) rpc_get_server_option(RPC_OSERVER_NAME),
11608                           argv);
11609             if (status != SS_SUCCESS) {
11610                cm_msg(MERROR, "rpc_server_accept", "cannot spawn subprocess");
11611 
11612                sprintf(str, "3");       /* 3 means cannot spawn subprocess */
11613                send(sock, str, strlen(str) + 1, 0);
11614                closesocket(sock);
11615                break;
11616             }
11617 
11618             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11619             send(sock, str, strlen(str) + 1, 0);
11620             closesocket(sock);
11621          } else {
11622             sprintf(str, "1 %s", cm_get_version());     /* 1 means ok */
11623             send(sock, str, strlen(str) + 1, 0);
11624             closesocket(sock);
11625          }
11626 
11627          /* look for next free entry */
11628          for (index = 0; index < MAX_RPC_CONNECTION; index++)
11629             if (_server_acception[index].recv_sock == 0)
11630                break;
11631          if (index == MAX_RPC_CONNECTION)
11632             return RPC_NET_ERROR;
11633          callback.index = index;
11634 
11635         /*----- multi thread server ------------------------*/
11636          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_MTHREAD)
11637             ss_thread_create(rpc_server_thread, (void *) (&callback));
11638 
11639         /*----- single thread server -----------------------*/
11640          if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_SINGLE ||
11641              rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
11642             rpc_server_callback(&callback);
11643 
11644          break;
11645 
11646       default:
11647          cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c'", command);
11648          closesocket(sock);
11649          break;
11650 
11651       }
11652    } else {                     /* if i>0 */
11653 
11654       /* lingering needed for PCTCP */
11655       ling.l_onoff = 1;
11656       ling.l_linger = 0;
11657       setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
11658       closesocket(sock);
11659    }
11660 
11661    return RPC_SUCCESS;
11662 }
11663 
11664 
11665 /********************************************************************/
11666 INT rpc_client_accept(int lsock)
11667 /********************************************************************\
11668 
11669   Routine: rpc_client_accept
11670 
11671   Purpose: Accept new incoming connections as a client
11672 
11673   Input:
11674     INT    lsock            Listen socket
11675 
11676   Output:
11677     none
11678 
11679   Function value:
11680     RPC_SUCCESS             Successful completion
11681     RPC_NET_ERROR           Error in socket call
11682     RPC_CONNCLOSED          Connection was closed
11683     RPC_SHUTDOWN            Listener shutdown
11684     RPC_EXCEED_BUFFER       Not enough memory for network buffer
11685 
11686 \********************************************************************/
11687 {
11688    INT index, i, version, status;
11689    int size, sock;
11690    struct sockaddr_in acc_addr;
11691    INT client_hw_type = 0, hw_type;
11692    char str[100], client_program[NAME_LENGTH];
11693    char host_name[HOST_NAME_LENGTH];
11694    INT convert_flags;
11695    char net_buffer[256], *p;
11696 
11697    size = sizeof(acc_addr);
11698    sock = accept(lsock, (struct sockaddr *) &acc_addr, (int *) &size);
11699 
11700    if (sock == -1)
11701       return RPC_NET_ERROR;
11702 
11703    /* get the name of the calling host */
11704 /* outcommented for speed reasons SR 7.10.98
11705 #ifdef OS_VXWORKS
11706   {
11707   status = hostGetByAddr(acc_addr.sin_addr.s_addr, host_name);
11708   if (status != 0)
11709     strcpy(host_name, "unknown");
11710   }
11711 #else
11712   phe = gethostbyaddr((char *) &acc_addr.sin_addr, 4, PF_INET);
11713   if (phe == NULL)
11714     strcpy(host_name, "unknown");
11715   strcpy(host_name, phe->h_name);
11716 #endif
11717 */
11718    strcpy(host_name, "");
11719 
11720    /* look for next free entry */
11721    for (index = 0; index < MAX_RPC_CONNECTION; index++)
11722       if (_server_acception[index].recv_sock == 0)
11723          break;
11724    if (index == MAX_RPC_CONNECTION) {
11725       closesocket(sock);
11726       return RPC_NET_ERROR;
11727    }
11728 
11729    /* receive string with timeout */
11730    i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
11731    if (i <= 0) {
11732       closesocket(sock);
11733       return RPC_NET_ERROR;
11734    }
11735 
11736    /* get remote computer info */
11737    p = strtok(net_buffer, " ");
11738    if (p != NULL) {
11739       client_hw_type = atoi(p);
11740       p = strtok(NULL, " ");
11741    }
11742    if (p != NULL) {
11743       version = atoi(p);
11744       p = strtok(NULL, " ");
11745    }
11746    if (p != NULL) {
11747       strcpy(client_program, p);
11748       p = strtok(NULL, " ");
11749    }
11750    if (p != NULL) {
11751       strcpy(host_name, p);
11752       p = strtok(NULL, " ");
11753    }
11754 
11755    /* save information in _server_acception structure */
11756    _server_acception[index].recv_sock = sock;
11757    _server_acception[index].send_sock = 0;
11758    _server_acception[index].event_sock = 0;
11759    _server_acception[index].remote_hw_type = client_hw_type;
11760    strcpy(_server_acception[index].host_name, host_name);
11761    strcpy(_server_acception[index].prog_name, client_program);
11762    _server_acception[index].tid = ss_gettid();
11763    _server_acception[index].last_activity = ss_millitime();
11764    _server_acception[index].watchdog_timeout = 0;
11765 
11766    /* send my own computer id */
11767    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11768    sprintf(str, "%d %s", hw_type, cm_get_version());
11769    status = send(sock, str, strlen(str) + 1, 0);
11770    if (status != (INT) strlen(str) + 1)
11771       return RPC_NET_ERROR;
11772 
11773    rpc_set_server_acception(index + 1);
11774    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11775    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11776 
11777    /* set callback function for ss_suspend */
11778    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11779                            (int (*)(void)) rpc_server_receive);
11780 
11781    return RPC_SUCCESS;
11782 }
11783 
11784 
11785 /********************************************************************/
11786 INT rpc_server_callback(struct callback_addr * pcallback)
11787 /********************************************************************\
11788 
11789   Routine: rpc_server_callback
11790 
11791   Purpose: Callback a remote client. Setup _server_acception entry
11792            with optional conversion flags and establish two-way
11793            TCP connection.
11794 
11795   Input:
11796     callback_addr pcallback Pointer to a callback structure
11797 
11798   Output:
11799     none
11800 
11801   Function value:
11802     RPC_SUCCESS             Successful completion
11803 
11804 \********************************************************************/
11805 {
11806    INT index, status;
11807    int recv_sock, send_sock, event_sock;
11808    struct sockaddr_in bind_addr;
11809    struct hostent *phe;
11810    char str[100], client_program[NAME_LENGTH];
11811    char host_name[HOST_NAME_LENGTH];
11812    INT client_hw_type, hw_type;
11813    INT convert_flags;
11814    char net_buffer[256];
11815    struct callback_addr callback;
11816    int flag;
11817 
11818    /* copy callback information */
11819    memcpy(&callback, pcallback, sizeof(callback));
11820    index = callback.index;
11821 
11822    /* create new sockets for TCP */
11823    recv_sock = socket(AF_INET, SOCK_STREAM, 0);
11824    send_sock = socket(AF_INET, SOCK_STREAM, 0);
11825    event_sock = socket(AF_INET, SOCK_STREAM, 0);
11826    if (event_sock == -1)
11827       return RPC_NET_ERROR;
11828 
11829    /* callback to remote node */
11830    memset(&bind_addr, 0, sizeof(bind_addr));
11831    bind_addr.sin_family = AF_INET;
11832    bind_addr.sin_port = htons(callback.host_port1);
11833 
11834 #ifdef OS_VXWORKS
11835    {
11836       INT host_addr;
11837 
11838       host_addr = hostGetByName(callback.host_name);
11839       memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
11840    }
11841 #else
11842    phe = gethostbyname(callback.host_name);
11843    if (phe == NULL) {
11844       cm_msg(MERROR, "rpc_server_callback", "cannot get host name");
11845       return RPC_NET_ERROR;
11846    }
11847    memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
11848 #endif
11849 
11850    /* connect receive socket */
11851 #ifdef OS_UNIX
11852    do {
11853       status = connect(recv_sock, (void *) &bind_addr, sizeof(bind_addr));
11854 
11855       /* don't return if an alarm signal was cought */
11856    } while (status == -1 && errno == EINTR);
11857 #else
11858    status = connect(recv_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11859 #endif
11860 
11861    if (status != 0) {
11862       cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket");
11863       goto error;
11864    }
11865 
11866    bind_addr.sin_port = htons(callback.host_port2);
11867 
11868    /* connect send socket */
11869 #ifdef OS_UNIX
11870    do {
11871       status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11872 
11873       /* don't return if an alarm signal was cought */
11874    } while (status == -1 && errno == EINTR);
11875 #else
11876    status = connect(send_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11877 #endif
11878 
11879    if (status != 0) {
11880       cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket");
11881       goto error;
11882    }
11883 
11884    bind_addr.sin_port = htons(callback.host_port3);
11885 
11886    /* connect event socket */
11887 #ifdef OS_UNIX
11888    do {
11889       status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11890 
11891       /* don't return if an alarm signal was cought */
11892    } while (status == -1 && errno == EINTR);
11893 #else
11894    status = connect(event_sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
11895 #endif
11896 
11897    if (status != 0) {
11898       cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket");
11899       goto error;
11900    }
11901 
11902    /* increase receive buffer size to 64k */
11903 #ifndef OS_ULTRIX               /* crashes ULTRIX... */
11904    flag = 0x10000;
11905    setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
11906 #endif
11907 
11908    if (recv_string(recv_sock, net_buffer, 256, 10000) <= 0) {
11909       cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
11910       goto error;
11911    }
11912 
11913    /* get remote computer info */
11914    sscanf(net_buffer, "%d", &client_hw_type);
11915 
11916    strcpy(client_program, strchr(net_buffer, ' ') + 1);
11917 
11918    /* get the name of the remote host */
11919 #ifdef OS_VXWORKS
11920    status = hostGetByAddr(bind_addr.sin_addr.s_addr, host_name);
11921    if (status != 0)
11922       strcpy(host_name, "unknown");
11923 #else
11924    phe = gethostbyaddr((char *) &bind_addr.sin_addr, 4, PF_INET);
11925    if (phe == NULL)
11926       strcpy(host_name, "unknown");
11927    else
11928       strcpy(host_name, phe->h_name);
11929 #endif
11930 
11931    /* save information in _server_acception structure */
11932    _server_acception[index].recv_sock = recv_sock;
11933    _server_acception[index].send_sock = send_sock;
11934    _server_acception[index].event_sock = event_sock;
11935    _server_acception[index].remote_hw_type = client_hw_type;
11936    strcpy(_server_acception[index].host_name, host_name);
11937    strcpy(_server_acception[index].prog_name, client_program);
11938    _server_acception[index].tid = ss_gettid();
11939    _server_acception[index].last_activity = ss_millitime();
11940    _server_acception[index].watchdog_timeout = 0;
11941 
11942    /* send my own computer id */
11943    hw_type = rpc_get_option(0, RPC_OHW_TYPE);
11944    sprintf(str, "%d", hw_type);
11945    send(recv_sock, str, strlen(str) + 1, 0);
11946 
11947    rpc_set_server_acception(index + 1);
11948    rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
11949    rpc_set_server_option(RPC_CONVERT_FLAGS, convert_flags);
11950 
11951    /* set callback function for ss_suspend */
11952    ss_suspend_set_dispatch(CH_SERVER, _server_acception,
11953                            (int (*)(void)) rpc_server_receive);
11954 
11955    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
11956       printf("Connection to %s:%s established\n",
11957              _server_acception[index].host_name, _server_acception[index].prog_name);
11958 
11959    return RPC_SUCCESS;
11960 
11961  error:
11962 
11963    closesocket(recv_sock);
11964    closesocket(send_sock);
11965    closesocket(event_sock);
11966 
11967    return RPC_NET_ERROR;
11968 }
11969 
11970 
11971 /********************************************************************/
11972 INT rpc_server_thread(void *pointer)
11973 /********************************************************************\
11974 
11975   Routine: rpc_server_thread
11976 
11977   Purpose: New thread for a multi-threaded server. Callback to the
11978            client and process RPC requests.
11979 
11980   Input:
11981     vcoid  pointer          pointer to callback_addr structure.
11982 
11983   Output:
11984     none
11985 
11986   Function value:
11987     RPC_SUCCESS             Successful completion
11988 
11989 \********************************************************************/
11990 {
11991    struct callback_addr callback;
11992    int status, mutex_alarm, mutex_elog;
11993    static DWORD last_checked = 0;
11994 
11995    memcpy(&callback, pointer, sizeof(callback));
11996 
11997    status = rpc_server_callback(&callback);
11998 
11999    if (status != RPC_SUCCESS)
12000       return status;
12001 
12002    /* create alarm and elog mutexes */
12003    ss_mutex_create("ALARM", &mutex_alarm);
12004    ss_mutex_create("ELOG", &mutex_elog);
12005    cm_set_experiment_mutex(mutex_alarm, mutex_elog);
12006 
12007    do {
12008       status = ss_suspend(5000, 0);
12009 
12010       if (rpc_check_channels() == RPC_NET_ERROR)
12011          break;
12012 
12013       /* check alarms every 10 seconds */
12014       if (!rpc_is_remote() && ss_time() - last_checked > 10) {
12015          al_check();
12016          last_checked = ss_time();
12017       }
12018 
12019    } while (status != SS_ABORT && status != SS_EXIT);
12020 
12021    /* delete entry in suspend table for this thread */
12022    ss_suspend_exit();
12023 
12024    return RPC_SUCCESS;
12025 }
12026 
12027 
12028 /********************************************************************/
12029 INT rpc_server_receive(INT index, int sock, BOOL check)
12030 /********************************************************************\
12031 
12032   Routine: rpc_server_receive
12033 
12034   Purpose: Receive rpc commands and execute them. Close the connection
12035            if client has broken TCP pipe.
12036 
12037   Input:
12038     INT    index            Index to _server_acception structure in-
12039                             dicating which connection got data.
12040     int    sock             Socket which got data
12041     BOOL   check            If TRUE, only check if connection is
12042                             broken. This may be called via
12043                             bm_receive_event/ss_suspend(..,MSG_BM)
12044 
12045   Output:
12046     none
12047 
12048   Function value:
12049     RPC_SUCCESS             Successful completion
12050     RPC_EXCEED_BUFFER       Not enough memeory to allocate buffer
12051     SS_EXIT                 Server connection was closed
12052     SS_ABORT                Server connection was broken
12053 
12054 \********************************************************************/
12055 {
12056    INT status, n_received;
12057    INT remaining, *pbh, start_time;
12058    char test_buffer[256], str[80];
12059    EVENT_HEADER *pevent;
12060 
12061    /* init network buffer */
12062    if (_net_recv_buffer_size == 0) {
12063       _net_recv_buffer = (char *) M_MALLOC(NET_BUFFER_SIZE);
12064       if (_net_recv_buffer == NULL) {
12065          cm_msg(MERROR, "rpc_server_receive",
12066                 "not enough memory to allocate network buffer");
12067          return RPC_EXCEED_BUFFER;
12068       }
12069       _net_recv_buffer_size = NET_BUFFER_SIZE;
12070    }
12071 
12072    /* only check if TCP connection is broken */
12073    if (check) {
12074       n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
12075 
12076       if (n_received == -1)
12077          cm_msg(MERROR, "rpc_server_receive",
12078                 "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", sizeof(test_buffer),
12079                 n_received, errno, strerror(errno));
12080 
12081       if (n_received <= 0)
12082          return SS_ABORT;
12083 
12084       return SS_SUCCESS;
12085    }
12086 
12087    remaining = 0;
12088 
12089    /* receive command */
12090    if (sock == _server_acception[index].recv_sock) {
12091       do {
12092          if (_server_acception[index].remote_hw_type == DR_ASCII)
12093             n_received = recv_string(_server_acception[index].recv_sock, _net_recv_buffer,
12094                                      NET_BUFFER_SIZE, 10000);
12095          else
12096             n_received =
12097                 recv_tcp_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0, &remaining);
12098 
12099          if (n_received <= 0) {
12100             status = SS_ABORT;
12101             cm_msg(MERROR, "rpc_server_receive", "recv_tcp_server() returned %d, abort",
12102                    n_received);
12103             goto error;
12104          }
12105 
12106          rpc_set_server_acception(index + 1);
12107 
12108          if (_server_acception[index].remote_hw_type == DR_ASCII)
12109             status = rpc_execute_ascii(_server_acception[index].recv_sock,
12110                                        _net_recv_buffer);
12111          else
12112             status = rpc_execute(_server_acception[index].recv_sock,
12113                                  _net_recv_buffer,
12114                                  _server_acception[index].convert_flags);
12115 
12116          if (status == SS_ABORT) {
12117             cm_msg(MERROR, "rpc_server_receive", "rpc_execute() returned %d, abort",
12118                    status);
12119             goto error;
12120          }
12121 
12122          if (status == SS_EXIT || status == RPC_SHUTDOWN) {
12123             if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12124                printf("Connection to %s:%s closed\n",
12125                       _server_acception[index].host_name,
12126                       _server_acception[index].prog_name);
12127             goto exit;
12128          }
12129 
12130       } while (remaining);
12131    } else {
12132       /* receive event */
12133       if (sock == _server_acception[index].event_sock) {
12134          start_time = ss_millitime();
12135 
12136          do {
12137             n_received =
12138                 recv_event_server(index, _net_recv_buffer, NET_BUFFER_SIZE, 0,
12139                                   &remaining);
12140 
12141             if (n_received <= 0) {
12142                status = SS_ABORT;
12143                cm_msg(MERROR, "rpc_server_receive",
12144                       "recv_event_server() returned %d, abort", n_received);
12145                goto error;
12146             }
12147 
12148             /* send event to buffer */
12149             pbh = (INT *) _net_recv_buffer;
12150             pevent = (EVENT_HEADER *) (pbh + 1);
12151 
12152             status =
12153                 bm_send_event(*pbh, pevent, pevent->data_size + sizeof(EVENT_HEADER),
12154                               SYNC);
12155             if (status != BM_SUCCESS)
12156                cm_msg(MERROR, "rpc_server_receive", "bm_send_event() returned %d",
12157                       status);
12158 
12159             /* repeat for maximum 0.5 sec */
12160          } while (ss_millitime() - start_time < 500 && remaining);
12161       }
12162    }
12163 
12164    return RPC_SUCCESS;
12165 
12166  error:
12167 
12168    strcpy(str, _server_acception[index].host_name);
12169    if (strchr(str, '.'))
12170       *strchr(str, '.') = 0;
12171    cm_msg(MTALK, "rpc_server_receive", "Program %s on host %s aborted",
12172           _server_acception[index].prog_name, str);
12173 
12174  exit:
12175 
12176    /* disconnect from experiment as MIDAS server */
12177    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE) {
12178       HNDLE hDB, hKey;
12179 
12180       cm_get_experiment_database(&hDB, &hKey);
12181 
12182       /* only disconnect from experiment if previously connected.
12183          Necessary for pure RPC servers (RPC_SRVR) */
12184       if (hDB) {
12185 #ifdef LOCAL_ROUTINES
12186          ss_alarm(0, cm_watchdog);
12187 #endif
12188 
12189          cm_delete_client_info(hDB, 0);
12190 
12191          bm_close_all_buffers();
12192          db_close_all_databases();
12193 
12194          rpc_deregister_functions();
12195 
12196          cm_set_experiment_database(0, 0);
12197 
12198          _msg_buffer = 0;
12199       }
12200    }
12201 
12202    /* close server connection */
12203    if (_server_acception[index].recv_sock)
12204       closesocket(_server_acception[index].recv_sock);
12205    if (_server_acception[index].send_sock)
12206       closesocket(_server_acception[index].send_sock);
12207    if (_server_acception[index].event_sock)
12208       closesocket(_server_acception[index].event_sock);
12209 
12210    /* free TCP cache */
12211    M_FREE(_server_acception[index].net_buffer);
12212    _server_acception[index].net_buffer = NULL;
12213 
12214    /* mark this entry as invalid */
12215    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12216 
12217    /* signal caller a shutdonw */
12218    if (status == RPC_SHUTDOWN)
12219       return status;
12220 
12221    /* don't abort if other than main connection is broken */
12222    if (rpc_get_server_option(RPC_OSERVER_TYPE) == ST_REMOTE)
12223       return SS_SUCCESS;
12224 
12225    return status;
12226 }
12227 
12228 
12229 /********************************************************************/
12230 INT rpc_server_shutdown(void)
12231 /********************************************************************\
12232 
12233   Routine: rpc_server_shutdown
12234 
12235   Purpose: Shutdown RPC server, abort all connections
12236 
12237   Input:
12238     none
12239 
12240   Output:
12241     none
12242 
12243   Function value:
12244     RPC_SUCCESS             Successful completion
12245 
12246 \********************************************************************/
12247 {
12248    INT i;
12249    struct linger ling;
12250 
12251    /* close all open connections */
12252    for (i = 0; i < MAX_RPC_CONNECTION; i++)
12253       if (_server_acception[i].recv_sock != 0) {
12254          /* lingering needed for PCTCP */
12255          ling.l_onoff = 1;
12256          ling.l_linger = 0;
12257          setsockopt(_server_acception[i].recv_sock,
12258                     SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12259          closesocket(_server_acception[i].recv_sock);
12260 
12261          if (_server_acception[i].send_sock) {
12262             setsockopt(_server_acception[i].send_sock,
12263                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12264             closesocket(_server_acception[i].send_sock);
12265          }
12266 
12267          if (_server_acception[i].event_sock) {
12268             setsockopt(_server_acception[i].event_sock,
12269                        SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
12270             closesocket(_server_acception[i].event_sock);
12271          }
12272 
12273          _server_acception[i].recv_sock = 0;
12274          _server_acception[i].send_sock = 0;
12275          _server_acception[i].event_sock = 0;
12276       }
12277 
12278    if (_lsock) {
12279       closesocket(_lsock);
12280       _lsock = 0;
12281       _server_registered = FALSE;
12282    }
12283 
12284    /* free suspend structures */
12285    ss_suspend_exit();
12286 
12287    return RPC_SUCCESS;
12288 }
12289 
12290 
12291 /********************************************************************/
12292 INT rpc_check_channels(void)
12293 /********************************************************************\
12294 
12295   Routine: rpc_check_channels
12296 
12297   Purpose: Check open rpc channels by sending watchdog messages
12298 
12299   Input:
12300     none
12301 
12302   Output:
12303     none
12304 
12305   Function value:
12306     RPC_SUCCESS             Channel is still alive
12307     RPC_NET_ERROR           Connection is broken
12308 
12309 \********************************************************************/
12310 {
12311    INT status, index, i, convert_flags;
12312    NET_COMMAND nc;
12313    fd_set readfds;
12314    struct timeval timeout;
12315 
12316    for (index = 0; index < MAX_RPC_CONNECTION; index++) {
12317       if (_server_acception[index].recv_sock &&
12318           _server_acception[index].tid == ss_gettid() &&
12319           _server_acception[index].watchdog_timeout &&
12320           (ss_millitime() - _server_acception[index].last_activity >
12321            (DWORD) _server_acception[index].watchdog_timeout)) {
12322 /* printf("Send watchdog message to %s on %s\n",
12323                 _server_acception[index].prog_name,
12324                 _server_acception[index].host_name); */
12325 
12326          /* send a watchdog message */
12327          nc.header.routine_id = MSG_WATCHDOG;
12328          nc.header.param_size = 0;
12329 
12330          convert_flags = rpc_get_server_option(RPC_CONVERT_FLAGS);
12331          if (convert_flags) {
12332             rpc_convert_single(&nc.header.routine_id, TID_DWORD, RPC_OUTGOING,
12333                                convert_flags);
12334             rpc_convert_single(&nc.header.param_size, TID_DWORD, RPC_OUTGOING,
12335                                convert_flags);
12336          }
12337 
12338          /* send the header to the client */
12339          i = send_tcp(_server_acception[index].send_sock,
12340                       (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
12341 
12342          if (i < 0)
12343             goto exit;
12344 
12345          /* make some timeout checking */
12346          FD_ZERO(&readfds);
12347          FD_SET(_server_acception[index].send_sock, &readfds);
12348          FD_SET(_server_acception[index].recv_sock, &readfds);
12349 
12350          timeout.tv_sec = _server_acception[index].watchdog_timeout / 1000;
12351          timeout.tv_usec = (_server_acception[index].watchdog_timeout % 1000) * 1000;
12352 
12353          do {
12354             status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12355 
12356             /* if an alarm signal was cought, restart select with reduced timeout */
12357             if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12358                timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12359 
12360          } while (status == -1);        /* dont return if an alarm signal was cought */
12361 
12362          if (!FD_ISSET(_server_acception[index].send_sock, &readfds) &&
12363              !FD_ISSET(_server_acception[index].recv_sock, &readfds))
12364             goto exit;
12365 
12366          /* receive result on send socket */
12367          if (FD_ISSET(_server_acception[index].send_sock, &readfds)) {
12368             i = recv_tcp(_server_acception[index].send_sock, (char *) &nc, sizeof(nc), 0);
12369             if (i <= 0)
12370                goto exit;
12371          }
12372       }
12373    }
12374 
12375    return RPC_SUCCESS;
12376 
12377  exit:
12378 
12379    cm_msg(MINFO, "rpc_check_channels", "client [%s]%s failed watchdog test after %d sec",
12380           _server_acception[index].host_name,
12381           _server_acception[index].prog_name,
12382           _server_acception[index].watchdog_timeout / 1000);
12383 
12384    /* disconnect from experiment */
12385    if (rpc_get_server_option(RPC_OSERVER_TYPE) != ST_REMOTE)
12386       cm_disconnect_experiment();
12387 
12388    /* close server connection */
12389    if (_server_acception[index].recv_sock)
12390       closesocket(_server_acception[index].recv_sock);
12391    if (_server_acception[index].send_sock)
12392       closesocket(_server_acception[index].send_sock);
12393    if (_server_acception[index].event_sock)
12394       closesocket(_server_acception[index].event_sock);
12395 
12396    /* free TCP cache */
12397    M_FREE(_server_acception[index].net_buffer);
12398    _server_acception[index].net_buffer = NULL;
12399 
12400    /* mark this entry as invalid */
12401    memset(&_server_acception[index], 0, sizeof(RPC_SERVER_ACCEPTION));
12402 
12403    return RPC_NET_ERROR;
12404 }
12405 
12406 /**dox***************************************************************/
12407 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12408 
12409 /**dox***************************************************************/
12410 /** @} */ /* end of rpcfunctionc */
12411 
12412 /**dox***************************************************************/
12413 /** @addtogroup bkfunctionc
12414  *  
12415  *  @{  */
12416 
12417 /********************************************************************\
12418 *                                                                    *
12419 *                 Bank functions                                     *
12420 *                                                                    *
12421 \********************************************************************/
12422 
12423 /********************************************************************/
12424 /**
12425 Initializes an event for Midas banks structure.
12426 Before banks can be created in an event, bk_init() has to be called first.
12427 @param event pointer to the area of event
12428 */
12429 void bk_init(void *event)
12430 {
12431    ((BANK_HEADER *) event)->data_size = 0;
12432    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
12433 }
12434 
12435 /**dox***************************************************************/
12436 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12437 
12438 /********************************************************************/
12439 BOOL bk_is32(void *event)
12440 /********************************************************************\
12441 
12442   Routine: bk_is32
12443 
12444   Purpose: Return true if banks inside event are 32-bit banks
12445 
12446   Input:
12447     void   *event           pointer to the event
12448 
12449   Output:
12450     none
12451 
12452   Function value:
12453     none
12454 
12455 \********************************************************************/
12456 {
12457    return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
12458 }
12459 
12460 /**dox***************************************************************/
12461 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12462 
12463 /********************************************************************/
12464 /**
12465 Initializes an event for Midas banks structure for large bank size (> 32KBytes)
12466 Before banks can be created in an event, bk_init32() has to be called first.
12467 @param event pointer to the area of event
12468 @return void
12469 */
12470 void bk_init32(void *event)
12471 {
12472    ((BANK_HEADER *) event)->data_size = 0;
12473    ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
12474 }
12475 
12476 /********************************************************************/
12477 /**
12478 Returns the size of an event containing banks.
12479 The total size of an event is the value returned by bk_size() plus the size
12480 of the event header (sizeof(EVENT_HEADER)).
12481 @param event pointer to the area of event
12482 @return number of bytes contained in data area of event
12483 */
12484 INT bk_size(void *event)
12485 {
12486    return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
12487 }
12488 
12489 /********************************************************************/
12490 /**
12491 Create a Midas bank.
12492 The data pointer pdata must be used as an address to fill a
12493 bank. It is incremented with every value written to the bank and finally points
12494 to a location just after the last byte of the bank. It is then passed to
12495 the function bk_close() to finish the bank creation.
12496 \code
12497 INT *pdata;
12498 bk_init(pevent);
12499 bk_create(pevent, "ADC0", TID_INT, &pdata);
12500 *pdata++ = 123
12501 *pdata++ = 456
12502 bk_close(pevent, pdata);
12503 \endcode
12504 @param event pointer to the data area
12505 @param name of the bank, must be exactly 4 charaters
12506 @param type type of bank, one of the @ref Midas_Data_Types values defined in
12507 midas.h
12508 @param pdata pointer to the data area of the newly created bank
12509 @return void
12510 */
12511 void bk_create(void *event, const char *name, WORD type, void *pdata)
12512 {
12513    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12514       BANK32 *pbk32;
12515 
12516       pbk32 =
12517           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12518                       ((BANK_HEADER *) event)->data_size);
12519       strncpy(pbk32->name, name, 4);
12520       pbk32->type = type;
12521       pbk32->data_size = 0;
12522       *((void **) pdata) = pbk32 + 1;
12523    } else {
12524       BANK *pbk;
12525 
12526       pbk =
12527           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12528                     ((BANK_HEADER *) event)->data_size);
12529       strncpy(pbk->name, name, 4);
12530       pbk->type = type;
12531       pbk->data_size = 0;
12532       *((void **) pdata) = pbk + 1;
12533    }
12534 }
12535 
12536 
12537 
12538 /**dox***************************************************************/
12539 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12540 
12541 /********************************************************************/
12542 int bk_delete(void *event, const char *name)
12543 /********************************************************************\
12544 
12545   Routine: bk_delete
12546 
12547   Purpose: Delete a MIDAS bank inside an event
12548 
12549   Input:
12550     void   *event           pointer to the event
12551     char   *name            Name of bank (exactly four letters)
12552 
12553   Function value:
12554     CM_SUCCESS              Bank has been deleted
12555     0                       Bank has not been found
12556 
12557 \********************************************************************/
12558 {
12559    BANK *pbk;
12560    BANK32 *pbk32;
12561    DWORD dname;
12562    int remaining;
12563 
12564    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12565       /* locate bank */
12566       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12567       strncpy((char *) &dname, name, 4);
12568       do {
12569          if (*((DWORD *) pbk32->name) == dname) {
12570             /* bank found, delete it */
12571             remaining =
12572                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12573                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk32 + 1) +
12574                                                      ALIGN8(pbk32->data_size));
12575 
12576             /* reduce total event size */
12577             ((BANK_HEADER *) event)->data_size -=
12578                 sizeof(BANK32) + ALIGN8(pbk32->data_size);
12579 
12580             /* copy remaining bytes */
12581             if (remaining > 0)
12582                memcpy(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
12583             return CM_SUCCESS;
12584          }
12585 
12586          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12587       } while ((DWORD) pbk32 - (DWORD) event <
12588                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12589    } else {
12590       /* locate bank */
12591       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12592       strncpy((char *) &dname, name, 4);
12593       do {
12594          if (*((DWORD *) pbk->name) == dname) {
12595             /* bank found, delete it */
12596             remaining =
12597                 (int) ((char *) event + ((BANK_HEADER *) event)->data_size +
12598                        sizeof(BANK_HEADER)) - (int) ((char *) (pbk + 1) +
12599                                                      ALIGN8(pbk->data_size));
12600 
12601             /* reduce total event size */
12602             ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
12603 
12604             /* copy remaining bytes */
12605             if (remaining > 0)
12606                memcpy(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
12607             return CM_SUCCESS;
12608          }
12609 
12610          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12611       } while ((DWORD) pbk - (DWORD) event <
12612                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12613    }
12614 
12615    return 0;
12616 }
12617 
12618 /**dox***************************************************************/
12619 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12620 
12621 /********************************************************************/
12622 /**
12623 Close the Midas bank priviously created by bk_create().
12624 The data pointer pdata must be obtained by bk_create() and
12625 used as an address to fill a bank. It is incremented with every value written
12626 to the bank and finally points to a location just after the last byte of the
12627 bank. It is then passed to bk_close() to finish the bank creation
12628 @param event pointer to current composed event
12629 @param pdata  pointer to the data
12630 @return number of bytes contained in bank
12631 */
12632 INT bk_close(void *event, void *pdata)
12633 {
12634    if (((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) {
12635       BANK32 *pbk32;
12636 
12637       pbk32 =
12638           (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) +
12639                       ((BANK_HEADER *) event)->data_size);
12640       pbk32->data_size = (DWORD) (INT) pdata - (INT) (pbk32 + 1);
12641       if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
12642          printf("Warning: bank %c%c%c%c has zero size\n",
12643                 pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
12644       ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
12645       return pbk32->data_size;
12646    } else {
12647       BANK *pbk;
12648 
12649       pbk =
12650           (BANK *) ((char *) (((BANK_HEADER *) event) + 1) +
12651                     ((BANK_HEADER *) event)->data_size);
12652       pbk->data_size = (WORD) (INT) pdata - (INT) (pbk + 1);
12653       if (pbk->type == TID_STRUCT && pbk->data_size == 0)
12654          printf("Warning: bank %c%c%c%c has zero size\n",
12655                 pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
12656       ((BANK_HEADER *) event)->data_size += sizeof(BANK) + ALIGN8(pbk->data_size);
12657       return pbk->data_size;
12658    }
12659 }
12660 
12661 /********************************************************************/
12662 /**
12663 Extract the MIDAS bank name listing of an event.
12664 The bklist should be dimensioned with STRING_BANKLIST_MAX
12665 which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
12666 \code
12667 INT adc_calib(EVENT_HEADER *pheader, void *pevent)
12668 {
12669   INT    n_adc, nbanks;
12670   WORD   *pdata;
12671   char   banklist[STRING_BANKLIST_MAX];
12672 
12673   // Display # of banks and list of banks in the event
12674   nbanks = bk_list(pevent, banklist);
12675   printf("#banks:%d List:%s\n", nbanks, banklist);
12676 
12677   // look for ADC0 bank, return if not present
12678   n_adc = bk_locate(pevent, "ADC0", &pdata);
12679   ...
12680 }
12681 \endcode
12682 @param event pointer to current composed event
12683 @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
12684 @return number of bank found in this event.
12685 */
12686 INT bk_list(void *event, char *bklist)
12687 {                               /* Full event */
12688    INT nbk, size;
12689    BANK *pmbk = NULL;
12690    BANK32 *pmbk32 = NULL;
12691    char *pdata;
12692 
12693    /* compose bank list */
12694    bklist[0] = 0;
12695    nbk = 0;
12696    do {
12697       /* scan all banks for bank name only */
12698       if (bk_is32(event)) {
12699          size = bk_iterate32(event, &pmbk32, &pdata);
12700          if (pmbk32 == NULL)
12701             break;
12702       } else {
12703          size = bk_iterate(event, &pmbk, &pdata);
12704          if (pmbk == NULL)
12705             break;
12706       }
12707       nbk++;
12708 
12709       if (nbk > BANKLIST_MAX) {
12710          cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
12711          return (nbk - 1);
12712       }
12713       if (bk_is32(event))
12714          strncat(bklist, (char *) pmbk32->name, 4);
12715       else
12716          strncat(bklist, (char *) pmbk->name, 4);
12717    }
12718    while (1);
12719    return (nbk);
12720 }
12721 
12722 /********************************************************************/
12723 /**
12724 Locates a MIDAS bank of given name inside an event.
12725 @param event pointer to current composed event
12726 @param name bank name to look for
12727 @param pdata pointer to data area of bank, NULL if bank not found
12728 @return number of values inside the bank
12729 */
12730 INT bk_locate(void *event, const char *name, void *pdata)
12731 {
12732    BANK *pbk;
12733    BANK32 *pbk32;
12734    DWORD dname;
12735 
12736    if (bk_is32(event)) {
12737       pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
12738       strncpy((char *) &dname, name, 4);
12739       do {
12740          if (*((DWORD *) pbk32->name) == dname) {
12741             *((void **) pdata) = pbk32 + 1;
12742             if (tid_size[pbk32->type & 0xFF] == 0)
12743                return pbk32->data_size;
12744             return pbk32->data_size / tid_size[pbk32->type & 0xFF];
12745          }
12746          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12747       } while ((DWORD) pbk32 - (DWORD) event <
12748                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12749    } else {
12750       pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12751       strncpy((char *) &dname, name, 4);
12752       do {
12753          if (*((DWORD *) pbk->name) == dname) {
12754             *((void **) pdata) = pbk + 1;
12755             if (tid_size[pbk->type & 0xFF] == 0)
12756                return pbk->data_size;
12757             return pbk->data_size / tid_size[pbk->type & 0xFF];
12758          }
12759          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12760       } while ((DWORD) pbk - (DWORD) event <
12761                ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
12762    }
12763 
12764    /* bank not found */
12765    *((void **) pdata) = NULL;
12766    return 0;
12767 }
12768 
12769 /********************************************************************/
12770 /**
12771 Finds a MIDAS bank of given name inside an event.
12772 @param pbkh pointer to current composed event
12773 @param name bank name to look for
12774 @param bklen number of elemtents in bank
12775 @param bktype bank type, one of TID_xxx
12776 @param pdata pointer to data area of bank, NULL if bank not found
12777 @return 1 if bank found, 0 otherwise
12778 */
12779 INT bk_find(BANK_HEADER * pbkh, const char *name, DWORD * bklen, DWORD * bktype,
12780             void **pdata)
12781 {
12782    BANK *pbk;
12783    BANK32 *pbk32;
12784    DWORD dname;
12785 
12786    if (bk_is32(pbkh)) {
12787       pbk32 = (BANK32 *) (pbkh + 1);
12788       strncpy((char *) &dname, name, 4);
12789       do {
12790          if (*((DWORD *) pbk32->name) == dname) {
12791             *((void **) pdata) = pbk32 + 1;
12792             if (tid_size[pbk32->type & 0xFF] == 0)
12793                *bklen = pbk32->data_size;
12794             else
12795                *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
12796 
12797             *bktype = pbk32->type;
12798             return 1;
12799          }
12800          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12801       } while ((DWORD) pbk32 - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
12802    } else {
12803       pbk = (BANK *) (pbkh + 1);
12804       strncpy((char *) &dname, name, 4);
12805       do {
12806          if (*((DWORD *) pbk->name) == dname) {
12807             *((void **) pdata) = pbk + 1;
12808             if (tid_size[pbk->type & 0xFF] == 0)
12809                *bklen = pbk->data_size;
12810             else
12811                *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
12812 
12813             *bktype = pbk->type;
12814             return 1;
12815          }
12816          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12817       } while ((DWORD) pbk - (DWORD) pbkh < pbkh->data_size + sizeof(BANK_HEADER));
12818    }
12819 
12820    /* bank not found */
12821    *((void **) pdata) = NULL;
12822    return 0;
12823 }
12824 
12825 /********************************************************************/
12826 /**
12827 Iterates through banks inside an event.
12828 The function can be used to enumerate all banks of an event.
12829 The returned pointer to the bank header has following structure:
12830 \code
12831 typedef struct {
12832 char   name[4];
12833 WORD   type;
12834 WORD   data_size;
12835 } BANK;
12836 \endcode
12837 where type is a TID_xxx value and data_size the size of the bank in bytes.
12838 \code
12839 BANK *pbk;
12840 INT  size;
12841 void *pdata;
12842 char name[5];
12843 pbk = NULL;
12844 do
12845 {
12846  size = bk_iterate(event, &pbk, &pdata);
12847  if (pbk == NULL)
12848   break;
12849  *((DWORD *)name) = *((DWORD *)(pbk)->name);
12850  name[4] = 0;
12851  printf("bank %s found\n", name);
12852 } while(TRUE);
12853 \endcode
12854 @param event Pointer to data area of event.
12855 @param pbk pointer to the bank header, must be NULL for the first call to
12856 this function.
12857 @param pdata Pointer to the bank header, must be NULL for the first
12858 call to this function
12859 @return Size of bank in bytes
12860 */
12861 INT bk_iterate(void *event, BANK ** pbk, void *pdata)
12862 {
12863    if (*pbk == NULL)
12864       *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
12865    else
12866       *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12867 
12868    *((void **) pdata) = (*pbk) + 1;
12869 
12870    if ((DWORD) * pbk - (DWORD) event >=
12871        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12872       *pbk = *((BANK **) pdata) = NULL;
12873       return 0;
12874    }
12875 
12876    return (*pbk)->data_size;
12877 }
12878 
12879 
12880 /**dox***************************************************************/
12881 #ifndef DOXYGEN_SHOULD_SKIP_THIS
12882 
12883 /********************************************************************/
12884 INT bk_iterate32(void *event, BANK32 ** pbk, void *pdata)
12885 /********************************************************************\
12886 
12887   Routine: bk_iterate
12888 
12889   Purpose: Iterate through 32 bit MIDAS banks inside an event
12890 
12891   Input:
12892     void   *event           pointer to the event
12893     BANK   **pbk32          must be NULL for the first call to bk_iterate
12894 
12895   Output:
12896     BANK   **pbk            pointer to the bank header
12897     void   *pdata           pointer to data area of the bank
12898 
12899   Function value:
12900     INT    size of the bank in bytes
12901 
12902 \********************************************************************/
12903 {
12904    if (*pbk == NULL)
12905       *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
12906    else
12907       *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
12908 
12909    *((void **) pdata) = (*pbk) + 1;
12910 
12911    if ((DWORD) * pbk - (DWORD) event >=
12912        ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
12913       *pbk = *((BANK32 **) pdata) = NULL;
12914       return 0;
12915    }
12916 
12917    return (*pbk)->data_size;
12918 }
12919 
12920 /**dox***************************************************************/
12921 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
12922 
12923 /********************************************************************/
12924 /**
12925 Swaps bytes from little endian to big endian or vice versa for a whole event.
12926 
12927 An event contains a flag which is set by bk_init() to identify
12928 the endian format of an event. If force is FALSE, this flag is evaluated and
12929 the event is only swapped if it is in the "wrong" format for this system.
12930 An event can be swapped to the "wrong" format on purpose for example by a
12931 front-end which wants to produce events in a "right" format for a back-end
12932 analyzer which has different byte ordering.
12933 @param event pointer to data area of event
12934 @param force If TRUE, the event is always swapped, if FALSE, the event
12935 is only swapped if it is in the wrong format.
12936 @return 1==event has been swap, 0==event has not been swapped.
12937 */
12938 INT bk_swap(void *event, BOOL force)
12939 {
12940    BANK_HEADER *pbh;
12941    BANK *pbk;
12942    BANK32 *pbk32;
12943    void *pdata;
12944    WORD type;
12945    BOOL b32;
12946 
12947    pbh = (BANK_HEADER *) event;
12948 
12949    /* only swap if flags in high 16-bit */
12950    if (pbh->flags < 0x10000 && !force)
12951       return 0;
12952 
12953    /* swap bank header */
12954    DWORD_SWAP(&pbh->data_size);
12955    DWORD_SWAP(&pbh->flags);
12956 
12957    /* check for 32bit banks */
12958    b32 = ((pbh->flags & BANK_FORMAT_32BIT) > 0);
12959 
12960    pbk = (BANK *) (pbh + 1);
12961    pbk32 = (BANK32 *) pbk;
12962 
12963    /* scan event */
12964    while ((PTYPE) pbk - (PTYPE) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
12965       /* swap bank header */
12966       if (b32) {
12967          DWORD_SWAP(&pbk32->type);
12968          DWORD_SWAP(&pbk32->data_size);
12969          pdata = pbk32 + 1;
12970          type = (WORD) pbk32->type;
12971       } else {
12972          WORD_SWAP(&pbk->type);
12973          WORD_SWAP(&pbk->data_size);
12974          pdata = pbk + 1;
12975          type = pbk->type;
12976       }
12977 
12978       /* pbk points to next bank */
12979       if (b32) {
12980          pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
12981          pbk = (BANK *) pbk32;
12982       } else {
12983          pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
12984          pbk32 = (BANK32 *) pbk;
12985       }
12986 
12987       switch (type) {
12988       case TID_WORD:
12989       case TID_SHORT:
12990          while ((PTYPE) pdata < (PTYPE) pbk) {
12991             WORD_SWAP(pdata);
12992             pdata = (void *) (((WORD *) pdata) + 1);
12993          }
12994          break;
12995 
12996       case TID_DWORD:
12997       case TID_INT:
12998       case TID_BOOL:
12999       case TID_FLOAT:
13000          while ((PTYPE) pdata < (PTYPE) pbk) {
13001             DWORD_SWAP(pdata);
13002             pdata = (void *) (((DWORD *) pdata) + 1);
13003          }
13004          break;
13005 
13006       case TID_DOUBLE:
13007          while ((PTYPE) pdata < (PTYPE) pbk) {
13008             QWORD_SWAP(pdata);
13009             pdata = (void *) (((double *) pdata) + 1);
13010          }
13011          break;
13012       }
13013    }
13014 
13015    return CM_SUCCESS;
13016 }
13017 
13018 /**dox***************************************************************/
13019 /** @} */ /* end of bkfunctionc */
13020 
13021 /**dox***************************************************************/
13022 /** @addtogroup hsfunctionc
13023  *  
13024  *  @{  */
13025 
13026 #if !defined(OS_VXWORKS)
13027 /********************************************************************\
13028 *                                                                    *
13029 *                 History functions                                  *
13030 *                                                                    *
13031 \********************************************************************/
13032 
13033 /**dox***************************************************************/
13034 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13035 
13036 static HISTORY *_history;
13037 static INT _history_entries = 0;
13038 static char _hs_path_name[MAX_STRING_LENGTH];
13039 
13040 /**dox***************************************************************/
13041 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
13042 
13043 /********************************************************************/
13044 /**
13045 Sets the path for future history file accesses. Should
13046 be called before any other history function is called.
13047 @param path             Directory where history files reside
13048 @return HS_SUCCESS
13049 */
13050 INT hs_set_path(char *path)
13051 {
13052    /* set path locally and remotely */
13053    if (rpc_is_remote())
13054       rpc_call(RPC_HS_SET_PATH, path);
13055 
13056    strcpy(_hs_path_name, path);
13057 
13058    /* check for trailing directory seperator */
13059    if (strlen(_hs_path_name) > 0 &&
13060        _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
13061       strcat(_hs_path_name, DIR_SEPARATOR_STR);
13062 
13063    return HS_SUCCESS;
13064 }
13065 
13066 
13067 /********************************************************************/
13068 /**
13069 Open history file belonging to certain date. Internal use
13070            only.
13071 @param ltime          Date for which a history file should be opened.
13072 @param suffix         File name suffix like "hst", "idx", "idf"
13073 @param mode           R/W access mode
13074 @param fh             File handle
13075 @return HS_SUCCESS
13076 */
13077 INT hs_open_file(DWORD ltime, char *suffix, INT mode, int *fh)
13078 {
13079    struct tm *tms;
13080    char file_name[256];
13081 
13082    /* generate new file name YYMMDD.xxx */
13083 #if !defined(OS_VXWORKS)
13084 #if !defined(OS_VMS)
13085    tzset();
13086 #endif
13087 #endif
13088    tms = localtime((const time_t *) &ltime);
13089 
13090    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name,
13091            tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
13092 
13093    /* open file, add O_BINARY flag for Windows NT */
13094    *fh = open(file_name, mode | O_BINARY, 0644);
13095 
13096    return HS_SUCCESS;
13097 }
13098 
13099 /**dox***************************************************************/
13100 #ifndef DOXYGEN_SHOULD_SKIP_THIS
13101 
13102 /********************************************************************/
13103 INT hs_gen_index(DWORD ltime)
13104 /********************************************************************\
13105 
13106   Routine: hs_gen_index
13107 
13108   Purpose: Regenerate index files ("idx" and "idf" files) for a given
13109            history file ("hst"). Interal use only.
13110 
13111   Input:
13112     DWORD  ltime            Date for which a history file should
13113                             be analyzed.
13114 
13115   Output:
13116     none
13117 
13118   Function value:
13119     HS_SUCCESS              Successful completion
13120     HS_FILE_ERROR           Index files cannot be created
13121 
13122 \********************************************************************/
13123 {
13124    char event_name[NAME_LENGTH];
13125    int fh, fhd, fhi;
13126    INT n;
13127    HIST_RECORD rec;
13128    INDEX_RECORD irec;
13129    DEF_RECORD def_rec;
13130 
13131    printf("Recovering index files...\n");
13132 
13133    if (ltime == 0)
13134       ltime = time(NULL);
13135 
13136    /* open new index file */
13137    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
13138    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
13139 
13140    if (fhd < 0 || fhi < 0) {
13141       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
13142       return HS_FILE_ERROR;
13143    }
13144 
13145    /* open history file */
13146    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13147    if (fh < 0)
13148       return HS_FILE_ERROR;
13149    lseek(fh, 0, SEEK_SET);
13150 
13151    /* loop over file records in .hst file */
13152    do {
13153       n = read(fh, (char *) &rec, sizeof(rec));
13154       if (n < sizeof(rec))
13155          break;
13156 
13157       /* check if record type is definition */
13158       if (rec.record_type == RT_DEF) {
13159          /* read name */
13160          read(fh, event_name, sizeof(event_name));
13161 
13162          printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
13163 
13164          /* write definition index record */
13165          def_rec.event_id = rec.event_id;
13166          memcpy(def_rec.event_name, event_name, sizeof(event_name));
13167          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
13168          write(fhd, (char *) &def_rec, sizeof(def_rec));
13169 
13170          /* skip tags */
13171          lseek(fh, rec.data_size, SEEK_CUR);
13172       } else {
13173          /* write index record */
13174          irec.event_id = rec.event_id;
13175          irec.time = rec.time;
13176          irec.offset = TELL(fh) - sizeof(rec);
13177          write(fhi, (char *) &irec, sizeof(irec));
13178 
13179          /* printf("ID %d, %s\n", rec.event_id, ctime((const time_t *)&irec.time)+4); */
13180 
13181          /* skip data */
13182          lseek(fh, rec.data_size, SEEK_CUR);
13183       }
13184 
13185    } while (TRUE);
13186 
13187    close(fh);
13188    close(fhi);
13189    close(fhd);
13190 
13191    printf("...done.\n");
13192 
13193    return HS_SUCCESS;
13194 }
13195 
13196 
13197 /********************************************************************/
13198 INT hs_search_file(DWORD * ltime, INT direction)
13199 /********************************************************************\
13200 
13201   Routine: hs_search_file
13202 
13203   Purpose: Search an history file for a given date. If not found,
13204            look for files after date (direction==1) or before date
13205            (direction==-1) up to one year.
13206 
13207   Input:
13208     DWORD  *ltime           Date of history file
13209     INT    direction        Search direction
13210 
13211   Output:
13212     DWORD  *ltime           Date of history file found
13213 
13214   Function value:
13215     HS_SUCCESS              Successful completion
13216     HS_FILE_ERROR           No file found
13217 
13218 \********************************************************************/
13219 {
13220    DWORD lt;
13221    int fh, fhd, fhi;
13222    struct tm *tms;
13223 
13224    if (*ltime == 0)
13225       *ltime = time(NULL);
13226 
13227    lt = *ltime;
13228    do {
13229       /* try to open history file for date "lt" */
13230       hs_open_file(lt, "hst", O_RDONLY, &fh);
13231 
13232       /* if not found, look for next day */
13233       if (fh < 0)
13234          lt += direction * 3600 * 24;
13235 
13236       /* stop if more than a year before starting point or in the future */
13237    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 &&
13238             lt < (DWORD) time(NULL));
13239 
13240    if (fh < 0)
13241       return HS_FILE_ERROR;
13242 
13243    if (lt != *ltime) {
13244       /* if switched to new day, set start_time to 0:00 */
13245       tms = localtime((const time_t *) &lt);
13246       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
13247       *ltime = mktime(tms);
13248    }
13249 
13250    /* check if index files are there */
13251    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
13252    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
13253 
13254    close(fh);
13255    close(fhd);
13256    close(fhi);
13257 
13258    /* generate them if not */
13259    if (fhd < 0 || fhi < 0)
13260       hs_gen_index(*ltime);
13261 
13262    return HS_SUCCESS;
13263 }
13264 
13265 
13266 /********************************************************************/
13267 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
13268 /********************************************************************\
13269 
13270   Routine: hs_define_evnet
13271 
13272   Purpose: Define a new event for which a history should be recorded.
13273            This routine must be called before any call to
13274            hs_write_event. It also should be called if the definition
13275            of the event has changed.
13276 
13277            The event definition is written directly to the history
13278            file. If the definition is identical to a previous
13279            definition, it is not written to the file.
13280 
13281 
13282   Input:
13283     DWORD  event_id         ID for this event. Must be unique.
13284     char   name             Name of this event
13285     TAG    tag              Tag list containing names and types of
13286                             variables in this event.
13287     DWORD  size             Size of tag array
13288 
13289   Output:
13290     <none>
13291 
13292   Function value:
13293     HS_SUCCESS              Successful completion
13294     HS_NO_MEMEORY           Out of memory
13295     HS_FILE_ERROR           Cannot open history file
13296 
13297 \********************************************************************/
13298 {
13299 /* History events are only written locally (?)
13300 
13301   if (rpc_is_remote())
13302     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
13303 */
13304    {
13305       HIST_RECORD rec, prev_rec;
13306       DEF_RECORD def_rec;
13307       char str[256], event_name[NAME_LENGTH], *buffer;
13308       int fh, fhi, fhd;
13309       INT i, n, len, index;
13310       struct tm *tmb;
13311 
13312       /* allocate new space for the new history descriptor */
13313       if (_history_entries == 0) {
13314          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
13315          memset(_history, 0, sizeof(HISTORY));
13316          if (_history == NULL)
13317             return HS_NO_MEMORY;
13318 
13319          _history_entries = 1;
13320          index = 0;
13321       } else {
13322          /* check if history already open */
13323          for (i = 0; i < _history_entries; i++)
13324             if (_history[i].event_id == event_id)
13325                break;
13326 
13327          /* if not found, create new one */
13328          if (i == _history_entries) {
13329             _history =
13330                 (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
13331             memset(&_history[_history_entries], 0, sizeof(HISTORY));
13332 
13333             _history_entries++;
13334             if (_history == NULL) {
13335                _history_entries--;
13336                return HS_NO_MEMORY;
13337             }
13338          }
13339          index = i;
13340       }
13341 
13342       /* assemble definition record header */
13343       rec.record_type = RT_DEF;
13344       rec.event_id = event_id;
13345       rec.time = time(NULL);
13346       rec.data_size = size;
13347       strncpy(event_name, name, NAME_LENGTH);
13348 
13349       /* pad tag names with zeos */
13350       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
13351          len = strlen(tag[i].name);
13352          memset(tag[i].name + len, 0, NAME_LENGTH - len);
13353       }
13354 
13355       /* if history structure not set up, do so now */
13356       if (!_history[index].hist_fh) {
13357          /* open history file */
13358          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13359          if (fh < 0)
13360             return HS_FILE_ERROR;
13361 
13362          /* open index files */
13363          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13364          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13365          lseek(fh, 0, SEEK_END);
13366          lseek(fhi, 0, SEEK_END);
13367          lseek(fhd, 0, SEEK_END);
13368 
13369          /* regenerate index if missing */
13370          if (TELL(fh) > 0 && TELL(fhd) == 0) {
13371             close(fh);
13372             close(fhi);
13373             close(fhd);
13374             hs_gen_index(rec.time);
13375             hs_open_file(rec.time, "hst", O_RDWR, &fh);
13376             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
13377             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
13378             lseek(fh, 0, SEEK_END);
13379             lseek(fhi, 0, SEEK_END);
13380             lseek(fhd, 0, SEEK_END);
13381          }
13382 
13383          tmb = localtime((const time_t *) &rec.time);
13384          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
13385 
13386          /* setup history structure */
13387          _history[index].hist_fh = fh;
13388          _history[index].index_fh = fhi;
13389          _history[index].def_fh = fhd;
13390          _history[index].def_offset = TELL(fh);
13391          _history[index].event_id = event_id;
13392          strcpy(_history[index].event_name, event_name);
13393          _history[index].base_time = mktime(tmb);
13394          _history[index].n_tag = size / sizeof(TAG);
13395          _history[index].tag = (TAG *) M_MALLOC(size);
13396          memcpy(_history[index].tag, tag, size);
13397 
13398          /* search previous definition */
13399          n = TELL(fhd) / sizeof(def_rec);
13400          def_rec.event_id = 0;
13401          for (i = n - 1; i >= 0; i--) {
13402             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13403             read(fhd, (char *) &def_rec, sizeof(def_rec));
13404             if (def_rec.event_id == event_id)
13405                break;
13406          }
13407          lseek(fhd, 0, SEEK_END);
13408 
13409          /* if definition found, compare it with new one */
13410          if (def_rec.event_id == event_id) {
13411             buffer = (char *) M_MALLOC(size);
13412             memset(buffer, 0, size);
13413 
13414             lseek(fh, def_rec.def_offset, SEEK_SET);
13415             read(fh, (char *) &prev_rec, sizeof(prev_rec));
13416             read(fh, str, NAME_LENGTH);
13417             read(fh, buffer, size);
13418             lseek(fh, 0, SEEK_END);
13419 
13420             if (prev_rec.data_size != size ||
13421                 strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13422                /* write definition to history file */
13423                write(fh, (char *) &rec, sizeof(rec));
13424                write(fh, event_name, NAME_LENGTH);
13425                write(fh, (char *) tag, size);
13426 
13427                /* write index record */
13428                def_rec.event_id = event_id;
13429                memcpy(def_rec.event_name, event_name, sizeof(event_name));
13430                def_rec.def_offset = _history[index].def_offset;
13431                write(fhd, (char *) &def_rec, sizeof(def_rec));
13432             } else
13433                /* definition identical, just remember old offset */
13434                _history[index].def_offset = def_rec.def_offset;
13435 
13436             M_FREE(buffer);
13437          } else {
13438             /* write definition to history file */
13439             write(fh, (char *) &rec, sizeof(rec));
13440             write(fh, event_name, NAME_LENGTH);
13441             write(fh, (char *) tag, size);
13442 
13443             /* write definition index record */
13444             def_rec.event_id = event_id;
13445             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13446             def_rec.def_offset = _history[index].def_offset;
13447             write(fhd, (char *) &def_rec, sizeof(def_rec));
13448          }
13449       } else {
13450          fh = _history[index].hist_fh;
13451          fhd = _history[index].def_fh;
13452 
13453          /* compare definition with previous definition */
13454          buffer = (char *) M_MALLOC(size);
13455          memset(buffer, 0, size);
13456 
13457          lseek(fh, _history[index].def_offset, SEEK_SET);
13458          read(fh, (char *) &prev_rec, sizeof(prev_rec));
13459          read(fh, str, NAME_LENGTH);
13460          read(fh, buffer, size);
13461 
13462          lseek(fh, 0, SEEK_END);
13463          lseek(fhd, 0, SEEK_END);
13464 
13465          if (prev_rec.data_size != size ||
13466              strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
13467             /* save new definition offset */
13468             _history[index].def_offset = TELL(fh);
13469 
13470             /* write definition to history file */
13471             write(fh, (char *) &rec, sizeof(rec));
13472             write(fh, event_name, NAME_LENGTH);
13473             write(fh, (char *) tag, size);
13474 
13475             /* write index record */
13476             def_rec.event_id = event_id;
13477             memcpy(def_rec.event_name, event_name, sizeof(event_name));
13478             def_rec.def_offset = _history[index].def_offset;
13479             write(fhd, (char *) &def_rec, sizeof(def_rec));
13480          }
13481 
13482          M_FREE(buffer);
13483       }
13484 
13485    }
13486 
13487    return HS_SUCCESS;
13488 }
13489 
13490 
13491 /********************************************************************/
13492 INT hs_write_event(DWORD event_id, void *data, DWORD size)
13493 /********************************************************************\
13494 
13495   Routine: hs_write_event
13496 
13497   Purpose: Write an event to a history file.
13498 
13499   Input:
13500     DWORD  event_id         Event ID
13501     void   *data            Data buffer containing event
13502     DWORD  size             Data buffer size in bytes
13503 
13504   Output:
13505     none
13506                             future hs_write_event
13507 
13508   Function value:
13509     HS_SUCCESS              Successful completion
13510     HS_NO_MEMEORY           Out of memory
13511     HS_FILE_ERROR           Cannot write to history file
13512     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
13513 
13514 \********************************************************************/
13515 {
13516 /* history events are only written locally (?)
13517 
13518   if (rpc_is_remote())
13519     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
13520 */
13521    HIST_RECORD rec, drec;
13522    DEF_RECORD def_rec;
13523    INDEX_RECORD irec;
13524    int fh, fhi, fhd;
13525    INT index;
13526    struct tm tmb, tmr;
13527 
13528    /* find index to history structure */
13529    for (index = 0; index < _history_entries; index++)
13530       if (_history[index].event_id == event_id)
13531          break;
13532    if (index == _history_entries)
13533       return HS_UNDEFINED_EVENT;
13534 
13535    /* assemble record header */
13536    rec.record_type = RT_DATA;
13537    rec.event_id = _history[index].event_id;
13538    rec.time = time(NULL);
13539    rec.def_offset = _history[index].def_offset;
13540    rec.data_size = size;
13541 
13542    irec.event_id = _history[index].event_id;
13543    irec.time = rec.time;
13544 
13545    /* check if new day */
13546    memcpy(&tmr, localtime((const time_t *) &rec.time), sizeof(tmr));
13547    memcpy(&tmb, localtime((const time_t *) &_history[index].base_time), sizeof(tmb));
13548 
13549    if (tmr.tm_yday != tmb.tm_yday) {
13550       /* close current history file */
13551       close(_history[index].hist_fh);
13552       close(_history[index].def_fh);
13553       close(_history[index].index_fh);
13554 
13555       /* open new history file */
13556       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
13557       if (fh < 0)
13558          return HS_FILE_ERROR;
13559 
13560       /* open new index file */
13561       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
13562       if (fhi < 0)
13563          return HS_FILE_ERROR;
13564 
13565       /* open new definition index file */
13566       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
13567       if (fhd < 0)
13568          return HS_FILE_ERROR;
13569 
13570       lseek(fh, 0, SEEK_END);
13571       lseek(fhi, 0, SEEK_END);
13572       lseek(fhd, 0, SEEK_END);
13573 
13574       /* remember new file handles */
13575       _history[index].hist_fh = fh;
13576       _history[index].index_fh = fhi;
13577       _history[index].def_fh = fhd;
13578 
13579       _history[index].def_offset = TELL(fh);
13580       rec.def_offset = _history[index].def_offset;
13581 
13582       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
13583       _history[index].base_time = mktime(&tmr);
13584 
13585       /* write definition from _history structure */
13586       drec.record_type = RT_DEF;
13587       drec.event_id = _history[index].event_id;
13588       drec.time = rec.time;
13589       drec.data_size = _history[index].n_tag * sizeof(TAG);
13590 
13591       write(fh, (char *) &drec, sizeof(drec));
13592       write(fh, _history[index].event_name, NAME_LENGTH);
13593       write(fh, (char *) _history[index].tag, drec.data_size);
13594 
13595       /* write definition index record */
13596       def_rec.event_id = _history[index].event_id;
13597       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
13598       def_rec.def_offset = _history[index].def_offset;
13599       write(fhd, (char *) &def_rec, sizeof(def_rec));
13600    }
13601 
13602    /* got to end of file */
13603    lseek(_history[index].hist_fh, 0, SEEK_END);
13604    irec.offset = TELL(_history[index].hist_fh);
13605 
13606    /* write record header */
13607    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
13608 
13609    /* write data */
13610    write(_history[index].hist_fh, (char *) data, size);
13611 
13612    /* write index record */
13613    lseek(_history[index].index_fh, 0, SEEK_END);
13614    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec))
13615       return HS_FILE_ERROR;
13616 
13617    return HS_SUCCESS;
13618 }
13619 
13620 
13621 /********************************************************************/
13622 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size,
13623                    INT event_id[], DWORD * id_size)
13624 /********************************************************************\
13625 
13626   Routine: hs_enum_events
13627 
13628   Purpose: Enumerate events for a given date
13629 
13630   Input:
13631     DWORD  ltime            Date at which events should be enumerated
13632 
13633   Output:
13634     char   *event_name      Array containing event names
13635     DWORD  *name_size       Size of name array
13636     char   *event_id        Array containing event IDs
13637     DWORD  *id_size         Size of ID array
13638 
13639   Function value:
13640     HS_SUCCESS              Successful completion
13641     HS_NO_MEMEORY           Out of memory
13642     HS_FILE_ERROR           Cannot open history file
13643 
13644 \********************************************************************/
13645 {
13646    int fh, fhd;
13647    INT status, i, j, n;
13648    DEF_RECORD def_rec;
13649 
13650    if (rpc_is_remote())
13651       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id,
13652                       id_size);
13653 
13654    /* search latest history file */
13655    status = hs_search_file(&ltime, -1);
13656    if (status != HS_SUCCESS) {
13657       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
13658       return HS_FILE_ERROR;
13659    }
13660 
13661    /* open history and definition files */
13662    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13663    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13664    if (fh < 0 || fhd < 0) {
13665       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
13666       return HS_FILE_ERROR;
13667    }
13668    lseek(fhd, 0, SEEK_SET);
13669 
13670    /* loop over definition index file */
13671    n = 0;
13672    do {
13673       /* read event definition */
13674       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13675       if (j < (int) sizeof(def_rec))
13676          break;
13677 
13678       /* look for existing entry for this event id */
13679       for (i = 0; i < n; i++)
13680          if (event_id[i] == (INT) def_rec.event_id) {
13681             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13682             break;
13683          }
13684 
13685       /* new entry found */
13686       if (i == n) {
13687          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
13688             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
13689             close(fh);
13690             close(fhd);
13691             return HS_NO_MEMORY;
13692          }
13693 
13694          /* copy definition record */
13695          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
13696          event_id[i] = def_rec.event_id;
13697          n++;
13698       }
13699    } while (TRUE);
13700 
13701    close(fh);
13702    close(fhd);
13703    *name_size = n * NAME_LENGTH;
13704    *id_size = n * sizeof(INT);
13705 
13706    return HS_SUCCESS;
13707 }
13708 
13709 
13710 /********************************************************************/
13711 INT hs_count_events(DWORD ltime, DWORD * count)
13712 /********************************************************************\
13713 
13714   Routine: hs_count_events
13715 
13716   Purpose: Count number of different events for a given date
13717 
13718   Input:
13719     DWORD  ltime            Date at which events should be counted
13720 
13721   Output:
13722     DWORD  *count           Number of different events found
13723 
13724   Function value:
13725     HS_SUCCESS              Successful completion
13726     HS_FILE_ERROR           Cannot open history file
13727 
13728 \********************************************************************/
13729 {
13730    int fh, fhd;
13731    INT status, i, j, n;
13732    DWORD *id;
13733    DEF_RECORD def_rec;
13734 
13735    if (rpc_is_remote())
13736       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
13737 
13738    /* search latest history file */
13739    status = hs_search_file(&ltime, -1);
13740    if (status != HS_SUCCESS) {
13741       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13742       return HS_FILE_ERROR;
13743    }
13744 
13745    /* open history and definition files */
13746    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13747    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13748    if (fh < 0 || fhd < 0) {
13749       cm_msg(MERROR, "hs_count_events", "cannot open index files");
13750       return HS_FILE_ERROR;
13751    }
13752 
13753    /* allocate event id array */
13754    lseek(fhd, 0, SEEK_END);
13755    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
13756    lseek(fhd, 0, SEEK_SET);
13757 
13758    /* loop over index file */
13759    n = 0;
13760    do {
13761       /* read definition index record */
13762       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
13763       if (j < (int) sizeof(def_rec))
13764          break;
13765 
13766       /* look for existing entries */
13767       for (i = 0; i < n; i++)
13768          if (id[i] == def_rec.event_id)
13769             break;
13770 
13771       /* new entry found */
13772       if (i == n) {
13773          id[i] = def_rec.event_id;
13774          n++;
13775       }
13776    } while (TRUE);
13777 
13778 
13779    M_FREE(id);
13780    close(fh);
13781    close(fhd);
13782    *count = n;
13783 
13784    return HS_SUCCESS;
13785 }
13786 
13787 
13788 /********************************************************************/
13789 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
13790 /********************************************************************\
13791 
13792   Routine: hs_get_event_id
13793 
13794   Purpose: Return event ID for a given name. If event cannot be found
13795            in current definition file, go back in time until found
13796 
13797   Input:
13798     DWORD  ltime            Date at which event ID should be looked for
13799 
13800   Output:
13801     DWORD  *id              Event ID
13802 
13803   Function value:
13804     HS_SUCCESS              Successful completion
13805     HS_FILE_ERROR           Cannot open history file
13806     HS_UNDEFINED_EVENT      Event "name" not found
13807 
13808 \********************************************************************/
13809 {
13810    int fh, fhd;
13811    INT status, i;
13812    DWORD lt;
13813    DEF_RECORD def_rec;
13814 
13815    if (rpc_is_remote())
13816       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
13817 
13818    /* search latest history file */
13819    if (ltime == 0)
13820       ltime = time(NULL);
13821 
13822    lt = ltime;
13823 
13824    do {
13825       status = hs_search_file(&lt, -1);
13826       if (status != HS_SUCCESS) {
13827          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
13828          return HS_FILE_ERROR;
13829       }
13830 
13831       /* open history and definition files */
13832       hs_open_file(lt, "hst", O_RDONLY, &fh);
13833       hs_open_file(lt, "idf", O_RDONLY, &fhd);
13834       if (fh < 0 || fhd < 0) {
13835          cm_msg(MERROR, "hs_count_events", "cannot open index files");
13836          return HS_FILE_ERROR;
13837       }
13838 
13839       /* loop over index file */
13840       *id = 0;
13841       do {
13842          /* read definition index record */
13843          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
13844          if (i < (int) sizeof(def_rec))
13845             break;
13846 
13847          if (strcmp(name, def_rec.event_name) == 0) {
13848             *id = def_rec.event_id;
13849             close(fh);
13850             close(fhd);
13851             return HS_SUCCESS;
13852          }
13853       } while (TRUE);
13854 
13855       close(fh);
13856       close(fhd);
13857 
13858       /* not found -> go back one day */
13859       lt -= 3600 * 24;
13860 
13861    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
13862 
13863    return HS_UNDEFINED_EVENT;
13864 }
13865 
13866 
13867 /********************************************************************/
13868 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
13869 /********************************************************************\
13870 
13871   Routine: hs_count_vars
13872 
13873   Purpose: Count number of variables for a given date and event id
13874 
13875   Input:
13876     DWORD  ltime            Date at which tags should be counted
13877 
13878   Output:
13879     DWORD  *count           Number of tags
13880 
13881   Function value:
13882     HS_SUCCESS              Successful completion
13883     HS_FILE_ERROR           Cannot open history file
13884 
13885 \********************************************************************/
13886 {
13887    int fh, fhd;
13888    INT i, n, status;
13889    DEF_RECORD def_rec;
13890    HIST_RECORD rec;
13891 
13892    if (rpc_is_remote())
13893       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
13894 
13895    /* search latest history file */
13896    status = hs_search_file(&ltime, -1);
13897    if (status != HS_SUCCESS) {
13898       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
13899       return HS_FILE_ERROR;
13900    }
13901 
13902    /* open history and definition files */
13903    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13904    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13905    if (fh < 0 || fhd < 0) {
13906       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
13907       return HS_FILE_ERROR;
13908    }
13909 
13910    /* search last definition */
13911    lseek(fhd, 0, SEEK_END);
13912    n = TELL(fhd) / sizeof(def_rec);
13913    def_rec.event_id = 0;
13914    for (i = n - 1; i >= 0; i--) {
13915       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13916       read(fhd, (char *) &def_rec, sizeof(def_rec));
13917       if (def_rec.event_id == event_id)
13918          break;
13919    }
13920    if (def_rec.event_id != event_id) {
13921       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
13922       return HS_FILE_ERROR;
13923    }
13924 
13925    /* read definition */
13926    lseek(fh, def_rec.def_offset, SEEK_SET);
13927    read(fh, (char *) &rec, sizeof(rec));
13928    *count = rec.data_size / sizeof(TAG);
13929 
13930    close(fh);
13931    close(fhd);
13932 
13933    return HS_SUCCESS;
13934 }
13935 
13936 
13937 /********************************************************************/
13938 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size,
13939                  DWORD * var_n, DWORD * n_size)
13940 /********************************************************************\
13941 
13942   Routine: hs_enum_vars
13943 
13944   Purpose: Enumerate variable tags for a given date and event id
13945 
13946   Input:
13947     DWORD  ltime            Date at which tags should be enumerated
13948     DWORD  event_id         Event ID
13949 
13950   Output:
13951     char   *var_name        Array containing variable names
13952     DWORD  *size            Size of name array
13953     DWORD  *var_n           Array size of variable
13954     DWORD  *n_size          Size of n array
13955 
13956   Function value:
13957     HS_SUCCESS              Successful completion
13958     HS_NO_MEMEORY           Out of memory
13959     HS_FILE_ERROR           Cannot open history file
13960 
13961 \********************************************************************/
13962 {
13963    char str[256];
13964    int fh, fhd;
13965    INT i, n, status;
13966    DEF_RECORD def_rec;
13967    HIST_RECORD rec;
13968    TAG *tag;
13969 
13970    if (rpc_is_remote())
13971       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
13972 
13973    /* search latest history file */
13974    status = hs_search_file(&ltime, -1);
13975    if (status != HS_SUCCESS) {
13976       cm_msg(MERROR, "hs_enum_tags", "cannot find recent history file");
13977       return HS_FILE_ERROR;
13978    }
13979 
13980    /* open history and definition files */
13981    hs_open_file(ltime, "hst", O_RDONLY, &fh);
13982    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
13983    if (fh < 0 || fhd < 0) {
13984       cm_msg(MERROR, "hs_enum_tags", "cannot open index files");
13985       return HS_FILE_ERROR;
13986    }
13987 
13988    /* search last definition */
13989    lseek(fhd, 0, SEEK_END);
13990    n = TELL(fhd) / sizeof(def_rec);
13991    def_rec.event_id = 0;
13992    for (i = n - 1; i >= 0; i--) {
13993       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
13994       read(fhd, (char *) &def_rec, sizeof(def_rec));
13995       if (def_rec.event_id == event_id)
13996          break;
13997    }
13998    if (def_rec.event_id != event_id) {
13999       cm_msg(MERROR, "hs_enum_tags", "event %d not found in index file", event_id);
14000       return HS_FILE_ERROR;
14001    }
14002 
14003    /* read definition header */
14004    lseek(fh, def_rec.def_offset, SEEK_SET);
14005    read(fh, (char *) &rec, sizeof(rec));
14006    read(fh, str, NAME_LENGTH);
14007 
14008    /* read event definition */
14009    n = rec.data_size / sizeof(TAG);
14010    tag = (TAG *) M_MALLOC(rec.data_size);
14011    read(fh, (char *) tag, rec.data_size);
14012 
14013    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
14014       /* store partial definition */
14015       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
14016          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14017          var_n[i] = tag[i].n_data;
14018       }
14019 
14020       cm_msg(MERROR, "hs_enum_tags", "tag buffer too small");
14021       M_FREE(tag);
14022       close(fh);
14023       close(fhd);
14024       return HS_NO_MEMORY;
14025    }
14026 
14027    /* store full definition */
14028    for (i = 0; i < n; i++) {
14029       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
14030       var_n[i] = tag[i].n_data;
14031    }
14032    *size = n * NAME_LENGTH;
14033    *n_size = n;
14034 
14035    M_FREE(tag);
14036    close(fh);
14037    close(fhd);
14038 
14039    return HS_SUCCESS;
14040 }
14041 
14042 
14043 /********************************************************************/
14044 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
14045 /********************************************************************\
14046 
14047   Routine: hs_get_var
14048 
14049   Purpose: Get definition for certain variable
14050 
14051   Input:
14052     DWORD  ltime            Date at which variable definition should
14053                             be returned
14054     DWORD  event_id         Event ID
14055     char   *var_name        Name of variable
14056 
14057   Output:
14058     INT    *type            Type of variable
14059     INT    *n_data          Number of items in variable
14060 
14061   Function value:
14062     HS_SUCCESS              Successful completion
14063     HS_NO_MEMEORY           Out of memory
14064     HS_FILE_ERROR           Cannot open history file
14065 
14066 \********************************************************************/
14067 {
14068    char str[256];
14069    int fh, fhd;
14070    INT i, n, status;
14071    DEF_RECORD def_rec;
14072    HIST_RECORD rec;
14073    TAG *tag;
14074 
14075    if (rpc_is_remote())
14076       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
14077 
14078    /* search latest history file */
14079    status = hs_search_file(&ltime, -1);
14080    if (status != HS_SUCCESS) {
14081       cm_msg(MERROR, "hs_enum_tags", "cannot find recent history file");
14082       return HS_FILE_ERROR;
14083    }
14084 
14085    /* open history and definition files */
14086    hs_open_file(ltime, "hst", O_RDONLY, &fh);
14087    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
14088    if (fh < 0 || fhd < 0) {
14089       cm_msg(MERROR, "hs_enum_tags", "cannot open index files");
14090       return HS_FILE_ERROR;
14091    }
14092 
14093    /* search last definition */
14094    lseek(fhd, 0, SEEK_END);
14095    n = TELL(fhd) / sizeof(def_rec);
14096    def_rec.event_id = 0;
14097    for (i = n - 1; i >= 0; i--) {
14098       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
14099       read(fhd, (char *) &def_rec, sizeof(def_rec));
14100       if (def_rec.event_id == event_id)
14101          break;
14102    }
14103    if (def_rec.event_id != event_id) {
14104       cm_msg(MERROR, "hs_enum_tags", "event %d not found in index file", event_id);
14105       return HS_FILE_ERROR;
14106    }
14107 
14108    /* read definition header */
14109    lseek(fh, def_rec.def_offset, SEEK_SET);
14110    read(fh, (char *) &rec, sizeof(rec));
14111    read(fh, str, NAME_LENGTH);
14112 
14113    /* read event definition */
14114    n = rec.data_size / sizeof(TAG);
14115    tag = (TAG *) M_MALLOC(rec.data_size);
14116    read(fh, (char *) tag, rec.data_size);
14117 
14118    /* search variable */
14119    for (i = 0; i < n; i++)
14120       if (strcmp(tag[i].name, var_name) == 0)
14121          break;
14122 
14123    close(fh);
14124    close(fhd);
14125 
14126    if (i < n) {
14127       *type = tag[i].type;
14128       *n_data = tag[i].n_data;
14129    } else {
14130       *type = *n_data = 0;
14131       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
14132       M_FREE(tag);
14133       return HS_UNDEFINED_VAR;
14134    }
14135 
14136    M_FREE(tag);
14137    return HS_SUCCESS;
14138 }
14139 
14140 
14141 /********************************************************************/
14142 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time,
14143             DWORD interval, char *tag_name, DWORD var_index,
14144             DWORD * time_buffer, DWORD * tbsize,
14145             void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
14146 /********************************************************************\
14147 
14148   Routine: hs_read
14149 
14150   Purpose: Read history for a variable at a certain time interval
14151 
14152   Input:
14153     DWORD  event_id         Event ID
14154     DWORD  start_time       Starting Date/Time
14155     DWORD  end_time         End Date/Time
14156     DWORD  interval         Minimum time in seconds between reported
14157                             events. Can be used to skip events
14158     char   *tag_name        Variable name inside event
14159     DWORD  var_index        Index if variable is array
14160 
14161   Output:
14162     DWORD  *time_buffer     Buffer containing times for each value
14163     DWORD  *tbsize          Size of time buffer
14164     void   *data_buffer     Buffer containing variable values
14165     DWORD  *dbsize          Data buffer size
14166     DWORD  *type            Type of variable (one of TID_xxx)
14167     DWORD  *n               Number of time/value pairs found
14168                             in specified interval and placed into
14169                             time_buffer and data_buffer
14170 
14171 
14172   Function value:
14173     HS_SUCCESS              Successful completion
14174     HS_NO_MEMEORY           Out of memory
14175     HS_FILE_ERROR           Cannot open history file
14176     HS_WRONG_INDEX          var_index exceeds array size of variable
14177     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
14178     HS_TRUNCATED            Buffer too small, data has been truncated
14179 
14180 \********************************************************************/
14181 {
14182    DWORD prev_time, last_irec_time;
14183    int fh, fhd, fhi, cp = 0;
14184    INT i, delta, index = 0, status, cache_size;
14185    INDEX_RECORD irec, *pirec;
14186    HIST_RECORD rec, drec;
14187    INT old_def_offset, var_size = 0, var_offset = 0;
14188    TAG *tag;
14189    char str[NAME_LENGTH];
14190    struct tm *tms;
14191    char *cache;
14192 
14193    if (rpc_is_remote())
14194       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval,
14195                       tag_name, var_index, time_buffer, tbsize, data_buffer,
14196                       dbsize, type, n);
14197 
14198    /* if not time given, use present to one hour in past */
14199    if (start_time == 0)
14200       start_time = time(NULL) - 3600;
14201    if (end_time == 0)
14202       end_time = time(NULL);
14203 
14204    /* search history file for start_time */
14205    status = hs_search_file(&start_time, 1);
14206    if (status != HS_SUCCESS) {
14207       cm_msg(MERROR, "hs_read", "cannot find recent history file");
14208       *tbsize = *dbsize = *n = 0;
14209       return HS_FILE_ERROR;
14210    }
14211 
14212    /* open history and definition files */
14213    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14214    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14215    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14216    if (fh < 0 || fhd < 0 || fhi < 0) {
14217       cm_msg(MERROR, "hs_read", "cannot open index files");
14218       *tbsize = *dbsize = *n = 0;
14219       return HS_FILE_ERROR;
14220    }
14221 
14222    /* try to read index file into cache */
14223    lseek(fhi, 0, SEEK_END);
14224    cache_size = TELL(fhi);
14225 
14226    if (cache_size > 0) {
14227       cache = (char *) M_MALLOC(cache_size);
14228       if (cache) {
14229          lseek(fhi, 0, SEEK_SET);
14230          i = read(fhi, cache, cache_size);
14231          if (i < cache_size) {
14232             M_FREE(cache);
14233             close(fh);
14234             close(fhd);
14235             close(fhi);
14236             return HS_FILE_ERROR;
14237          }
14238       }
14239 
14240       /* search record closest to start time */
14241       if (cache == NULL) {
14242          lseek(fhi, 0, SEEK_END);
14243          delta = (TELL(fhi) / sizeof(irec)) / 2;
14244          lseek(fhi, delta * sizeof(irec), SEEK_SET);
14245          do {
14246             delta = (int) (abs(delta) / 2.0 + 0.5);
14247             read(fhi, (char *) &irec, sizeof(irec));
14248             if (irec.time > start_time)
14249                delta = -delta;
14250 
14251             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14252          } while (abs(delta) > 1 && irec.time != start_time);
14253          read(fhi, (char *) &irec, sizeof(irec));
14254          if (irec.time > start_time)
14255             delta = -abs(delta);
14256 
14257          i = TELL(fhi) + (delta - 1) * sizeof(irec);
14258          if (i <= 0)
14259             lseek(fhi, 0, SEEK_SET);
14260          else
14261             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14262          read(fhi, (char *) &irec, sizeof(irec));
14263       } else {
14264          delta = (cache_size / sizeof(irec)) / 2;
14265          cp = delta * sizeof(irec);
14266          do {
14267             delta = (int) (abs(delta) / 2.0 + 0.5);
14268             pirec = (INDEX_RECORD *) (cache + cp);
14269             if (pirec->time > start_time)
14270                delta = -delta;
14271 
14272             cp = cp + delta * sizeof(irec);
14273          } while (abs(delta) > 1 && pirec->time != start_time);
14274          pirec = (INDEX_RECORD *) (cache + cp);
14275          if (pirec->time > start_time)
14276             delta = -abs(delta);
14277 
14278          if (cp <= delta * (int) sizeof(irec))
14279             cp = 0;
14280          else
14281             cp = cp + delta * sizeof(irec);
14282 
14283          if (cp >= cache_size)
14284             cp = cache_size - sizeof(irec);
14285          if (cp < 0)
14286             cp = 0;
14287 
14288          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
14289          cp += sizeof(irec);
14290       }
14291    } else {                     /* file size > 0 */
14292 
14293       cache = NULL;
14294       irec.time = start_time;
14295    }
14296 
14297    /* read records, skip wrong IDs */
14298    old_def_offset = -1;
14299    *n = 0;
14300    prev_time = 0;
14301    last_irec_time = 0;
14302    do {
14303       if (irec.time < last_irec_time) {
14304          cm_msg(MERROR, "hs_read",
14305                 "corrupted history data: time does not increase: %d -> %d",
14306                 last_irec_time, irec.time);
14307          *tbsize = *dbsize = *n = 0;
14308          return HS_FILE_ERROR;
14309       }
14310       last_irec_time = irec.time;
14311       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14312          /* check if record time more than "interval" seconds after previous time */
14313          if (irec.time >= prev_time + interval) {
14314             prev_time = irec.time;
14315             lseek(fh, irec.offset, SEEK_SET);
14316             read(fh, (char *) &rec, sizeof(rec));
14317 
14318             /* if definition changed, read new definition */
14319             if ((INT) rec.def_offset != old_def_offset) {
14320                lseek(fh, rec.def_offset, SEEK_SET);
14321                read(fh, (char *) &drec, sizeof(drec));
14322                read(fh, str, NAME_LENGTH);
14323 
14324                tag = (TAG *) M_MALLOC(drec.data_size);
14325                if (tag == NULL) {
14326                   *n = *tbsize = *dbsize = 0;
14327                   if (cache)
14328                      M_FREE(cache);
14329                   close(fh);
14330                   close(fhd);
14331                   close(fhi);
14332                   return HS_NO_MEMORY;
14333                }
14334                read(fh, (char *) tag, drec.data_size);
14335 
14336                /* find index of tag_name in new definition */
14337                index = -1;
14338                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
14339                   if (equal_ustring(tag[i].name, tag_name)) {
14340                      index = i;
14341                      break;
14342                   }
14343 
14344                /*
14345                   if ((DWORD) i == drec.data_size/sizeof(TAG))
14346                   {
14347                   *n = *tbsize = *dbsize = 0;
14348                   if (cache)
14349                   M_FREE(cache);
14350 
14351                   return HS_UNDEFINED_VAR;
14352                   }
14353                 */
14354 
14355                if (index >= 0 && var_index >= tag[i].n_data) {
14356                   *n = *tbsize = *dbsize = 0;
14357                   if (cache)
14358                      M_FREE(cache);
14359                   M_FREE(tag);
14360                   close(fh);
14361                   close(fhd);
14362                   close(fhi);
14363                   return HS_WRONG_INDEX;
14364                }
14365 
14366                /* calculate offset for variable */
14367                if (index >= 0) {
14368                   *type = tag[i].type;
14369 
14370                   /* loop over all previous variables */
14371                   for (i = 0, var_offset = 0; i < index; i++)
14372                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
14373 
14374                   /* strings have size n_data */
14375                   if (tag[index].type == TID_STRING)
14376                      var_size = tag[i].n_data;
14377                   else
14378                      var_size = rpc_tid_size(tag[index].type);
14379 
14380                   var_offset += var_size * var_index;
14381                }
14382 
14383                M_FREE(tag);
14384                old_def_offset = rec.def_offset;
14385                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14386             }
14387 
14388             if (index >= 0) {
14389                /* check if data fits in buffers */
14390                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
14391                   *dbsize = (*n) * var_size;
14392                   *tbsize = (*n) * sizeof(DWORD);
14393                   if (cache)
14394                      M_FREE(cache);
14395                   close(fh);
14396                   close(fhd);
14397                   close(fhi);
14398                   return HS_TRUNCATED;
14399                }
14400 
14401                /* copy time from header */
14402                time_buffer[*n] = irec.time;
14403 
14404                /* copy data from record */
14405                lseek(fh, var_offset, SEEK_CUR);
14406                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
14407 
14408                /* increment counter */
14409                (*n)++;
14410             }
14411          }
14412       }
14413 
14414       /* read next index record */
14415       if (cache) {
14416          if (cp >= cache_size) {
14417             i = -1;
14418             M_FREE(cache);
14419             cache = NULL;
14420          } else
14421             i = sizeof(irec);
14422 
14423          if (cp < cache_size) {
14424             memcpy(&irec, cache + cp, sizeof(irec));
14425             cp += sizeof(irec);
14426          }
14427       } else
14428          i = read(fhi, (char *) &irec, sizeof(irec));
14429 
14430       /* end of file: search next history file */
14431       if (i <= 0) {
14432          close(fh);
14433          close(fhd);
14434          close(fhi);
14435 
14436          /* advance one day */
14437          tms = localtime((const time_t *) &last_irec_time);
14438          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14439          last_irec_time = mktime(tms);
14440 
14441          last_irec_time += 3600 * 24;
14442 
14443          if (last_irec_time > end_time)
14444             break;
14445 
14446          /* search next file */
14447          status = hs_search_file(&last_irec_time, 1);
14448          if (status != HS_SUCCESS)
14449             break;
14450 
14451          /* open history and definition files */
14452          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14453          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14454          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14455          if (fh < 0 || fhd < 0 || fhi < 0) {
14456             cm_msg(MERROR, "hs_read", "cannot open index files");
14457             break;
14458          }
14459 
14460          /* try to read index file into cache */
14461          lseek(fhi, 0, SEEK_END);
14462          cache_size = TELL(fhi);
14463          lseek(fhi, 0, SEEK_SET);
14464          cache = (char *) M_MALLOC(cache_size);
14465          if (cache) {
14466             i = read(fhi, cache, cache_size);
14467             if (i < cache_size)
14468                break;
14469             /* read first record */
14470             cp = 0;
14471             memcpy(&irec, cache, sizeof(irec));
14472          } else {
14473             /* read first record */
14474             i = read(fhi, (char *) &irec, sizeof(irec));
14475             if (i <= 0)
14476                break;
14477          }
14478 
14479          /* old definition becomes invalid */
14480          old_def_offset = -1;
14481       }
14482    } while (irec.time < end_time);
14483 
14484    if (cache)
14485       M_FREE(cache);
14486    close(fh);
14487    close(fhd);
14488    close(fhi);
14489 
14490    *dbsize = *n * var_size;
14491    *tbsize = *n * sizeof(DWORD);
14492 
14493    return HS_SUCCESS;
14494 }
14495 
14496 
14497 /********************************************************************/
14498 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time,
14499             DWORD interval, BOOL binary_time)
14500 /********************************************************************\
14501 
14502   Routine: hs_dump
14503 
14504   Purpose: Display history for a given event at stdout. The output
14505            can be redirected to be read by Excel for example.
14506 
14507   Input:
14508     DWORD  event_id         Event ID
14509     DWORD  start_time       Starting Date/Time
14510     DWORD  end_time         End Date/Time
14511     DWORD  interval         Minimum time in seconds between reported
14512                             events. Can be used to skip events
14513     BOOL   binary_time      Display DWORD time stamp
14514   Output:
14515     <screen output>
14516 
14517   Function value:
14518     HS_SUCCESS              Successful completion
14519     HS_FILE_ERROR           Cannot open history file
14520 
14521 \********************************************************************/
14522 {
14523    DWORD prev_time, last_irec_time;
14524    int fh, fhd, fhi;
14525    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
14526    INDEX_RECORD irec;
14527    HIST_RECORD rec, drec;
14528    INT old_def_offset, offset;
14529    TAG *tag = NULL, *old_tag = NULL;
14530    char str[NAME_LENGTH], data_buffer[10000];
14531    struct tm *tms;
14532 
14533    /* if not time given, use present to one hour in past */
14534    if (start_time == 0)
14535       start_time = time(NULL) - 3600;
14536    if (end_time == 0)
14537       end_time = time(NULL);
14538 
14539    /* search history file for start_time */
14540    status = hs_search_file(&start_time, 1);
14541    if (status != HS_SUCCESS) {
14542       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
14543       return HS_FILE_ERROR;
14544    }
14545 
14546    /* open history and definition files */
14547    hs_open_file(start_time, "hst", O_RDONLY, &fh);
14548    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
14549    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
14550    if (fh < 0 || fhd < 0 || fhi < 0) {
14551       cm_msg(MERROR, "hs_dump", "cannot open index files");
14552       return HS_FILE_ERROR;
14553    }
14554 
14555    /* search record closest to start time */
14556    lseek(fhi, 0, SEEK_END);
14557    delta = (TELL(fhi) / sizeof(irec)) / 2;
14558    lseek(fhi, delta * sizeof(irec), SEEK_SET);
14559    do {
14560       delta = (int) (abs(delta) / 2.0 + 0.5);
14561       read(fhi, (char *) &irec, sizeof(irec));
14562       if (irec.time > start_time)
14563          delta = -delta;
14564 
14565       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14566    } while (abs(delta) > 1 && irec.time != start_time);
14567    read(fhi, (char *) &irec, sizeof(irec));
14568    if (irec.time > start_time)
14569       delta = -abs(delta);
14570 
14571    i = TELL(fhi) + (delta - 1) * sizeof(irec);
14572    if (i <= 0)
14573       lseek(fhi, 0, SEEK_SET);
14574    else
14575       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
14576    read(fhi, (char *) &irec, sizeof(irec));
14577 
14578    /* read records, skip wrong IDs */
14579    old_def_offset = -1;
14580    prev_time = 0;
14581    last_irec_time = 0;
14582    do {
14583       if (irec.time < last_irec_time) {
14584          cm_msg(MERROR, "hs_dump",
14585                 "corrupted history data: time does not increase: %d -> %d",
14586                 last_irec_time, irec.time);
14587          return HS_FILE_ERROR;
14588       }
14589       last_irec_time = irec.time;
14590       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
14591          if (irec.time >= prev_time + interval) {
14592             prev_time = irec.time;
14593             lseek(fh, irec.offset, SEEK_SET);
14594             read(fh, (char *) &rec, sizeof(rec));
14595 
14596             /* if definition changed, read new definition */
14597             if ((INT) rec.def_offset != old_def_offset) {
14598                lseek(fh, rec.def_offset, SEEK_SET);
14599                read(fh, (char *) &drec, sizeof(drec));
14600                read(fh, str, NAME_LENGTH);
14601 
14602                if (tag == NULL)
14603                   tag = (TAG *) M_MALLOC(drec.data_size);
14604                else
14605                   tag = (TAG *) realloc(tag, drec.data_size);
14606                if (tag == NULL)
14607                   return HS_NO_MEMORY;
14608                read(fh, (char *) tag, drec.data_size);
14609                n_tag = drec.data_size / sizeof(TAG);
14610 
14611                /* print tag names if definition has changed */
14612                if (old_tag == NULL || old_n_tag != n_tag ||
14613                    memcmp(old_tag, tag, drec.data_size) != 0) {
14614                   printf("Date\t");
14615                   for (i = 0; i < n_tag; i++) {
14616                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
14617                         printf("%s\t", tag[i].name);
14618                      else
14619                         for (j = 0; j < (INT) tag[i].n_data; j++)
14620                            printf("%s%d\t", tag[i].name, j);
14621                   }
14622                   printf("\n");
14623 
14624                   if (old_tag == NULL)
14625                      old_tag = (TAG *) M_MALLOC(drec.data_size);
14626                   else
14627                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
14628                   memcpy(old_tag, tag, drec.data_size);
14629                   old_n_tag = n_tag;
14630                }
14631 
14632                old_def_offset = rec.def_offset;
14633                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
14634             }
14635 
14636             /* print time from header */
14637             if (binary_time)
14638                printf("%li ", irec.time);
14639             else {
14640                sprintf(str, "%s", ctime((const time_t *) &irec.time) + 4);
14641                str[20] = '\t';
14642                printf(str);
14643             }
14644 
14645             /* read data */
14646             read(fh, data_buffer, rec.data_size);
14647 
14648             /* interprete data from tag definition */
14649             offset = 0;
14650             for (i = 0; i < n_tag; i++) {
14651                /* strings have a length of n_data */
14652                if (tag[i].type == TID_STRING) {
14653                   printf("%s\t", data_buffer + offset);
14654                   offset += tag[i].n_data;
14655                } else if (tag[i].n_data == 1) {
14656                   /* non-array data */
14657                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14658                              tag[i].type);
14659                   printf("%s\t", str);
14660                   offset += rpc_tid_size(tag[i].type);
14661                } else
14662                   /* loop over array data */
14663                   for (j = 0; j < (INT) tag[i].n_data; j++) {
14664                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0,
14665                                 tag[i].type);
14666                      printf("%s\t", str);
14667                      offset += rpc_tid_size(tag[i].type);
14668                   }
14669             }
14670             printf("\n");
14671          }
14672       }
14673 
14674       /* read next index record */
14675       i = read(fhi, (char *) &irec, sizeof(irec));
14676 
14677       /* end of file: search next history file */
14678       if (i <= 0) {
14679          close(fh);
14680          close(fhd);
14681          close(fhi);
14682 
14683          /* advance one day */
14684          tms = localtime((const time_t *) &last_irec_time);
14685          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
14686          last_irec_time = mktime(tms);
14687 
14688          last_irec_time += 3600 * 24;
14689          if (last_irec_time > end_time)
14690             break;
14691 
14692          /* search next file */
14693          status = hs_search_file(&last_irec_time, 1);
14694          if (status != HS_SUCCESS)
14695             break;
14696 
14697          /* open history and definition files */
14698          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
14699          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
14700          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
14701          if (fh < 0 || fhd < 0 || fhi < 0) {
14702             cm_msg(MERROR, "hs_dump", "cannot open index files");
14703             break;
14704          }
14705 
14706          /* read first record */
14707          i = read(fhi, (char *) &irec, sizeof(irec));
14708          if (i <= 0)
14709             break;
14710 
14711          /* old definition becomes invalid */
14712          old_def_offset = -1;
14713       }
14714    } while (irec.time < end_time);
14715 
14716    M_FREE(tag);
14717    M_FREE(old_tag);
14718    close(fh);
14719    close(fhd);
14720    close(fhi);
14721 
14722    return HS_SUCCESS;
14723 }
14724 
14725 
14726 /********************************************************************/
14727 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
14728 /********************************************************************\
14729 
14730   Routine: hs_fdump
14731 
14732   Purpose: Display history for a given history file
14733 
14734   Input:
14735     char   *file_name       Name of file to dump
14736     DWORD  event_id         Event ID
14737     BOOL   binary_time      Display DWORD time stamp
14738 
14739   Output:
14740     <screen output>
14741 
14742   Function value:
14743     HS_SUCCESS              Successful completion
14744     HS_FILE_ERROR           Cannot open history file
14745 
14746 \********************************************************************/
14747 {
14748    int fh;
14749    INT n;
14750    HIST_RECORD rec;
14751    char event_name[NAME_LENGTH];
14752    char str[80];
14753 
14754    /* open file, add O_BINARY flag for Windows NT */
14755    fh = open(file_name, O_RDONLY | O_BINARY, 0644);
14756    if (fh < 0) {
14757       cm_msg(MERROR, "hs_fdump", "cannot open file %s", file_name);
14758       return HS_FILE_ERROR;
14759    }
14760 
14761    /* loop over file records in .hst file */
14762    do {
14763       n = read(fh, (char *) &rec, sizeof(rec));
14764       if (n < sizeof(rec))
14765          break;
14766 
14767       /* check if record type is definition */
14768       if (rec.record_type == RT_DEF) {
14769          /* read name */
14770          read(fh, event_name, sizeof(event_name));
14771 
14772          if (rec.event_id == id || id == 0)
14773             printf("Event definition %s, ID %ld\n", event_name, rec.event_id);
14774 
14775          /* skip tags */
14776          lseek(fh, rec.data_size, SEEK_CUR);
14777       } else {
14778          /* print data record */
14779          if (binary_time)
14780             sprintf(str, "%li ", rec.time);
14781          else {
14782             strcpy(str, ctime((const time_t *) &rec.time) + 4);
14783             str[15] = 0;
14784          }
14785          if (rec.event_id == id || id == 0)
14786             printf("ID %ld, %s, size %ld\n", rec.event_id, str, rec.data_size);
14787 
14788          /* skip data */
14789          lseek(fh, rec.data_size, SEEK_CUR);
14790       }
14791 
14792    } while (TRUE);
14793 
14794    close(fh);
14795 
14796    return HS_SUCCESS;
14797 }
14798 #endif                          /* OS_VXWORKS hs section */
14799 
14800 /**dox***************************************************************/
14801 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14802 
14803 /**dox***************************************************************/
14804 /** @} */ /* end of hsfunctionc */
14805 
14806 /**dox***************************************************************/
14807 /** @addtogroup elfunctionc
14808  *  
14809  *  @{  */
14810 
14811 /**dox***************************************************************/
14812 #ifndef DOXYGEN_SHOULD_SKIP_THIS
14813 
14814 /********************************************************************\
14815 *                                                                    *
14816 *               Electronic logbook functions                         *
14817 *                                                                    *
14818 \********************************************************************/
14819 
14820 /********************************************************************/
14821 void el_decode(char *message, char *key, char *result, int size)
14822 {
14823    char *rstart = result;
14824    char *pc;
14825 
14826    if (result == NULL)
14827       return;
14828 
14829    *result = 0;
14830 
14831    if (strstr(message, key)) {
14832       for (pc = strstr(message, key) + strlen(key); *pc != '\n';)
14833          *result++ = *pc++;
14834       *result = 0;
14835    }
14836 
14837    assert(strlen(rstart) < size);
14838 }
14839 
14840 /**dox***************************************************************/
14841 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
14842 
14843 /********************************************************************/
14844 /**
14845 Submit an ELog entry.
14846 @param run  Run Number.
14847 @param author Message author.
14848 @param type Message type.
14849 @param system Message system.
14850 @param subject Subject.
14851 @param text Message text.
14852 @param reply_to In reply to this message.
14853 @param encoding Text encoding, either HTML or plain.
14854 @param afilename1   File name of attachment.
14855 @param buffer1      File contents.
14856 @param buffer_size1 Size of buffer in bytes.
14857 @param afilename2   File name of attachment.
14858 @param buffer2      File contents.
14859 @param buffer_size2 Size of buffer in bytes.
14860 @param afilename3   File name of attachment.
14861 @param buffer3      File contents.
14862 @param buffer_size3 Size of buffer in bytes.
14863 @param tag          If given, edit existing message.
14864 @param tag_size     Maximum size of tag.
14865 @return EL_SUCCESS
14866 */
14867 INT el_submit(int run, char *author, char *type, char *system, char *subject,
14868               char *text, char *reply_to, char *encoding,
14869               char *afilename1, char *buffer1, INT buffer_size1,
14870               char *afilename2, char *buffer2, INT buffer_size2,
14871               char *afilename3, char *buffer3, INT buffer_size3, char *tag, INT tag_size)
14872 {
14873    if (rpc_is_remote())
14874       return rpc_call(RPC_EL_SUBMIT, run, author, type, system, subject,
14875                       text, reply_to, encoding,
14876                       afilename1, buffer1, buffer_size1,
14877                       afilename2, buffer2, buffer_size2,
14878                       afilename3, buffer3, buffer_size3, tag, tag_size);
14879 
14880 #ifdef LOCAL_ROUTINES
14881    {
14882       INT n, size, fh, status, run_number, mutex, buffer_size = 0, index, offset =
14883           0, tail_size = 0;
14884       struct tm *tms = NULL;
14885       char afilename[256], file_name[256], afile_name[3][256], dir[256], str[256],
14886           start_str[80], end_str[80], last[80], date[80], thread[80], attachment[256];
14887       HNDLE hDB;
14888       time_t now;
14889       char message[10000], *p, *buffer = NULL;
14890       BOOL bedit;
14891 
14892       cm_get_experiment_database(&hDB, NULL);
14893 
14894       bedit = (tag[0] != 0);
14895 
14896       /* request semaphore */
14897       cm_get_experiment_mutex(NULL, &mutex);
14898       ss_mutex_wait_for(mutex, 0);
14899 
14900       /* get run number from ODB if not given */
14901       if (run > 0)
14902          run_number = run;
14903       else {
14904          /* get run number */
14905          size = sizeof(run_number);
14906          status =
14907              db_get_value(hDB, 0, "/Runinfo/Run number", &run_number, &size, TID_INT,
14908                           TRUE);
14909          assert(status == SUCCESS);
14910       }
14911 
14912       if (run_number < 0) {
14913          cm_msg(MERROR, "el_submit", "aborting on attempt to use invalid run number %d",
14914                 run_number);
14915          abort();
14916       }
14917 
14918       for (index = 0; index < 3; index++) {
14919          /* generate filename for attachment */
14920          afile_name[index][0] = file_name[0] = 0;
14921 
14922          if (index == 0) {
14923             strcpy(afilename, afilename1);
14924             buffer = buffer1;
14925             buffer_size = buffer_size1;
14926          } else if (index == 1) {
14927             strcpy(afilename, afilename2);
14928             buffer = buffer2;
14929             buffer_size = buffer_size2;
14930          } else if (index == 2) {
14931             strcpy(afilename, afilename3);
14932             buffer = buffer3;
14933             buffer_size = buffer_size3;
14934          }
14935 
14936          if (afilename[0]) {
14937             strcpy(file_name, afilename);
14938             p = file_name;
14939             while (strchr(p, ':'))
14940                p = strchr(p, ':') + 1;
14941             while (strchr(p, '\\'))
14942                p = strchr(p, '\\') + 1; /* NT */
14943             while (strchr(p, '/'))
14944                p = strchr(p, '/') + 1;  /* Unix */
14945             while (strchr(p, ']'))
14946                p = strchr(p, ']') + 1;  /* VMS */
14947 
14948             /* assemble ELog filename */
14949             if (p[0]) {
14950                dir[0] = 0;
14951                if (hDB > 0) {
14952                   size = sizeof(dir);
14953                   memset(dir, 0, size);
14954                   status =
14955                       db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING,
14956                                    FALSE);
14957                   if (status != DB_SUCCESS)
14958                      db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING,
14959                                   TRUE);
14960 
14961                   if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
14962                      strcat(dir, DIR_SEPARATOR_STR);
14963                }
14964 #if !defined(OS_VXWORKS)
14965 #if !defined(OS_VMS)
14966                tzset();
14967 #endif
14968 #endif
14969 
14970                time((time_t *) & now);
14971                tms = localtime((const time_t *) &now);
14972 
14973                strcpy(str, p);
14974                sprintf(afile_name[index], "%02d%02d%02d_%02d%02d%02d_%s",
14975                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14976                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14977                sprintf(file_name, "%s%02d%02d%02d_%02d%02d%02d_%s", dir,
14978                        tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday,
14979                        tms->tm_hour, tms->tm_min, tms->tm_sec, str);
14980 
14981                /* save attachment */
14982                fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
14983                if (fh < 0) {
14984                   cm_msg(MERROR, "el_submit", "Cannot write attachment file \"%s\"",
14985                          file_name);
14986                } else {
14987                   write(fh, buffer, buffer_size);
14988                   close(fh);
14989                }
14990             }
14991          }
14992       }
14993 
14994       /* generate new file name YYMMDD.log in data directory */
14995       cm_get_experiment_database(&hDB, NULL);
14996 
14997       size = sizeof(dir);
14998       memset(dir, 0, size);
14999       status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15000       if (status != DB_SUCCESS)
15001          db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15002 
15003       if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15004          strcat(dir, DIR_SEPARATOR_STR);
15005 
15006 #if !defined(OS_VXWORKS)
15007 #if !defined(OS_VMS)
15008       tzset();
15009 #endif
15010 #endif
15011 
15012       if (bedit) {
15013          /* edit existing message */
15014          strcpy(str, tag);
15015          if (strchr(str, '.')) {
15016             offset = atoi(strchr(str, '.') + 1);
15017             *strchr(str, '.') = 0;
15018          }
15019          sprintf(file_name, "%s%s.log", dir, str);
15020          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15021          if (fh < 0) {
15022             ss_mutex_release(mutex);
15023             return EL_FILE_ERROR;
15024          }
15025          lseek(fh, offset, SEEK_SET);
15026          read(fh, str, 16);
15027          size = atoi(str + 9);
15028          read(fh, message, size);
15029 
15030          el_decode(message, "Date: ", date, sizeof(date));
15031          el_decode(message, "Thread: ", thread, sizeof(thread));
15032          el_decode(message, "Attachment: ", attachment, sizeof(attachment));
15033 
15034          /* buffer tail of logfile */
15035          lseek(fh, 0, SEEK_END);
15036          tail_size = TELL(fh) - (offset + size);
15037 
15038          if (tail_size > 0) {
15039             buffer = (char *) M_MALLOC(tail_size);
15040             if (buffer == NULL) {
15041                close(fh);
15042                ss_mutex_release(mutex);
15043                return EL_FILE_ERROR;
15044             }
15045 
15046             lseek(fh, offset + size, SEEK_SET);
15047             n = read(fh, buffer, tail_size);
15048          }
15049          lseek(fh, offset, SEEK_SET);
15050       } else {
15051          /* create new message */
15052          time((time_t *) & now);
15053          tms = localtime((const time_t *) &now);
15054 
15055          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15056                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15057 
15058          fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15059          if (fh < 0) {
15060             ss_mutex_release(mutex);
15061             return EL_FILE_ERROR;
15062          }
15063 
15064          strcpy(date, ctime(&now));
15065          date[24] = 0;
15066 
15067          if (reply_to[0])
15068             sprintf(thread, "%16s %16s", reply_to, "0");
15069          else
15070             sprintf(thread, "%16s %16s", "0", "0");
15071 
15072          lseek(fh, 0, SEEK_END);
15073       }
15074 
15075       /* compose message */
15076 
15077       sprintf(message, "Date: %s\n", date);
15078       sprintf(message + strlen(message), "Thread: %s\n", thread);
15079       sprintf(message + strlen(message), "Run: %d\n", run_number);
15080       sprintf(message + strlen(message), "Author: %s\n", author);
15081       sprintf(message + strlen(message), "Type: %s\n", type);
15082       sprintf(message + strlen(message), "System: %s\n", system);
15083       sprintf(message + strlen(message), "Subject: %s\n", subject);
15084 
15085       /* keep original attachment if edit and no new attachment */
15086       if (bedit && afile_name[0][0] == 0 && afile_name[1][0] == 0 &&
15087           afile_name[2][0] == 0)
15088          sprintf(message + strlen(message), "Attachment: %s", attachment);
15089       else {
15090          sprintf(message + strlen(message), "Attachment: %s", afile_name[0]);
15091          if (afile_name[1][0])
15092             sprintf(message + strlen(message), ",%s", afile_name[1]);
15093          if (afile_name[2][0])
15094             sprintf(message + strlen(message), ",%s", afile_name[2]);
15095       }
15096       sprintf(message + strlen(message), "\n");
15097 
15098       sprintf(message + strlen(message), "Encoding: %s\n", encoding);
15099       sprintf(message + strlen(message), "========================================\n");
15100       strcat(message, text);
15101 
15102       assert(strlen(message) < sizeof(message)); /* bomb out on array overrun. */
15103 
15104       size = 0;
15105       sprintf(start_str, "$Start$: %6d\n", size);
15106       sprintf(end_str, "$End$:   %6d\n\f", size);
15107 
15108       size = strlen(message) + strlen(start_str) + strlen(end_str);
15109 
15110       if (tag != NULL && !bedit)
15111          sprintf(tag, "%02d%02d%02d.%d", tms->tm_year % 100, tms->tm_mon + 1,
15112                  tms->tm_mday, (int) TELL(fh));
15113 
15114       /* size has to fit in 6 digits */
15115       assert(size < 999999);
15116 
15117       sprintf(start_str, "$Start$: %6d\n", size);
15118       sprintf(end_str, "$End$:   %6d\n\f", size);
15119 
15120       write(fh, start_str, strlen(start_str));
15121       write(fh, message, strlen(message));
15122       write(fh, end_str, strlen(end_str));
15123 
15124       if (bedit) {
15125          if (tail_size > 0) {
15126             n = write(fh, buffer, tail_size);
15127             M_FREE(buffer);
15128          }
15129 
15130          /* truncate file here */
15131 #ifdef OS_WINNT
15132          chsize(fh, TELL(fh));
15133 #else
15134          ftruncate(fh, TELL(fh));
15135 #endif
15136       }
15137 
15138       close(fh);
15139 
15140       /* if reply, mark original message */
15141       if (reply_to[0] && !bedit) {
15142          strcpy(last, reply_to);
15143          do {
15144             status = el_search_message(last, &fh, FALSE);
15145             if (status == EL_SUCCESS) {
15146                /* position to next thread location */
15147                lseek(fh, 72, SEEK_CUR);
15148                memset(str, 0, sizeof(str));
15149                read(fh, str, 16);
15150                lseek(fh, -16, SEEK_CUR);
15151 
15152                /* if no reply yet, set it */
15153                if (atoi(str) == 0) {
15154                   sprintf(str, "%16s", tag);
15155                   write(fh, str, 16);
15156                   close(fh);
15157                   break;
15158                } else {
15159                   /* if reply set, find last one in chain */
15160                   strcpy(last, strtok(str, " "));
15161                   close(fh);
15162                }
15163             } else
15164                /* stop on error */
15165                break;
15166 
15167          } while (TRUE);
15168       }
15169 
15170       /* release elog mutex */
15171       ss_mutex_release(mutex);
15172    }
15173 #endif                          /* LOCAL_ROUTINES */
15174 
15175    return EL_SUCCESS;
15176 }
15177 
15178 /**dox***************************************************************/
15179 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15180 
15181 /********************************************************************/
15182 INT el_search_message(char *tag, int *fh, BOOL walk)
15183 {
15184    int i, size, offset, direction, last, status;
15185    struct tm *tms, ltms;
15186    DWORD lt, ltime, lact;
15187    char str[256], file_name[256], dir[256];
15188    HNDLE hDB;
15189 
15190 #if !defined(OS_VXWORKS)
15191 #if !defined(OS_VMS)
15192    tzset();
15193 #endif
15194 #endif
15195 
15196    /* open file */
15197    cm_get_experiment_database(&hDB, NULL);
15198 
15199    size = sizeof(dir);
15200    memset(dir, 0, size);
15201    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15202    if (status != DB_SUCCESS)
15203       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15204 
15205    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15206       strcat(dir, DIR_SEPARATOR_STR);
15207 
15208    /* check tag for direction */
15209    direction = 0;
15210    if (strpbrk(tag, "+-")) {
15211       direction = atoi(strpbrk(tag, "+-"));
15212       *strpbrk(tag, "+-") = 0;
15213    }
15214 
15215    /* if tag is given, open file directly */
15216    if (tag[0]) {
15217       /* extract time structure from tag */
15218       tms = &ltms;
15219       memset(tms, 0, sizeof(struct tm));
15220       tms->tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
15221       tms->tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
15222       tms->tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
15223       tms->tm_hour = 12;
15224 
15225       if (tms->tm_year < 90)
15226          tms->tm_year += 100;
15227       ltime = lt = mktime(tms);
15228 
15229       strcpy(str, tag);
15230       if (strchr(str, '.')) {
15231          offset = atoi(strchr(str, '.') + 1);
15232          *strchr(str, '.') = 0;
15233       } else
15234          return EL_FILE_ERROR;
15235 
15236       do {
15237          tms = localtime((const time_t *) &ltime);
15238 
15239          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15240                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15241          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15242 
15243          if (*fh < 0) {
15244             if (!walk)
15245                return EL_FILE_ERROR;
15246 
15247             if (direction == -1)
15248                ltime -= 3600 * 24;      /* one day back */
15249             else
15250                ltime += 3600 * 24;      /* go forward one day */
15251 
15252             /* set new tag */
15253             tms = localtime((const time_t *) &ltime);
15254             sprintf(tag, "%02d%02d%02d.0", tms->tm_year % 100, tms->tm_mon + 1,
15255                     tms->tm_mday);
15256          }
15257 
15258          /* in forward direction, stop today */
15259          if (direction != -1 && ltime > (DWORD) time(NULL) + 3600 * 24)
15260             break;
15261 
15262          /* in backward direction, go back 10 years */
15263          if (direction == -1 && abs((INT) lt - (INT) ltime) > 3600 * 24 * 365 * 10)
15264             break;
15265 
15266       } while (*fh < 0);
15267 
15268       if (*fh < 0)
15269          return EL_FILE_ERROR;
15270 
15271       lseek(*fh, offset, SEEK_SET);
15272 
15273       /* check if start of message */
15274       i = read(*fh, str, 15);
15275       if (i <= 0) {
15276          close(*fh);
15277          return EL_FILE_ERROR;
15278       }
15279 
15280       if (strncmp(str, "$Start$: ", 9) != 0) {
15281          close(*fh);
15282          return EL_FILE_ERROR;
15283       }
15284 
15285       lseek(*fh, offset, SEEK_SET);
15286    }
15287 
15288    /* open most recent file if no tag given */
15289    if (tag[0] == 0) {
15290       time((long *) &lt);
15291       ltime = lt;
15292       do {
15293          tms = localtime((const time_t *) &ltime);
15294 
15295          sprintf(file_name, "%s%02d%02d%02d.log", dir,
15296                  tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15297          *fh = open(file_name, O_RDWR | O_BINARY, 0644);
15298 
15299          if (*fh < 0)
15300             ltime -= 3600 * 24; /* one day back */
15301 
15302       } while (*fh < 0 && (INT) lt - (INT) ltime < 3600 * 24 * 365);
15303 
15304       if (*fh < 0)
15305          return EL_FILE_ERROR;
15306 
15307       /* remember tag */
15308       sprintf(tag, "%02d%02d%02d", tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15309 
15310       lseek(*fh, 0, SEEK_END);
15311 
15312       sprintf(tag + strlen(tag), ".%d", (int) TELL(*fh));
15313    }
15314 
15315 
15316    if (direction == -1) {
15317       /* seek previous message */
15318 
15319       if (TELL(*fh) == 0) {
15320          /* go back one day */
15321          close(*fh);
15322 
15323          lt = ltime;
15324          do {
15325             lt -= 3600 * 24;
15326             tms = localtime((const time_t *) &lt);
15327             sprintf(str, "%02d%02d%02d.0",
15328                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15329 
15330             status = el_search_message(str, fh, FALSE);
15331 
15332          } while (status != EL_SUCCESS && (INT) ltime - (INT) lt < 3600 * 24 * 365);
15333 
15334          if (status != EL_SUCCESS)
15335             return EL_FIRST_MSG;
15336 
15337          /* adjust tag */
15338          strcpy(tag, str);
15339 
15340          /* go to end of current file */
15341          lseek(*fh, 0, SEEK_END);
15342       }
15343 
15344       /* read previous message size */
15345       lseek(*fh, -17, SEEK_CUR);
15346       i = read(*fh, str, 17);
15347       if (i <= 0) {
15348          close(*fh);
15349          return EL_FILE_ERROR;
15350       }
15351 
15352       if (strncmp(str, "$End$: ", 7) != 0) {
15353          close(*fh);
15354          return EL_FILE_ERROR;
15355       }
15356         
15357       /* make sure the input string to atoi() is zero-terminated:
15358        * $End$:      355garbage
15359        * 01234567890123456789 */
15360       str[15] = 0;
15361 
15362       size = atoi(str + 7);
15363       assert(size > 15);
15364 
15365       lseek(*fh, -size, SEEK_CUR);
15366 
15367       /* adjust tag */
15368       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15369    }
15370 
15371    if (direction == 1) {
15372       /* seek next message */
15373 
15374       /* read current message size */
15375       last = TELL(*fh);
15376 
15377       i = read(*fh, str, 15);
15378       if (i <= 0) {
15379          close(*fh);
15380          return EL_FILE_ERROR;
15381       }
15382       lseek(*fh, -15, SEEK_CUR);
15383 
15384       if (strncmp(str, "$Start$: ", 9) != 0) {
15385          close(*fh);
15386          return EL_FILE_ERROR;
15387       }
15388 
15389       /* make sure the input string to atoi() is zero-terminated
15390        * $Start$:    606garbage
15391        * 01234567890123456789 */
15392       str[15] = 0;
15393 
15394       size = atoi(str+9);
15395       assert(size > 15);
15396 
15397       lseek(*fh, size, SEEK_CUR);
15398 
15399       /* if EOF, goto next day */
15400       i = read(*fh, str, 15);
15401       if (i < 15) {
15402          close(*fh);
15403          time((long *) &lact);
15404 
15405          lt = ltime;
15406          do {
15407             lt += 3600 * 24;
15408             tms = localtime((const time_t *) &lt);
15409             sprintf(str, "%02d%02d%02d.0",
15410                     tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday);
15411 
15412             status = el_search_message(str, fh, FALSE);
15413 
15414          } while (status != EL_SUCCESS && (INT) lt - (INT) lact < 3600 * 24);
15415 
15416          if (status != EL_SUCCESS)
15417             return EL_LAST_MSG;
15418 
15419          /* adjust tag */
15420          strcpy(tag, str);
15421 
15422          /* go to beginning of current file */
15423          lseek(*fh, 0, SEEK_SET);
15424       } else
15425          lseek(*fh, -15, SEEK_CUR);
15426 
15427       /* adjust tag */
15428       sprintf(strchr(tag, '.') + 1, "%d", (int) TELL(*fh));
15429    }
15430 
15431    return EL_SUCCESS;
15432 }
15433 
15434 
15435 /********************************************************************/
15436 INT el_retrieve(char *tag, char *date, int *run, char *author, char *type,
15437                 char *system, char *subject, char *text, int *textsize,
15438                 char *orig_tag, char *reply_tag,
15439                 char *attachment1, char *attachment2, char *attachment3, char *encoding)
15440 /********************************************************************\
15441 
15442   Routine: el_retrieve
15443 
15444   Purpose: Retrieve an ELog entry by its message tab
15445 
15446   Input:
15447     char   *tag             tag in the form YYMMDD.offset
15448     int    *size            Size of text buffer
15449 
15450   Output:
15451     char   *tag             tag of retrieved message
15452     char   *date            Date/time of message recording
15453     int    *run             Run number
15454     char   *author          Message author
15455     char   *type            Message type
15456     char   *system          Message system
15457     char   *subject         Subject
15458     char   *text            Message text
15459     char   *orig_tag        Original message if this one is a reply
15460     char   *reply_tag       Reply for current message
15461     char   *attachment1/2/3 File attachment
15462     char   *encoding        Encoding of message
15463     int    *size            Actual message text size
15464 
15465   Function value:
15466     EL_SUCCESS              Successful completion
15467     EL_LAST_MSG             Last message in log
15468 
15469 \********************************************************************/
15470 {
15471    int size, fh = 0, offset, search_status, rd;
15472    char str[256], *p;
15473    char message[10000], thread[256], attachment_all[256];
15474 
15475    if (tag[0]) {
15476       search_status = el_search_message(tag, &fh, TRUE);
15477       if (search_status != EL_SUCCESS)
15478          return search_status;
15479    } else {
15480       /* open most recent message */
15481       strcpy(tag, "-1");
15482       search_status = el_search_message(tag, &fh, TRUE);
15483       if (search_status != EL_SUCCESS)
15484          return search_status;
15485    }
15486 
15487    /* extract message size */
15488    offset = TELL(fh);
15489    rd = read(fh, str, 15);
15490    assert(rd == 15);
15491    
15492    /* make sure the input string is zero-terminated before we call atoi() */
15493    str[15] = 0;
15494 
15495    /* get size */
15496    size = atoi(str+9);
15497    
15498    assert(strncmp(str,"$Start$:",8) == 0);
15499    assert(size > 15);
15500    assert(size < sizeof(message));
15501    
15502    memset(message, 0, sizeof(message));
15503 
15504    rd = read(fh, message, size);
15505    assert(rd > 0);
15506    assert((rd+15 == size)||(rd == size));
15507 
15508    close(fh);
15509 
15510    /* decode message */
15511    if (strstr(message, "Run: ") && run)
15512       *run = atoi(strstr(message, "Run: ") + 5);
15513 
15514    el_decode(message, "Date: ",     date,     80); /* size from show_elog_submit_query() */
15515    el_decode(message, "Thread: ", thread, sizeof(thread));
15516    el_decode(message, "Author: ",   author,   80); /* size from show_elog_submit_query() */
15517    el_decode(message, "Type: ",     type,     80); /* size from show_elog_submit_query() */
15518    el_decode(message, "System: ",   system,   80); /* size from show_elog_submit_query() */
15519    el_decode(message, "Subject: ",  subject, 256); /* size from show_elog_submit_query() */
15520    el_decode(message, "Attachment: ", attachment_all, sizeof(attachment_all));
15521    el_decode(message, "Encoding: ", encoding, 80); /* size from show_elog_submit_query() */
15522 
15523    /* break apart attachements */
15524    if (attachment1 && attachment2 && attachment3) {
15525       attachment1[0] = attachment2[0] = attachment3[0] = 0;
15526       p = strtok(attachment_all, ",");
15527       if (p != NULL) {
15528          strcpy(attachment1, p);
15529          p = strtok(NULL, ",");
15530          if (p != NULL) {
15531             strcpy(attachment2, p);
15532             p = strtok(NULL, ",");
15533             if (p != NULL)
15534                strcpy(attachment3, p);
15535          }
15536       }
15537 
15538       assert(strlen(attachment1) < 256); /* size from show_elog_submit_query() */
15539       assert(strlen(attachment2) < 256); /* size from show_elog_submit_query() */
15540       assert(strlen(attachment3) < 256); /* size from show_elog_submit_query() */
15541    }
15542 
15543    /* conver thread in reply-to and reply-from */
15544    if (orig_tag != NULL && reply_tag != NULL) {
15545       p = strtok(thread, " \r");
15546       if (p != NULL)
15547          strcpy(orig_tag, p);
15548       else
15549          strcpy(orig_tag, "");
15550       p = strtok(NULL, " \r");
15551       if (p != NULL)
15552          strcpy(reply_tag, p);
15553       else
15554          strcpy(reply_tag, "");
15555       if (atoi(orig_tag) == 0)
15556          orig_tag[0] = 0;
15557       if (atoi(reply_tag) == 0)
15558          reply_tag[0] = 0;
15559    }
15560 
15561    p = strstr(message, "========================================\n");
15562 
15563    if (text != NULL) {
15564       if (p != NULL) {
15565          p += 41;
15566          if ((int) strlen(p) >= *textsize) {
15567             strncpy(text, p, *textsize - 1);
15568             text[*textsize - 1] = 0;
15569             return EL_TRUNCATED;
15570          } else {
15571             strcpy(text, p);
15572 
15573             /* strip end tag */
15574             if (strstr(text, "$End$"))
15575                *strstr(text, "$End$") = 0;
15576 
15577             *textsize = strlen(text);
15578          }
15579       } else {
15580          text[0] = 0;
15581          *textsize = 0;
15582       }
15583    }
15584 
15585    if (search_status == EL_LAST_MSG)
15586       return EL_LAST_MSG;
15587 
15588    return EL_SUCCESS;
15589 }
15590 
15591 
15592 /********************************************************************/
15593 INT el_search_run(int run, char *return_tag)
15594 /********************************************************************\
15595 
15596   Routine: el_search_run
15597 
15598   Purpose: Find first message belonging to a specific run
15599 
15600   Input:
15601     int    run              Run number
15602 
15603   Output:
15604     char   *tag             tag of retrieved message
15605 
15606   Function value:
15607     EL_SUCCESS              Successful completion
15608     EL_LAST_MSG             Last message in log
15609 
15610 \********************************************************************/
15611 {
15612    int actual_run, fh, status;
15613    char tag[256];
15614 
15615    tag[0] = return_tag[0] = 0;
15616 
15617    do {
15618       /* open first message in file */
15619       strcat(tag, "-1");
15620       status = el_search_message(tag, &fh, TRUE);
15621       if (status == EL_FIRST_MSG)
15622          break;
15623       if (status != EL_SUCCESS)
15624          return status;
15625       close(fh);
15626 
15627       if (strchr(tag, '.') != NULL)
15628          strcpy(strchr(tag, '.'), ".0");
15629 
15630       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15631                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15632    } while (actual_run >= run);
15633 
15634    while (actual_run < run) {
15635       strcat(tag, "+1");
15636       status = el_search_message(tag, &fh, TRUE);
15637       if (status == EL_LAST_MSG)
15638          break;
15639       if (status != EL_SUCCESS)
15640          return status;
15641       close(fh);
15642 
15643       el_retrieve(tag, NULL, &actual_run, NULL, NULL,
15644                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
15645    }
15646 
15647    strcpy(return_tag, tag);
15648 
15649    if (status == EL_LAST_MSG || status == EL_FIRST_MSG)
15650       return status;
15651 
15652    return EL_SUCCESS;
15653 }
15654 
15655 
15656 /********************************************************************/
15657 INT el_delete_message(char *tag)
15658 /********************************************************************\
15659 
15660   Routine: el_submit
15661 
15662   Purpose: Submit an ELog entry
15663 
15664   Input:
15665     char   *tag             Message tage
15666 
15667   Output:
15668     <none>
15669 
15670   Function value:
15671     EL_SUCCESS              Successful completion
15672 
15673 \********************************************************************/
15674 {
15675 #ifdef LOCAL_ROUTINES
15676    INT n, size, fh, mutex, offset = 0, tail_size, status;
15677    char dir[256], str[256], file_name[256];
15678    HNDLE hDB;
15679    char *buffer = NULL;
15680 
15681    cm_get_experiment_database(&hDB, NULL);
15682 
15683    /* request semaphore */
15684    cm_get_experiment_mutex(NULL, &mutex);
15685    ss_mutex_wait_for(mutex, 0);
15686 
15687    /* generate file name YYMMDD.log in data directory */
15688    cm_get_experiment_database(&hDB, NULL);
15689 
15690    size = sizeof(dir);
15691    memset(dir, 0, size);
15692    status = db_get_value(hDB, 0, "/Logger/Elog dir", dir, &size, TID_STRING, FALSE);
15693    if (status != DB_SUCCESS)
15694       db_get_value(hDB, 0, "/Logger/Data dir", dir, &size, TID_STRING, TRUE);
15695 
15696    if (dir[0] != 0 && dir[strlen(dir) - 1] != DIR_SEPARATOR)
15697       strcat(dir, DIR_SEPARATOR_STR);
15698 
15699    strcpy(str, tag);
15700    if (strchr(str, '.')) {
15701       offset = atoi(strchr(str, '.') + 1);
15702       *strchr(str, '.') = 0;
15703    }
15704    sprintf(file_name, "%s%s.log", dir, str);
15705    fh = open(file_name, O_CREAT | O_RDWR | O_BINARY, 0644);
15706    if (fh < 0) {
15707       ss_mutex_release(mutex);
15708       return EL_FILE_ERROR;
15709    }
15710    lseek(fh, offset, SEEK_SET);
15711    read(fh, str, 16);
15712    size = atoi(str + 9);
15713 
15714    /* buffer tail of logfile */
15715    lseek(fh, 0, SEEK_END);
15716    tail_size = TELL(fh) - (offset + size);
15717 
15718    if (tail_size > 0) {
15719       buffer = (char *) M_MALLOC(tail_size);
15720       if (buffer == NULL) {
15721          close(fh);
15722          ss_mutex_release(mutex);
15723          return EL_FILE_ERROR;
15724       }
15725 
15726       lseek(fh, offset + size, SEEK_SET);
15727       n = read(fh, buffer, tail_size);
15728    }
15729    lseek(fh, offset, SEEK_SET);
15730 
15731    if (tail_size > 0) {
15732       n = write(fh, buffer, tail_size);
15733       M_FREE(buffer);
15734    }
15735 
15736    /* truncate file here */
15737 #ifdef OS_WINNT
15738    chsize(fh, TELL(fh));
15739 #else
15740    ftruncate(fh, TELL(fh));
15741 #endif
15742 
15743    /* if file length gets zero, delete file */
15744    tail_size = lseek(fh, 0, SEEK_END);
15745    close(fh);
15746 
15747    if (tail_size == 0)
15748       remove(file_name);
15749 
15750    /* release elog mutex */
15751    ss_mutex_release(mutex);
15752 #endif                          /* LOCAL_ROUTINES */
15753 
15754    return EL_SUCCESS;
15755 }
15756 
15757 /**dox***************************************************************/
15758 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15759 
15760 /**dox***************************************************************/
15761 /** @} */ /* end of elfunctionc */
15762 
15763 /**dox***************************************************************/
15764 /** @addtogroup alfunctionc
15765  *  
15766  *  @{  */
15767 
15768 /**dox***************************************************************/
15769 #ifndef DOXYGEN_SHOULD_SKIP_THIS
15770 
15771 /********************************************************************\
15772 *                                                                    *
15773 *                     Alarm functions                                *
15774 *                                                                    *
15775 \********************************************************************/
15776 
15777 /********************************************************************/
15778 BOOL al_evaluate_condition(char *condition, char *value)
15779 {
15780    HNDLE hDB, hkey;
15781    int i, j, index, size;
15782    KEY key;
15783    double value1, value2;
15784    char str[256], op[3], function[80];
15785    char data[10000];
15786    DWORD time;
15787 
15788    strcpy(str, condition);
15789    op[1] = op[2] = 0;
15790    value1 = value2 = 0;
15791    index = 0;
15792 
15793    /* find value and operator */
15794    for (i = strlen(str) - 1; i > 0; i--)
15795       if (strchr("<>=!", str[i]) != NULL)
15796          break;
15797    op[0] = str[i];
15798    value2 = atof(str + i + 1);
15799    str[i] = 0;
15800 
15801    if (i > 0 && strchr("<>=!", str[i - 1])) {
15802       op[1] = op[0];
15803       op[0] = str[--i];
15804       str[i] = 0;
15805    }
15806 
15807    i--;
15808    while (i > 0 && str[i] == ' ')
15809       i--;
15810    str[i + 1] = 0;
15811 
15812    /* check if function */
15813    function[0] = 0;
15814    if (str[i] == ')') {
15815       str[i--] = 0;
15816       if (strchr(str, '(')) {
15817          *strchr(str, '(') = 0;
15818          strcpy(function, str);
15819          for (i = strlen(str) + 1, j = 0; str[i]; i++, j++)
15820             str[j] = str[i];
15821          str[j] = 0;
15822          i = j - 1;
15823       }
15824    }
15825 
15826    /* find key */
15827    if (str[i] == ']') {
15828       str[i--] = 0;
15829       while (i > 0 && isdigit(str[i]))
15830          i--;
15831       index = atoi(str + i + 1);
15832       str[i] = 0;
15833    }
15834 
15835    cm_get_experiment_database(&hDB, NULL);
15836    db_find_key(hDB, 0, str, &hkey);
15837    if (!hkey) {
15838       cm_msg(MERROR, "al_evaluate_condition",
15839              "Cannot find key %s to evaluate alarm condition", str);
15840       if (value)
15841          strcpy(value, "unknown");
15842       return FALSE;
15843    }
15844 
15845    if (equal_ustring(function, "access")) {
15846       /* check key access time */
15847       db_get_key_time(hDB, hkey, &time);
15848       sprintf(str, "%ld", time);
15849       value1 = atof(str);
15850    } else {
15851       /* get key data and convert to double */
15852       db_get_key(hDB, hkey, &key);
15853       size = sizeof(data);
15854       db_get_data(hDB, hkey, data, &size, key.type);
15855       db_sprintf(str, data, size, index, key.type);
15856       value1 = atof(str);
15857    }
15858 
15859    /* return value */
15860    if (value)
15861       strcpy(value, str);
15862 
15863    /* now do logical operation */
15864    if (strcmp(op, "=") == 0)
15865       return value1 == value2;
15866    if (strcmp(op, "==") == 0)
15867       return value1 == value2;
15868    if (strcmp(op, "!=") == 0)
15869       return value1 != value2;
15870    if (strcmp(op, "<") == 0)
15871       return value1 < value2;
15872    if (strcmp(op, ">") == 0)
15873       return value1 > value2;
15874    if (strcmp(op, "<=") == 0)
15875       return value1 <= value2;
15876    if (strcmp(op, ">=") == 0)
15877       return value1 >= value2;
15878 
15879    return FALSE;
15880 }
15881 
15882 /**dox***************************************************************/
15883 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
15884 
15885 /********************************************************************/
15886 /**
15887 Trigger a certain alarm.
15888 \code  ...
15889   lazy.alarm[0] = 0;
15890   size = sizeof(lazy.alarm);
15891   db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
15892 
15893   // trigger alarm if defined
15894   if (lazy.alarm[0])
15895     al_trigger_alarm("Tape", "Tape full...load new one!", lazy.alarm, "Tape full", AT_INTERNAL);
15896   ...
15897 \endcode
15898 @param alarm_name Alarm name, defined in /alarms/alarms
15899 @param alarm_message Optional message which goes with alarm
15900 @param default_class If alarm is not yet defined under
15901                     /alarms/alarms/<alarm_name>, a new one
15902                     is created and this default class is used.
15903 @param cond_str String displayed in alarm condition
15904 @param type Alarm type, one of AT_xxx
15905 @return AL_SUCCESS, AL_INVALID_NAME
15906 */
15907 INT al_trigger_alarm(char *alarm_name, char *alarm_message, char *default_class,
15908                      char *cond_str, INT type)
15909 {
15910    if (rpc_is_remote())
15911       return rpc_call(RPC_AL_TRIGGER_ALARM, alarm_name, alarm_message,
15912                       default_class, cond_str, type);
15913 
15914 #ifdef LOCAL_ROUTINES
15915    {
15916       int status, size;
15917       HNDLE hDB, hkeyalarm;
15918       char str[256];
15919       ALARM alarm;
15920       BOOL flag;
15921       ALARM_ODB_STR(alarm_odb_str);
15922 
15923       cm_get_experiment_database(&hDB, NULL);
15924 
15925       /* check online mode */
15926       flag = TRUE;
15927       size = sizeof(flag);
15928       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
15929       if (!flag)
15930          return AL_SUCCESS;
15931 
15932       /* find alarm */
15933       sprintf(str, "/Alarms/Alarms/%s", alarm_name);
15934       db_find_key(hDB, 0, str, &hkeyalarm);
15935       if (!hkeyalarm) {
15936          /* alarm must be an internal analyzer alarm, so create a default alarm */
15937          status = db_create_record(hDB, 0, str, strcomb(alarm_odb_str));
15938          db_find_key(hDB, 0, str, &hkeyalarm);
15939          if (!hkeyalarm) {
15940             cm_msg(MERROR, "al_trigger_alarm", "Cannot create alarm record");
15941             return AL_ERROR_ODB;
15942          }
15943 
15944          if (default_class && default_class[0])
15945             db_set_value(hDB, hkeyalarm, "Alarm Class", default_class, 32, 1, TID_STRING);
15946          status = TRUE;
15947          db_set_value(hDB, hkeyalarm, "Active", &status, sizeof(status), 1, TID_BOOL);
15948       }
15949 
15950       /* set parameters for internal alarms */
15951       if (type != AT_EVALUATED && type != AT_PERIODIC) {
15952          db_set_value(hDB, hkeyalarm, "Type", &type, sizeof(INT), 1, TID_INT);
15953          strcpy(str, cond_str);
15954          db_set_value(hDB, hkeyalarm, "Condition", str, 256, 1, TID_STRING);
15955       }
15956 
15957       size = sizeof(alarm);
15958       status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15959       if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
15960          /* make sure alarm record has right structure */
15961          db_check_record(hDB, hkeyalarm, "", strcomb(alarm_odb_str), TRUE);
15962 
15963          size = sizeof(alarm);
15964          status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
15965          if (status != DB_SUCCESS) {
15966             cm_msg(MERROR, "al_trigger_alarm", "Cannot get alarm record");
15967             return AL_ERROR_ODB;
15968          }
15969       }
15970 
15971       /* if internal alarm, check if active and check interval */
15972       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15973          /* check global alarm flag */
15974          flag = TRUE;
15975          size = sizeof(flag);
15976          db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL,
15977                       TRUE);
15978          if (!flag)
15979             return AL_SUCCESS;
15980 
15981          if (!alarm.active)
15982             return AL_SUCCESS;
15983 
15984          if ((INT) ss_time() - (INT) alarm.checked_last < alarm.check_interval)
15985             return AL_SUCCESS;
15986 
15987          /* now the alarm will be triggered, so save time */
15988          alarm.checked_last = ss_time();
15989       }
15990 
15991       /* write back alarm message for internal alarms */
15992       if (alarm.type != AT_EVALUATED && alarm.type != AT_PERIODIC) {
15993          strncpy(alarm.alarm_message, alarm_message, 79);
15994          alarm.alarm_message[79] = 0;
15995       }
15996 
15997       /* now trigger alarm class defined in this alarm */
15998       if (alarm.alarm_class[0])
15999          al_trigger_class(alarm.alarm_class, alarm_message, alarm.triggered > 0);
16000 
16001       /* signal alarm being triggered */
16002       cm_asctime(str, sizeof(str));
16003 
16004       if (!alarm.triggered)
16005          strcpy(alarm.time_triggered_first, str);
16006 
16007       alarm.triggered++;
16008       strcpy(alarm.time_triggered_last, str);
16009 
16010       alarm.checked_last = ss_time();
16011 
16012       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16013       if (status != DB_SUCCESS) {
16014          cm_msg(MERROR, "al_trigger_alarm", "Cannot update alarm record");
16015          return AL_ERROR_ODB;
16016       }
16017 
16018    }
16019 #endif                          /* LOCAL_ROUTINES */
16020 
16021    return AL_SUCCESS;
16022 }
16023 
16024 /**dox***************************************************************/
16025 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16026 
16027 /********************************************************************/
16028 INT al_trigger_class(char *alarm_class, char *alarm_message, BOOL first)
16029 /********************************************************************\
16030 
16031   Routine: al_trigger_class
16032 
16033   Purpose: Trigger a certain alarm class
16034 
16035   Input:
16036     char   *alarm_class     Alarm class, must be defined in
16037                             /alarms/classes
16038     char   *alarm_message   Optional message which goes with alarm
16039     BOOL   first            TRUE if alarm is triggered first time
16040                             (used for elog)
16041 
16042   Output:
16043 
16044   Function value:
16045     AL_INVALID_NAME         Alarm class not defined
16046     AL_SUCCESS              Successful completion
16047 
16048 \********************************************************************/
16049 {
16050    int status, size, state;
16051    HNDLE hDB, hkeyclass;
16052    char str[256], command[256], tag[32];
16053    ALARM_CLASS ac;
16054 
16055    cm_get_experiment_database(&hDB, NULL);
16056 
16057    /* get alarm class */
16058    sprintf(str, "/Alarms/Classes/%s", alarm_class);
16059    db_find_key(hDB, 0, str, &hkeyclass);
16060    if (!hkeyclass) {
16061       cm_msg(MERROR, "al_trigger_class", "Alarm class %s not found in ODB", alarm_class);
16062       return AL_INVALID_NAME;
16063    }
16064 
16065    size = sizeof(ac);
16066    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16067    if (status != DB_SUCCESS) {
16068       cm_msg(MERROR, "al_trigger_class", "Cannot get alarm class record");
16069       return AL_ERROR_ODB;
16070    }
16071 
16072    /* write system message */
16073    if (ac.write_system_message &&
16074        (INT) ss_time() - (INT) ac.system_message_last > ac.system_message_interval) {
16075       sprintf(str, "%s: %s", alarm_class, alarm_message);
16076       cm_msg(MTALK, "al_trigger_class", str);
16077       ac.system_message_last = ss_time();
16078    }
16079 
16080    /* write elog message on first trigger */
16081    if (ac.write_elog_message && first)
16082       el_submit(0, "Alarm system", "Alarm", "General", alarm_class, str,
16083                 "", "plain", "", "", 0, "", "", 0, "", "", 0, tag, 32);
16084 
16085    /* execute command */
16086    if (ac.execute_command[0] &&
16087        ac.execute_interval > 0 &&
16088        (INT) ss_time() - (INT) ac.execute_last > ac.execute_interval) {
16089       sprintf(str, "%s: %s", alarm_class, alarm_message);
16090       sprintf(command, ac.execute_command, str);
16091       cm_msg(MINFO, "al_trigger_class", "Execute: %s", command);
16092       ss_system(command);
16093       ac.execute_last = ss_time();
16094    }
16095 
16096    /* stop run */
16097    if (ac.stop_run) {
16098       state = STATE_STOPPED;
16099       size = sizeof(state);
16100       db_get_value(hDB, 0, "/Runinfo/State", &state, &size, TID_INT, TRUE);
16101       if (state != STATE_STOPPED)
16102          cm_transition(TR_STOP, 0, NULL, 0, ASYNC, FALSE);
16103    }
16104 
16105    status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16106    if (status != DB_SUCCESS) {
16107       cm_msg(MERROR, "al_trigger_class", "Cannot update alarm class record");
16108       return AL_ERROR_ODB;
16109    }
16110 
16111    return AL_SUCCESS;
16112 }
16113 
16114 
16115 /********************************************************************/
16116 INT al_reset_alarm(char *alarm_name)
16117 /********************************************************************\
16118 
16119   Routine: al_reset_alarm
16120 
16121   Purpose: Reset (acknowledge) alarm
16122 
16123   Input:
16124     char   *alarm_name      Alarm name, must be defined in /Alarms/Alarms
16125                             If NULL reset all alarms
16126 
16127   Output:
16128     <none>
16129 
16130   Function value:
16131     AL_INVALID_NAME         Alarm name not defined
16132     AL_RESET                Alarm was triggered and reset
16133     AL_SUCCESS              Successful completion
16134 
16135 \********************************************************************/
16136 {
16137    int status, size, i;
16138    HNDLE hDB, hkeyalarm, hkeyclass, hsubkey;
16139    KEY key;
16140    char str[256];
16141    ALARM alarm;
16142    ALARM_CLASS ac;
16143 
16144    cm_get_experiment_database(&hDB, NULL);
16145 
16146    if (alarm_name == NULL) {
16147       /* reset all alarms */
16148       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyalarm);
16149       if (hkeyalarm) {
16150          for (i = 0;; i++) {
16151             db_enum_link(hDB, hkeyalarm, i, &hsubkey);
16152 
16153             if (!hsubkey)
16154                break;
16155 
16156             db_get_key(hDB, hsubkey, &key);
16157             al_reset_alarm(key.name);
16158          }
16159       }
16160       return AL_SUCCESS;
16161    }
16162 
16163    /* find alarm and alarm class */
16164    sprintf(str, "/Alarms/Alarms/%s", alarm_name);
16165    db_find_key(hDB, 0, str, &hkeyalarm);
16166    if (!hkeyalarm) {
16167       cm_msg(MERROR, "al_reset_alarm", "Alarm %s not found in ODB", alarm_name);
16168       return AL_INVALID_NAME;
16169    }
16170 
16171    size = sizeof(alarm);
16172    status = db_get_record(hDB, hkeyalarm, &alarm, &size, 0);
16173    if (status != DB_SUCCESS) {
16174       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm record");
16175       return AL_ERROR_ODB;
16176    }
16177 
16178    sprintf(str, "/Alarms/Classes/%s", alarm.alarm_class);
16179    db_find_key(hDB, 0, str, &hkeyclass);
16180    if (!hkeyclass) {
16181       cm_msg(MERROR, "al_reset_alarm", "Alarm class %s not found in ODB",
16182              alarm.alarm_class);
16183       return AL_INVALID_NAME;
16184    }
16185 
16186    size = sizeof(ac);
16187    status = db_get_record(hDB, hkeyclass, &ac, &size, 0);
16188    if (status != DB_SUCCESS) {
16189       cm_msg(MERROR, "al_reset_alarm", "Cannot get alarm class record");
16190       return AL_ERROR_ODB;
16191    }
16192 
16193    if (alarm.triggered) {
16194       alarm.triggered = 0;
16195       alarm.time_triggered_first[0] = 0;
16196       alarm.time_triggered_last[0] = 0;
16197       alarm.checked_last = 0;
16198 
16199       ac.system_message_last = 0;
16200       ac.execute_last = 0;
16201 
16202       status = db_set_record(hDB, hkeyalarm, &alarm, sizeof(alarm), 0);
16203       if (status != DB_SUCCESS) {
16204          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm record");
16205          return AL_ERROR_ODB;
16206       }
16207       status = db_set_record(hDB, hkeyclass, &ac, sizeof(ac), 0);
16208       if (status != DB_SUCCESS) {
16209          cm_msg(MERROR, "al_reset_alarm", "Cannot update alarm class record");
16210          return AL_ERROR_ODB;
16211       }
16212       return AL_RESET;
16213    }
16214 
16215    return AL_SUCCESS;
16216 }
16217 
16218 
16219 /********************************************************************/
16220 INT al_check()
16221 /********************************************************************\
16222 
16223   Routine: al_scan
16224 
16225   Purpose: Scan ODB alarams and programs
16226 
16227   Input:
16228 
16229   Output:
16230 
16231   Function value:
16232     AL_SUCCESS              Successful completion
16233 
16234 \********************************************************************/
16235 {
16236    if (rpc_is_remote())
16237       return rpc_call(RPC_AL_CHECK);
16238 
16239 #ifdef LOCAL_ROUTINES
16240    {
16241       INT i, status, size, mutex;
16242       HNDLE hDB, hkeyroot, hkey;
16243       KEY key;
16244       ALARM alarm;
16245       char str[256], value[256];
16246       DWORD now;
16247       PROGRAM_INFO program_info;
16248       BOOL flag;
16249 
16250       ALARM_CLASS_STR(alarm_class_str);
16251       ALARM_ODB_STR(alarm_odb_str);
16252       ALARM_PERIODIC_STR(alarm_periodic_str);
16253 
16254       cm_get_experiment_database(&hDB, NULL);
16255 
16256       if (hDB == 0)
16257          return AL_SUCCESS;     /* called from server not yet connected */
16258 
16259       /* check online mode */
16260       flag = TRUE;
16261       size = sizeof(flag);
16262       db_get_value(hDB, 0, "/Runinfo/Online Mode", &flag, &size, TID_INT, TRUE);
16263       if (!flag)
16264          return AL_SUCCESS;
16265 
16266       /* check global alarm flag */
16267       flag = TRUE;
16268       size = sizeof(flag);
16269       db_get_value(hDB, 0, "/Alarms/Alarm system active", &flag, &size, TID_BOOL, TRUE);
16270       if (!flag)
16271          return AL_SUCCESS;
16272 
16273       /* request semaphore */
16274       cm_get_experiment_mutex(&mutex, NULL);
16275       status = ss_mutex_wait_for(mutex, 100);
16276       if (status != SS_SUCCESS)
16277          return SUCCESS;        /* someone else is doing alarm business */
16278 
16279       /* check ODB alarms */
16280       db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16281       if (!hkeyroot) {
16282          /* create default ODB alarm */
16283          status =
16284              db_create_record(hDB, 0, "/Alarms/Alarms/Demo ODB", strcomb(alarm_odb_str));
16285          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16286          if (!hkeyroot) {
16287             ss_mutex_release(mutex);
16288             return SUCCESS;
16289          }
16290 
16291          status =
16292              db_create_record(hDB, 0, "/Alarms/Alarms/Demo periodic",
16293                               strcomb(alarm_periodic_str));
16294          db_find_key(hDB, 0, "/Alarms/Alarms", &hkeyroot);
16295          if (!hkeyroot) {
16296             ss_mutex_release(mutex);
16297             return SUCCESS;
16298          }
16299 
16300          /* create default alarm classes */
16301          status =
16302              db_create_record(hDB, 0, "/Alarms/Classes/Alarm", strcomb(alarm_class_str));
16303          status =
16304              db_create_record(hDB, 0, "/Alarms/Classes/Warning",
16305                               strcomb(alarm_class_str));
16306          if (status != DB_SUCCESS) {
16307             ss_mutex_release(mutex);
16308             return SUCCESS;
16309          }
16310       }
16311 
16312       for (i = 0;; i++) {
16313          status = db_enum_key(hDB, hkeyroot, i, &hkey);
16314          if (status == DB_NO_MORE_SUBKEYS)
16315             break;
16316 
16317          db_get_key(hDB, hkey, &key);
16318 
16319          size = sizeof(alarm);
16320          status = db_get_record(hDB, hkey, &alarm, &size, 0);
16321          if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16322             /* make sure alarm record has right structure */
16323             db_check_record(hDB, hkey, "", strcomb(alarm_odb_str), TRUE);
16324             size = sizeof(alarm);
16325             status = db_get_record(hDB, hkey, &alarm, &size, 0);
16326             if (status != DB_SUCCESS || alarm.type < 1 || alarm.type > AT_LAST) {
16327                cm_msg(MERROR, "al_check", "Cannot get alarm record");
16328                continue;
16329             }
16330          }
16331 
16332          /* check periodic alarm only when active */
16333          if (alarm.active &&
16334              alarm.type == AT_PERIODIC &&
16335              alarm.check_interval > 0 &&
16336              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16337             /* if checked_last has not been set, set it to current time */
16338             if (alarm.checked_last == 0) {
16339                alarm.checked_last = ss_time();
16340                db_set_record(hDB, hkey, &alarm, size, 0);
16341             } else
16342                al_trigger_alarm(key.name, alarm.alarm_message, alarm.alarm_class, "",
16343                                 AT_PERIODIC);
16344          }
16345 
16346          /* check alarm only when active and not internal */
16347          if (alarm.active &&
16348              alarm.type == AT_EVALUATED &&
16349              alarm.check_interval > 0 &&
16350              (INT) ss_time() - (INT) alarm.checked_last > alarm.check_interval) {
16351             /* if condition is true, trigger alarm */
16352             if (al_evaluate_condition(alarm.condition, value)) {
16353                sprintf(str, alarm.alarm_message, value);
16354                al_trigger_alarm(key.name, str, alarm.alarm_class, "", AT_EVALUATED);
16355             } else {
16356                alarm.checked_last = ss_time();
16357                status = db_set_record(hDB, hkey, &alarm, sizeof(alarm), 0);
16358                if (status != DB_SUCCESS) {
16359                   cm_msg(MERROR, "al_check", "Cannot write back alarm record");
16360                   continue;
16361                }
16362             }
16363          }
16364       }
16365 
16366       /* check /programs alarms */
16367       db_find_key(hDB, 0, "/Programs", &hkeyroot);
16368       if (hkeyroot) {
16369          for (i = 0;; i++) {
16370             status = db_enum_key(hDB, hkeyroot, i, &hkey);
16371             if (status == DB_NO_MORE_SUBKEYS)
16372                break;
16373 
16374             db_get_key(hDB, hkey, &key);
16375 
16376             /* don't check "execute on xxx" */
16377             if (key.type != TID_KEY)
16378                continue;
16379 
16380             size = sizeof(program_info);
16381             status = db_get_record(hDB, hkey, &program_info, &size, 0);
16382             if (status != DB_SUCCESS) {
16383                cm_msg(MERROR, "al_check", "Cannot get program info record");
16384                continue;
16385             }
16386 
16387             now = ss_time();
16388 
16389             rpc_get_name(str);
16390             str[strlen(key.name)] = 0;
16391             if (!equal_ustring(str, key.name) &&
16392                 cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
16393                if (program_info.first_failed == 0)
16394                   program_info.first_failed = now;
16395 
16396                /* fire alarm when not running for more than what specified in check interval */
16397                if (now - program_info.first_failed >= program_info.check_interval / 1000) {
16398                   /* if not running and alarm calss defined, trigger alarm */
16399                   if (program_info.alarm_class[0]) {
16400                      sprintf(str, "Program %s is not running", key.name);
16401                      al_trigger_alarm(key.name, str, program_info.alarm_class,
16402                                       "Program not running", AT_PROGRAM);
16403                   }
16404 
16405                   /* auto restart program */
16406                   if (program_info.auto_restart && program_info.start_command[0]) {
16407                      ss_system(program_info.start_command);
16408                      program_info.first_failed = 0;
16409                      cm_msg(MTALK, "al_check", "Program %s restarted", key.name);
16410                   }
16411                }
16412             } else
16413                program_info.first_failed = 0;
16414 
16415             db_set_record(hDB, hkey, &program_info, sizeof(program_info), 0);
16416          }
16417       }
16418 
16419       ss_mutex_release(mutex);
16420    }
16421 #endif                          /* LOCAL_COUTINES */
16422 
16423    return SUCCESS;
16424 }
16425 
16426 /**dox***************************************************************/
16427 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16428 
16429 /** @} */ /* end of alfunctionc */
16430 
16431 /***** sKIP eb_xxx **************************************************/
16432 /**dox***************************************************************/
16433 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16434 /***** sKIP eb_xxx **************************************************/
16435 
16436 #if !defined(OS_VXWORKS)
16437 /********************************************************************\
16438 *                                                                    *
16439 *                 Event buffer functions                             *
16440 *                                                                    *
16441 \********************************************************************/
16442 
16443 /* PAA several modification in the eb_xxx()
16444    also new function eb_buffer_full()
16445 */
16446 static char *_event_ring_buffer = NULL;
16447 static INT _eb_size;
16448 static char *_eb_read_pointer, *_eb_write_pointer, *_eb_end_pointer;
16449 
16450 /********************************************************************/
16451 INT eb_create_buffer(INT size)
16452 /********************************************************************\
16453 
16454   Routine: eb_create_buffer
16455 
16456   Purpose: Create an event buffer. Has to be called initially before
16457            any other eb_xxx function
16458 
16459   Input:
16460     INT    size             Size in bytes
16461 
16462   Output:
16463     none
16464 
16465   Function value:
16466     CM_SUCCESS              Successful completion
16467     BM_NO_MEMEORY           Out of memory
16468 
16469 \********************************************************************/
16470 {
16471    _event_ring_buffer = (char *) M_MALLOC(size);
16472    if (_event_ring_buffer == NULL)
16473       return BM_NO_MEMORY;
16474 
16475    memset(_event_ring_buffer, 0, size);
16476    _eb_size = size;
16477 
16478    _eb_write_pointer = _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16479 
16480    _send_sock = rpc_get_event_sock();
16481 
16482    return CM_SUCCESS;
16483 }
16484 
16485 /********************************************************************/
16486 INT eb_free_buffer()
16487 /********************************************************************\
16488 
16489   Routine: eb_free_buffer
16490 
16491   Purpose: Free memory allocated voa eb_create_buffer
16492 
16493   Input:
16494     none
16495 
16496   Output:
16497     none
16498 
16499   Function value:
16500     CM_SUCCESS              Successful completion
16501 
16502 \********************************************************************/
16503 {
16504    if (_event_ring_buffer)
16505       M_FREE(_event_ring_buffer);
16506 
16507    _eb_size = 0;
16508    return CM_SUCCESS;
16509 }
16510 
16511 
16512 /********************************************************************/
16513 INT eb_free_space(void)
16514 /********************************************************************\
16515 
16516   Routine: eb_free_space
16517 
16518   Purpose: Compute and return usable free space in the event buffer
16519 
16520   Input:
16521     none
16522 
16523   Output:
16524     none
16525 
16526   Function value:
16527     INT    Number of usable free bytes in the event buffer
16528 
16529 \********************************************************************/
16530 {
16531    INT free;
16532 
16533    if (_event_ring_buffer == NULL) {
16534       cm_msg(MERROR, "eb_get_pointer", "please call eb_create_buffer first");
16535       return -1;
16536    }
16537 
16538    if (_eb_write_pointer >= _eb_read_pointer) {
16539       free = _eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer);
16540    } else if (_eb_write_pointer >= _event_ring_buffer) {
16541       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16542    } else if (_eb_end_pointer == _event_ring_buffer) {
16543       _eb_write_pointer = _event_ring_buffer;
16544       free = _eb_size;
16545    } else if (_eb_read_pointer == _event_ring_buffer) {
16546       free = 0;
16547    } else {
16548       _eb_write_pointer = _event_ring_buffer;
16549       free = (PTYPE) _eb_read_pointer - (PTYPE) _eb_write_pointer;
16550    }
16551 
16552    return free;
16553 }
16554 
16555 
16556 /********************************************************************/
16557 DWORD eb_get_level()
16558 /********************************************************************\
16559 
16560   Routine: eb_get_level
16561 
16562   Purpose: Return filling level of event buffer in percent
16563 
16564   Input:
16565     none
16566 
16567   Output:
16568     none
16569 
16570   Function value:
16571     DWORD level              0..99
16572 
16573 \********************************************************************/
16574 {
16575    INT size;
16576 
16577    size = _eb_size - eb_free_space();
16578 
16579    return (100 * size) / _eb_size;
16580 }
16581 
16582 
16583 /********************************************************************/
16584 BOOL eb_buffer_full(void)
16585 /********************************************************************\
16586 
16587   Routine: eb_buffer_full
16588 
16589   Purpose: Test if there is sufficient space in the event buffer
16590     for another event
16591 
16592   Input:
16593     none
16594 
16595   Output:
16596     none
16597 
16598   Function value:
16599     BOOL  Is there enough space for another event in the event buffer
16600 
16601 \********************************************************************/
16602 {
16603    INT free;
16604 
16605    free = eb_free_space();
16606 
16607    /* if max. event won't fit, return zero */
16608    return (free < MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT));
16609 }
16610 
16611 
16612 /********************************************************************/
16613 EVENT_HEADER *eb_get_pointer()
16614 /********************************************************************\
16615 
16616   Routine: eb_get_pointer
16617 
16618   Purpose: Get pointer to next free location in event buffer
16619 
16620   Input:
16621     none
16622 
16623   Output:
16624     none
16625 
16626   Function value:
16627     EVENT_HEADER *            Pointer to free location
16628 
16629 \********************************************************************/
16630 {
16631    /* if max. event won't fit, return zero */
16632    if (eb_buffer_full()) {
16633 #ifdef OS_VXWORKS
16634       logMsg("eb_get_pointer(): Event won't fit: read=%d, write=%d, end=%d\n",
16635              _eb_read_pointer - _event_ring_buffer,
16636              _eb_write_pointer - _event_ring_buffer,
16637              _eb_end_pointer - _event_ring_buffer, 0, 0, 0);
16638 #endif
16639       return NULL;
16640    }
16641 
16642    /* leave space for buffer handle */
16643    return (EVENT_HEADER *) (_eb_write_pointer + sizeof(INT));
16644 }
16645 
16646 
16647 /********************************************************************/
16648 INT eb_increment_pointer(INT buffer_handle, INT event_size)
16649 /********************************************************************\
16650 
16651   Routine: eb_increment_pointer
16652 
16653   Purpose: Increment write pointer of event buffer after an event
16654            has been copied into the buffer (at an address previously
16655            obtained via eb_get_pointer)
16656 
16657   Input:
16658     INT buffer_handle         Buffer handle event should be sent to
16659     INT event_size            Event size in bytes including header
16660 
16661   Output:
16662     none
16663 
16664   Function value:
16665     CM_SUCCESS                Successful completion
16666 
16667 \********************************************************************/
16668 {
16669    INT aligned_event_size;
16670 
16671    /* if not connected remotely, use bm_send_event */
16672    if (_send_sock == 0)
16673       return bm_send_event(buffer_handle,
16674                            _eb_write_pointer + sizeof(INT), event_size, SYNC);
16675 
16676    aligned_event_size = ALIGN8(event_size);
16677 
16678    /* copy buffer handle */
16679    *((INT *) _eb_write_pointer) = buffer_handle;
16680    _eb_write_pointer += sizeof(INT) + aligned_event_size;
16681 
16682    if (_eb_write_pointer > _eb_end_pointer)
16683       _eb_end_pointer = _eb_write_pointer;
16684 
16685    if (_eb_write_pointer > _event_ring_buffer + _eb_size)
16686       cm_msg(MERROR, "eb_increment_pointer",
16687              "event size (%d) exeeds maximum event size (%d)", event_size,
16688              MAX_EVENT_SIZE);
16689 
16690    if (_eb_size - ((PTYPE) _eb_write_pointer - (PTYPE) _event_ring_buffer) <
16691        MAX_EVENT_SIZE + sizeof(EVENT_HEADER) + sizeof(INT)) {
16692       _eb_write_pointer = _event_ring_buffer;
16693 
16694       /* avoid rp==wp */
16695       if (_eb_read_pointer == _event_ring_buffer)
16696          _eb_write_pointer--;
16697    }
16698 
16699    return CM_SUCCESS;
16700 }
16701 
16702 
16703 /********************************************************************/
16704 INT eb_send_events(BOOL send_all)
16705 /********************************************************************\
16706 
16707   Routine: eb_send_events
16708 
16709   Purpose: Send events from the event buffer to the server
16710 
16711   Input:
16712     BOOL send_all             If FALSE, only send events if buffer
16713                               contains more than _opt_tcp_size bytes
16714 
16715   Output:
16716     none
16717 
16718   Function value:
16719     CM_SUCCESS                Successful completion
16720 
16721 \********************************************************************/
16722 {
16723    char *eb_wp, *eb_ep;
16724    INT size, i;
16725 
16726    /* write pointers are volatile, so make copy */
16727    eb_ep = _eb_end_pointer;
16728    eb_wp = _eb_write_pointer;
16729 
16730    if (eb_wp == _eb_read_pointer)
16731       return CM_SUCCESS;
16732    if (eb_wp > _eb_read_pointer) {
16733       size = (PTYPE) eb_wp - (PTYPE) _eb_read_pointer;
16734 
16735       /* don't send if less than optimal TCP buffer size available */
16736       if (size < (INT) _opt_tcp_size && !send_all)
16737          return CM_SUCCESS;
16738    } else {
16739       /* send last piece of event buffer */
16740       size = (PTYPE) eb_ep - (PTYPE) _eb_read_pointer;
16741    }
16742 
16743    while (size > _opt_tcp_size) {
16744       /* send buffer */
16745       i = send_tcp(_send_sock, _eb_read_pointer, _opt_tcp_size, 0);
16746       if (i < 0) {
16747          printf("send_tcp() returned %d\n", i);
16748          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16749          return RPC_NET_ERROR;
16750       }
16751 
16752       _eb_read_pointer += _opt_tcp_size;
16753       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16754          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16755 
16756       size -= _opt_tcp_size;
16757    }
16758 
16759    if (send_all || eb_wp < _eb_read_pointer) {
16760       /* send buffer */
16761       i = send_tcp(_send_sock, _eb_read_pointer, size, 0);
16762       if (i < 0) {
16763          printf("send_tcp() returned %d\n", i);
16764          cm_msg(MERROR, "eb_send_events", "send_tcp() failed");
16765          return RPC_NET_ERROR;
16766       }
16767 
16768       _eb_read_pointer += size;
16769       if (_eb_read_pointer == eb_ep && eb_wp < eb_ep)
16770          _eb_read_pointer = _eb_end_pointer = _event_ring_buffer;
16771    }
16772 
16773    /* Check for case where eb_wp = eb_ring_buffer - 1 */
16774    if (eb_wp < _event_ring_buffer && _eb_end_pointer == _event_ring_buffer) {
16775       return CM_SUCCESS;
16776    }
16777 
16778    if (eb_wp != _eb_read_pointer)
16779       return BM_MORE_EVENTS;
16780 
16781    return CM_SUCCESS;
16782 }
16783 
16784 #endif                          /* OS_VXWORKS  eb section */
16785 
16786 /**dox***************************************************************/
16787 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16788 
16789 /**dox***************************************************************/
16790 /** @addtogroup dmfunctionc
16791  *  
16792  *  @{  */
16793 
16794 /**dox***************************************************************/
16795 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16796 
16797 /********************************************************************\
16798 *                                                                    *
16799 *                 Dual memory buffer functions                       *
16800 *                                                                    *
16801 * Provide a dual memory buffer scheme for handling front-end         *
16802 * event. This code as been requested for allowing contemporary       *
16803 * task handling a)acquisition, b)network transfer if possible.       *
16804 * The pre-compiler switch will determine the mode of operation.      *
16805 * if DM_DUAL_THREAD is defined in mfe.c, it is expected to have      *
16806 * a seperate task taking care of the dm_area_send                    *
16807 *                                                                    *
16808 * "*" : visible functions                                            *
16809 * dm_buffer_create():     *Setup the dual memory buffer              *
16810 *                          Setup semaphore                           *
16811 *                          Spawn second thread                       *
16812 * dm_buffer_release():    *Release memory allocation for dm          *
16813 *                          Force a kill of 2nd thread                *
16814 *                          Remove semaphore                          *
16815 * dm_area_full():         *Check for both area being full            *
16816 *                          None blocking, may be used for interrupt  *
16817 *                          disable.                                  *
16818 * dm_pointer_get()     :  *Check memory space and return pointer     *
16819 *                          Blocking function with timeout if no more *
16820 *                          space for next event. If error will abort.*
16821 * dm_pointer_increment(): *Move pointer to next free location        *
16822 *                          None blocking. performs bm_send_event if  *
16823 *                          local connection.                         *
16824 * dm_area_send():         *Transfer FULL buffer(s)                   *
16825 *                          None blocking function.                   *
16826 *                          if DUAL_THREAD: Give sem_send semaphore   *
16827 *                          else transfer FULL buffer                 *
16828 * dm_area_flush():        *Transfer all remaining events from dm     *
16829 *                          Blocking function with timeout            *
16830 *                          if DUAL_THREAD: Give sem_flush semaphore. *
16831 * dm_task():               Secondary thread handling DUAL_THREAD     *
16832 *                          mechanism. Serves 2 requests:             *
16833 *                          dm_send:  Transfer FULL buffer only.      *
16834 *                          dm_flush: Transfer ALL buffers.           *
16835 * dm_area_switch():        internal, used by dm_pointer_get()        *
16836 * dm_active_full():        internal: check space in current buffer   *
16837 * dm_buffer_send():        internal: send data for given area        *
16838 * dm_buffer_time_get():    interal: return the time stamp of the     *
16839 *                          last switch                               *
16840 \********************************************************************/
16841 
16842 #define DM_FLUSH       10       /* flush request for DUAL_THREAD */
16843 #define DM_SEND        11       /* FULL send request for DUAL_THREAD */
16844 #define DM_KILL        12       /* Kill request for 2nd thread */
16845 #define DM_TIMEOUT     13       /* "timeout" return state in flush request for DUAL_THREAD */
16846 #define DM_ACTIVE_NULL 14       /* "both buffer were/are FULL with no valid area" return state */
16847 
16848 typedef struct {
16849    char *pt;                    /* top pointer    memory buffer          */
16850    char *pw;                    /* write pointer  memory buffer          */
16851    char *pe;                    /* end   pointer  memory buffer          */
16852    char *pb;                    /* bottom pointer memory buffer          */
16853    BOOL full;                   /* TRUE if memory buffer is full         */
16854    DWORD serial;                /* full buffer serial# for evt order     */
16855 } DMEM_AREA;
16856 
16857 typedef struct {
16858    DMEM_AREA *pa;               /* active memory buffer */
16859    DMEM_AREA area1;             /* mem buffer area 1 */
16860    DMEM_AREA area2;             /* mem buffer area 2 */
16861    DWORD serial;                /* overall buffer serial# for evt order     */
16862    INT action;                  /* for multi thread configuration */
16863    DWORD last_active;           /* switch time stamp */
16864    HNDLE sem_send;              /* semaphore for dm_task */
16865    HNDLE sem_flush;             /* semaphore for dm_task */
16866 } DMEM_BUFFER;
16867 
16868 DMEM_BUFFER dm;
16869 INT dm_user_max_event_size;
16870 
16871 /**dox***************************************************************/
16872 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
16873 
16874 /********************************************************************/
16875 /**
16876 Setup a dual memory buffer. Has to be called initially before
16877            any other dm_xxx function
16878 @param size             Size in bytes
16879 @param user_max_event_size max event size
16880 @return CM_SUCCESS, BM_NO_MEMORY, BM_MEMSIZE_MISMATCH
16881 */
16882 INT dm_buffer_create(INT size, INT user_max_event_size)
16883 {
16884 
16885    dm.area1.pt = (char *) M_MALLOC(size);
16886    if (dm.area1.pt == NULL)
16887       return (BM_NO_MEMORY);
16888    dm.area2.pt = (char *) M_MALLOC(size);
16889    if (dm.area2.pt == NULL)
16890       return (BM_NO_MEMORY);
16891 
16892    /* check user event size against the system MAX_EVENT_SIZE */
16893    if (user_max_event_size > MAX_EVENT_SIZE) {
16894       cm_msg(MERROR, "dm_buffer_create", "user max event size too large");
16895       return BM_MEMSIZE_MISMATCH;
16896    }
16897    dm_user_max_event_size = user_max_event_size;
16898 
16899    memset(dm.area1.pt, 0, size);
16900    memset(dm.area2.pt, 0, size);
16901 
16902    /* initialize pointers */
16903    dm.area1.pb = dm.area1.pt + size - 1024;
16904    dm.area1.pw = dm.area1.pe = dm.area1.pt;
16905    dm.area2.pb = dm.area2.pt + size - 1024;
16906    dm.area2.pw = dm.area2.pe = dm.area2.pt;
16907 
16908   /*-PAA-*/
16909 #ifdef DM_DEBUG
16910    printf(" in dm_buffer_create ---------------------------------\n");
16911    printf(" %i %p %p %p %p\n", size, dm.area1.pt, dm.area1.pw, dm.area1.pe, dm.area1.pb);
16912    printf(" %i %p %p %p %p\n", size, dm.area2.pt, dm.area2.pw, dm.area2.pe, dm.area2.pb);
16913 #endif
16914 
16915    /* activate first area */
16916    dm.pa = &dm.area1;
16917 
16918    /* Default not full */
16919    dm.area1.full = dm.area2.full = FALSE;
16920 
16921    /* Reset serial buffer number with proper starting sequence */
16922    dm.area1.serial = dm.area2.serial = 0;
16923    /* ensure proper serial on next increment */
16924    dm.serial = 1;
16925 
16926    /* set active buffer time stamp */
16927    dm.last_active = ss_millitime();
16928 
16929    /* get socket for event sending */
16930    _send_sock = rpc_get_event_sock();
16931 
16932 #ifdef DM_DUAL_THREAD
16933    {
16934       INT status;
16935       VX_TASK_SPAWN starg;
16936 
16937       /* create semaphore */
16938       status = ss_mutex_create("send", &dm.sem_send);
16939       if (status != SS_CREATED && status != SS_SUCCESS) {
16940          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create send");
16941          return status;
16942       }
16943       status = ss_mutex_create("flush", &dm.sem_flush);
16944       if (status != SS_CREATED && status != SS_SUCCESS) {
16945          cm_msg(MERROR, "dm_buffer_create", "error in ss_mutex_create flush");
16946          return status;
16947       }
16948       /* spawn dm_task */
16949       memset(&starg, 0, sizeof(VX_TASK_SPAWN));
16950 
16951 #ifdef OS_VXWORKS
16952       /* Fill up the necessary arguments */
16953       strcpy(starg.name, "areaSend");
16954       starg.priority = 120;
16955       starg.stackSize = 20000;
16956 #endif
16957 
16958       if ((status = ss_thread_create(dm_task, (void *) &starg))
16959           != SS_SUCCESS) {
16960          cm_msg(MERROR, "dm_buffer_create", "error in ss_thread_create");
16961          return status;
16962       }
16963 #ifdef OS_WINNT
16964       /* necessary for true MUTEX (NT) */
16965       ss_mutex_wait_for(dm.sem_send, 0);
16966 #endif
16967    }
16968 #endif                          /* DM_DUAL_THREAD */
16969 
16970    return CM_SUCCESS;
16971 }
16972 
16973 /**dox***************************************************************/
16974 #ifndef DOXYGEN_SHOULD_SKIP_THIS
16975 
16976 /********************************************************************/
16977 INT dm_buffer_release(void)
16978 /********************************************************************\
16979   Routine: dm_buffer_release
16980 
16981   Purpose: Release dual memory buffers
16982   Input:
16983     none
16984   Output:
16985     none
16986   Function value:
16987     CM_SUCCESS              Successful completion
16988 \********************************************************************/
16989 {
16990    if (dm.area1.pt) {
16991       free(dm.area1.pt);
16992       dm.area1.pt = NULL;
16993    }
16994    if (dm.area2.pt) {
16995       free(dm.area2.pt);
16996       dm.area2.pt = NULL;
16997    }
16998    dm.serial = 0;
16999    dm.area1.full = dm.area2.full = TRUE;
17000    dm.area1.serial = dm.area2.serial = 0;
17001 
17002 #ifdef DM_DUAL_THREAD
17003    /* kill spawned dm_task */
17004    dm.action = DM_KILL;
17005    ss_mutex_release(dm.sem_send);
17006    ss_mutex_release(dm.sem_flush);
17007 
17008    /* release semaphore */
17009    ss_mutex_delete(dm.sem_send, 0);
17010    ss_mutex_delete(dm.sem_flush, 0);
17011 #endif
17012 
17013    return CM_SUCCESS;
17014 }
17015 
17016 /********************************************************************/
17017 INLINE DMEM_AREA *dm_area_switch(void)
17018 /********************************************************************\
17019   Routine: dm_area_switch
17020 
17021   Purpose: set active area to the other empty area or NULL if both
17022            area are full. May have to check the serial consistancy...
17023   Input:
17024     none
17025   Output:
17026     none
17027   Function value:
17028     DMEM_AREA *            Pointer to active area or both full
17029 \********************************************************************/
17030 {
17031    volatile BOOL full1, full2;
17032 
17033    full1 = dm.area1.full;
17034    full2 = dm.area2.full;
17035 
17036    if (!full1 && !full2) {
17037       if (dm.area1.serial <= dm.area2.serial)
17038          return (&(dm.area1));
17039       else
17040          return (&(dm.area2));
17041    }
17042 
17043    if (!full1) {
17044       return (&(dm.area1));
17045    } else if (!full2) {
17046       return (&(dm.area2));
17047    }
17048    return (NULL);
17049 }
17050 
17051 /********************************************************************/
17052 INLINE BOOL dm_area_full(void)
17053 /********************************************************************\
17054   Routine: dm_area_full
17055 
17056   Purpose: Test if both area are full in order to block interrupt
17057   Input:
17058     none
17059   Output:
17060     none
17061   Function value:
17062     BOOL         TRUE if not enough space for another event
17063 \********************************************************************/
17064 {
17065    if (dm.pa == NULL || (dm.area1.full && dm.area2.full))
17066       return TRUE;
17067    return FALSE;
17068 }
17069 
17070 /********************************************************************/
17071 INLINE BOOL dm_active_full(void)
17072 /********************************************************************\
17073   Routine: dm_active_full
17074 
17075   Purpose: Test if there is sufficient space in either event buffer
17076            for another event.
17077   Input:
17078     none
17079   Output:
17080     none
17081   Function value:
17082     BOOL         TRUE if not enough space for another event
17083 \********************************************************************/
17084 {
17085    /* catch both full areas, waiting for transfer */
17086    if (dm.pa == NULL)
17087       return TRUE;
17088    /* Check the space in the active buffer only
17089       as I don't switch buffer here */
17090    if (dm.pa->full)
17091       return TRUE;
17092    return (((PTYPE) dm.pa->pb - (PTYPE) dm.pa->pw) < (INT)
17093            (dm_user_max_event_size + sizeof(EVENT_HEADER) + sizeof(INT)));
17094 }
17095 
17096 /********************************************************************/
17097 DWORD dm_buffer_time_get(void)
17098 /********************************************************************\
17099   Routine: dm_buffer_time_get
17100 
17101   Purpose: return the time from the last buffer switch.
17102 
17103   Input:
17104     none
17105   Output:
17106     none
17107   Function value:
17108     DWORD        time stamp
17109 
17110 \********************************************************************/
17111 {
17112    return (dm.last_active);
17113 }
17114 
17115 
17116 /********************************************************************/
17117 EVENT_HEADER *dm_pointer_get(void)
17118 /********************************************************************\
17119   Routine: dm_pointer_get
17120 
17121   Purpose: Get pointer to next free location in event buffer.
17122            after 10sec tries, it times out return NULL indicating a
17123            serious problem, i.e. abort.
17124   REMARK : Cannot be called twice in a raw due to +sizeof(INT)
17125   Input:
17126     none
17127   Output:
17128     DM_BUFFER * dm    local valid dm to work on
17129   Function value:
17130     EVENT_HEADER *    Pointer to free location
17131     NULL              cannot after several attempt get free space => abort
17132 \********************************************************************/
17133 {
17134    int timeout, status;
17135 
17136    /* Is there still space in the active area ? */
17137    if (!dm_active_full())
17138       return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17139 
17140    /* no more space => switch area */
17141 
17142    /* Tag current area with global dm.serial for order consistency */
17143    dm.pa->serial = dm.serial++;
17144 
17145    /* set active buffer time stamp */
17146    dm.last_active = ss_millitime();
17147 
17148    /* mark current area full */
17149    dm.pa->full = TRUE;
17150 
17151    /* Trigger/do data transfer (Now/don't wait) */
17152    if ((status = dm_area_send()) == RPC_NET_ERROR) {
17153       cm_msg(MERROR, "dm_pointer_get()", "Net error or timeout %i", status);
17154       return NULL;
17155    }
17156 
17157    /* wait switch completion (max 10 sec) */
17158    timeout = ss_millitime();    /* with timeout */
17159    while ((ss_millitime() - timeout) < 10000) {
17160       dm.pa = dm_area_switch();
17161       if (dm.pa != NULL)
17162          return (EVENT_HEADER *) (dm.pa->pw + sizeof(INT));
17163       ss_sleep(200);
17164 #ifdef DM_DEBUG
17165       printf(" waiting for space ... %i  dm_buffer  %i %i %i %i %i \n",
17166              ss_millitime() - timeout, dm.area1.full, dm.area2.full, dm.area1.serial,
17167              dm.area2.serial, dm.serial);
17168 #endif
17169    }
17170 
17171    /* Time running out abort */
17172    cm_msg(MERROR, "dm_pointer_get", "Timeout due to buffer full");
17173    return NULL;
17174 }
17175 
17176 
17177 /********************************************************************/
17178 int dm_pointer_increment(INT buffer_handle, INT event_size)
17179 /********************************************************************\
17180   Routine: dm_pointer_increment
17181 
17182   Purpose: Increment write pointer of event buffer after an event
17183            has been copied into the buffer (at an address previously
17184            obtained via dm_pointer_get)
17185   Input:
17186     INT buffer_handle         Buffer handle event should be sent to
17187     INT event_size            Event size in bytes including header
17188   Output:
17189     none
17190   Function value:
17191     CM_SUCCESS                Successful completion
17192     status                    from bm_send_event for local connection
17193 \********************************************************************/
17194 {
17195    INT aligned_event_size;
17196 
17197    /* if not connected remotely, use bm_send_event */
17198    if (_send_sock == 0) {
17199       *((INT *) dm.pa->pw) = buffer_handle;
17200       return bm_send_event(buffer_handle, dm.pa->pw + sizeof(INT), event_size, SYNC);
17201    }
17202    aligned_event_size = ALIGN8(event_size);
17203 
17204    *((INT *) dm.pa->pw) = buffer_handle;
17205 
17206    /* adjust write pointer */
17207    dm.pa->pw += sizeof(INT) + aligned_event_size;
17208 
17209    /* adjust end pointer */
17210    dm.pa->pe = dm.pa->pw;
17211 
17212    return CM_SUCCESS;
17213 }
17214 
17215 /********************************************************************/
17216 INLINE INT dm_buffer_send(DMEM_AREA * larea)
17217 /********************************************************************\
17218   Routine: dm_buffer_send
17219 
17220   Purpose: Ship data to the cache in fact!
17221            Basically the same do loop is done in the send_tcp.
17222            but _opt_tcp_size preveal if <= NET_TCP_SIZE.
17223            Introduced for bringing tcp option to user code.
17224   Input:
17225     DMEM_AREA * larea   The area to work with.
17226   Output:
17227     none
17228   Function value:
17229     CM_SUCCESS       Successful completion
17230     DM_ACTIVE_NULL   Both area were/are full
17231     RPC_NET_ERROR    send error
17232 \********************************************************************/
17233 {
17234    INT tot_size, nwrite;
17235    char *lpt;
17236 
17237    /* if not connected remotely, use bm_send_event */
17238    if (_send_sock == 0)
17239       return bm_flush_cache(*((INT *) dm.pa->pw), ASYNC);
17240 
17241    /* alias */
17242    lpt = larea->pt;
17243 
17244    /* Get overall buffer size */
17245    tot_size = (PTYPE) larea->pe - (PTYPE) lpt;
17246 
17247    /* shortcut for speed */
17248    if (tot_size == 0)
17249       return CM_SUCCESS;
17250 
17251 #ifdef DM_DEBUG
17252    printf("lpt:%p size:%i ", lpt, tot_size);
17253 #endif
17254    nwrite = send_tcp(_send_sock, lpt, tot_size, 0);
17255 #ifdef DM_DEBUG
17256    printf("nwrite:%i  errno:%i\n", nwrite, errno);
17257 #endif
17258    if (nwrite < 0)
17259       return RPC_NET_ERROR;
17260 
17261    /* reset area */
17262    larea->pw = larea->pe = larea->pt;
17263    larea->full = FALSE;
17264    return CM_SUCCESS;
17265 }
17266 
17267 /********************************************************************/
17268 INT dm_area_send(void)
17269 /********************************************************************\
17270   Routine: dm_area_send
17271 
17272   Purpose: Empty the FULL area only in proper event order
17273            Meant to be use either in mfe.c scheduler on every event
17274 
17275   Dual memory scheme:
17276    DM_DUAL_THREAD : Trigger sem_send
17277    !DM_DUAL_THREAD: empty full buffer in order, return active to area1
17278                     if dm.pa is NULL (were both full) and now both are empty
17279 
17280   Input:
17281     none
17282   Output:
17283     none
17284   Function value:
17285     CM_SUCCESS                Successful completion
17286     RPC_NET_ERROR             send error
17287 \********************************************************************/
17288 {
17289 #ifdef DM_DUAL_THREAD
17290    INT status;
17291 
17292    /* force a DM_SEND if possible. Don't wait for completion */
17293    dm.action = DM_SEND;
17294    ss_mutex_release(dm.sem_send);
17295 #ifdef OS_WINNT
17296    /* necessary for true MUTEX (NT) */
17297    status = ss_mutex_wait_for(dm.sem_send, 1);
17298    if (status == SS_NO_MUTEX) {
17299       printf(" timeout while waiting for sem_send\n");
17300       return RPC_NET_ERROR;
17301    }
17302 #endif
17303 
17304    return CM_SUCCESS;
17305 #else
17306    /* ---------- NOT IN DUAL THREAD ----------- */
17307    INT status = 0;
17308 
17309    /* if no DUAL thread everything is local then */
17310    /* select the full area */
17311    if (dm.area1.full && dm.area2.full)
17312       if (dm.area1.serial <= dm.area2.serial)
17313          status = dm_buffer_send(&dm.area1);
17314       else
17315          status = dm_buffer_send(&dm.area2);
17316    else if (dm.area1.full)
17317       status = dm_buffer_send(&dm.area1);
17318    else if (dm.area2.full)
17319       status = dm_buffer_send(&dm.area2);
17320    if (status != CM_SUCCESS)
17321       return status;            /* catch transfer error too */
17322 
17323    if (dm.pa == NULL) {
17324       printf(" sync send dm.pa:%p full 1%li 2%li\n", dm.pa, dm.area1.full, dm.area2.full);
17325       dm.pa = &dm.area1;
17326    }
17327    return CM_SUCCESS;
17328 #endif
17329 }
17330 
17331 /********************************************************************/
17332 INT dm_task(void *pointer)
17333 /********************************************************************\
17334   Routine: dm_task
17335 
17336   Purpose: async send events doing a double purpose:
17337   a) send full buffer if found (DM_SEND) set by dm_active_full
17338   b) flush full areas (DM_FLUSH) set by dm_area_flush
17339   Input:
17340   none
17341   Output:
17342   none
17343   Function value:
17344   none
17345   \********************************************************************/
17346 {
17347 #ifdef DM_DUAL_THREAD
17348    INT status, timeout;
17349 
17350    printf("Semaphores initialization ... in areaSend ");
17351    /* Check or Wait for semaphore to be setup */
17352    timeout = ss_millitime();
17353    while ((ss_millitime() - timeout < 3000) && (dm.sem_send == 0))
17354       ss_sleep(200);
17355    if (dm.sem_send == 0)
17356       goto kill;
17357 
17358 #ifdef OS_WINNT
17359    /* necessary for true MUTEX (NT) get semaphore */
17360    ss_mutex_wait_for(dm.sem_flush, 0);
17361 #endif
17362 
17363    /* Main FOREVER LOOP */
17364    printf("task areaSend ready...\n");
17365    while (1) {
17366       if (!dm_area_full()) {
17367          /* wait semaphore here ........ 0 == forever */
17368          ss_mutex_wait_for(dm.sem_send, 0);
17369 #ifdef OS_WINNT
17370          /* necessary for true MUTEX (NT) give semaphore */
17371          ss_mutex_release(dm.sem_send);
17372 #endif
17373       }
17374       if (dm.action == DM_SEND) {
17375 #ifdef DM_DEBUG
17376          printf("Send %i %i ", dm.area1.full, dm.area2.full);
17377 #endif
17378          /* DM_SEND : Empty the oldest buffer only. */
17379          if (dm.area1.full && dm.area2.full) {
17380             if (dm.area1.serial <= dm.area2.serial)
17381                status = dm_buffer_send(&dm.area1);
17382             else
17383                status = dm_buffer_send(&dm.area2);
17384          } else if (dm.area1.full)
17385             status = dm_buffer_send(&dm.area1);
17386          else if (dm.area2.full)
17387             status = dm_buffer_send(&dm.area2);
17388 
17389          if (status != CM_SUCCESS) {
17390             cm_msg(MERROR, "dm_task", "network error %i", status);
17391             goto kill;
17392          }
17393       } /* if DM_SEND */
17394       else if (dm.action == DM_FLUSH) {
17395          /* DM_FLUSH: User is waiting for completion (i.e. No more incomming
17396             events) Empty both area in order independently of being full or not */
17397          if (dm.area1.serial <= dm.area2.serial) {
17398             status = dm_buffer_send(&dm.area1);
17399             if (status != CM_SUCCESS)
17400                goto error;
17401             status = dm_buffer_send(&dm.area2);
17402             if (status != CM_SUCCESS)
17403                goto error;
17404          } else {
17405             status = dm_buffer_send(&dm.area2);
17406             if (status != CM_SUCCESS)
17407                goto error;
17408             status = dm_buffer_send(&dm.area1);
17409             if (status != CM_SUCCESS)
17410                goto error;
17411          }
17412          /* reset counter */
17413          dm.area1.serial = 0;
17414          dm.area2.serial = dm.serial = 1;
17415 #ifdef DM_DEBUG
17416          printf("dm.action: Flushing ...\n");
17417 #endif
17418          /* reset area to #1 */
17419          dm.pa = &dm.area1;
17420 
17421          /* release user */
17422          ss_mutex_release(dm.sem_flush);
17423 #ifdef OS_WINNT
17424          /* necessary for true MUTEX (NT) get semaphore back */
17425          ss_mutex_wait_for(dm.sem_flush, 0);
17426 #endif
17427       }
17428       /* if FLUSH */
17429       if (dm.action == DM_KILL)
17430          goto kill;
17431 
17432    }                            /* FOREVER (go back wainting for semaphore) */
17433 
17434    /* kill spawn now */
17435  error:
17436    cm_msg(MERROR, "dm_area_flush", "aSync Net error");
17437  kill:
17438    ss_mutex_release(dm.sem_flush);
17439 #ifdef OS_WINNT
17440    ss_mutex_wait_for(dm.sem_flush, 1);
17441 #endif
17442    cm_msg(MERROR, "areaSend", "task areaSend exiting now");
17443    exit;
17444    return 1;
17445 #else
17446    printf("DM_DUAL_THREAD not defined\n");
17447    return 0;
17448 #endif
17449 }
17450 
17451 /********************************************************************/
17452 INT dm_area_flush(void)
17453 /********************************************************************\
17454   Routine: dm_area_flush
17455 
17456   Purpose: Flush all the events in the areas.
17457            Used in mfe for BOR events, periodic events and
17458            if rate to low in main loop once a second. The standard
17459            data transfer should be done/triggered by dm_area_send (sync/async)
17460            in dm_pointer_get().
17461   Input:
17462     none
17463   Output:
17464     none
17465   Function value:
17466     CM_SUCCESS       Successful completion
17467     RPC_NET_ERROR    send error
17468 \********************************************************************/
17469 {
17470    INT status;
17471 #ifdef DM_DUAL_THREAD
17472    /* request FULL flush */
17473    dm.action = DM_FLUSH;
17474    ss_mutex_release(dm.sem_send);
17475 #ifdef OS_WINNT
17476    /* necessary for true MUTEX (NT) get semaphore back */
17477    ss_mutex_wait_for(dm.sem_send, 0);
17478 #endif
17479 
17480    /* important to wait for completion before continue with timeout
17481       timeout specified milliseconds */
17482    status = ss_mutex_wait_for(dm.sem_flush, 10000);
17483 #ifdef DM_DEBUG
17484    printf("dm_area_flush after waiting %i\n", status);
17485 #endif
17486 #ifdef OS_WINNT
17487    ss_mutex_release(dm.sem_flush);      /* give it back now */
17488 #endif
17489 
17490    return status;
17491 #else
17492    /* full flush done here */
17493    /* select in order both area independently of being full or not */
17494    if (dm.area1.serial <= dm.area2.serial) {
17495       status = dm_buffer_send(&dm.area1);
17496       if (status != CM_SUCCESS)
17497          return status;
17498       status = dm_buffer_send(&dm.area2);
17499       if (status != CM_SUCCESS)
17500          return status;
17501    } else {
17502       status = dm_buffer_send(&dm.area2);
17503       if (status != CM_SUCCESS)
17504          return status;
17505       status = dm_buffer_send(&dm.area1);
17506       if (status != CM_SUCCESS)
17507          return status;
17508    }
17509    /* reset serial counter */
17510    dm.area1.serial = dm.area2.serial = 0;
17511    dm.last_active = ss_millitime();
17512    return CM_SUCCESS;
17513 #endif
17514 }
17515 
17516 /********************************************************************/
17517 
17518 /**dox***************************************************************/
17519 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
17520 
17521 /**dox***************************************************************/
17522 /** @} */ /* end of dmfunctionc */
17523 
17524 /**dox***************************************************************/
17525 /** @} */ /* end of midasincludecode */

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