Line data Source code
1 : /********************************************************************\
2 :
3 : Name: MIDAS.C
4 : Created by: Stefan Ritt
5 :
6 : Contents: MIDAS main library funcitons
7 :
8 : $Id$
9 :
10 : \********************************************************************/
11 :
12 : #undef NDEBUG // midas required assert() to be always enabled
13 :
14 : #include "midas.h"
15 : #include "msystem.h"
16 : #include "git-revision.h"
17 : #include "mstrlcpy.h"
18 : #include "odbxx.h"
19 :
20 : #include <assert.h>
21 : #include <signal.h>
22 : #include <sys/resource.h>
23 :
24 : #include <mutex>
25 : #include <deque>
26 : #include <thread>
27 : #include <atomic>
28 : #include <algorithm>
29 :
30 : /**dox***************************************************************/
31 : /** @file midas.c
32 : The main core C-code for Midas.
33 : */
34 :
35 : /**
36 : \mainpage MIDAS code documentation
37 :
38 : Welcome to the doxygen-generated documentation for the MIDAS source code.
39 :
40 : This documentation is intended to be used as reference by MIDAS developers
41 : and advanced users.
42 :
43 : Documentation for new users, general information on MIDAS, examples,
44 : user discussion, mailing lists and forums,
45 : can be found through the MIDAS Wiki at http://midas.triumf.ca
46 : */
47 :
48 : /** @defgroup cmfunctionc Common Functions (cm_xxx)
49 : */
50 : /** @defgroup bmfunctionc Event Buffer Functions (bm_xxx)
51 : */
52 : /** @defgroup msgfunctionc Message Functions (msg_xxx)
53 : */
54 : /** @defgroup bkfunctionc Data Bank Functions (bk_xxx)
55 : */
56 : /** @defgroup rpc_xxx RPC Functions (rpc_xxx)
57 : */
58 : /** @defgroup rbfunctionc Ring Buffer Functions (rb_xxx)
59 : */
60 :
61 : /**dox***************************************************************/
62 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
63 :
64 : /********************************************************************/
65 : /* data type sizes */
66 : static const int tid_size[] = {
67 : 0, /* tid == 0 not defined */
68 : 1, /* TID_UINT8 unsigned byte 0 255 */
69 : 1, /* TID_INT8 signed byte -128 127 */
70 : 1, /* TID_CHAR single character 0 255 */
71 : 2, /* TID_UINT16 two bytes 0 65535 */
72 : 2, /* TID_INT16 signed word -32768 32767 */
73 : 4, /* TID_UINT32 four bytes 0 2^32-1 */
74 : 4, /* TID_INT32 signed dword -2^31 2^31-1 */
75 : 4, /* TID_BOOL four bytes bool 0 1 */
76 : 4, /* TID_FLOAT 4 Byte float format */
77 : 8, /* TID_DOUBLE 8 Byte float format */
78 : 4, /* TID_BITFIELD 32 Bits Bitfield 0000... 11111... */
79 : 0, /* TID_STRING zero terminated string */
80 : 0, /* TID_ARRAY variable length array of unkown type */
81 : 0, /* TID_STRUCT C structure */
82 : 0, /* TID_KEY key in online database */
83 : 0, /* TID_LINK link in online database */
84 : 8, /* TID_INT64 8 bytes int -2^63 2^63-1 */
85 : 8 /* TID_UINT64 8 bytes unsigned int 0 2^64-1 */
86 : };
87 :
88 : /* data type names */
89 : static const char *tid_name_old[] = {
90 : "NULL",
91 : "BYTE",
92 : "SBYTE",
93 : "CHAR",
94 : "WORD",
95 : "SHORT",
96 : "DWORD",
97 : "INT",
98 : "BOOL",
99 : "FLOAT",
100 : "DOUBLE",
101 : "BITFIELD",
102 : "STRING",
103 : "ARRAY",
104 : "STRUCT",
105 : "KEY",
106 : "LINK",
107 : "INT64",
108 : "UINT64"
109 : };
110 :
111 : static const char *tid_name[] = {
112 : "NULL",
113 : "UINT8",
114 : "INT8",
115 : "CHAR",
116 : "UINT16",
117 : "INT16",
118 : "UINT32",
119 : "INT32",
120 : "BOOL",
121 : "FLOAT",
122 : "DOUBLE",
123 : "BITFIELD",
124 : "STRING",
125 : "ARRAY",
126 : "STRUCT",
127 : "KEY",
128 : "LINK",
129 : "INT64",
130 : "UINT64"
131 : };
132 :
133 5 : std::string cm_transition_name(int transition)
134 : {
135 7 : if (transition == TR_START) return "START";
136 6 : if (transition == TR_STOP) return "STOP";
137 5 : if (transition == TR_PAUSE) return "PAUSE";
138 4 : if (transition == TR_RESUME) return "RESUME";
139 3 : if (transition == TR_STARTABORT) return "STARTABORT";
140 0 : if (transition == TR_DEFERRED) return "DEFERRED";
141 0 : return msprintf("UNKNOWN TRANSITION %d", transition);
142 : }
143 :
144 : const char *mname[] = {
145 : "January",
146 : "February",
147 : "March",
148 : "April",
149 : "May",
150 : "June",
151 : "July",
152 : "August",
153 : "September",
154 : "October",
155 : "November",
156 : "December"
157 : };
158 :
159 : /* Globals */
160 : #ifdef OS_MSDOS
161 : extern unsigned _stklen = 60000U;
162 : #endif
163 :
164 : extern DATABASE *_database;
165 : extern INT _database_entries;
166 :
167 : //
168 : // locking rules for gBuffers and gBuffersMutex:
169 : //
170 : // - all access to gBuffers must be done while holding gBufferMutex
171 : // - while holding gBufferMutex:
172 : // - taking additional locks not permitted (no calling odb, no locking event buffers, etc)
173 : // - calling functions that can take additional locks not permitted (no calling db_xxx(), bm_xxx(), etc)
174 : // - calling functions that can come back recursively not permitted
175 : //
176 : // after obtaining a BUFFER*pbuf pointer from gBuffers:
177 : //
178 : // - holding gBuffersMutex is not required
179 : // - to access pbuf data, must hold buffer_mutex or call bm_lock_buffer()
180 : // - except for:
181 : // pbuf->attached - no need to hold a lock (std::atomic)
182 : // pbuf->buffer_name - no need to hold a lock (constant data, only changed by bm_open_buffer())
183 : //
184 : // object life time:
185 : //
186 : // - gBuffers never shrinks
187 : // - new BUFFER objects are created by bm_open_buffer(), added to gBuffers when ready for use, pbuf->attached set to true
188 : // - bm_close_buffer() sets pbuf->attached to false
189 : // - BUFFER objects are never deleted to avoid race between delete and bm_send_event() & co
190 : // - BUFFER objects are never reused, bm_open_buffer() always creates a new object
191 : // - gBuffers[i] set to NULL are empty slots available for reuse
192 : // - closed buffers have corresponding gBuffers[i]->attached set to false
193 : //
194 :
195 : static std::mutex gBuffersMutex; // protects gBuffers vector itself, but not it's contents!
196 : static std::vector<BUFFER*> gBuffers;
197 :
198 : static INT _msg_buffer = 0;
199 : static EVENT_HANDLER *_msg_dispatch = NULL;
200 :
201 : /* Event request descriptor */
202 :
203 : struct EventRequest
204 : {
205 : INT buffer_handle = 0; /* Buffer handle */
206 : short int event_id = 0; /* same as in EVENT_HEADER */
207 : short int trigger_mask = 0; /* same as in EVENT_HEADER */
208 : EVENT_HANDLER* dispatcher = NULL; /* Dispatcher func. */
209 :
210 0 : void clear()
211 : {
212 0 : buffer_handle = 0;
213 0 : event_id = 0;
214 0 : trigger_mask = 0;
215 0 : dispatcher = NULL;
216 0 : }
217 : };
218 :
219 : static std::mutex _request_list_mutex;
220 : static std::vector<EventRequest> _request_list;
221 :
222 : //static char *_tcp_buffer = NULL;
223 : //static INT _tcp_wp = 0;
224 : //static INT _tcp_rp = 0;
225 : //static INT _tcp_sock = 0;
226 :
227 : static MUTEX_T *_mutex_rpc = NULL; // mutex to protect RPC calls
228 :
229 : static void (*_debug_print)(const char *) = NULL;
230 :
231 : static INT _debug_mode = 0;
232 :
233 : static int _rpc_connect_timeout = 10000;
234 :
235 : // for use on a single machine it is best to restrict RPC access to localhost
236 : // by binding the RPC listener socket to the localhost IP address.
237 : static int disable_bind_rpc_to_localhost = 0;
238 :
239 : /* table for transition functions */
240 :
241 : struct TRANS_TABLE {
242 : INT transition;
243 : INT sequence_number;
244 : INT (*func)(INT, char *);
245 : };
246 :
247 : static std::mutex _trans_table_mutex;
248 : static std::vector<TRANS_TABLE> _trans_table;
249 :
250 : static TRANS_TABLE _deferred_trans_table[] = {
251 : {TR_START, 0, NULL},
252 : {TR_STOP, 0, NULL},
253 : {TR_PAUSE, 0, NULL},
254 : {TR_RESUME, 0, NULL},
255 : {0, 0, NULL}
256 : };
257 :
258 : static BOOL _rpc_registered = FALSE;
259 : static int _rpc_listen_socket = 0;
260 :
261 : static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
262 :
263 : void cm_ctrlc_handler(int sig);
264 :
265 : typedef struct {
266 : INT code;
267 : const char *string;
268 : } ERROR_TABLE;
269 :
270 : static const ERROR_TABLE _error_table[] = {
271 : {CM_WRONG_PASSWORD, "Wrong password"},
272 : {CM_UNDEF_EXP, "Experiment not defined"},
273 : {CM_UNDEF_ENVIRON,
274 : "\"exptab\" file not found and MIDAS_DIR or MIDAS_EXPTAB environment variable is not defined"},
275 : {RPC_NET_ERROR, "Cannot connect to remote host"},
276 : {0, NULL}
277 : };
278 :
279 : typedef struct {
280 : void *adr;
281 : int size;
282 : char file[80];
283 : int line;
284 : } DBG_MEM_LOC;
285 :
286 : static DBG_MEM_LOC *_mem_loc = NULL;
287 : static INT _n_mem = 0;
288 :
289 : struct TR_PARAM
290 : {
291 : INT transition;
292 : INT run_number;
293 : char *errstr;
294 : INT errstr_size;
295 : INT async_flag;
296 : INT debug_flag;
297 : std::atomic_int status{0};
298 : std::atomic_bool finished{false};
299 : std::atomic<std::thread*> thread{NULL};
300 : };
301 :
302 : static TR_PARAM _trp;
303 :
304 : /*------------------------------------------------------------------*/
305 :
306 0 : void *dbg_malloc(unsigned int size, char *file, int line) {
307 : FILE *f;
308 : void *adr;
309 : int i;
310 :
311 0 : adr = malloc(size);
312 :
313 : /* search for deleted entry */
314 0 : for (i = 0; i < _n_mem; i++)
315 0 : if (_mem_loc[i].adr == NULL)
316 0 : break;
317 :
318 0 : if (i == _n_mem) {
319 0 : _n_mem++;
320 0 : if (!_mem_loc)
321 0 : _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
322 : else
323 0 : _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
324 : }
325 :
326 0 : assert(_mem_loc != NULL);
327 :
328 0 : _mem_loc[i].adr = adr;
329 0 : _mem_loc[i].size = size;
330 0 : strcpy(_mem_loc[i].file, file);
331 0 : _mem_loc[i].line = line;
332 :
333 0 : f = fopen("mem.txt", "w");
334 :
335 0 : assert(f != NULL);
336 :
337 0 : for (i = 0; i < _n_mem; i++)
338 0 : if (_mem_loc[i].adr)
339 0 : fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size, _mem_loc[i].adr);
340 :
341 0 : fclose(f);
342 :
343 0 : return adr;
344 : }
345 :
346 0 : void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line) {
347 : void *adr;
348 :
349 0 : adr = dbg_malloc(size * count, file, line);
350 0 : if (adr)
351 0 : memset(adr, 0, size * count);
352 :
353 0 : return adr;
354 : }
355 :
356 0 : void dbg_free(void *adr, char *file, int line) {
357 : FILE *f;
358 : int i;
359 :
360 0 : free(adr);
361 :
362 0 : for (i = 0; i < _n_mem; i++)
363 0 : if (_mem_loc[i].adr == adr)
364 0 : break;
365 :
366 0 : if (i < _n_mem)
367 0 : _mem_loc[i].adr = NULL;
368 :
369 0 : f = fopen("mem.txt", "w");
370 :
371 0 : assert(f != NULL);
372 :
373 0 : for (i = 0; i < _n_mem; i++)
374 0 : if (_mem_loc[i].adr)
375 0 : fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
376 0 : file, line, _mem_loc[i].size, _mem_loc[i].adr);
377 :
378 0 : fclose(f);
379 0 : }
380 :
381 0 : static std::vector<std::string> split(const char* sep, const std::string& s)
382 : {
383 0 : unsigned sep_len = strlen(sep);
384 0 : std::vector<std::string> v;
385 0 : std::string::size_type pos = 0;
386 : while (1) {
387 0 : std::string::size_type next = s.find(sep, pos);
388 0 : if (next == std::string::npos) {
389 0 : v.push_back(s.substr(pos));
390 0 : break;
391 : }
392 0 : v.push_back(s.substr(pos, next-pos));
393 0 : pos = next+sep_len;
394 0 : }
395 0 : return v;
396 0 : }
397 :
398 0 : static std::string join(const char* sep, const std::vector<std::string>& v)
399 : {
400 0 : std::string s;
401 :
402 0 : for (unsigned i=0; i<v.size(); i++) {
403 0 : if (i>0) {
404 0 : s += sep;
405 : }
406 0 : s += v[i];
407 : }
408 :
409 0 : return s;
410 0 : }
411 :
412 2 : bool ends_with_char(const std::string& s, char c)
413 : {
414 2 : if (s.length() < 1)
415 0 : return false;
416 2 : return s[s.length()-1] == c;
417 : }
418 :
419 26 : std::string msprintf(const char *format, ...) {
420 : va_list ap, ap1;
421 26 : va_start(ap, format);
422 26 : va_copy(ap1, ap);
423 26 : size_t size = vsnprintf(nullptr, 0, format, ap1) + 1;
424 26 : char *buffer = (char *)malloc(size);
425 26 : if (!buffer) {
426 0 : va_end(ap);
427 0 : va_end(ap1);
428 0 : return "";
429 : }
430 26 : vsnprintf(buffer, size, format, ap);
431 26 : va_end(ap);
432 26 : va_end(ap1);
433 26 : std::string s(buffer);
434 26 : free(buffer);
435 26 : return s;
436 26 : }
437 :
438 : /********************************************************************\
439 : * *
440 : * Common message functions *
441 : * *
442 : \********************************************************************/
443 :
444 : typedef int (*MessagePrintCallback)(const char *);
445 :
446 : static std::atomic<MessagePrintCallback> _message_print{puts};
447 :
448 : static std::atomic_int _message_mask_system{MT_ALL};
449 : static std::atomic_int _message_mask_user{MT_ALL};
450 :
451 :
452 : /**dox***************************************************************/
453 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
454 :
455 : /**dox***************************************************************/
456 : /** @addtogroup msgfunctionc
457 : *
458 : * @{ */
459 :
460 : /********************************************************************/
461 : /**
462 : Convert error code to string. Used after cm_connect_experiment to print
463 : error string in command line programs or windows programs.
464 : @param code Error code as defined in midas.h
465 : @param string Error string
466 : @return CM_SUCCESS
467 : */
468 0 : std::string cm_get_error(INT code)
469 : {
470 0 : for (int i = 0; _error_table[i].code; i++) {
471 0 : if (_error_table[i].code == code) {
472 0 : return _error_table[i].string;
473 : }
474 : }
475 :
476 0 : return msprintf("unlisted status code %d", code);
477 : }
478 :
479 : /********************************************************************/
480 2 : int cm_msg_early_init(void) {
481 :
482 2 : return CM_SUCCESS;
483 : }
484 :
485 : /********************************************************************/
486 :
487 2 : int cm_msg_open_buffer(void) {
488 : //printf("cm_msg_open_buffer!\n");
489 2 : if (_msg_buffer == 0) {
490 2 : int status = bm_open_buffer(MESSAGE_BUFFER_NAME, MESSAGE_BUFFER_SIZE, &_msg_buffer);
491 2 : if (status != BM_SUCCESS && status != BM_CREATED) {
492 0 : return status;
493 : }
494 : }
495 2 : return CM_SUCCESS;
496 : }
497 :
498 : /********************************************************************/
499 :
500 4 : int cm_msg_close_buffer(void) {
501 : //printf("cm_msg_close_buffer!\n");
502 4 : if (_msg_buffer) {
503 2 : bm_close_buffer(_msg_buffer);
504 2 : _msg_buffer = 0;
505 : }
506 4 : return CM_SUCCESS;
507 : }
508 :
509 : /********************************************************************/
510 :
511 : /**
512 : Retrieve list of message facilities by searching logfiles on disk
513 : @param list List of facilities
514 : @return status SUCCESS
515 : */
516 :
517 0 : INT EXPRT cm_msg_facilities(STRING_LIST *list) {
518 0 : std::string path;
519 :
520 0 : cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
521 :
522 : /* extract directory name from full path name of midas.log */
523 0 : size_t pos = path.rfind(DIR_SEPARATOR);
524 0 : if (pos != std::string::npos) {
525 0 : path.resize(pos);
526 : } else {
527 0 : path = "";
528 : }
529 :
530 : //printf("cm_msg_facilities: path [%s]\n", path.c_str());
531 :
532 0 : STRING_LIST flist;
533 :
534 0 : ss_file_find(path.c_str(), "*.log", &flist);
535 :
536 0 : for (size_t i = 0; i < flist.size(); i++) {
537 0 : const char *p = flist[i].c_str();
538 0 : if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
539 0 : size_t pos = flist[i].rfind('.');
540 0 : if (pos != std::string::npos) {
541 0 : flist[i].resize(pos);
542 : }
543 0 : list->push_back(flist[i]);
544 : }
545 : }
546 :
547 0 : return SUCCESS;
548 0 : }
549 :
550 : /********************************************************************/
551 :
552 15 : void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
553 : HNDLE hDB;
554 : int status;
555 :
556 15 : status = cm_get_experiment_database(&hDB, NULL);
557 :
558 : // check for call to cm_msg() before MIDAS is fully initialized
559 : // or after MIDAS is partially shutdown.
560 15 : if (status != CM_SUCCESS) {
561 0 : if (filename)
562 0 : *filename = std::string(fac) + ".log";
563 0 : if (linkname)
564 0 : *linkname = "";
565 0 : if (linktarget)
566 0 : *linktarget = "";
567 0 : return;
568 : }
569 :
570 15 : if (filename)
571 15 : *filename = "";
572 15 : if (linkname)
573 15 : *linkname = "";
574 15 : if (linktarget)
575 15 : *linktarget = "";
576 :
577 15 : std::string facility;
578 15 : if (fac && fac[0])
579 15 : facility = fac;
580 : else
581 0 : facility = "midas";
582 :
583 15 : std::string message_format;
584 15 : db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
585 15 : if (message_format.find('%') != std::string::npos) {
586 : /* replace stings such as %y%m%d with current date */
587 : struct tm tms;
588 :
589 0 : ss_tzset();
590 0 : if (t == 0)
591 0 : time(&t);
592 0 : localtime_r(&t, &tms);
593 :
594 : char de[256];
595 0 : de[0] = '_';
596 0 : strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
597 0 : message_format = de;
598 : }
599 :
600 15 : std::string message_dir;
601 15 : db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
602 15 : if (message_dir.empty()) {
603 15 : db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
604 15 : if (message_dir.empty()) {
605 0 : message_dir = cm_get_path();
606 0 : if (message_dir.empty()) {
607 0 : message_dir = ss_getcwd();
608 : }
609 : }
610 : }
611 :
612 : // prepend experiment directory
613 15 : if (message_dir[0] != DIR_SEPARATOR)
614 0 : message_dir = cm_get_path() + message_dir;
615 :
616 15 : if (message_dir.back() != DIR_SEPARATOR)
617 0 : message_dir.push_back(DIR_SEPARATOR);
618 :
619 15 : if (filename)
620 15 : *filename = message_dir + facility + message_format + ".log";
621 15 : if (!message_format.empty()) {
622 0 : if (linkname)
623 0 : *linkname = message_dir + facility + ".log";
624 0 : if (linktarget)
625 0 : *linktarget = facility + message_format + ".log";
626 : }
627 15 : }
628 :
629 : /********************************************************************/
630 : /**
631 : Set message masks. When a message is generated by calling cm_msg(),
632 : it can got to two destinatinons. First a user defined callback routine
633 : and second to the "SYSMSG" buffer.
634 :
635 : A user defined callback receives all messages which satisfy the user_mask.
636 :
637 : \code
638 : int message_print(const char *msg)
639 : {
640 : char str[160];
641 :
642 : memset(str, ' ', 159);
643 : str[159] = 0;
644 : if (msg[0] == '[')
645 : msg = strchr(msg, ']')+2;
646 : memcpy(str, msg, strlen(msg));
647 : ss_printf(0, 20, str);
648 : return 0;
649 : }
650 : ...
651 : cm_set_msg_print(MT_ALL, MT_ALL, message_print);
652 : ...
653 : \endcode
654 : @param system_mask Bit masks for MERROR, MINFO etc. to send system messages.
655 : @param user_mask Bit masks for MERROR, MINFO etc. to send messages to the user callback.
656 : @param func Function which receives all printout. By setting "puts",
657 : messages are just printed to the screen.
658 : @return CM_SUCCESS
659 : */
660 2 : INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
661 2 : _message_mask_system = system_mask;
662 2 : _message_mask_user = user_mask;
663 2 : _message_print = func;
664 :
665 2 : return BM_SUCCESS;
666 : }
667 :
668 : /********************************************************************/
669 : /**
670 : Write message to logging file. Called by cm_msg.
671 : @attention May burn your fingers
672 : @param message_type Message type
673 : @param message Message string
674 : @param facility Message facility, filename in which messages will be written
675 : @return CM_SUCCESS
676 : */
677 15 : INT cm_msg_log(INT message_type, const char *facility, const char *message) {
678 : INT status;
679 :
680 15 : if (rpc_is_remote()) {
681 0 : if (rpc_is_connected()) {
682 0 : status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
683 0 : if (status != RPC_SUCCESS) {
684 0 : fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log because rpc_call(RPC_CM_MSG_LOG) failed with status %d\n", message, status);
685 : }
686 0 : return status;
687 : } else {
688 0 : fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
689 0 : return RPC_NET_ERROR;
690 : }
691 : }
692 :
693 15 : if (message_type != MT_DEBUG) {
694 15 : std::string filename, linkname, linktarget;
695 :
696 15 : cm_msg_get_logfile(facility, 0, &filename, &linkname, &linktarget);
697 :
698 : #ifdef OS_LINUX
699 15 : if (!linkname.empty()) {
700 : //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
701 : // If filename does not exist, user just switched from non-date format to date format.
702 : // In that case we must copy linkname to filename, otherwise messages might get lost.
703 0 : if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
704 0 : ss_file_copy(linkname.c_str(), filename.c_str(), true);
705 : }
706 :
707 0 : unlink(linkname.c_str());
708 0 : status = symlink(linktarget.c_str(), linkname.c_str());
709 0 : if (status != 0) {
710 0 : fprintf(stderr,
711 : "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
712 0 : linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
713 : }
714 : }
715 : #endif
716 :
717 15 : int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
718 15 : if (fh < 0) {
719 0 : fprintf(stderr,
720 : "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
721 0 : message, filename.c_str(), errno, strerror(errno));
722 : } else {
723 :
724 : struct timeval tv;
725 : struct tm tms;
726 :
727 15 : ss_tzset();
728 15 : gettimeofday(&tv, NULL);
729 15 : localtime_r(&tv.tv_sec, &tms);
730 :
731 : char str[256];
732 15 : strftime(str, sizeof(str), "%H:%M:%S", &tms);
733 15 : sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
734 15 : strftime(str + strlen(str), sizeof(str), "%G/%m/%d", &tms);
735 :
736 15 : std::string msg;
737 15 : msg += str;
738 15 : msg += " ";
739 15 : msg += message;
740 15 : msg += "\n";
741 :
742 : /* avoid c++ complaint about comparison between
743 : unsigned size_t returned by msg.length() and
744 : signed ssize_t returned by write() */
745 15 : ssize_t len = msg.length();
746 :
747 : /* atomic write, no need to take a semaphore */
748 15 : ssize_t wr = write(fh, msg.c_str(), len);
749 :
750 15 : if (wr < 0) {
751 0 : fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
752 15 : } else if (wr != len) {
753 0 : fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", short write() wrote %d instead of %d bytes\n", message, filename.c_str(), (int)wr, (int)len);
754 : }
755 :
756 15 : close(fh);
757 15 : }
758 15 : }
759 :
760 15 : return CM_SUCCESS;
761 : }
762 :
763 :
764 15 : static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
765 : {
766 : /* strip path */
767 15 : const char* pc = filename + strlen(filename);
768 143 : while (*pc != '\\' && *pc != '/' && pc != filename)
769 128 : pc--;
770 15 : if (pc != filename)
771 15 : pc++;
772 :
773 : /* convert type to string */
774 15 : std::string type_str;
775 15 : if (message_type & MT_ERROR)
776 11 : type_str += MT_ERROR_STR;
777 15 : if (message_type & MT_INFO)
778 0 : type_str += MT_INFO_STR;
779 15 : if (message_type & MT_DEBUG)
780 0 : type_str += MT_DEBUG_STR;
781 15 : if (message_type & MT_USER)
782 0 : type_str += MT_USER_STR;
783 15 : if (message_type & MT_LOG)
784 4 : type_str += MT_LOG_STR;
785 15 : if (message_type & MT_TALK)
786 0 : type_str += MT_TALK_STR;
787 :
788 15 : std::string message;
789 :
790 : /* print client name into string */
791 15 : if (message_type == MT_USER)
792 0 : message = msprintf("[%s] ", routine);
793 : else {
794 15 : std::string name = rpc_get_name();
795 15 : if (name.length() > 0)
796 15 : message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
797 : else
798 0 : message = "";
799 15 : }
800 :
801 : /* preceed error messages with file and line info */
802 15 : if (message_type == MT_ERROR) {
803 11 : message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
804 4 : } else if (message_type == MT_USER) {
805 0 : message = msprintf("[%s,%s] ", routine, type_str.c_str());
806 : }
807 :
808 15 : int bufsize = 1024;
809 15 : char* buf = (char*)malloc(bufsize);
810 15 : assert(buf);
811 :
812 15 : for (int i=0; i<10; i++) {
813 : va_list ap;
814 15 : va_copy(ap, *argptr);
815 :
816 : /* print argument list into message */
817 15 : int n = vsnprintf(buf, bufsize-1, format, ap);
818 :
819 : //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
820 :
821 15 : va_end(ap);
822 :
823 15 : if (n < bufsize) {
824 15 : break;
825 : }
826 :
827 0 : bufsize += 100;
828 0 : bufsize *= 2;
829 0 : buf = (char*)realloc(buf, bufsize);
830 0 : assert(buf);
831 : }
832 :
833 15 : message += buf;
834 15 : free(buf);
835 :
836 30 : return message;
837 15 : }
838 :
839 15 : static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
840 : //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
841 :
842 : /* send event if not of type MLOG */
843 15 : if (message_type != MT_LOG) {
844 11 : if (_msg_buffer) {
845 : /* copy message to event */
846 11 : size_t len = strlen(send_message);
847 11 : int event_length = sizeof(EVENT_HEADER) + len + 1;
848 11 : char event[event_length];
849 11 : EVENT_HEADER *pevent = (EVENT_HEADER *) event;
850 :
851 11 : memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
852 :
853 : /* setup the event header and send the message */
854 11 : bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
855 11 : if (ts)
856 11 : pevent->time_stamp = ts;
857 : //printf("cm_msg_send_event: len %d, header %d, allocated %d, data_size %d, bm_send_event %p+%d\n", (int)len, (int)sizeof(EVENT_HEADER), event_length, pevent->data_size, pevent, (int)(pevent->data_size + sizeof(EVENT_HEADER)));
858 11 : bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
859 11 : }
860 : }
861 :
862 15 : return CM_SUCCESS;
863 : }
864 :
865 : struct msg_buffer_entry {
866 : DWORD ts;
867 : int message_type;
868 : std::string message;
869 : };
870 :
871 : static std::deque<msg_buffer_entry> gMsgBuf;
872 : static std::mutex gMsgBufMutex;
873 :
874 : /********************************************************************/
875 : /**
876 : This routine can be called to process messages buffered by cm_msg(). Normally
877 : it is called from cm_yield() and cm_disconnect_experiment() to make sure
878 : all accumulated messages are processed.
879 : */
880 5 : INT cm_msg_flush_buffer() {
881 : int i;
882 :
883 : //printf("cm_msg_flush_buffer!\n");
884 :
885 20 : for (i = 0; i < 100; i++) {
886 20 : msg_buffer_entry e;
887 : {
888 20 : std::lock_guard<std::mutex> lock(gMsgBufMutex);
889 20 : if (gMsgBuf.empty())
890 5 : break;
891 15 : e = gMsgBuf.front();
892 15 : gMsgBuf.pop_front();
893 : // implicit unlock
894 20 : }
895 :
896 : /* log message */
897 15 : cm_msg_log(e.message_type, "midas", e.message.c_str());
898 :
899 : /* send message to SYSMSG */
900 15 : int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
901 15 : if (status != CM_SUCCESS)
902 0 : return status;
903 20 : }
904 :
905 5 : return CM_SUCCESS;
906 : }
907 :
908 : /********************************************************************/
909 : /**
910 : This routine can be called whenever an internal error occurs
911 : or an informative message is produced. Different message
912 : types can be enabled or disabled by setting the type bits
913 : via cm_set_msg_print().
914 : @attention Do not add the "\n" escape carriage control at the end of the
915 : formated line as it is already added by the client on the receiving side.
916 : \code
917 : ...
918 : cm_msg(MINFO, "my program", "This is a information message only);
919 : cm_msg(MERROR, "my program", "This is an error message with status:%d", my_status);
920 : cm_msg(MTALK, "my_program", My program is Done!");
921 : ...
922 : \endcode
923 : @param message_type (See @ref midas_macro).
924 : @param filename Name of source file where error occured
925 : @param line Line number where error occured
926 : @param routine Routine name.
927 : @param format message to printout, ... Parameters like for printf()
928 : @return CM_SUCCESS
929 : */
930 15 : INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
931 : {
932 15 : DWORD ts = ss_time();
933 :
934 : /* print argument list into message */
935 15 : std::string message;
936 : va_list argptr;
937 15 : va_start(argptr, format);
938 15 : message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
939 15 : va_end(argptr);
940 :
941 : //printf("message [%s]\n", message.c_str());
942 :
943 : /* call user function if set via cm_set_msg_print */
944 15 : MessagePrintCallback f = _message_print.load();
945 15 : if (f != NULL && (message_type & _message_mask_user) != 0) {
946 15 : if (message_type != MT_LOG) { // do not print MLOG messages
947 11 : (*f)(message.c_str());
948 : }
949 : }
950 :
951 : /* return if system mask is not set */
952 15 : if ((message_type & _message_mask_system) == 0) {
953 0 : return CM_SUCCESS;
954 : }
955 :
956 15 : gMsgBufMutex.lock();
957 15 : gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
958 15 : gMsgBufMutex.unlock();
959 :
960 15 : return CM_SUCCESS;
961 15 : }
962 :
963 : /********************************************************************/
964 : /**
965 : This routine is similar to @ref cm_msg().
966 : It differs from cm_msg() only by the logging destination being a file
967 : given through the argument list i.e:\b facility
968 : @internal
969 : @attention Do not add the "\n" escape carriage control at the end of the
970 : formated line as it is already added by the client on the receiving side.
971 : The first arg in the following example uses the predefined
972 : macro MINFO which handles automatically the first 3 arguments of the function
973 : (see @ref midas_macro).
974 : \code ...
975 : cm_msg1(MINFO, "my_log_file", "my_program"," My message status:%d", status);
976 : ...
977 : //----- File my_log_file.log
978 : Thu Nov 8 17:59:28 2001 [my_program] My message status:1
979 : \endcode
980 : @param message_type See @ref midas_macro.
981 : @param filename Name of source file where error occured
982 : @param line Line number where error occured
983 : @param facility Logging file name
984 : @param routine Routine name
985 : @param format message to printout, ... Parameters like for printf()
986 : @return CM_SUCCESS
987 : */
988 0 : INT cm_msg1(INT message_type, const char *filename, INT line,
989 : const char *facility, const char *routine, const char *format, ...) {
990 : va_list argptr;
991 0 : std::string message;
992 : static BOOL in_routine = FALSE;
993 :
994 : /* avoid recursive calles */
995 0 : if (in_routine)
996 0 : return 0;
997 :
998 0 : in_routine = TRUE;
999 :
1000 : /* print argument list into message */
1001 0 : va_start(argptr, format);
1002 0 : message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
1003 0 : va_end(argptr);
1004 :
1005 : /* call user function if set via cm_set_msg_print */
1006 0 : MessagePrintCallback f = _message_print.load();
1007 0 : if (f != NULL && (message_type & _message_mask_user) != 0)
1008 0 : (*f)(message.c_str());
1009 :
1010 : /* return if system mask is not set */
1011 0 : if ((message_type & _message_mask_system) == 0) {
1012 0 : in_routine = FALSE;
1013 0 : return CM_SUCCESS;
1014 : }
1015 :
1016 : /* send message to SYSMSG */
1017 0 : cm_msg_send_event(0, message_type, message.c_str());
1018 :
1019 : /* log message */
1020 0 : cm_msg_log(message_type, facility, message.c_str());
1021 :
1022 0 : in_routine = FALSE;
1023 :
1024 0 : return CM_SUCCESS;
1025 0 : }
1026 :
1027 : /********************************************************************/
1028 : /**
1029 : Register a dispatch function for receiving system messages.
1030 : - example code from mlxspeaker.c
1031 : \code
1032 : void receive_message(HNDLE hBuf, HNDLE id, EVENT_HEADER *header, void *message)
1033 : {
1034 : char str[256], *pc, *sp;
1035 : // print message
1036 : printf("%s\n", (char *)(message));
1037 :
1038 : printf("evID:%x Mask:%x Serial:%i Size:%d\n"
1039 : ,header->event_id
1040 : ,header->trigger_mask
1041 : ,header->serial_number
1042 : ,header->data_size);
1043 : pc = strchr((char *)(message),']')+2;
1044 : ...
1045 : // skip none talking message
1046 : if (header->trigger_mask == MT_TALK ||
1047 : header->trigger_mask == MT_USER)
1048 : ...
1049 : }
1050 :
1051 : int main(int argc, char *argv[])
1052 : {
1053 : ...
1054 : // now connect to server
1055 : status = cm_connect_experiment(host_name, exp_name, "Speaker", NULL);
1056 : if (status != CM_SUCCESS)
1057 : return 1;
1058 : // Register callback for messages
1059 : cm_msg_register(receive_message);
1060 : ...
1061 : }
1062 : \endcode
1063 : @param func Dispatch function.
1064 : @return CM_SUCCESS or bm_open_buffer and bm_request_event return status
1065 : */
1066 0 : INT cm_msg_register(EVENT_HANDLER *func) {
1067 : INT status, id;
1068 :
1069 : // we should only come here after the message buffer
1070 : // was opened by cm_connect_experiment()
1071 0 : assert(_msg_buffer);
1072 :
1073 0 : _msg_dispatch = func;
1074 :
1075 0 : status = bm_request_event(_msg_buffer, EVENTID_ALL, TRIGGER_ALL, GET_NONBLOCKING, &id, func);
1076 :
1077 0 : return status;
1078 : }
1079 :
1080 0 : static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1081 0 : int new_message_length = strlen(new_message);
1082 0 : int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1083 : char buf[100];
1084 : int buf_length;
1085 :
1086 : //printf("add_message: new message %d, length %d, new end: %d, allocated: %d, maybe reallocate size %d\n", new_message_length, *length, *length + new_message_length, *allocated, new_allocated);
1087 :
1088 0 : if (*length + new_message_length + 100 > *allocated) {
1089 0 : *messages = (char *) realloc(*messages, new_allocated);
1090 0 : assert(*messages != NULL);
1091 0 : *allocated = new_allocated;
1092 : }
1093 :
1094 0 : if (*length > 0)
1095 0 : if ((*messages)[(*length) - 1] != '\n') {
1096 0 : (*messages)[*length] = '\n'; // separator between messages
1097 0 : (*length) += 1;
1098 : }
1099 :
1100 0 : sprintf(buf, "%ld ", tstamp);
1101 0 : buf_length = strlen(buf);
1102 0 : memcpy(&((*messages)[*length]), buf, buf_length);
1103 0 : (*length) += buf_length;
1104 :
1105 0 : memcpy(&((*messages)[*length]), new_message, new_message_length);
1106 0 : (*length) += new_message_length;
1107 0 : (*messages)[*length] = 0; // make sure string is NUL terminated
1108 0 : }
1109 :
1110 : /* Retrieve message from an individual file. Internal use only */
1111 0 : static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1112 : int *num_messages) {
1113 : BOOL stop;
1114 : int fh;
1115 : char *p, str[1000];
1116 : struct stat stat_buf;
1117 : time_t tstamp, tstamp_valid, tstamp_last;
1118 :
1119 0 : ss_tzset(); // required by localtime_r()
1120 :
1121 0 : *num_messages = 0;
1122 :
1123 0 : fh = open(filename, O_RDONLY | O_TEXT, 0644);
1124 0 : if (fh < 0) {
1125 0 : cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1126 0 : strerror(errno));
1127 0 : return SS_FILE_ERROR;
1128 : }
1129 :
1130 : /* read whole file into memory */
1131 0 : fstat(fh, &stat_buf);
1132 0 : ssize_t size = stat_buf.st_size;
1133 :
1134 : /* if file is too big, only read tail of file */
1135 0 : ssize_t maxsize = 10 * 1024 * 1024;
1136 0 : if (size > maxsize) {
1137 0 : lseek(fh, -maxsize, SEEK_END);
1138 : //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1139 0 : size = maxsize;
1140 : }
1141 :
1142 0 : char *buffer = (char *) malloc(size + 1);
1143 :
1144 0 : if (buffer == NULL) {
1145 0 : cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1146 0 : filename, errno, strerror(errno));
1147 0 : close(fh);
1148 0 : return SS_FILE_ERROR;
1149 : }
1150 :
1151 0 : ssize_t rd = read(fh, buffer, size);
1152 :
1153 0 : if (rd != size) {
1154 0 : cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1155 0 : (int) size, filename, (int) rd, errno, strerror(errno));
1156 0 : close(fh);
1157 0 : return SS_FILE_ERROR;
1158 : }
1159 :
1160 0 : buffer[size] = 0;
1161 0 : close(fh);
1162 :
1163 0 : p = buffer + size - 1;
1164 0 : tstamp_last = tstamp_valid = 0;
1165 0 : stop = FALSE;
1166 :
1167 0 : while (*p == '\n' || *p == '\r')
1168 0 : p--;
1169 :
1170 : int n;
1171 0 : for (n = 0; !stop && p > buffer;) {
1172 :
1173 : /* go to beginning of line */
1174 : int i;
1175 0 : for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1176 0 : p--;
1177 :
1178 : /* limit line length to sizeof(str) */
1179 0 : if (i >= (int) sizeof(str))
1180 0 : i = sizeof(str) - 1;
1181 :
1182 0 : if (p == buffer) {
1183 0 : i++;
1184 0 : memcpy(str, p, i);
1185 : } else
1186 0 : memcpy(str, p + 1, i);
1187 0 : str[i] = 0;
1188 0 : if (strchr(str, '\n'))
1189 0 : *strchr(str, '\n') = 0;
1190 0 : if (strchr(str, '\r'))
1191 0 : *strchr(str, '\r') = 0;
1192 0 : mstrlcat(str, "\n", sizeof(str));
1193 :
1194 : // extract time tag
1195 : time_t now;
1196 0 : time(&now);
1197 :
1198 : struct tm tms;
1199 0 : localtime_r(&now, &tms); // must call tzset() beforehand!
1200 :
1201 0 : if (str[0] >= '0' && str[0] <= '9') {
1202 : // new format
1203 0 : tms.tm_hour = atoi(str);
1204 0 : tms.tm_min = atoi(str + 3);
1205 0 : tms.tm_sec = atoi(str + 6);
1206 0 : tms.tm_year = atoi(str + 13) - 1900;
1207 0 : tms.tm_mon = atoi(str + 18) - 1;
1208 0 : tms.tm_mday = atoi(str + 21);
1209 : } else {
1210 : // old format
1211 0 : tms.tm_hour = atoi(str + 11);
1212 0 : tms.tm_min = atoi(str + 14);
1213 0 : tms.tm_sec = atoi(str + 17);
1214 0 : tms.tm_year = atoi(str + 20) - 1900;
1215 0 : for (i = 0; i < 12; i++)
1216 0 : if (strncmp(str + 4, mname[i], 3) == 0)
1217 0 : break;
1218 0 : tms.tm_mon = i;
1219 0 : tms.tm_mday = atoi(str + 8);
1220 : }
1221 0 : tstamp = ss_mktime(&tms);
1222 0 : if (tstamp != -1)
1223 0 : tstamp_valid = tstamp;
1224 :
1225 : // for new messages (n=0!), stop when t reached
1226 0 : if (n_messages == 0) {
1227 0 : if (tstamp_valid < t)
1228 0 : break;
1229 : }
1230 :
1231 : // for old messages, stop when all messages belonging to tstamp_last are sent
1232 0 : if (n_messages != 0) {
1233 0 : if (tstamp_last > 0 && tstamp_valid < tstamp_last)
1234 0 : break;
1235 : }
1236 :
1237 0 : if (t == 0 || tstamp == -1 ||
1238 0 : (n_messages > 0 && tstamp <= t) ||
1239 0 : (n_messages == 0 && tstamp >= t)) {
1240 :
1241 0 : n++;
1242 :
1243 0 : add_message(messages, length, allocated, tstamp, str);
1244 : }
1245 :
1246 0 : while (*p == '\n' || *p == '\r')
1247 0 : p--;
1248 :
1249 0 : if (n_messages == 1)
1250 0 : stop = TRUE;
1251 0 : else if (n_messages > 1) {
1252 : // continue collecting messages until time stamp differs from current one
1253 0 : if (n == n_messages)
1254 0 : tstamp_last = tstamp_valid;
1255 :
1256 : // if all messages without time tags, just return after n
1257 0 : if (n == n_messages && tstamp_valid == 0)
1258 0 : break;
1259 : }
1260 : }
1261 :
1262 0 : free(buffer);
1263 :
1264 0 : *num_messages = n;
1265 :
1266 0 : return CM_SUCCESS;
1267 : }
1268 :
1269 : /********************************************************************/
1270 : /**
1271 : Retrieve old messages from log file
1272 : @param facility Logging facility ("midas", "chat", "lazy", ...)
1273 : @param t Return messages logged before and including time t, value 0 means start with newest messages
1274 : @param min_messages Minimum number of messages to return
1275 : @param messages messages, newest first, separated by \n characters. caller should free() this buffer at the end.
1276 : @param num_messages Number of messages returned
1277 : @return CM_SUCCESS
1278 : */
1279 0 : INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages) {
1280 0 : std::string filename, linkname;
1281 : INT n, i;
1282 : time_t filedate;
1283 0 : int length = 0;
1284 0 : int allocated = 0;
1285 :
1286 0 : time(&filedate);
1287 0 : cm_msg_get_logfile(facility, filedate, &filename, &linkname, NULL);
1288 :
1289 : //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1290 :
1291 : // see if file exists, use linkname if not
1292 0 : if (!linkname.empty()) {
1293 0 : if (!ss_file_exist(filename.c_str()))
1294 0 : filename = linkname;
1295 : }
1296 :
1297 0 : if (ss_file_exist(filename.c_str())) {
1298 0 : cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1299 : } else {
1300 0 : n = 0;
1301 : }
1302 :
1303 : /* if there is no symlink, then there is no additional log files to read */
1304 0 : if (linkname.empty()) {
1305 0 : *num_messages = n;
1306 0 : return CM_SUCCESS;
1307 : }
1308 :
1309 : //printf("read more messages %d %d!\n", n, n_message);
1310 :
1311 0 : int missing = 0;
1312 0 : while (n < n_message) {
1313 0 : filedate -= 3600 * 24; // go one day back
1314 :
1315 0 : cm_msg_get_logfile(facility, filedate, &filename, NULL, NULL);
1316 :
1317 : //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1318 :
1319 0 : if (ss_file_exist(filename.c_str())) {
1320 0 : cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1321 0 : n += i;
1322 0 : missing = 0;
1323 : } else {
1324 0 : missing++;
1325 : }
1326 :
1327 : // stop if ten consecutive files are not found
1328 0 : if (missing > 10)
1329 0 : break;
1330 : }
1331 :
1332 0 : *num_messages = n;
1333 :
1334 0 : return CM_SUCCESS;
1335 0 : }
1336 :
1337 : /********************************************************************/
1338 : /**
1339 : Retrieve newest messages from "midas" facility log file
1340 : @param n_message Number of messages to retrieve
1341 : @param message buf_size bytes of messages, separated
1342 : by \n characters. The returned number
1343 : of bytes is normally smaller than the
1344 : initial buf_size, since only full
1345 : lines are returned.
1346 : @param *buf_size Size of message buffer to fill
1347 : @return CM_SUCCESS, CM_TRUNCATED
1348 : */
1349 0 : INT cm_msg_retrieve(INT n_message, char *message, INT buf_size) {
1350 : int status;
1351 0 : char *messages = NULL;
1352 0 : int num_messages = 0;
1353 :
1354 0 : if (rpc_is_remote())
1355 0 : return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1356 :
1357 0 : status = cm_msg_retrieve2("midas", 0, n_message, &messages, &num_messages);
1358 :
1359 0 : if (messages) {
1360 0 : mstrlcpy(message, messages, buf_size);
1361 0 : int len = strlen(messages);
1362 0 : if (len > buf_size)
1363 0 : status = CM_TRUNCATED;
1364 0 : free(messages);
1365 : }
1366 :
1367 0 : return status;
1368 : }
1369 :
1370 : /**dox***************************************************************/
1371 : /** @} *//* end of msgfunctionc */
1372 :
1373 : /**dox***************************************************************/
1374 : /** @addtogroup cmfunctionc
1375 : *
1376 : * @{ */
1377 :
1378 : /********************************************************************/
1379 : /**
1380 : Get time from MIDAS server and set local time.
1381 : @param seconds Time in seconds
1382 : @return CM_SUCCESS
1383 : */
1384 0 : INT cm_synchronize(DWORD *seconds) {
1385 : INT sec, status;
1386 :
1387 : /* if connected to server, get time from there */
1388 0 : if (rpc_is_remote()) {
1389 0 : status = rpc_call(RPC_CM_SYNCHRONIZE, &sec);
1390 :
1391 : /* set local time */
1392 0 : if (status == CM_SUCCESS)
1393 0 : ss_settime(sec);
1394 : }
1395 :
1396 : /* return time to caller */
1397 0 : if (seconds != NULL) {
1398 0 : *seconds = ss_time();
1399 : }
1400 :
1401 0 : return CM_SUCCESS;
1402 : }
1403 :
1404 : /********************************************************************/
1405 : /**
1406 : Get time from MIDAS server and set local time.
1407 : @param str return time string
1408 : @param buf_size Maximum size of str
1409 : @return CM_SUCCESS
1410 : */
1411 0 : INT cm_asctime(char *str, INT buf_size) {
1412 : /* if connected to server, get time from there */
1413 0 : if (rpc_is_remote())
1414 0 : return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1415 :
1416 : /* return local time */
1417 0 : mstrlcpy(str, ss_asctime().c_str(), buf_size);
1418 :
1419 0 : return CM_SUCCESS;
1420 : }
1421 :
1422 : /********************************************************************/
1423 : /**
1424 : Get time from MIDAS server and set local time.
1425 : @return return time string
1426 : */
1427 0 : std::string cm_asctime() {
1428 : /* if connected to server, get time from there */
1429 0 : if (rpc_is_remote()) {
1430 : char buf[256];
1431 0 : int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1432 0 : if (status == CM_SUCCESS) {
1433 0 : return buf;
1434 : } else {
1435 0 : return "";
1436 : }
1437 : }
1438 :
1439 : /* return local time */
1440 0 : return ss_asctime();
1441 : }
1442 :
1443 : /********************************************************************/
1444 : /**
1445 : Get time from ss_time on server.
1446 : @param t string
1447 : @return CM_SUCCESS
1448 : */
1449 0 : INT cm_time(DWORD *t) {
1450 : /* if connected to server, get time from there */
1451 0 : if (rpc_is_remote())
1452 0 : return rpc_call(RPC_CM_TIME, t);
1453 :
1454 : /* return local time */
1455 0 : *t = ss_time();
1456 :
1457 0 : return CM_SUCCESS;
1458 : }
1459 :
1460 : /**dox***************************************************************/
1461 : /** @} *//* end of cmfunctionc */
1462 :
1463 : /********************************************************************\
1464 : * *
1465 : * cm_xxx - Common Functions to buffer & database *
1466 : * *
1467 : \********************************************************************/
1468 :
1469 : /* Globals */
1470 :
1471 : static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1472 : static HNDLE _hDB = 0; /* Database handle */
1473 : static std::string _experiment_name;
1474 : static std::string _client_name;
1475 : static std::string _path_name;
1476 : static INT _watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
1477 : INT _semaphore_alarm = -1;
1478 : INT _semaphore_elog = -1;
1479 : INT _semaphore_history = -1;
1480 : //INT _semaphore_msg = -1;
1481 :
1482 : /**dox***************************************************************/
1483 : /** @addtogroup cmfunctionc
1484 : *
1485 : * @{ */
1486 :
1487 : /**
1488 : Return version number of current MIDAS library as a string
1489 : @return version number
1490 : */
1491 0 : const char *cm_get_version() {
1492 0 : return MIDAS_VERSION;
1493 : }
1494 :
1495 : /**
1496 : Return git revision number of current MIDAS library as a string
1497 : @return revision number
1498 : */
1499 0 : const char *cm_get_revision() {
1500 0 : return GIT_REVISION;
1501 : }
1502 :
1503 : /********************************************************************/
1504 : /**
1505 : Set path to actual experiment. This function gets called
1506 : by cm_connect_experiment if the connection is established
1507 : to a local experiment (not through the TCP/IP server).
1508 : The path is then used for all shared memory routines.
1509 : @param path Pathname
1510 : @return CM_SUCCESS
1511 : */
1512 3 : INT cm_set_path(const char *path) {
1513 3 : assert(path);
1514 3 : assert(path[0] != 0);
1515 :
1516 3 : _path_name = path;
1517 :
1518 3 : if (_path_name.back() != DIR_SEPARATOR) {
1519 0 : _path_name += DIR_SEPARATOR_STR;
1520 : }
1521 :
1522 : //printf("cm_set_path [%s]\n", _path_name.c_str());
1523 :
1524 3 : return CM_SUCCESS;
1525 : }
1526 :
1527 : /********************************************************************/
1528 : /**
1529 : Return the path name previously set with cm_set_path.
1530 : @param path Pathname
1531 : @return CM_SUCCESS
1532 : */
1533 0 : INT cm_get_path(char *path, int path_size) {
1534 : // check that we were not accidentally called
1535 : // with the size of the pointer to a string
1536 : // instead of the size of the string buffer
1537 0 : assert(path_size != sizeof(char *));
1538 0 : assert(path);
1539 0 : assert(_path_name.length() > 0);
1540 :
1541 0 : mstrlcpy(path, _path_name.c_str(), path_size);
1542 :
1543 0 : return CM_SUCCESS;
1544 : }
1545 :
1546 : /********************************************************************/
1547 : /**
1548 : Return the path name previously set with cm_set_path.
1549 : @param path Pathname
1550 : @return CM_SUCCESS
1551 : */
1552 85 : std::string cm_get_path() {
1553 85 : assert(_path_name.length() > 0);
1554 85 : return _path_name;
1555 : }
1556 :
1557 : /********************************************************************/
1558 : /* C++ wrapper for cm_get_path */
1559 :
1560 0 : INT EXPRT cm_get_path_string(std::string *path) {
1561 0 : assert(path != NULL);
1562 0 : assert(_path_name.length() > 0);
1563 0 : *path = _path_name;
1564 0 : return CM_SUCCESS;
1565 : }
1566 :
1567 : /********************************************************************/
1568 : /**
1569 : Set name of the experiment
1570 : @param name Experiment name
1571 : @return CM_SUCCESS
1572 : */
1573 3 : INT cm_set_experiment_name(const char *name) {
1574 3 : _experiment_name = name;
1575 3 : return CM_SUCCESS;
1576 : }
1577 :
1578 : /********************************************************************/
1579 : /**
1580 : Return the experiment name
1581 : @param name Pointer to user string, size should be at least NAME_LENGTH
1582 : @param name_size Size of user string
1583 : @return CM_SUCCESS
1584 : */
1585 0 : INT cm_get_experiment_name(char *name, int name_length) {
1586 0 : mstrlcpy(name, _experiment_name.c_str(), name_length);
1587 0 : return CM_SUCCESS;
1588 : }
1589 :
1590 : /********************************************************************/
1591 : /**
1592 : Return the experiment name
1593 : @return experiment name
1594 : */
1595 17 : std::string cm_get_experiment_name() {
1596 17 : return _experiment_name;
1597 : }
1598 :
1599 : /**dox***************************************************************/
1600 : /** @} *//* end of cmfunctionc */
1601 :
1602 : /**dox***************************************************************/
1603 : /** @addtogroup cmfunctionc
1604 : *
1605 : * @{ */
1606 :
1607 : #ifdef LOCAL_ROUTINES
1608 :
1609 : struct exptab_entry {
1610 : std::string name;
1611 : std::string directory;
1612 : std::string user;
1613 : };
1614 :
1615 : struct exptab_struct {
1616 : std::string filename;
1617 : std::vector<exptab_entry> exptab;
1618 : };
1619 :
1620 : static exptab_struct _exptab; // contents of exptab file
1621 :
1622 : /**
1623 : Scan the "exptab" file for MIDAS experiment names and save them
1624 : for later use by rpc_server_accept(). The file is first searched
1625 : under $MIDAS/exptab if present, then the directory from argv[0] is probed.
1626 : @return CM_SUCCESS<br>
1627 : CM_UNDEF_EXP exptab not found and MIDAS_DIR not set
1628 : */
1629 2 : INT cm_read_exptab(exptab_struct *exptab)
1630 : {
1631 2 : exptab->exptab.clear();
1632 :
1633 : /* MIDAS_DIR overrides exptab */
1634 2 : if (getenv("MIDAS_DIR")) {
1635 0 : exptab->filename = "MIDAS_DIR";
1636 :
1637 0 : exptab_entry e;
1638 :
1639 0 : if (getenv("MIDAS_EXPT_NAME")) {
1640 0 : e.name = getenv("MIDAS_EXPT_NAME");
1641 : } else {
1642 0 : e.name = "Default";
1643 0 : cm_msg(MERROR, "cm_read_exptab", "Experiments that use MIDAS_DIR must also set MIDAS_EXPT_NAME to the name of the experiment! Using experiment name \"%s\"", e.name.c_str());
1644 : }
1645 :
1646 0 : e.directory = getenv("MIDAS_DIR");
1647 0 : e.user = "";
1648 :
1649 0 : exptab->exptab.push_back(e);
1650 :
1651 0 : return CM_SUCCESS;
1652 0 : }
1653 :
1654 : /* default directory for different OSes */
1655 : #if defined (OS_WINNT)
1656 : std::string str;
1657 : if (getenv("SystemRoot"))
1658 : str = getenv("SystemRoot");
1659 : else if (getenv("windir"))
1660 : str = getenv("windir");
1661 : else
1662 : str = "";
1663 :
1664 : std::string alt_str = str;
1665 : str += "\\system32\\exptab";
1666 : alt_str += "\\system\\exptab";
1667 : #elif defined (OS_UNIX)
1668 4 : std::string str = "/etc/exptab";
1669 2 : std::string alt_str = "/exptab";
1670 : #else
1671 : std::strint str = "exptab";
1672 : std::string alt_str = "exptab";
1673 : #endif
1674 :
1675 : /* MIDAS_EXPTAB overrides default directory */
1676 2 : if (getenv("MIDAS_EXPTAB")) {
1677 2 : str = getenv("MIDAS_EXPTAB");
1678 2 : alt_str = getenv("MIDAS_EXPTAB");
1679 : }
1680 :
1681 2 : exptab->filename = str;
1682 :
1683 : /* read list of available experiments */
1684 2 : FILE* f = fopen(str.c_str(), "r");
1685 2 : if (f == NULL) {
1686 0 : f = fopen(alt_str.c_str(), "r");
1687 0 : if (f == NULL)
1688 0 : return CM_UNDEF_ENVIRON;
1689 0 : exptab->filename = alt_str;
1690 : }
1691 :
1692 2 : if (f != NULL) {
1693 : do {
1694 : char buf[256];
1695 4 : memset(buf, 0, sizeof(buf));
1696 4 : char* str = fgets(buf, sizeof(buf)-1, f);
1697 4 : if (str == NULL)
1698 2 : break;
1699 2 : if (str[0] == 0) continue; // empty line
1700 2 : if (str[0] == '#') continue; // comment line
1701 :
1702 2 : exptab_entry e;
1703 :
1704 : // following code emulates the function of this sprintf():
1705 : //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1706 :
1707 : // skip leading spaces
1708 2 : while (*str && isspace(*str))
1709 0 : str++;
1710 :
1711 2 : char* p1 = str;
1712 2 : char* p2 = str;
1713 :
1714 18 : while (*p2 && !isspace(*p2))
1715 16 : p2++;
1716 :
1717 2 : ssize_t len = p2-p1;
1718 :
1719 2 : if (len<1)
1720 0 : continue;
1721 :
1722 : //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1723 :
1724 2 : e.name = std::string(p1, len);
1725 :
1726 2 : if (*p2 == 0)
1727 0 : continue;
1728 :
1729 2 : str = p2;
1730 :
1731 : // skip leading spaces
1732 4 : while (*str && isspace(*str))
1733 2 : str++;
1734 :
1735 2 : p1 = str;
1736 2 : p2 = str;
1737 :
1738 78 : while (*p2 && !isspace(*p2))
1739 76 : p2++;
1740 :
1741 2 : len = p2-p1;
1742 :
1743 2 : if (len<1)
1744 0 : continue;
1745 :
1746 : //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1747 :
1748 2 : e.directory = std::string(p1, len);
1749 :
1750 2 : if (*p2 == 0)
1751 0 : continue;
1752 :
1753 2 : str = p2;
1754 :
1755 : // skip leading spaces
1756 4 : while (*str && isspace(*str))
1757 2 : str++;
1758 :
1759 2 : p1 = str;
1760 2 : p2 = str;
1761 :
1762 18 : while (*p2 && !isspace(*p2))
1763 16 : p2++;
1764 :
1765 2 : len = p2-p1;
1766 :
1767 : //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1768 :
1769 2 : e.user = std::string(p1, len);
1770 :
1771 : /* check for trailing directory separator */
1772 2 : if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1773 2 : e.directory += DIR_SEPARATOR_STR;
1774 : }
1775 :
1776 2 : exptab->exptab.push_back(e);
1777 4 : } while (!feof(f));
1778 2 : fclose(f);
1779 : }
1780 :
1781 : #if 0
1782 : cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1783 : for (unsigned j=0; j<exptab->exptab.size(); j++) {
1784 : cm_msg(MINFO, "cm_read_exptab", "entry %d, experiment \"%s\", directory \"%s\", user \"%s\"", j, exptab->exptab[j].name.c_str(), exptab->exptab[j].directory.c_str(), exptab->exptab[j].user.c_str());
1785 : }
1786 : #endif
1787 :
1788 2 : return CM_SUCCESS;
1789 2 : }
1790 :
1791 : /********************************************************************/
1792 : /**
1793 : Return location of exptab file
1794 : @param s Pointer to string buffer
1795 : @param size Size of string buffer
1796 : @return CM_SUCCESS
1797 : */
1798 0 : int cm_get_exptab_filename(char *s, int size) {
1799 0 : mstrlcpy(s, _exptab.filename.c_str(), size);
1800 0 : return CM_SUCCESS;
1801 : }
1802 :
1803 1 : std::string cm_get_exptab_filename() {
1804 1 : return _exptab.filename;
1805 : }
1806 :
1807 : /********************************************************************/
1808 : /**
1809 : Return exptab information for given experiment
1810 : @param s Pointer to string buffer
1811 : @param size Size of string buffer
1812 : @return CM_SUCCESS
1813 : */
1814 3 : int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1815 :
1816 3 : if (_exptab.exptab.size() == 0) {
1817 0 : int status = cm_read_exptab(&_exptab);
1818 0 : if (status != CM_SUCCESS)
1819 0 : return status;
1820 : }
1821 :
1822 3 : for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1823 3 : if (_exptab.exptab[i].name == expname) {
1824 3 : if (dir)
1825 3 : *dir = _exptab.exptab[i].directory;
1826 3 : if (user)
1827 3 : *user = _exptab.exptab[i].user;
1828 3 : return CM_SUCCESS;
1829 : }
1830 : }
1831 0 : if (dir)
1832 0 : *dir = "";
1833 0 : if (user)
1834 0 : *user = "";
1835 0 : return CM_UNDEF_EXP;
1836 : }
1837 :
1838 : /********************************************************************/
1839 : /**
1840 : Return exptab information for given experiment
1841 : @param s Pointer to string buffer
1842 : @param size Size of string buffer
1843 : @return CM_SUCCESS
1844 : */
1845 0 : int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1846 0 : std::string sdir, suser;
1847 0 : int status = cm_get_exptab(expname, &sdir, &suser);
1848 0 : if (status == CM_SUCCESS) {
1849 0 : if (dir)
1850 0 : mstrlcpy(dir, sdir.c_str(), dir_size);
1851 0 : if (user)
1852 0 : mstrlcpy(user, suser.c_str(), user_size);
1853 0 : return CM_SUCCESS;
1854 : }
1855 0 : return CM_UNDEF_EXP;
1856 0 : }
1857 :
1858 : #endif // LOCAL_ROUTINES
1859 :
1860 : /********************************************************************/
1861 : /**
1862 : Delete client info from database
1863 : @param hDB Database handle
1864 : @param pid PID of entry to delete, zero for this process.
1865 : @return CM_SUCCESS
1866 : */
1867 2 : INT cm_delete_client_info(HNDLE hDB, INT pid) {
1868 : /* only do it if local */
1869 2 : if (!rpc_is_remote()) {
1870 2 : db_delete_client_info(hDB, pid);
1871 : }
1872 2 : return CM_SUCCESS;
1873 : }
1874 :
1875 : /********************************************************************/
1876 : /**
1877 : Check if a client with a /system/client/xxx entry has
1878 : a valid entry in the ODB client table. If not, remove
1879 : that client from the /system/client tree.
1880 : @param hDB Handle to online database
1881 : @param hKeyClient Handle to client key
1882 : @return CM_SUCCESS, CM_NO_CLIENT
1883 : */
1884 0 : INT cm_check_client(HNDLE hDB, HNDLE hKeyClient) {
1885 0 : if (rpc_is_remote())
1886 0 : return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
1887 :
1888 : #ifdef LOCAL_ROUTINES
1889 0 : return db_check_client(hDB, hKeyClient);
1890 : #endif /*LOCAL_ROUTINES */
1891 : return CM_SUCCESS;
1892 : }
1893 :
1894 : /********************************************************************/
1895 : /**
1896 : Set client information in online database and return handle
1897 : @param hDB Handle to online database
1898 : @param hKeyClient returned key
1899 : @param host_name server name
1900 : @param client_name Name of this program as it will be seen
1901 : by other clients.
1902 : @param hw_type Type of byte order
1903 : @param password MIDAS password
1904 : @param watchdog_timeout Default watchdog timeout, can be overwritten
1905 : by ODB setting /programs/\<name\>/Watchdog timeout
1906 : @return CM_SUCCESS
1907 : */
1908 2 : INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name,
1909 : char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1910 2 : if (rpc_is_remote())
1911 0 : return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
1912 0 : host_name, client_name, hw_type, password, watchdog_timeout);
1913 :
1914 : #ifdef LOCAL_ROUTINES
1915 : {
1916 : INT status, pid, data, i, idx, size;
1917 : HNDLE hKey, hSubkey;
1918 : char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
1919 : BOOL call_watchdog, allow;
1920 2 : PROGRAM_INFO_STR(program_info_str);
1921 :
1922 : /* check security if password is present */
1923 2 : status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1924 2 : if (hKey) {
1925 : /* get password */
1926 0 : size = sizeof(pwd);
1927 0 : db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1928 :
1929 : /* first check allowed hosts list */
1930 0 : allow = FALSE;
1931 0 : db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1932 0 : if (hKey && db_find_key(hDB, hKey, host_name, &hKey) == DB_SUCCESS)
1933 0 : allow = TRUE;
1934 :
1935 : /* check allowed programs list */
1936 0 : db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1937 0 : if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
1938 0 : allow = TRUE;
1939 :
1940 : /* now check password */
1941 0 : if (!allow && strcmp(password, pwd) != 0) {
1942 0 : if (password[0])
1943 0 : cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
1944 0 : return CM_WRONG_PASSWORD;
1945 : }
1946 : }
1947 :
1948 : /* make following operation atomic by locking database */
1949 2 : db_lock_database(hDB);
1950 :
1951 : /* check if entry with this pid exists already */
1952 2 : pid = ss_getpid();
1953 :
1954 2 : sprintf(str, "System/Clients/%0d", pid);
1955 2 : status = db_find_key(hDB, 0, str, &hKey);
1956 2 : if (status == DB_SUCCESS) {
1957 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
1958 0 : db_delete_key(hDB, hKey, TRUE);
1959 : }
1960 :
1961 2 : if (strlen(client_name) >= NAME_LENGTH)
1962 0 : client_name[NAME_LENGTH] = 0;
1963 :
1964 2 : strcpy(name, client_name);
1965 2 : strcpy(orig_name, client_name);
1966 :
1967 : /* check if client name already exists */
1968 2 : status = db_find_key(hDB, 0, "System/Clients", &hKey);
1969 :
1970 4 : for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
1971 2 : for (i = 0;; i++) {
1972 4 : status = db_enum_key(hDB, hKey, i, &hSubkey);
1973 4 : if (status == DB_NO_MORE_SUBKEYS)
1974 2 : break;
1975 :
1976 2 : if (status == DB_SUCCESS) {
1977 2 : size = sizeof(str);
1978 2 : status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
1979 2 : if (status != DB_SUCCESS)
1980 2 : continue;
1981 : }
1982 :
1983 : /* check if client is living */
1984 0 : if (cm_check_client(hDB, hSubkey) == CM_NO_CLIENT)
1985 0 : continue;
1986 :
1987 0 : if (equal_ustring(str, name)) {
1988 0 : sprintf(name, "%s%d", client_name, idx);
1989 0 : break;
1990 : }
1991 : }
1992 : }
1993 :
1994 : /* set name */
1995 2 : sprintf(str, "System/Clients/%0d/Name", pid);
1996 2 : status = db_set_value(hDB, 0, str, name, NAME_LENGTH, 1, TID_STRING);
1997 2 : if (status != DB_SUCCESS) {
1998 0 : db_unlock_database(hDB);
1999 0 : cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
2000 0 : return status;
2001 : }
2002 :
2003 : /* copy new client name */
2004 2 : strcpy(client_name, name);
2005 2 : db_set_client_name(hDB, client_name);
2006 :
2007 : /* set also as rpc name */
2008 2 : rpc_set_name(client_name);
2009 :
2010 : /* use /system/clients/PID as root */
2011 2 : sprintf(str, "System/Clients/%0d", pid);
2012 2 : db_find_key(hDB, 0, str, &hKey);
2013 :
2014 : /* set host name */
2015 2 : status = db_set_value(hDB, hKey, "Host", host_name, HOST_NAME_LENGTH, 1, TID_STRING);
2016 2 : if (status != DB_SUCCESS) {
2017 0 : db_unlock_database(hDB);
2018 0 : return status;
2019 : }
2020 :
2021 : /* set computer id */
2022 2 : status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2023 2 : if (status != DB_SUCCESS) {
2024 0 : db_unlock_database(hDB);
2025 0 : return status;
2026 : }
2027 :
2028 : /* set server port */
2029 2 : data = 0;
2030 2 : status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2031 2 : if (status != DB_SUCCESS) {
2032 0 : db_unlock_database(hDB);
2033 0 : return status;
2034 : }
2035 :
2036 : /* lock client entry */
2037 2 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
2038 :
2039 : /* get (set) default watchdog timeout */
2040 2 : size = sizeof(watchdog_timeout);
2041 2 : sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
2042 2 : db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2043 :
2044 : /* define /programs entry */
2045 2 : sprintf(str, "/Programs/%s", orig_name);
2046 2 : db_create_record(hDB, 0, str, strcomb1(program_info_str).c_str());
2047 :
2048 : /* save handle for ODB and client */
2049 2 : cm_set_experiment_database(hDB, hKey);
2050 :
2051 : /* save watchdog timeout */
2052 2 : cm_get_watchdog_params(&call_watchdog, NULL);
2053 2 : cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2054 :
2055 : /* end of atomic operations */
2056 2 : db_unlock_database(hDB);
2057 :
2058 : /* touch notify key to inform others */
2059 2 : data = 0;
2060 2 : db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2061 :
2062 2 : *hKeyClient = hKey;
2063 : }
2064 : #endif /* LOCAL_ROUTINES */
2065 :
2066 2 : return CM_SUCCESS;
2067 : }
2068 :
2069 : /********************************************************************/
2070 : /**
2071 : Get current client name
2072 : @return current client name
2073 : */
2074 2 : std::string cm_get_client_name()
2075 : {
2076 : INT status;
2077 : HNDLE hDB, hKey;
2078 :
2079 : /* get root key of client */
2080 2 : cm_get_experiment_database(&hDB, &hKey);
2081 2 : if (!hDB) {
2082 0 : return "unknown";
2083 : }
2084 :
2085 2 : std::string name;
2086 :
2087 2 : status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2088 2 : if (status != DB_SUCCESS) {
2089 0 : return "unknown";
2090 : }
2091 :
2092 : //printf("get client name: [%s]\n", name.c_str());
2093 :
2094 2 : return name;
2095 2 : }
2096 :
2097 : /********************************************************************/
2098 : /**
2099 : Returns MIDAS environment variables.
2100 : @attention This function can be used to evaluate the standard MIDAS
2101 : environment variables before connecting to an experiment
2102 : (see @ref Environment_variables).
2103 : The usual way is that the host name and experiment name are first derived
2104 : from the environment variables MIDAS_SERVER_HOST and MIDAS_EXPT_NAME.
2105 : They can then be superseded by command line parameters with -h and -e flags.
2106 : \code
2107 : #include <stdio.h>
2108 : #include <midas.h>
2109 : main(int argc, char *argv[])
2110 : {
2111 : INT status, i;
2112 : char host_name[256],exp_name[32];
2113 :
2114 : // get default values from environment
2115 : cm_get_environment(host_name, exp_name);
2116 :
2117 : // parse command line parameters
2118 : for (i=1 ; i<argc ; i++)
2119 : {
2120 : if (argv[i][0] == '-')
2121 : {
2122 : if (i+1 >= argc || argv[i+1][0] == '-')
2123 : goto usage;
2124 : if (argv[i][1] == 'e')
2125 : strcpy(exp_name, argv[++i]);
2126 : else if (argv[i][1] == 'h')
2127 : strcpy(host_name, argv[++i]);
2128 : else
2129 : {
2130 : usage:
2131 : printf("usage: test [-h Hostname] [-e Experiment]\n\n");
2132 : return 1;
2133 : }
2134 : }
2135 : }
2136 : status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
2137 : if (status != CM_SUCCESS)
2138 : return 1;
2139 : ...do anyting...
2140 : cm_disconnect_experiment();
2141 : }
2142 : \endcode
2143 : @param host_name Contents of MIDAS_SERVER_HOST environment variable.
2144 : @param host_name_size string length
2145 : @param exp_name Contents of MIDAS_EXPT_NAME environment variable.
2146 : @param exp_name_size string length
2147 : @return CM_SUCCESS
2148 : */
2149 1 : INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
2150 1 : if (host_name)
2151 1 : host_name[0] = 0;
2152 1 : if (exp_name)
2153 1 : exp_name[0] = 0;
2154 :
2155 1 : if (host_name && getenv("MIDAS_SERVER_HOST"))
2156 0 : mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2157 :
2158 1 : if (exp_name && getenv("MIDAS_EXPT_NAME"))
2159 0 : mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2160 :
2161 1 : return CM_SUCCESS;
2162 : }
2163 :
2164 1 : INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2165 1 : if (host_name)
2166 1 : *host_name = "";
2167 1 : if (exp_name)
2168 1 : *exp_name = "";
2169 :
2170 1 : if (host_name && getenv("MIDAS_SERVER_HOST"))
2171 0 : *host_name = getenv("MIDAS_SERVER_HOST");
2172 :
2173 1 : if (exp_name && getenv("MIDAS_EXPT_NAME"))
2174 0 : *exp_name = getenv("MIDAS_EXPT_NAME");
2175 :
2176 1 : return CM_SUCCESS;
2177 : }
2178 :
2179 : #ifdef LOCAL_ROUTINES
2180 :
2181 2 : int cm_set_experiment_local(const char* exp_name)
2182 : {
2183 2 : std::string exp_name1;
2184 :
2185 2 : if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2186 1 : exp_name1 = exp_name;
2187 : } else {
2188 1 : int status = cm_select_experiment_local(&exp_name1);
2189 1 : if (status != CM_SUCCESS)
2190 0 : return status;
2191 : }
2192 :
2193 2 : std::string expdir, expuser;
2194 :
2195 2 : int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2196 :
2197 2 : if (status != CM_SUCCESS) {
2198 0 : cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" not found in exptab file \"%s\"", exp_name1.c_str(), cm_get_exptab_filename().c_str());
2199 0 : return CM_UNDEF_EXP;
2200 : }
2201 :
2202 2 : if (!ss_dir_exist(expdir.c_str())) {
2203 0 : cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2204 0 : return CM_UNDEF_EXP;
2205 : }
2206 :
2207 2 : cm_set_experiment_name(exp_name1.c_str());
2208 2 : cm_set_path(expdir.c_str());
2209 :
2210 2 : return CM_SUCCESS;
2211 2 : }
2212 :
2213 : #endif // LOCAL_ROUTINES
2214 :
2215 : /********************************************************************/
2216 2 : void cm_check_connect(void) {
2217 2 : if (_hKeyClient) {
2218 0 : cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2219 0 : cm_msg_flush_buffer();
2220 : }
2221 2 : }
2222 :
2223 : /********************************************************************/
2224 : /**
2225 : This function connects to an existing MIDAS experiment.
2226 : This must be the first call in a MIDAS application.
2227 : It opens three TCP connection to the remote host (one for RPC calls,
2228 : one to send events and one for hot-link notifications from the remote host)
2229 : and writes client information into the ODB under /System/Clients.
2230 : @attention All MIDAS applications should evaluate the MIDAS_SERVER_HOST
2231 : and MIDAS_EXPT_NAME environment variables as defaults to the host name and
2232 : experiment name (see @ref Environment_variables).
2233 : For that purpose, the function cm_get_environment()
2234 : should be called prior to cm_connect_experiment(). If command line
2235 : parameters -h and -e are used, the evaluation should be done between
2236 : cm_get_environment() and cm_connect_experiment(). The function
2237 : cm_disconnect_experiment() must be called before a MIDAS application exits.
2238 : \code
2239 : #include <stdio.h>
2240 : #include <midas.h>
2241 : main(int argc, char *argv[])
2242 : {
2243 : INT status, i;
2244 : char host_name[256],exp_name[32];
2245 :
2246 : // get default values from environment
2247 : cm_get_environment(host_name, exp_name);
2248 :
2249 : // parse command line parameters
2250 : for (i=1 ; i<argc ; i++)
2251 : {
2252 : if (argv[i][0] == '-')
2253 : {
2254 : if (i+1 >= argc || argv[i+1][0] == '-')
2255 : goto usage;
2256 : if (argv[i][1] == 'e')
2257 : strcpy(exp_name, argv[++i]);
2258 : else if (argv[i][1] == 'h')
2259 : strcpy(host_name, argv[++i]);
2260 : else
2261 : {
2262 : usage:
2263 : printf("usage: test [-h Hostname] [-e Experiment]\n\n");
2264 : return 1;
2265 : }
2266 : }
2267 : }
2268 : status = cm_connect_experiment(host_name, exp_name, "Test", NULL);
2269 : if (status != CM_SUCCESS)
2270 : return 1;
2271 : ...do operations...
2272 : cm_disconnect_experiment();
2273 : }
2274 : \endcode
2275 : @param host_name Specifies host to connect to. Must be a valid IP host name.
2276 : The string can be empty ("") if to connect to the local computer.
2277 : @param exp_name Specifies the experiment to connect to.
2278 : If this string is empty, the number of defined experiments in exptab is checked.
2279 : If only one experiment is defined, the function automatically connects to this
2280 : one. If more than one experiment is defined, a list is presented and the user
2281 : can interactively select one experiment.
2282 : @param client_name Client name of the calling program as it can be seen by
2283 : others (like the scl command in ODBEdit).
2284 : @param func Callback function to read in a password if security has
2285 : been enabled. In all command line applications this function is NULL which
2286 : invokes an internal ss_gets() function to read in a password.
2287 : In windows environments (MS Windows, X Windows) a function can be supplied to
2288 : open a dialog box and read in the password. The argument of this function must
2289 : be the returned password.
2290 : @return CM_SUCCESS, CM_UNDEF_EXP, CM_SET_ERROR, RPC_NET_ERROR <br>
2291 : CM_VERSION_MISMATCH MIDAS library version different on local and remote computer
2292 : */
2293 0 : INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2294 : INT status;
2295 :
2296 0 : status = cm_connect_experiment1(host_name, exp_name, client_name, func, DEFAULT_ODB_SIZE, DEFAULT_WATCHDOG_TIMEOUT);
2297 0 : cm_msg_flush_buffer();
2298 0 : if (status != CM_SUCCESS) {
2299 0 : std::string s = cm_get_error(status);
2300 0 : puts(s.c_str());
2301 0 : }
2302 :
2303 0 : return status;
2304 : }
2305 :
2306 : /********************************************************************/
2307 : /**
2308 : Connect to a MIDAS experiment (to the online database) on
2309 : a specific host.
2310 : @internal
2311 : */
2312 2 : INT cm_connect_experiment1(const char *host_name, const char *default_exp_name,
2313 : const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2314 : INT status, size;
2315 : char client_name1[NAME_LENGTH];
2316 : char password[NAME_LENGTH], str[256];
2317 2 : HNDLE hDB = 0, hKeyClient = 0;
2318 : BOOL call_watchdog;
2319 :
2320 2 : ss_tzset(); // required for localtime_r()
2321 :
2322 2 : if (_hKeyClient)
2323 0 : cm_disconnect_experiment();
2324 :
2325 2 : cm_msg_early_init();
2326 :
2327 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2328 : //cm_msg_flush_buffer();
2329 :
2330 2 : rpc_set_name(client_name);
2331 :
2332 : /* check for local host */
2333 2 : if (equal_ustring(host_name, "local"))
2334 0 : host_name = NULL;
2335 :
2336 : #ifdef OS_WINNT
2337 : {
2338 : WSADATA WSAData;
2339 :
2340 : /* Start windows sockets */
2341 : if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2342 : return RPC_NET_ERROR;
2343 : }
2344 : #endif
2345 :
2346 2 : std::string default_exp_name1;
2347 2 : if (default_exp_name)
2348 2 : default_exp_name1 = default_exp_name;
2349 :
2350 : /* connect to MIDAS server */
2351 2 : if (host_name && host_name[0]) {
2352 0 : if (default_exp_name1.length() == 0) {
2353 0 : status = cm_select_experiment_remote(host_name, &default_exp_name1);
2354 0 : if (status != CM_SUCCESS)
2355 0 : return status;
2356 : }
2357 :
2358 0 : cm_set_experiment_name(default_exp_name1.c_str());
2359 :
2360 0 : status = rpc_server_connect(host_name, default_exp_name1.c_str());
2361 0 : if (status != RPC_SUCCESS)
2362 0 : return status;
2363 :
2364 : /* register MIDAS library functions */
2365 0 : status = rpc_register_functions(rpc_get_internal_list(1), NULL);
2366 0 : if (status != RPC_SUCCESS)
2367 0 : return status;
2368 : } else {
2369 : /* lookup path for *SHM files and save it */
2370 :
2371 : #ifdef LOCAL_ROUTINES
2372 2 : status = cm_set_experiment_local(default_exp_name1.c_str());
2373 2 : if (status != CM_SUCCESS)
2374 0 : return status;
2375 :
2376 2 : default_exp_name1 = cm_get_experiment_name();
2377 :
2378 2 : ss_suspend_init_odb_port();
2379 :
2380 : INT semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg;
2381 :
2382 : /* create alarm and elog semaphores */
2383 2 : status = ss_semaphore_create("ALARM", &semaphore_alarm);
2384 2 : if (status != SS_CREATED && status != SS_SUCCESS) {
2385 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2386 0 : return status;
2387 : }
2388 2 : status = ss_semaphore_create("ELOG", &semaphore_elog);
2389 2 : if (status != SS_CREATED && status != SS_SUCCESS) {
2390 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2391 0 : return status;
2392 : }
2393 2 : status = ss_semaphore_create("HISTORY", &semaphore_history);
2394 2 : if (status != SS_CREATED && status != SS_SUCCESS) {
2395 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2396 0 : return status;
2397 : }
2398 2 : status = ss_semaphore_create("MSG", &semaphore_msg);
2399 2 : if (status != SS_CREATED && status != SS_SUCCESS) {
2400 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2401 0 : return status;
2402 : }
2403 :
2404 2 : cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
2405 : #else
2406 : return CM_UNDEF_EXP;
2407 : #endif
2408 : }
2409 :
2410 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2411 : //cm_msg_flush_buffer();
2412 :
2413 : /* open ODB */
2414 2 : if (odb_size == 0)
2415 1 : odb_size = DEFAULT_ODB_SIZE;
2416 :
2417 2 : status = db_open_database("ODB", odb_size, &hDB, client_name);
2418 2 : if (status != DB_SUCCESS && status != DB_CREATED) {
2419 0 : cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2420 0 : return status;
2421 : }
2422 :
2423 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2424 : //cm_msg_flush_buffer();
2425 :
2426 2 : int odb_timeout = db_set_lock_timeout(hDB, 0);
2427 2 : size = sizeof(odb_timeout);
2428 2 : status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2429 2 : if (status != DB_SUCCESS) {
2430 0 : cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2431 : }
2432 :
2433 2 : if (odb_timeout > 0) {
2434 2 : db_set_lock_timeout(hDB, odb_timeout);
2435 : }
2436 :
2437 2 : BOOL protect_odb = FALSE;
2438 2 : size = sizeof(protect_odb);
2439 2 : status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2440 2 : if (status != DB_SUCCESS) {
2441 0 : cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2442 : }
2443 :
2444 2 : if (protect_odb) {
2445 0 : db_protect_database(hDB);
2446 : }
2447 :
2448 2 : BOOL enable_core_dumps = FALSE;
2449 2 : size = sizeof(enable_core_dumps);
2450 2 : status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2451 2 : if (status != DB_SUCCESS) {
2452 0 : cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2453 : }
2454 :
2455 2 : if (enable_core_dumps) {
2456 : #ifdef RLIMIT_CORE
2457 : struct rlimit limit;
2458 0 : limit.rlim_cur = RLIM_INFINITY;
2459 0 : limit.rlim_max = RLIM_INFINITY;
2460 0 : status = setrlimit(RLIMIT_CORE, &limit);
2461 0 : if (status != 0) {
2462 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2463 0 : strerror(errno));
2464 : }
2465 : #else
2466 : #warning setrlimit(RLIMIT_CORE) is not available
2467 : #endif
2468 : }
2469 :
2470 2 : size = sizeof(disable_bind_rpc_to_localhost);
2471 2 : status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2472 : TID_BOOL, TRUE);
2473 2 : if (status != DB_SUCCESS) {
2474 0 : cm_msg(MERROR, "cm_connect_experiment1",
2475 : "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2476 : }
2477 :
2478 2 : std::string local_host_name;
2479 :
2480 : /* now setup client info */
2481 2 : if (!disable_bind_rpc_to_localhost)
2482 2 : local_host_name = "localhost";
2483 : else
2484 0 : local_host_name = ss_gethostname();
2485 :
2486 : /* check watchdog timeout */
2487 2 : if (watchdog_timeout == 0)
2488 0 : watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2489 :
2490 2 : strcpy(client_name1, client_name);
2491 2 : password[0] = 0;
2492 2 : status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2493 :
2494 2 : if (status == CM_WRONG_PASSWORD) {
2495 0 : if (func == NULL)
2496 0 : strcpy(str, ss_getpass("Password: "));
2497 : else
2498 0 : func(str);
2499 :
2500 0 : strcpy(password, ss_crypt(str, "mi"));
2501 0 : status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2502 0 : if (status != CM_SUCCESS) {
2503 : /* disconnect */
2504 0 : if (rpc_is_remote())
2505 0 : rpc_server_disconnect();
2506 0 : cm_disconnect_experiment();
2507 :
2508 0 : return status;
2509 : }
2510 : }
2511 :
2512 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2513 : //cm_msg_flush_buffer();
2514 :
2515 : /* tell the rest of MIDAS that ODB is open for business */
2516 :
2517 2 : cm_set_experiment_database(hDB, hKeyClient);
2518 :
2519 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2520 : //cm_msg_flush_buffer();
2521 :
2522 : /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2523 : * to get event buffer size, etc */
2524 :
2525 2 : status = cm_msg_open_buffer();
2526 2 : if (status != CM_SUCCESS) {
2527 0 : cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2528 0 : return status;
2529 : }
2530 :
2531 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2532 : //cm_msg_flush_buffer();
2533 :
2534 : /* set experiment name in ODB if not present */
2535 2 : std::string current_name;
2536 2 : db_get_value_string(hDB, 0, "/Experiment/Name", 0, ¤t_name, TRUE);
2537 2 : if (current_name.length() == 0 || current_name == "Default") {
2538 1 : db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2539 : }
2540 :
2541 2 : if (!rpc_is_remote()) {
2542 : /* experiment path is only set for local connections */
2543 : /* set data dir in ODB */
2544 2 : std::string path = cm_get_path();
2545 2 : db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2546 2 : }
2547 :
2548 : /* register server to be able to be called by other clients */
2549 2 : status = cm_register_server();
2550 2 : if (status != CM_SUCCESS) {
2551 0 : cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2552 0 : if (!equal_ustring(client_name, "odbedit")) {
2553 0 : return status;
2554 : }
2555 : }
2556 :
2557 : /* set watchdog timeout */
2558 2 : cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2559 2 : size = sizeof(watchdog_timeout);
2560 2 : sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2561 2 : db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2562 2 : cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2563 :
2564 : /* set command line */
2565 2 : std::string cmdline = ss_get_cmdline();
2566 2 : std::string path = "/Programs/" + std::string(client_name);
2567 2 : midas::odb prog(path);
2568 6 : if (!midas::odb::exists(path + "/Start command") ||
2569 6 : prog["Start command"] == std::string(""))
2570 2 : prog["Start command"].set_string_size(cmdline, 256);
2571 :
2572 : /* get final client name */
2573 2 : std::string xclient_name = rpc_get_name();
2574 :
2575 : /* startup message is not displayed */
2576 2 : cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2577 :
2578 : /* enable system and user messages to stdout as default */
2579 2 : cm_set_msg_print(MT_ALL, MT_ALL, puts);
2580 :
2581 : /* call cm_check_connect when exiting */
2582 2 : atexit((void (*)(void)) cm_check_connect);
2583 :
2584 : /* register ctrl-c handler */
2585 2 : ss_ctrlc_handler(cm_ctrlc_handler);
2586 :
2587 : //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2588 : //cm_msg_flush_buffer();
2589 :
2590 2 : return CM_SUCCESS;
2591 2 : }
2592 :
2593 : #ifdef LOCAL_ROUTINES
2594 : /********************************************************************/
2595 : /**
2596 : Read exptab and return all defined experiments in *exp_name[MAX_EXPERIMENTS]
2597 : @param host_name Internet host name.
2598 : @param exp_name list of experiment names
2599 : @return CM_SUCCESS, RPC_NET_ERROR
2600 : */
2601 2 : INT cm_list_experiments_local(STRING_LIST *exp_names) {
2602 2 : assert(exp_names != NULL);
2603 2 : exp_names->clear();
2604 :
2605 2 : if (_exptab.exptab.size() == 0) {
2606 2 : int status = cm_read_exptab(&_exptab);
2607 2 : if (status != CM_SUCCESS)
2608 0 : return status;
2609 : }
2610 :
2611 4 : for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2612 2 : exp_names->push_back(_exptab.exptab[i].name);
2613 : }
2614 :
2615 2 : return CM_SUCCESS;
2616 : }
2617 : #endif // LOCAL_ROUTINES
2618 :
2619 : /********************************************************************/
2620 : /**
2621 : Connect to a MIDAS server and return all defined
2622 : experiments in *exp_name[MAX_EXPERIMENTS]
2623 : @param host_name Internet host name.
2624 : @param exp_name list of experiment names
2625 : @return CM_SUCCESS, RPC_NET_ERROR
2626 : */
2627 0 : INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names) {
2628 : INT status;
2629 : INT sock;
2630 0 : int port = MIDAS_TCP_PORT;
2631 : char hname[256];
2632 : char *s;
2633 :
2634 0 : assert(exp_names != NULL);
2635 0 : exp_names->clear();
2636 :
2637 : /* extract port number from host_name */
2638 0 : mstrlcpy(hname, host_name, sizeof(hname));
2639 0 : s = strchr(hname, ':');
2640 0 : if (s) {
2641 0 : *s = 0;
2642 0 : port = strtoul(s + 1, NULL, 0);
2643 : }
2644 :
2645 0 : std::string errmsg;
2646 :
2647 0 : status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2648 :
2649 0 : if (status != SS_SUCCESS) {
2650 0 : cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2651 0 : return RPC_NET_ERROR;
2652 : }
2653 :
2654 : /* request experiment list */
2655 0 : send(sock, "I", 2, 0);
2656 :
2657 : while (1) {
2658 : char str[256];
2659 :
2660 0 : status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2661 :
2662 0 : if (status < 0)
2663 0 : return RPC_NET_ERROR;
2664 :
2665 0 : if (status == 0)
2666 0 : break;
2667 :
2668 0 : exp_names->push_back(str);
2669 0 : }
2670 :
2671 0 : ss_socket_close(&sock);
2672 :
2673 0 : return CM_SUCCESS;
2674 0 : }
2675 :
2676 : #ifdef LOCAL_ROUTINES
2677 : /********************************************************************/
2678 : /**
2679 : Read exptab and select an experiment
2680 : from the experiments available on this server
2681 : @internal
2682 : @param exp_name selected experiment name
2683 : @return CM_SUCCESS
2684 : */
2685 1 : INT cm_select_experiment_local(std::string *exp_name) {
2686 : INT status;
2687 1 : STRING_LIST expts;
2688 :
2689 1 : assert(exp_name != NULL);
2690 :
2691 : /* retrieve list of experiments and make selection */
2692 1 : status = cm_list_experiments_local(&expts);
2693 1 : if (status != CM_SUCCESS)
2694 0 : return status;
2695 :
2696 1 : if (expts.size() == 1) {
2697 1 : *exp_name = expts[0];
2698 0 : } else if (expts.size() > 1) {
2699 0 : printf("Available experiments on local computer:\n");
2700 :
2701 0 : for (unsigned i = 0; i < expts.size(); i++) {
2702 0 : printf("%d : %s\n", i, expts[i].c_str());
2703 : }
2704 :
2705 : while (1) {
2706 0 : printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2707 : char str[32];
2708 0 : ss_gets(str, 32);
2709 0 : int isel = atoi(str);
2710 0 : if (isel < 0)
2711 0 : continue;
2712 0 : if (isel >= (int)expts.size())
2713 0 : continue;
2714 0 : *exp_name = expts[isel];
2715 0 : break;
2716 0 : }
2717 : } else {
2718 0 : return CM_UNDEF_EXP;
2719 : }
2720 :
2721 1 : return CM_SUCCESS;
2722 1 : }
2723 : #endif // LOCAL_ROUTINES
2724 :
2725 : /********************************************************************/
2726 : /**
2727 : Connect to a MIDAS server and select an experiment
2728 : from the experiments available on this server
2729 : @internal
2730 : @param host_name Internet host name.
2731 : @param exp_name selected experiment name
2732 : @return CM_SUCCESS, RPC_NET_ERROR
2733 : */
2734 0 : INT cm_select_experiment_remote(const char *host_name, std::string *exp_name) {
2735 : INT status;
2736 0 : STRING_LIST expts;
2737 :
2738 0 : assert(exp_name != NULL);
2739 :
2740 : /* retrieve list of experiments and make selection */
2741 0 : status = cm_list_experiments_remote(host_name, &expts);
2742 0 : if (status != CM_SUCCESS)
2743 0 : return status;
2744 :
2745 0 : if (expts.size() > 1) {
2746 0 : printf("Available experiments on server %s:\n", host_name);
2747 :
2748 0 : for (unsigned i = 0; i < expts.size(); i++) {
2749 0 : printf("%d : %s\n", i, expts[i].c_str());
2750 : }
2751 :
2752 : while (1) {
2753 0 : printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2754 : char str[32];
2755 0 : ss_gets(str, 32);
2756 0 : int isel = atoi(str);
2757 0 : if (isel < 0)
2758 0 : continue;
2759 0 : if (isel >= (int)expts.size())
2760 0 : continue;
2761 0 : *exp_name = expts[isel];
2762 0 : break;
2763 0 : }
2764 : } else {
2765 0 : *exp_name = expts[0];
2766 : }
2767 :
2768 0 : return CM_SUCCESS;
2769 0 : }
2770 :
2771 : /********************************************************************/
2772 : /**
2773 : Connect to a MIDAS client of the current experiment
2774 : @internal
2775 : @param client_name Name of client to connect to. This name
2776 : is set by the other client via the
2777 : cm_connect_experiment call.
2778 : @param hConn Connection handle
2779 : @return CM_SUCCESS, CM_NO_CLIENT
2780 : */
2781 0 : INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2782 : HNDLE hDB, hKeyRoot, hSubkey, hKey;
2783 : INT status, i, length, port;
2784 : char name[NAME_LENGTH], host_name[HOST_NAME_LENGTH];
2785 :
2786 : /* find client entry in ODB */
2787 0 : cm_get_experiment_database(&hDB, &hKey);
2788 :
2789 0 : status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2790 0 : if (status != DB_SUCCESS)
2791 0 : return status;
2792 :
2793 0 : i = 0;
2794 : do {
2795 : /* search for client with specific name */
2796 0 : status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
2797 0 : if (status == DB_NO_MORE_SUBKEYS)
2798 0 : return CM_NO_CLIENT;
2799 :
2800 0 : status = db_find_key(hDB, hSubkey, "Name", &hKey);
2801 0 : if (status != DB_SUCCESS)
2802 0 : return status;
2803 :
2804 0 : length = NAME_LENGTH;
2805 0 : status = db_get_data(hDB, hKey, name, &length, TID_STRING);
2806 0 : if (status != DB_SUCCESS)
2807 0 : return status;
2808 :
2809 0 : if (equal_ustring(name, client_name)) {
2810 0 : status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2811 0 : if (status != DB_SUCCESS)
2812 0 : return status;
2813 :
2814 0 : length = sizeof(INT);
2815 0 : status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2816 0 : if (status != DB_SUCCESS)
2817 0 : return status;
2818 :
2819 0 : status = db_find_key(hDB, hSubkey, "Host", &hKey);
2820 0 : if (status != DB_SUCCESS)
2821 0 : return status;
2822 :
2823 0 : length = sizeof(host_name);
2824 0 : status = db_get_data(hDB, hKey, host_name, &length, TID_STRING);
2825 0 : if (status != DB_SUCCESS)
2826 0 : return status;
2827 :
2828 : /* client found -> connect to its server port */
2829 0 : return rpc_client_connect(host_name, port, client_name, hConn);
2830 : }
2831 :
2832 :
2833 : } while (TRUE);
2834 : }
2835 :
2836 : static void rpc_client_shutdown();
2837 :
2838 : /********************************************************************/
2839 : /**
2840 : Disconnect from a MIDAS client
2841 : @param hConn Connection handle obtained via
2842 : cm_connect_client()
2843 : @param bShutdown If TRUE, disconnect from client and
2844 : shut it down (exit the client program)
2845 : by sending a RPC_SHUTDOWN message
2846 : @return see rpc_client_disconnect()
2847 : */
2848 0 : INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown) {
2849 0 : return rpc_client_disconnect(hConn, bShutdown);
2850 : }
2851 :
2852 : /********************************************************************/
2853 : /**
2854 : Disconnect from a MIDAS experiment.
2855 : @attention Should be the last call to a MIDAS library function in an
2856 : application before it exits. This function removes the client information
2857 : from the ODB, disconnects all TCP connections and frees all internal
2858 : allocated memory. See cm_connect_experiment() for example.
2859 : @return CM_SUCCESS
2860 : */
2861 2 : INT cm_disconnect_experiment(void) {
2862 : HNDLE hDB, hKey;
2863 :
2864 : //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2865 : //cm_msg_flush_buffer();
2866 :
2867 : /* wait on any transition thread */
2868 2 : if (_trp.transition && !_trp.finished) {
2869 0 : printf("Waiting for transition to finish...\n");
2870 : do {
2871 0 : ss_sleep(10);
2872 0 : } while (!_trp.finished);
2873 : }
2874 :
2875 : /* stop the watchdog thread */
2876 2 : cm_stop_watchdog_thread();
2877 :
2878 : /* send shutdown notification */
2879 2 : std::string client_name = rpc_get_name();
2880 :
2881 2 : std::string local_host_name;
2882 :
2883 2 : if (!disable_bind_rpc_to_localhost)
2884 2 : local_host_name = "localhost";
2885 : else {
2886 0 : local_host_name = ss_gethostname();
2887 : //if (strchr(local_host_name, '.'))
2888 : // *strchr(local_host_name, '.') = 0;
2889 : }
2890 :
2891 : /* disconnect message not displayed */
2892 2 : cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2893 2 : cm_msg_flush_buffer();
2894 :
2895 2 : if (rpc_is_remote()) {
2896 0 : if (rpc_is_connected()) {
2897 : /* close open records */
2898 0 : db_close_all_records();
2899 :
2900 0 : cm_msg_close_buffer();
2901 : }
2902 :
2903 0 : rpc_client_shutdown();
2904 0 : rpc_server_disconnect();
2905 :
2906 0 : cm_set_experiment_database(0, 0);
2907 : } else {
2908 2 : rpc_client_shutdown();
2909 :
2910 : /* delete client info */
2911 2 : cm_get_experiment_database(&hDB, &hKey);
2912 :
2913 2 : if (hDB)
2914 2 : cm_delete_client_info(hDB, 0);
2915 :
2916 : //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2917 : //cm_msg_flush_buffer();
2918 :
2919 2 : cm_msg_close_buffer();
2920 2 : bm_close_all_buffers();
2921 2 : db_close_all_databases();
2922 :
2923 2 : cm_set_experiment_database(0, 0);
2924 :
2925 : //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2926 : //cm_msg_flush_buffer();
2927 : }
2928 :
2929 2 : if (!rpc_is_mserver())
2930 2 : rpc_server_shutdown();
2931 :
2932 : /* free RPC list */
2933 2 : rpc_deregister_functions();
2934 :
2935 : //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2936 : //cm_msg_flush_buffer();
2937 :
2938 : /* last flush before we delete the message ring buffer */
2939 2 : cm_msg_flush_buffer();
2940 :
2941 : //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2942 : //cm_msg_flush_buffer();
2943 :
2944 2 : return CM_SUCCESS;
2945 2 : }
2946 :
2947 : /********************************************************************/
2948 : /**
2949 : Set the handle to the ODB for the currently connected experiment
2950 : @param hDB Database handle
2951 : @param hKeyClient Key handle of client structure
2952 : @return CM_SUCCESS
2953 : */
2954 6 : INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient) {
2955 : //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
2956 :
2957 6 : _hDB = hDB;
2958 6 : _hKeyClient = hKeyClient;
2959 :
2960 : //if (hDB == 0) {
2961 : // rpc_set_server_option(RPC_ODB_HANDLE, 0);
2962 : //}
2963 :
2964 6 : return CM_SUCCESS;
2965 : }
2966 :
2967 :
2968 :
2969 : /**dox***************************************************************/
2970 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
2971 :
2972 : /********************************************************************/
2973 2 : INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
2974 : /********************************************************************\
2975 :
2976 : Routine: cm_set_experiment_semaphore
2977 :
2978 : Purpose: Set the handle to the experiment wide semaphorees
2979 :
2980 : Input:
2981 : INT semaphore_alarm Alarm semaphore
2982 : INT semaphore_elog Elog semaphore
2983 : INT semaphore_history History semaphore
2984 : INT semaphore_msg Message semaphore
2985 :
2986 : Output:
2987 : none
2988 :
2989 : Function value:
2990 : CM_SUCCESS Successful completion
2991 :
2992 : \********************************************************************/
2993 : {
2994 2 : _semaphore_alarm = semaphore_alarm;
2995 2 : _semaphore_elog = semaphore_elog;
2996 2 : _semaphore_history = semaphore_history;
2997 : //_semaphore_msg = semaphore_msg;
2998 :
2999 2 : return CM_SUCCESS;
3000 : }
3001 :
3002 : /**dox***************************************************************/
3003 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
3004 :
3005 : /********************************************************************/
3006 : /**
3007 : Get the handle to the ODB from the currently connected experiment.
3008 :
3009 : @attention This function returns the handle of the online database (ODB) which
3010 : can be used in future db_xxx() calls. The hkeyclient key handle can be used
3011 : to access the client information in the ODB. If the client key handle is not needed,
3012 : the parameter can be NULL.
3013 : \code
3014 : HNDLE hDB, hkeyclient;
3015 : char name[32];
3016 : int size;
3017 : db_get_experiment_database(&hdb, &hkeyclient);
3018 : size = sizeof(name);
3019 : db_get_value(hdb, hkeyclient, "Name", name, &size, TID_STRING, TRUE);
3020 : printf("My name is %s\n", name);
3021 : \endcode
3022 : @param hDB Database handle.
3023 : @param hKeyClient Handle for key where search starts, zero for root.
3024 : @return CM_SUCCESS
3025 : */
3026 36 : INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient) {
3027 36 : if (_hDB) {
3028 : //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3029 36 : if (hDB != NULL)
3030 36 : *hDB = _hDB;
3031 36 : if (hKeyClient != NULL)
3032 13 : *hKeyClient = _hKeyClient;
3033 36 : return CM_SUCCESS;
3034 : } else {
3035 : //printf("cm_get_experiment_database no init\n");
3036 0 : if (hDB != NULL)
3037 0 : *hDB = 0;
3038 0 : if (hKeyClient != NULL)
3039 0 : *hKeyClient = 0;
3040 0 : return CM_DB_ERROR;
3041 : }
3042 : }
3043 :
3044 : /**dox***************************************************************/
3045 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
3046 :
3047 : /********************************************************************/
3048 0 : INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
3049 : /********************************************************************\
3050 :
3051 : Routine: cm_get_experiment_semaphore
3052 :
3053 : Purpose: Get the handle to the experiment wide semaphores
3054 :
3055 : Input:
3056 : none
3057 :
3058 : Output:
3059 : INT semaphore_alarm Alarm semaphore
3060 : INT semaphore_elog Elog semaphore
3061 : INT semaphore_history History semaphore
3062 : INT semaphore_msg Message semaphore
3063 :
3064 : Function value:
3065 : CM_SUCCESS Successful completion
3066 :
3067 : \********************************************************************/
3068 : {
3069 0 : if (semaphore_alarm)
3070 0 : *semaphore_alarm = _semaphore_alarm;
3071 0 : if (semaphore_elog)
3072 0 : *semaphore_elog = _semaphore_elog;
3073 0 : if (semaphore_history)
3074 0 : *semaphore_history = _semaphore_history;
3075 : //if (semaphore_msg)
3076 : // *semaphore_msg = _semaphore_msg;
3077 0 : if (semaphore_msg)
3078 0 : *semaphore_msg = -1;
3079 :
3080 0 : return CM_SUCCESS;
3081 : }
3082 :
3083 : /**dox***************************************************************/
3084 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
3085 :
3086 : #ifdef LOCAL_ROUTINES
3087 : static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3088 : static int bm_lock_buffer_read_cache(BUFFER *pbuf);
3089 : static int bm_lock_buffer_write_cache(BUFFER *pbuf);
3090 : static int bm_lock_buffer_mutex(BUFFER *pbuf);
3091 : static int xbm_lock_buffer(BUFFER *pbuf);
3092 : static void xbm_unlock_buffer(BUFFER *pbuf);
3093 :
3094 : class bm_lock_buffer_guard
3095 : {
3096 : public:
3097 : bool fDebug = false;
3098 :
3099 : public:
3100 25 : bm_lock_buffer_guard(BUFFER* pbuf, bool do_not_lock=false) // ctor
3101 25 : {
3102 25 : assert(pbuf != NULL);
3103 25 : fBuf = pbuf;
3104 25 : if (do_not_lock) {
3105 0 : if (fDebug)
3106 0 : printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3107 0 : return;
3108 : }
3109 25 : if (fDebug)
3110 0 : printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3111 25 : int status = xbm_lock_buffer(fBuf);
3112 25 : if (status != BM_SUCCESS) {
3113 0 : fLocked = false;
3114 0 : fError = true;
3115 0 : fStatus = status;
3116 : } else {
3117 25 : fLocked = true;
3118 : }
3119 : }
3120 :
3121 25 : ~bm_lock_buffer_guard() // dtor
3122 : {
3123 25 : if (fInvalid) {
3124 0 : if (fDebug)
3125 0 : printf("lock_buffer_guard(invalid) dtor\n");
3126 : } else {
3127 25 : assert(fBuf != NULL);
3128 25 : if (fDebug)
3129 0 : printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3130 25 : if (fLocked) {
3131 17 : xbm_unlock_buffer(fBuf);
3132 17 : fLocked = false;
3133 17 : fError = false;
3134 : }
3135 25 : fBuf = NULL;
3136 : }
3137 25 : }
3138 :
3139 : // make object uncopyable
3140 : bm_lock_buffer_guard(const bm_lock_buffer_guard&) = delete;
3141 : bm_lock_buffer_guard& operator=(const bm_lock_buffer_guard&) = delete;
3142 :
3143 8 : void unlock()
3144 : {
3145 8 : assert(fBuf != NULL);
3146 8 : if (fDebug)
3147 0 : printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3148 8 : assert(fLocked);
3149 8 : xbm_unlock_buffer(fBuf);
3150 8 : fLocked = false;
3151 8 : fError = false;
3152 8 : }
3153 :
3154 0 : bool relock()
3155 : {
3156 0 : assert(fBuf != NULL);
3157 0 : if (fDebug)
3158 0 : printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3159 0 : assert(!fLocked);
3160 0 : int status = xbm_lock_buffer(fBuf);
3161 0 : if (status != BM_SUCCESS) {
3162 0 : fLocked = false;
3163 0 : fError = true;
3164 0 : fStatus = status;
3165 : } else {
3166 0 : fLocked = true;
3167 : }
3168 0 : return fLocked;
3169 : }
3170 :
3171 0 : void invalidate()
3172 : {
3173 0 : assert(fBuf != NULL);
3174 0 : if (fDebug)
3175 0 : printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3176 0 : assert(!fLocked);
3177 0 : fInvalid = true;
3178 0 : fBuf = NULL;
3179 0 : }
3180 :
3181 25 : bool is_locked() const
3182 : {
3183 25 : return fLocked;
3184 : }
3185 :
3186 : bool is_error() const
3187 : {
3188 : return fError;
3189 : }
3190 :
3191 0 : int get_status() const
3192 : {
3193 0 : return fStatus;
3194 : }
3195 :
3196 23 : BUFFER* get_pbuf() const
3197 : {
3198 23 : assert(!fInvalid); // pbuf was deleted
3199 23 : assert(fBuf); // we do not return NULL
3200 23 : return fBuf;
3201 : }
3202 :
3203 : private:
3204 : BUFFER* fBuf = NULL;
3205 : bool fLocked = false;
3206 : bool fError = false;
3207 : bool fInvalid = false;
3208 : int fStatus = 0;
3209 : };
3210 :
3211 : static BUFFER_CLIENT *bm_get_my_client_locked(bm_lock_buffer_guard& pbuf_guard);
3212 :
3213 : #endif
3214 :
3215 : static INT bm_notify_client(const char *buffer_name, int s);
3216 :
3217 : static INT bm_push_event(const char *buffer_name);
3218 :
3219 : static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3220 : EVENT_HEADER *pevent, void *pdata,
3221 : EVENT_HANDLER *dispatcher);
3222 :
3223 : /********************************************************************/
3224 : /**
3225 : Sets the internal watchdog flags and the own timeout.
3226 : If call_watchdog is TRUE, the cm_watchdog routine is called
3227 : periodically from the system to show other clients that
3228 : this application is "alive". On UNIX systems, the
3229 : alarm() timer is used which is then not available for
3230 : user purposes.
3231 :
3232 : The timeout specifies the time, after which the calling
3233 : application should be considered "dead" by other clients.
3234 : Normally, the cm_watchdog() routines is called periodically.
3235 : If a client crashes, this does not occur any more. Then
3236 : other clients can detect this and clear all buffer and
3237 : database entries of this application so they are not
3238 : blocked any more. If this application should not checked
3239 : by others, the timeout can be specified as zero.
3240 : It might be useful for debugging purposes to do so,
3241 : because if a debugger comes to a breakpoint and stops
3242 : the application, the periodic call of cm_watchdog
3243 : is disabled and the client looks like dead.
3244 :
3245 : If the timeout is not zero, but the watchdog is not
3246 : called (call_watchdog == FALSE), the user must ensure
3247 : to call cm_watchdog periodically with a period of
3248 : WATCHDOG_INTERVAL milliseconds or less.
3249 :
3250 : An application which calles system routines which block
3251 : the alarm signal for some time, might increase the
3252 : timeout to the maximum expected blocking time before
3253 : issuing the calls. One example is the logger doing
3254 : Exabyte tape IO, which can take up to one minute.
3255 : @param call_watchdog Call the cm_watchdog routine periodically
3256 : @param timeout Timeout for this application in ms
3257 : @return CM_SUCCESS
3258 : */
3259 4 : INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
3260 : {
3261 : #ifdef LOCAL_ROUTINES
3262 4 : _watchdog_timeout = timeout;
3263 :
3264 4 : std::vector<BUFFER*> mybuffers;
3265 :
3266 4 : gBuffersMutex.lock();
3267 4 : mybuffers = gBuffers;
3268 4 : gBuffersMutex.unlock();
3269 :
3270 : /* set watchdog timeout of all open buffers */
3271 6 : for (BUFFER* pbuf : mybuffers) {
3272 :
3273 2 : if (!pbuf || !pbuf->attached)
3274 0 : continue;
3275 :
3276 2 : bm_lock_buffer_guard pbuf_guard(pbuf);
3277 :
3278 2 : if (!pbuf_guard.is_locked())
3279 0 : continue;
3280 :
3281 2 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
3282 :
3283 : /* clear entry from client structure in buffer header */
3284 2 : pclient->watchdog_timeout = timeout;
3285 :
3286 : /* show activity */
3287 2 : pclient->last_activity = ss_millitime();
3288 2 : }
3289 :
3290 : /* set watchdog timeout for ODB */
3291 4 : db_set_watchdog_params(timeout);
3292 :
3293 : #endif /* LOCAL_ROUTINES */
3294 :
3295 4 : return CM_SUCCESS;
3296 4 : }
3297 :
3298 4 : INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
3299 : {
3300 : /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3301 4 : _watchdog_timeout = timeout;
3302 :
3303 4 : if (rpc_is_remote()) { // we are connected remotely
3304 :
3305 0 : return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
3306 :
3307 4 : } else if (rpc_is_mserver()) { // we are the mserver
3308 :
3309 0 : RPC_SERVER_ACCEPTION* sa = rpc_get_mserver_acception();
3310 0 : if (sa)
3311 0 : sa->watchdog_timeout = timeout;
3312 :
3313 : /* write timeout value to client entry in ODB */
3314 : HNDLE hDB, hKey;
3315 0 : cm_get_experiment_database(&hDB, &hKey);
3316 :
3317 0 : if (hDB) {
3318 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
3319 0 : db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3320 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3321 : }
3322 :
3323 : /* set the watchdog for the local mserver program */
3324 0 : return cm_set_watchdog_params_local(call_watchdog, timeout);
3325 :
3326 : } else { // only running locally
3327 :
3328 4 : return cm_set_watchdog_params_local(call_watchdog, timeout);
3329 :
3330 : }
3331 : }
3332 :
3333 : /********************************************************************/
3334 : /**
3335 : Return the current watchdog parameters
3336 : @param call_watchdog Call the cm_watchdog routine periodically
3337 : @param timeout Timeout for this application in seconds
3338 : @return CM_SUCCESS
3339 : */
3340 9 : INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout) {
3341 9 : if (call_watchdog)
3342 7 : *call_watchdog = FALSE;
3343 9 : if (timeout)
3344 7 : *timeout = _watchdog_timeout;
3345 :
3346 9 : return CM_SUCCESS;
3347 : }
3348 :
3349 : /********************************************************************/
3350 : /**
3351 : Return watchdog information about specific client
3352 : @param hDB ODB handle
3353 : @param client_name ODB client name
3354 : @param timeout Timeout for this application in seconds
3355 : @param last Last time watchdog was called in msec
3356 : @return CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
3357 : */
3358 :
3359 0 : INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3360 0 : if (rpc_is_remote())
3361 0 : return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3362 :
3363 : #ifdef LOCAL_ROUTINES
3364 0 : return db_get_watchdog_info(hDB, client_name, timeout, last);
3365 : #else /* LOCAL_ROUTINES */
3366 : return CM_SUCCESS;
3367 : #endif /* LOCAL_ROUTINES */
3368 : }
3369 :
3370 :
3371 : /**dox***************************************************************/
3372 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
3373 :
3374 : /********************************************************************/
3375 :
3376 2 : static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3377 : int status;
3378 : int i, last;
3379 : KEY key;
3380 : int max_size;
3381 : char *str;
3382 :
3383 : // if (index != -99)
3384 : // cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3385 :
3386 2 : status = db_get_key(hDB, hKey, &key);
3387 :
3388 2 : if (status != DB_SUCCESS)
3389 0 : return;
3390 :
3391 : //printf("clear rpc hosts!\n");
3392 2 : rpc_clear_allowed_hosts();
3393 :
3394 2 : max_size = key.item_size;
3395 2 : str = (char *) malloc(max_size);
3396 :
3397 2 : last = 0;
3398 13 : for (i = 0; i < key.num_values; i++) {
3399 11 : int size = max_size;
3400 11 : status = db_get_data_index(hDB, hKey, str, &size, i, TID_STRING);
3401 11 : if (status != DB_SUCCESS)
3402 0 : break;
3403 :
3404 11 : if (strlen(str) < 1) // skip emties
3405 9 : continue;
3406 :
3407 2 : if (str[0] == '#') // skip commented-out entries
3408 0 : continue;
3409 :
3410 : //printf("add rpc hosts %d [%s]\n", i, str);
3411 2 : rpc_add_allowed_host(str);
3412 2 : last = i;
3413 : }
3414 :
3415 2 : if (key.num_values - last < 10) {
3416 1 : int new_size = last + 10;
3417 1 : status = db_set_num_values(hDB, hKey, new_size);
3418 1 : if (status != DB_SUCCESS) {
3419 0 : cm_msg(MERROR, "load_rpc_hosts",
3420 : "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3421 : }
3422 : }
3423 :
3424 2 : free(str);
3425 : }
3426 :
3427 2 : static void init_rpc_hosts(HNDLE hDB) {
3428 : int status;
3429 : char buf[256];
3430 : int size, i;
3431 : HNDLE hKey;
3432 :
3433 2 : strcpy(buf, "localhost");
3434 2 : size = sizeof(buf);
3435 :
3436 2 : status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3437 :
3438 2 : if (status != DB_SUCCESS) {
3439 0 : cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3440 : status);
3441 0 : return;
3442 : }
3443 :
3444 2 : size = sizeof(i);
3445 2 : i = 0;
3446 2 : status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3447 :
3448 2 : if (status != DB_SUCCESS) {
3449 0 : cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3450 0 : return;
3451 : }
3452 :
3453 2 : if (i != 0) // RPC hosts check is disabled
3454 0 : return;
3455 :
3456 2 : status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3457 :
3458 2 : if (status != DB_SUCCESS || hKey == 0) {
3459 0 : cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3460 : status);
3461 0 : return;
3462 : }
3463 :
3464 2 : load_rpc_hosts(hDB, hKey, -99, NULL);
3465 :
3466 2 : status = db_watch(hDB, hKey, load_rpc_hosts, NULL);
3467 :
3468 2 : if (status != DB_SUCCESS) {
3469 0 : cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3470 0 : return;
3471 : }
3472 : }
3473 :
3474 : /********************************************************************/
3475 2 : INT cm_register_server(void)
3476 : /********************************************************************\
3477 :
3478 : Routine: cm_register_server
3479 :
3480 : Purpose: Register a server which can be called from other clients
3481 : of a specific experiment.
3482 :
3483 : Input:
3484 : none
3485 :
3486 : Output:
3487 : none
3488 :
3489 : Function value:
3490 : CM_SUCCESS Successful completion
3491 :
3492 : \********************************************************************/
3493 : {
3494 2 : if (!_rpc_registered) {
3495 : INT status;
3496 : int size;
3497 : HNDLE hDB, hKey;
3498 : char name[NAME_LENGTH];
3499 : char str[256];
3500 2 : int port = 0;
3501 :
3502 2 : cm_get_experiment_database(&hDB, &hKey);
3503 :
3504 2 : size = sizeof(name);
3505 2 : status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3506 :
3507 2 : if (status != DB_SUCCESS) {
3508 0 : cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3509 0 : return status;
3510 : }
3511 :
3512 2 : mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3513 2 : mstrlcat(str, name, sizeof(str));
3514 :
3515 2 : size = sizeof(port);
3516 2 : status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3517 :
3518 2 : if (status != DB_SUCCESS) {
3519 0 : cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3520 0 : return status;
3521 : }
3522 :
3523 2 : int lport = 0; // actual port number assigned to us by the OS
3524 :
3525 2 : status = rpc_register_server(port, &_rpc_listen_socket, &lport);
3526 2 : if (status != RPC_SUCCESS) {
3527 0 : cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3528 0 : return status;
3529 : }
3530 :
3531 2 : _rpc_registered = TRUE;
3532 :
3533 : /* register MIDAS library functions */
3534 2 : rpc_register_functions(rpc_get_internal_list(1), NULL);
3535 :
3536 : /* store port number in ODB */
3537 :
3538 2 : status = db_find_key(hDB, hKey, "Server Port", &hKey);
3539 2 : if (status != DB_SUCCESS) {
3540 0 : cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3541 0 : return status;
3542 : }
3543 :
3544 : /* unlock database */
3545 2 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
3546 :
3547 : /* set value */
3548 2 : status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3549 2 : if (status != DB_SUCCESS) {
3550 0 : cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3551 0 : return status;
3552 : }
3553 :
3554 : /* lock database */
3555 2 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3556 :
3557 2 : init_rpc_hosts(hDB);
3558 : }
3559 :
3560 2 : return CM_SUCCESS;
3561 : }
3562 :
3563 : /**dox***************************************************************/
3564 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
3565 :
3566 : /********************************************************************/
3567 : /**
3568 : Registers a callback function for run transitions.
3569 : This function internally registers the transition callback
3570 : function and publishes its request for transition notification by writing
3571 : a transition request to /System/Clients/\<pid\>/Transition XXX.
3572 : Other clients making a transition scan the transition requests of all clients
3573 : and call their transition callbacks via RPC.
3574 :
3575 : Clients can register for transitions (Start/Stop/Pause/Resume) in a given
3576 : sequence. All sequence numbers given in the registration are sorted on
3577 : a transition and the clients are contacted in ascending order. By default,
3578 : all programs register with a sequence number of 500. The logger however
3579 : uses 200 for start, so that it can open files before the other clients
3580 : are contacted, and 800 for stop, so that the files get closed when all
3581 : other clients have gone already through the stop trantition.
3582 :
3583 : The callback function returns CM_SUCCESS if it can perform the transition or
3584 : a value larger than one in case of error. An error string can be copied
3585 : into the error variable.
3586 : @attention The callback function will be called on transitions from inside the
3587 : cm_yield() function which therefore must be contained in the main program loop.
3588 : \code
3589 : INT start(INT run_number, char *error)
3590 : {
3591 : if (<not ok>)
3592 : {
3593 : strcpy(error, "Cannot start because ...");
3594 : return 2;
3595 : }
3596 : printf("Starting run %d\n", run_number);
3597 : return CM_SUCCESS;
3598 : }
3599 : main()
3600 : {
3601 : ...
3602 : cm_register_transition(TR_START, start, 500);
3603 : do
3604 : {
3605 : status = cm_yield(1000);
3606 : } while (status != RPC_SHUTDOWN &&
3607 : status != SS_ABORT);
3608 : ...
3609 : }
3610 : \endcode
3611 : @param transition Transition to register for (see @ref state_transition)
3612 : @param func Callback function.
3613 : @param sequence_number Sequence number for that transition (1..1000)
3614 : @return CM_SUCCESS
3615 : */
3616 5 : INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3617 : INT status;
3618 : HNDLE hDB, hKey, hKeyTrans;
3619 : KEY key;
3620 : char str[256];
3621 :
3622 : /* check for valid transition */
3623 5 : if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME && transition != TR_STARTABORT) {
3624 0 : cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3625 0 : return CM_INVALID_TRANSITION;
3626 : }
3627 :
3628 5 : cm_get_experiment_database(&hDB, &hKey);
3629 :
3630 5 : rpc_register_function(RPC_RC_TRANSITION, rpc_transition_dispatch);
3631 :
3632 : /* register new transition request */
3633 :
3634 : {
3635 5 : std::lock_guard<std::mutex> guard(_trans_table_mutex);
3636 :
3637 15 : for (size_t i = 0; i < _trans_table.size(); i++) {
3638 10 : if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3639 0 : cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3640 0 : return CM_INVALID_TRANSITION;
3641 : }
3642 : }
3643 :
3644 5 : bool found = false;
3645 15 : for (size_t i = 0; i < _trans_table.size(); i++) {
3646 10 : if (!_trans_table[i].transition) {
3647 0 : _trans_table[i].transition = transition;
3648 0 : _trans_table[i].sequence_number = sequence_number;
3649 0 : _trans_table[i].func = func;
3650 0 : found = true;
3651 0 : break;
3652 : }
3653 : }
3654 :
3655 5 : if (!found) {
3656 : TRANS_TABLE tt;
3657 5 : tt.transition = transition;
3658 5 : tt.sequence_number = sequence_number;
3659 5 : tt.func = func;
3660 5 : _trans_table.push_back(tt);
3661 : }
3662 :
3663 : // implicit unlock
3664 5 : }
3665 :
3666 5 : sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3667 :
3668 : /* unlock database */
3669 5 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
3670 :
3671 : /* set value */
3672 5 : status = db_find_key(hDB, hKey, str, &hKeyTrans);
3673 5 : if (!hKeyTrans) {
3674 5 : status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3675 5 : if (status != DB_SUCCESS)
3676 0 : return status;
3677 : } else {
3678 0 : status = db_get_key(hDB, hKeyTrans, &key);
3679 0 : if (status != DB_SUCCESS)
3680 0 : return status;
3681 0 : status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3682 0 : if (status != DB_SUCCESS)
3683 0 : return status;
3684 : }
3685 :
3686 : /* re-lock database */
3687 5 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3688 :
3689 5 : return CM_SUCCESS;
3690 : }
3691 :
3692 0 : INT cm_deregister_transition(INT transition) {
3693 : INT status;
3694 : HNDLE hDB, hKey, hKeyTrans;
3695 : char str[256];
3696 :
3697 : /* check for valid transition */
3698 0 : if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME && transition != TR_STARTABORT) {
3699 0 : cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3700 0 : return CM_INVALID_TRANSITION;
3701 : }
3702 :
3703 0 : cm_get_experiment_database(&hDB, &hKey);
3704 :
3705 : {
3706 0 : std::lock_guard<std::mutex> guard(_trans_table_mutex);
3707 :
3708 : /* remove existing transition request */
3709 0 : for (size_t i = 0; i < _trans_table.size(); i++) {
3710 0 : if (_trans_table[i].transition == transition) {
3711 0 : _trans_table[i].transition = 0;
3712 0 : _trans_table[i].sequence_number = 0;
3713 0 : _trans_table[i].func = NULL;
3714 : }
3715 : }
3716 :
3717 : // implicit unlock
3718 0 : }
3719 :
3720 0 : sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3721 :
3722 : /* unlock database */
3723 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
3724 :
3725 : /* set value */
3726 0 : status = db_find_key(hDB, hKey, str, &hKeyTrans);
3727 0 : if (hKeyTrans) {
3728 0 : status = db_delete_key(hDB, hKeyTrans, FALSE);
3729 0 : if (status != DB_SUCCESS)
3730 0 : return status;
3731 : }
3732 :
3733 : /* re-lock database */
3734 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3735 :
3736 0 : return CM_SUCCESS;
3737 : }
3738 :
3739 : /********************************************************************/
3740 : /**
3741 : Change the transition sequence for the calling program.
3742 : @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
3743 : @param sequence_number New sequence number, should be between 1 and 1000
3744 : @return CM_SUCCESS
3745 : */
3746 0 : INT cm_set_transition_sequence(INT transition, INT sequence_number) {
3747 : INT status;
3748 : HNDLE hDB, hKey;
3749 : char str[256];
3750 :
3751 : /* check for valid transition */
3752 0 : if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME) {
3753 0 : cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3754 0 : return CM_INVALID_TRANSITION;
3755 : }
3756 :
3757 : {
3758 0 : std::lock_guard<std::mutex> guard(_trans_table_mutex);
3759 :
3760 0 : int count = 0;
3761 0 : for (size_t i = 0; i < _trans_table.size(); i++) {
3762 0 : if (_trans_table[i].transition == transition) {
3763 0 : _trans_table[i].sequence_number = sequence_number;
3764 0 : count++;
3765 : }
3766 : }
3767 :
3768 0 : if (count == 0) {
3769 0 : cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3770 0 : return CM_INVALID_TRANSITION;
3771 0 : } else if (count > 1) {
3772 0 : cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3773 0 : return CM_INVALID_TRANSITION;
3774 : }
3775 :
3776 : /* Change local sequence number for this transition type */
3777 :
3778 0 : for (size_t i = 0; i < _trans_table.size(); i++) {
3779 0 : if (_trans_table[i].transition == transition) {
3780 0 : _trans_table[i].sequence_number = sequence_number;
3781 : }
3782 : }
3783 :
3784 : // implicit unlock
3785 0 : }
3786 :
3787 0 : cm_get_experiment_database(&hDB, &hKey);
3788 :
3789 : /* unlock database */
3790 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
3791 :
3792 0 : sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3793 :
3794 : /* set value */
3795 0 : status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3796 0 : if (status != DB_SUCCESS)
3797 0 : return status;
3798 :
3799 : /* re-lock database */
3800 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3801 :
3802 0 : return CM_SUCCESS;
3803 :
3804 : }
3805 :
3806 0 : INT cm_set_client_run_state(INT state) {
3807 : INT status;
3808 : HNDLE hDB, hKey;
3809 : KEY key;
3810 :
3811 0 : cm_get_experiment_database(&hDB, &hKey);
3812 :
3813 : /* check that hKey is still valid */
3814 0 : status = db_get_key(hDB, hKey, &key);
3815 :
3816 0 : if (status != DB_SUCCESS) {
3817 0 : cm_msg(MERROR, "cm_set_client_run_state",
3818 : "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3819 : hKey);
3820 0 : return status;
3821 : }
3822 :
3823 : /* unlock database */
3824 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
3825 :
3826 : /* set value */
3827 0 : status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3828 0 : if (status != DB_SUCCESS)
3829 0 : return status;
3830 :
3831 : /* re-lock database */
3832 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3833 :
3834 0 : return CM_SUCCESS;
3835 :
3836 : }
3837 :
3838 : /**dox***************************************************************/
3839 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
3840 :
3841 : static INT _requested_transition;
3842 : static DWORD _deferred_transition_mask;
3843 :
3844 : /**dox***************************************************************/
3845 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
3846 :
3847 : /********************************************************************/
3848 : /**
3849 : Register a deferred transition handler. If a client is
3850 : registered as a deferred transition handler, it may defer
3851 : a requested transition by returning FALSE until a certain
3852 : condition (like a motor reaches its end position) is
3853 : reached.
3854 : @param transition One of TR_xxx
3855 : @param (*func) Function which gets called whenever
3856 : a transition is requested. If it returns
3857 : FALSE, the transition is not performed.
3858 : @return CM_SUCCESS, \<error\> Error from ODB access
3859 : */
3860 0 : INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL)) {
3861 : INT status, size;
3862 : char tr_key_name[256];
3863 : HNDLE hDB, hKey;
3864 :
3865 0 : cm_get_experiment_database(&hDB, &hKey);
3866 :
3867 0 : for (int i = 0; _deferred_trans_table[i].transition; i++)
3868 0 : if (_deferred_trans_table[i].transition == transition)
3869 0 : _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3870 :
3871 : /* set new transition mask */
3872 0 : _deferred_transition_mask |= transition;
3873 :
3874 0 : sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3875 :
3876 : /* unlock database */
3877 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
3878 :
3879 : /* set value */
3880 0 : int i = 0;
3881 0 : status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3882 0 : if (status != DB_SUCCESS)
3883 0 : return status;
3884 :
3885 : /* re-lock database */
3886 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
3887 :
3888 : /* hot link requested transition */
3889 0 : size = sizeof(_requested_transition);
3890 0 : db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3891 0 : db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3892 0 : status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
3893 0 : if (status != DB_SUCCESS) {
3894 0 : cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3895 0 : return status;
3896 : }
3897 :
3898 0 : return CM_SUCCESS;
3899 : }
3900 :
3901 : /********************************************************************/
3902 : /**
3903 : Check for any deferred transition. If a deferred transition
3904 : handler has been registered via the
3905 : cm_register_deferred_transition function, this routine
3906 : should be called regularly. It checks if a transition
3907 : request is pending. If so, it calld the registered handler
3908 : if the transition should be done and then actually does
3909 : the transition.
3910 : @return CM_SUCCESS, \<error\> Error from cm_transition()
3911 : */
3912 0 : INT cm_check_deferred_transition() {
3913 : INT i, status;
3914 : char str[256];
3915 : static BOOL first;
3916 :
3917 0 : if (_requested_transition == 0)
3918 0 : first = TRUE;
3919 :
3920 0 : if (_requested_transition & _deferred_transition_mask) {
3921 0 : for (i = 0; _deferred_trans_table[i].transition; i++)
3922 0 : if (_deferred_trans_table[i].transition == _requested_transition)
3923 0 : break;
3924 :
3925 0 : if (_deferred_trans_table[i].transition == _requested_transition) {
3926 0 : if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func)(_requested_transition, first)) {
3927 0 : status = cm_transition(_requested_transition | TR_DEFERRED, 0, str, sizeof(str), TR_SYNC, FALSE);
3928 0 : if (status != CM_SUCCESS)
3929 0 : cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3930 :
3931 : /* bypass hotlink and set _requested_transition directly to zero */
3932 0 : _requested_transition = 0;
3933 :
3934 0 : return status;
3935 : }
3936 0 : first = FALSE;
3937 : }
3938 : }
3939 :
3940 0 : return SUCCESS;
3941 : }
3942 :
3943 :
3944 : /**dox***************************************************************/
3945 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
3946 :
3947 : /********************************************************************/
3948 :
3949 : /**dox***************************************************************/
3950 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
3951 :
3952 : struct TrClient {
3953 : int transition = 0;
3954 : int run_number = 0;
3955 : int async_flag = 0;
3956 : int debug_flag = 0;
3957 : int sequence_number = 0;
3958 : std::vector<int> wait_for_index;
3959 : std::string host_name;
3960 : std::string client_name;
3961 : int port = 0;
3962 : std::string key_name; /* this client key name in /System/Clients */
3963 : std::atomic_int status{0};
3964 : std::thread* thread = NULL;
3965 : std::string errorstr;
3966 : DWORD init_time = 0; // time when tr_client created
3967 : std::string waiting_for_client; // name of client we are waiting for
3968 : DWORD connect_timeout = 0;
3969 : DWORD connect_start_time = 0; // time when client rpc connection is started
3970 : DWORD connect_end_time = 0; // time when client rpc connection is finished
3971 : DWORD rpc_timeout = 0;
3972 : DWORD rpc_start_time = 0; // time client rpc call is started
3973 : DWORD rpc_end_time = 0; // time client rpc call is finished
3974 : DWORD end_time = 0; // time client thread is finished
3975 :
3976 0 : TrClient() // ctor
3977 0 : {
3978 : // empty
3979 0 : }
3980 :
3981 0 : ~TrClient() // dtor
3982 : {
3983 : //printf("TrClient::dtor: client \"%s\"\n", client_name);
3984 0 : assert(thread == NULL);
3985 0 : }
3986 :
3987 : void Print() const
3988 : {
3989 : printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3990 : if (wait_for_index.size() > 0) {
3991 : printf(", wait for:");
3992 : for (size_t i=0; i<wait_for_index.size(); i++) {
3993 : printf(" %d", wait_for_index[i]);
3994 : }
3995 : }
3996 : }
3997 : };
3998 :
3999 0 : static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
4000 0 : return arg1->sequence_number < arg2->sequence_number;
4001 : }
4002 :
4003 : /*------------------------------------------------------------------*/
4004 :
4005 : struct TrState {
4006 : int transition = 0;
4007 : int run_number = 0;
4008 : int async_flag = 0;
4009 : int debug_flag = 0;
4010 : int status = 0;
4011 : std::string errorstr;
4012 : DWORD start_time = 0;
4013 : DWORD end_time = 0;
4014 : std::vector<std::unique_ptr<TrClient>> clients;
4015 : };
4016 :
4017 : /*------------------------------------------------------------------*/
4018 :
4019 0 : static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4020 : {
4021 0 : DWORD end_time = ss_millitime();
4022 :
4023 0 : if (transition != TR_STARTABORT) {
4024 0 : db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4025 0 : db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4026 :
4027 0 : if (errorstr) {
4028 0 : db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4029 0 : } else if (status == CM_SUCCESS) {
4030 0 : const char *buf = "Success";
4031 0 : db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4032 : } else {
4033 : char buf[256];
4034 0 : sprintf(buf, "status %d", status);
4035 0 : db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4036 : }
4037 : }
4038 :
4039 0 : tr->status = status;
4040 0 : tr->end_time = end_time;
4041 0 : if (errorstr) {
4042 0 : tr->errorstr = errorstr;
4043 : } else {
4044 0 : tr->errorstr = "(null)";
4045 : }
4046 :
4047 0 : return status;
4048 : }
4049 :
4050 : /*------------------------------------------------------------------*/
4051 :
4052 0 : static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client) {
4053 : //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4054 :
4055 : int status;
4056 : HNDLE hKey;
4057 :
4058 0 : if (tr_client->transition == TR_STARTABORT) {
4059 0 : status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4060 0 : status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4061 0 : if (status != DB_SUCCESS)
4062 0 : return;
4063 : } else {
4064 0 : status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4065 0 : status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4066 0 : if (status != DB_SUCCESS)
4067 0 : return;
4068 : }
4069 :
4070 : // same client_name can exist with different sequence numbers!
4071 0 : std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4072 :
4073 0 : status = db_create_key(hDB, hKey, keyname.c_str(), TID_KEY);
4074 0 : status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4075 0 : if (status != DB_SUCCESS)
4076 0 : return;
4077 :
4078 0 : DWORD now = ss_millitime();
4079 :
4080 : //int transition;
4081 : //int run_number;
4082 : //int async_flag;
4083 : //int debug_flag;
4084 0 : status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4085 0 : status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4086 0 : status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4087 0 : status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4088 0 : status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4089 0 : status = db_set_value(hDB, hKey, "waiting_for_client", tr_client->waiting_for_client.c_str(), tr_client->waiting_for_client.length() + 1, 1, TID_STRING);
4090 0 : status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4091 0 : status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4092 0 : status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4093 0 : status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4094 0 : status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4095 0 : status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4096 0 : status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4097 0 : status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4098 0 : status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4099 0 : status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4100 0 : }
4101 :
4102 : /*------------------------------------------------------------------*/
4103 :
4104 : /* Perform a detached transition through the external "mtransition" program */
4105 0 : static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4106 : HNDLE hDB;
4107 : int status;
4108 : const char *args[100];
4109 0 : std::string path;
4110 : char debug_arg[256];
4111 : char start_arg[256];
4112 0 : std::string expt_name;
4113 0 : std::string mserver_hostname;
4114 :
4115 0 : int iarg = 0;
4116 :
4117 0 : cm_get_experiment_database(&hDB, NULL);
4118 :
4119 0 : const char *midassys = getenv("MIDASSYS");
4120 0 : if (midassys) {
4121 0 : path += midassys;
4122 0 : path += DIR_SEPARATOR_STR;
4123 0 : path += "bin";
4124 0 : path += DIR_SEPARATOR_STR;
4125 : }
4126 0 : path += "mtransition";
4127 :
4128 0 : args[iarg++] = path.c_str();
4129 :
4130 0 : if (rpc_is_remote()) {
4131 : /* if connected to mserver, pass connection info to mtransition */
4132 0 : mserver_hostname = rpc_get_mserver_hostname();
4133 0 : args[iarg++] = "-h";
4134 0 : args[iarg++] = mserver_hostname.c_str();
4135 : }
4136 :
4137 : /* get experiment name from ODB */
4138 0 : db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4139 :
4140 0 : if (expt_name.length() > 0) {
4141 0 : args[iarg++] = "-e";
4142 0 : args[iarg++] = expt_name.c_str();
4143 : }
4144 :
4145 0 : if (debug_flag) {
4146 0 : args[iarg++] = "-d";
4147 :
4148 0 : sprintf(debug_arg, "%d", debug_flag);
4149 0 : args[iarg++] = debug_arg;
4150 : }
4151 :
4152 0 : if (transition == TR_STOP)
4153 0 : args[iarg++] = "STOP";
4154 0 : else if (transition == TR_PAUSE)
4155 0 : args[iarg++] = "PAUSE";
4156 0 : else if (transition == TR_RESUME)
4157 0 : args[iarg++] = "RESUME";
4158 0 : else if (transition == TR_START) {
4159 0 : args[iarg++] = "START";
4160 :
4161 0 : sprintf(start_arg, "%d", run_number);
4162 0 : args[iarg++] = start_arg;
4163 : }
4164 :
4165 0 : args[iarg++] = NULL;
4166 :
4167 : #if 0
4168 : for (iarg = 0; args[iarg] != NULL; iarg++) {
4169 : printf("arg[%d] [%s]\n", iarg, args[iarg]);
4170 : }
4171 : #endif
4172 :
4173 0 : status = ss_spawnv(P_DETACH, args[0], args);
4174 :
4175 0 : if (status != SS_SUCCESS) {
4176 0 : if (errstr != NULL) {
4177 0 : sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4178 : }
4179 0 : return CM_SET_ERROR;
4180 : }
4181 :
4182 0 : return CM_SUCCESS;
4183 0 : }
4184 :
4185 : /*------------------------------------------------------------------*/
4186 :
4187 : /* contact a client via RPC and execute the remote transition */
4188 0 : static int cm_transition_call(TrState* s, int idx) {
4189 : INT old_timeout, status, i, t1, t0, size;
4190 : HNDLE hDB;
4191 0 : HNDLE hConn = -1;
4192 0 : int connect_timeout = 10000;
4193 0 : int timeout = 120000;
4194 :
4195 0 : cm_get_experiment_database(&hDB, NULL);
4196 0 : assert(hDB);
4197 :
4198 0 : TrClient *tr_client = s->clients[idx].get();
4199 :
4200 0 : tr_client->errorstr = "";
4201 : //tr_client->init_time = ss_millitime();
4202 0 : tr_client->waiting_for_client = "";
4203 0 : tr_client->connect_timeout = 0;
4204 0 : tr_client->connect_start_time = 0;
4205 0 : tr_client->connect_end_time = 0;
4206 0 : tr_client->rpc_timeout = 0;
4207 0 : tr_client->rpc_start_time = 0;
4208 0 : tr_client->rpc_end_time = 0;
4209 0 : tr_client->end_time = 0;
4210 :
4211 0 : write_tr_client_to_odb(hDB, tr_client);
4212 :
4213 : /* wait for predecessor if set */
4214 0 : if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4215 : while (1) {
4216 0 : TrClient* wait_for = NULL;
4217 :
4218 0 : for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4219 0 : int wait_for_index = tr_client->wait_for_index[i];
4220 :
4221 0 : assert(wait_for_index >= 0);
4222 0 : assert(wait_for_index < (int)s->clients.size());
4223 :
4224 0 : TrClient *t = s->clients[wait_for_index].get();
4225 :
4226 0 : if (!t)
4227 0 : continue;
4228 :
4229 0 : if (t->status == 0) {
4230 0 : wait_for = t;
4231 0 : break;
4232 : }
4233 :
4234 0 : if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4235 0 : cm_msg(MERROR, "cm_transition_call", "Transition %d aborted: client \"%s\" returned status %d", tr_client->transition, t->client_name.c_str(), int(t->status));
4236 0 : tr_client->status = -1;
4237 0 : tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4238 0 : tr_client->end_time = ss_millitime();
4239 0 : write_tr_client_to_odb(hDB, tr_client);
4240 0 : return CM_SUCCESS;
4241 : }
4242 : }
4243 :
4244 0 : if (wait_for == NULL)
4245 0 : break;
4246 :
4247 0 : tr_client->waiting_for_client = wait_for->client_name;
4248 0 : write_tr_client_to_odb(hDB, tr_client);
4249 :
4250 0 : if (tr_client->debug_flag == 1)
4251 0 : printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4252 :
4253 0 : i = 0;
4254 0 : size = sizeof(i);
4255 0 : status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4256 :
4257 0 : if (status == DB_SUCCESS && i == 0) {
4258 0 : cm_msg(MERROR, "cm_transition_call", "Client \"%s\" transition %d aborted while waiting for client \"%s\": \"/Runinfo/Transition in progress\" was cleared", tr_client->client_name.c_str(), tr_client->transition, wait_for->client_name.c_str());
4259 0 : tr_client->status = -1;
4260 0 : tr_client->errorstr = "Canceled";
4261 0 : tr_client->end_time = ss_millitime();
4262 0 : write_tr_client_to_odb(hDB, tr_client);
4263 0 : return CM_SUCCESS;
4264 : }
4265 :
4266 0 : ss_sleep(100);
4267 0 : };
4268 : }
4269 :
4270 0 : tr_client->waiting_for_client[0] = 0;
4271 :
4272 : /* contact client if transition mask set */
4273 0 : if (tr_client->debug_flag == 1)
4274 0 : printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4275 0 : if (tr_client->debug_flag == 2)
4276 0 : cm_msg(MINFO, "cm_transition_call", "cm_transition_call: Connecting to client \"%s\" on host %s...", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4277 :
4278 : /* get transition timeout for rpc connect */
4279 0 : size = sizeof(timeout);
4280 0 : db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4281 :
4282 0 : if (connect_timeout < 1000)
4283 0 : connect_timeout = 1000;
4284 :
4285 : /* get transition timeout */
4286 0 : size = sizeof(timeout);
4287 0 : db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4288 :
4289 0 : if (timeout < 1000)
4290 0 : timeout = 1000;
4291 :
4292 : /* set our timeout for rpc_client_connect() */
4293 : //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4294 0 : rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4295 :
4296 0 : tr_client->connect_timeout = connect_timeout;
4297 0 : tr_client->connect_start_time = ss_millitime();
4298 :
4299 0 : write_tr_client_to_odb(hDB, tr_client);
4300 :
4301 : /* client found -> connect to its server port */
4302 0 : status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4303 :
4304 0 : rpc_set_timeout(RPC_HNDLE_CONNECT, old_timeout);
4305 :
4306 0 : tr_client->connect_end_time = ss_millitime();
4307 0 : write_tr_client_to_odb(hDB, tr_client);
4308 :
4309 0 : if (status != RPC_SUCCESS) {
4310 0 : cm_msg(MERROR, "cm_transition_call",
4311 : "cannot connect to client \"%s\" on host %s, port %d, status %d",
4312 : tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4313 0 : tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4314 :
4315 : /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4316 0 : cm_shutdown(tr_client->client_name.c_str(), TRUE);
4317 0 : cm_cleanup(tr_client->client_name.c_str(), TRUE);
4318 :
4319 0 : if (tr_client->transition != TR_STOP) {
4320 : /* indicate abort */
4321 0 : i = 1;
4322 0 : db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4323 0 : i = 0;
4324 0 : db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4325 : }
4326 :
4327 0 : tr_client->status = status;
4328 0 : tr_client->end_time = ss_millitime();
4329 :
4330 0 : write_tr_client_to_odb(hDB, tr_client);
4331 0 : return status;
4332 : }
4333 :
4334 0 : if (tr_client->debug_flag == 1)
4335 0 : printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4336 0 : if (tr_client->debug_flag == 2)
4337 0 : cm_msg(MINFO, "cm_transition_call",
4338 : "cm_transition: Connection established to client \"%s\" on host %s",
4339 : tr_client->client_name.c_str(), tr_client->host_name.c_str());
4340 :
4341 : /* call RC_TRANSITION on remote client with increased timeout */
4342 : //old_timeout = rpc_get_timeout(hConn);
4343 0 : rpc_set_timeout(hConn, timeout, &old_timeout);
4344 :
4345 0 : tr_client->rpc_timeout = timeout;
4346 0 : tr_client->rpc_start_time = ss_millitime();
4347 0 : write_tr_client_to_odb(hDB, tr_client);
4348 :
4349 0 : if (tr_client->debug_flag == 1)
4350 0 : printf("Executing RPC transition client \"%s\" on host %s...\n",
4351 : tr_client->client_name.c_str(), tr_client->host_name.c_str());
4352 0 : if (tr_client->debug_flag == 2)
4353 0 : cm_msg(MINFO, "cm_transition_call",
4354 : "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4355 : tr_client->client_name.c_str(), tr_client->host_name.c_str());
4356 :
4357 0 : t0 = ss_millitime();
4358 :
4359 : char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4360 0 : errorstr[0] = 0;
4361 :
4362 0 : status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4363 :
4364 0 : tr_client->errorstr = errorstr;
4365 :
4366 0 : t1 = ss_millitime();
4367 :
4368 0 : tr_client->rpc_end_time = ss_millitime();
4369 :
4370 0 : write_tr_client_to_odb(hDB, tr_client);
4371 :
4372 : /* fix for clients returning 0 as error code */
4373 0 : if (status == 0)
4374 0 : status = FE_ERR_HW;
4375 :
4376 : /* reset timeout */
4377 0 : rpc_set_timeout(hConn, old_timeout);
4378 :
4379 : //DWORD t2 = ss_millitime();
4380 :
4381 0 : if (tr_client->debug_flag == 1)
4382 0 : printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4383 : tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4384 0 : if (tr_client->debug_flag == 2)
4385 0 : cm_msg(MINFO, "cm_transition_call",
4386 : "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4387 : tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4388 :
4389 0 : if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4390 0 : tr_client->errorstr = msprintf("RPC network error or timeout from client \'%s\' on host \"%s\"", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4391 : /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4392 0 : cm_shutdown(tr_client->client_name.c_str(), TRUE);
4393 0 : cm_cleanup(tr_client->client_name.c_str(), TRUE);
4394 0 : } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4395 0 : tr_client->errorstr = msprintf("Unknown error %d from client \'%s\' on host \"%s\"", status, tr_client->client_name.c_str(), tr_client->host_name.c_str());
4396 : }
4397 :
4398 0 : tr_client->status = status;
4399 0 : tr_client->end_time = ss_millitime();
4400 :
4401 : // write updated status and end_time to ODB
4402 :
4403 0 : write_tr_client_to_odb(hDB, tr_client);
4404 :
4405 : #if 0
4406 : printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4407 : hConn,
4408 : tr_client->client_name.c_str(),
4409 : tr_client->init_time - tr_client->init_time,
4410 : tr_client->connect_start_time - tr_client->init_time,
4411 : tr_client->connect_end_time - tr_client->init_time,
4412 : tr_client->rpc_start_time - tr_client->init_time,
4413 : tr_client->rpc_end_time - tr_client->init_time,
4414 : t2 - tr_client->init_time,
4415 : tr_client->end_time - tr_client->init_time);
4416 : #endif
4417 :
4418 0 : return CM_SUCCESS;
4419 : }
4420 :
4421 : /*------------------------------------------------------------------*/
4422 :
4423 0 : static int cm_transition_call_direct(TrClient *tr_client)
4424 : {
4425 : HNDLE hDB;
4426 :
4427 0 : cm_get_experiment_database(&hDB, NULL);
4428 :
4429 0 : DWORD now = ss_millitime();
4430 :
4431 0 : tr_client->errorstr = "";
4432 : //tr_client->init_time = now;
4433 0 : tr_client->waiting_for_client = "";
4434 0 : tr_client->connect_timeout = 0;
4435 0 : tr_client->connect_start_time = now;
4436 0 : tr_client->connect_end_time = now;
4437 0 : tr_client->rpc_timeout = 0;
4438 0 : tr_client->rpc_start_time = 0;
4439 0 : tr_client->rpc_end_time = 0;
4440 0 : tr_client->end_time = 0;
4441 :
4442 0 : write_tr_client_to_odb(hDB, tr_client);
4443 :
4444 : // find registered handler
4445 : // NB: this code should match same code in rpc_transition_dispatch()
4446 : // NB: only use the first handler, this is how MIDAS always worked
4447 : // NB: we could run all handlers, but we can return the status and error string of only one of them.
4448 :
4449 0 : _trans_table_mutex.lock();
4450 0 : size_t n = _trans_table.size();
4451 0 : _trans_table_mutex.unlock();
4452 :
4453 0 : for (size_t i = 0; i < n; i++) {
4454 0 : _trans_table_mutex.lock();
4455 0 : TRANS_TABLE tt = _trans_table[i];
4456 0 : _trans_table_mutex.unlock();
4457 0 : if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4458 : /* call registered function */
4459 0 : if (tt.func) {
4460 0 : if (tr_client->debug_flag == 1)
4461 0 : printf("Calling local transition callback\n");
4462 0 : if (tr_client->debug_flag == 2)
4463 0 : cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4464 :
4465 0 : tr_client->rpc_start_time = ss_millitime();
4466 :
4467 0 : write_tr_client_to_odb(hDB, tr_client);
4468 :
4469 : char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4470 0 : errorstr[0] = 0;
4471 :
4472 0 : tr_client->status = tt.func(tr_client->run_number, errorstr);
4473 :
4474 0 : tr_client->errorstr = errorstr;
4475 :
4476 0 : tr_client->rpc_end_time = ss_millitime();
4477 :
4478 0 : if (tr_client->debug_flag == 1)
4479 0 : printf("Local transition callback finished, status %d\n", int(tr_client->status));
4480 0 : if (tr_client->debug_flag == 2)
4481 0 : cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4482 :
4483 0 : tr_client->end_time = ss_millitime();
4484 :
4485 : // write status and end_time to ODB
4486 :
4487 0 : write_tr_client_to_odb(hDB, tr_client);
4488 :
4489 0 : return tr_client->status;
4490 : }
4491 : }
4492 : }
4493 :
4494 0 : cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4495 :
4496 0 : tr_client->status = CM_SUCCESS;
4497 0 : tr_client->end_time = ss_millitime();
4498 :
4499 : // write status and end_time to ODB
4500 :
4501 0 : write_tr_client_to_odb(hDB, tr_client);
4502 :
4503 0 : return CM_SUCCESS;
4504 : }
4505 :
4506 : /********************************************************************/
4507 : /**
4508 : Performs a run transition (Start/Stop/Pause/Resume).
4509 :
4510 : Synchronous/Asynchronous flag.
4511 : If set to TR_ASYNC, the transition is done
4512 : asynchronously, meaning that clients are connected and told to execute their
4513 : callback routine, but no result is awaited. The return value is
4514 : specified by the transition callback function on the remote clients. If all callbacks
4515 : can perform the transition, CM_SUCCESS is returned. If one callback cannot
4516 : perform the transition, the return value of this callback is returned from
4517 : cm_transition().
4518 : The async_flag is usually FALSE so that transition callbacks can block a
4519 : run transition in case of problems and return an error string. The only exception are
4520 : situations where a run transition is performed automatically by a program which
4521 : cannot block in a transition. For example the logger can cause a run stop when a
4522 : disk is nearly full but it cannot block in the cm_transition() function since it
4523 : has its own run stop callback which must flush buffers and close disk files and
4524 : tapes.
4525 : \code
4526 : ...
4527 : i = 1;
4528 : db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4529 :
4530 : status = cm_transition(TR_START, new_run_number, str, sizeof(str), SYNC, debug_flag);
4531 : if (status != CM_SUCCESS)
4532 : {
4533 : // in case of error
4534 : printf("Error: %s\n", str);
4535 : }
4536 : ...
4537 : \endcode
4538 : @param transition TR_START, TR_PAUSE, TR_RESUME or TR_STOP.
4539 : @param run_number New run number. If zero, use current run number plus one.
4540 : @param errstr returned error string.
4541 : @param errstr_size Size of error string.
4542 : @param async_flag TR_SYNC: synchronization flag (TR_SYNC:wait completion, TR_ASYNC: retun immediately)
4543 : @param debug_flag If 1 output debugging information, if 2 output via cm_msg().
4544 : @return CM_SUCCESS, \<error\> error code from remote client
4545 : */
4546 0 : static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4547 : {
4548 : INT i, status, size, sequence_number, port, state;
4549 : HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hKeyTrans;
4550 : DWORD seconds;
4551 : char tr_key_name[256];
4552 : KEY key;
4553 : BOOL deferred;
4554 : char xerrstr[TRANSITION_ERROR_STRING_LENGTH];
4555 :
4556 : //printf("cm_transition2: transition %d, run_number %d, errstr %p, errstr_size %d, async_flag %d, debug_flag %d\n", transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4557 :
4558 : /* if needed, use internal error string */
4559 0 : if (!errstr) {
4560 0 : errstr = xerrstr;
4561 0 : errstr_size = sizeof(xerrstr);
4562 : }
4563 :
4564 : /* erase error string */
4565 0 : errstr[0] = 0;
4566 :
4567 : /* get key of local client */
4568 0 : cm_get_experiment_database(&hDB, &hKeylocal);
4569 :
4570 0 : deferred = (transition & TR_DEFERRED) > 0;
4571 0 : transition &= ~TR_DEFERRED;
4572 :
4573 : /* check for valid transition */
4574 0 : if (transition != TR_START && transition != TR_STOP && transition != TR_PAUSE && transition != TR_RESUME
4575 0 : && transition != TR_STARTABORT) {
4576 0 : cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4577 0 : mstrlcpy(errstr, "Invalid transition request", errstr_size);
4578 0 : return CM_INVALID_TRANSITION;
4579 : }
4580 :
4581 : /* check if transition in progress */
4582 0 : if (!deferred) {
4583 0 : i = 0;
4584 0 : size = sizeof(i);
4585 0 : db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4586 0 : if (i == 1) {
4587 0 : if (errstr) {
4588 0 : sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4589 0 : mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4590 : }
4591 0 : cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4592 0 : return CM_TRANSITION_IN_PROGRESS;
4593 : }
4594 : }
4595 :
4596 : /* indicate transition in progress */
4597 0 : i = transition;
4598 0 : db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4599 :
4600 : /* clear run abort flag */
4601 0 : i = 0;
4602 0 : db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4603 :
4604 : /* construct new transition state */
4605 :
4606 0 : TrState s;
4607 :
4608 0 : s.transition = transition;
4609 0 : s.run_number = run_number;
4610 0 : s.async_flag = async_flag;
4611 0 : s.debug_flag = debug_flag;
4612 0 : s.status = 0;
4613 0 : s.errorstr[0] = 0;
4614 0 : s.start_time = ss_millitime();
4615 0 : s.end_time = 0;
4616 :
4617 : /* construct the ODB tree /System/Transition */
4618 :
4619 0 : status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4620 0 : if (status == DB_SUCCESS) {
4621 0 : db_delete_key(hDB, hKey, FALSE);
4622 : }
4623 :
4624 0 : if (transition != TR_STARTABORT) {
4625 0 : status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4626 0 : if (status == DB_SUCCESS) {
4627 0 : db_delete_key(hDB, hKey, FALSE);
4628 : }
4629 : }
4630 :
4631 0 : if (transition != TR_STARTABORT) {
4632 0 : db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4633 0 : db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4634 0 : db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4635 0 : db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4636 0 : status = 0;
4637 0 : db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4638 0 : db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4639 0 : db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4640 : }
4641 :
4642 : /* check for alarms */
4643 0 : i = 0;
4644 0 : size = sizeof(i);
4645 0 : db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4646 0 : if (i == TRUE && transition == TR_START) {
4647 0 : al_check();
4648 0 : std::string alarms;
4649 0 : if (al_get_alarms(&alarms) > 0) {
4650 0 : cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4651 0 : mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4652 0 : mstrlcat(errstr, alarms.c_str(), errstr_size);
4653 0 : return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4654 : }
4655 0 : }
4656 :
4657 : /* check for required programs */
4658 0 : i = 0;
4659 0 : size = sizeof(i);
4660 0 : db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4661 0 : if (i == TRUE && transition == TR_START) {
4662 :
4663 : HNDLE hkeyroot, hkey;
4664 :
4665 : /* check /programs alarms */
4666 0 : db_find_key(hDB, 0, "/Programs", &hkeyroot);
4667 0 : if (hkeyroot) {
4668 0 : for (i = 0;; i++) {
4669 0 : BOOL program_info_required = FALSE;
4670 0 : status = db_enum_key(hDB, hkeyroot, i, &hkey);
4671 0 : if (status == DB_NO_MORE_SUBKEYS)
4672 0 : break;
4673 :
4674 0 : db_get_key(hDB, hkey, &key);
4675 :
4676 : /* don't check "execute on xxx" */
4677 0 : if (key.type != TID_KEY)
4678 0 : continue;
4679 :
4680 0 : size = sizeof(program_info_required);
4681 0 : status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4682 0 : if (status != DB_SUCCESS) {
4683 0 : cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4684 0 : continue;
4685 : }
4686 :
4687 0 : if (program_info_required) {
4688 0 : std::string name = rpc_get_name();
4689 0 : std::string str = name;
4690 0 : str.resize(strlen(key.name));
4691 0 : if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4692 0 : cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4693 0 : std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4694 0 : mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4695 0 : return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4696 0 : }
4697 0 : }
4698 0 : }
4699 : }
4700 : }
4701 :
4702 : /* do detached transition via mtransition tool */
4703 0 : if (async_flag & TR_DETACH) {
4704 0 : status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4705 0 : return tr_finish(hDB, &s, transition, status, errstr);
4706 : }
4707 :
4708 0 : mstrlcpy(errstr, "Unknown error", errstr_size);
4709 :
4710 0 : if (debug_flag == 0) {
4711 0 : size = sizeof(i);
4712 0 : db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4713 : }
4714 :
4715 : /* if no run number is given, get it from ODB and increment it */
4716 0 : if (run_number == 0) {
4717 0 : size = sizeof(run_number);
4718 0 : status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4719 0 : assert(status == SUCCESS);
4720 0 : if (transition == TR_START) {
4721 0 : run_number++;
4722 : }
4723 0 : s.run_number = run_number;
4724 :
4725 0 : if (transition != TR_STARTABORT) {
4726 0 : db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4727 : }
4728 : }
4729 :
4730 0 : if (run_number <= 0) {
4731 0 : cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4732 0 : abort();
4733 : }
4734 :
4735 : /* Set new run number in ODB */
4736 0 : if (transition == TR_START) {
4737 0 : if (debug_flag == 1)
4738 0 : printf("Setting run number %d in ODB\n", run_number);
4739 0 : if (debug_flag == 2)
4740 0 : cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4741 :
4742 0 : status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4743 0 : if (status != DB_SUCCESS) {
4744 0 : cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4745 0 : abort();
4746 : }
4747 : }
4748 :
4749 0 : if (deferred) {
4750 0 : if (debug_flag == 1)
4751 0 : printf("Clearing /Runinfo/Requested transition\n");
4752 0 : if (debug_flag == 2)
4753 0 : cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4754 :
4755 : /* remove transition request */
4756 0 : i = 0;
4757 0 : db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4758 : } else {
4759 0 : status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4760 0 : if (status != DB_SUCCESS) {
4761 0 : cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4762 0 : if (errstr)
4763 0 : mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4764 0 : return tr_finish(hDB, &s, transition, status, errstr);
4765 : }
4766 :
4767 : /* check if deferred transition already in progress */
4768 0 : size = sizeof(i);
4769 0 : db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4770 0 : if (i) {
4771 0 : if (errstr) {
4772 0 : mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4773 0 : mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4774 : }
4775 0 : return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4776 : }
4777 :
4778 0 : std::string trname = cm_transition_name(transition);
4779 :
4780 0 : sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4781 :
4782 : /* search database for clients with deferred transition request */
4783 0 : for (i = 0, status = 0;; i++) {
4784 0 : status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4785 0 : if (status == DB_NO_MORE_SUBKEYS)
4786 0 : break;
4787 :
4788 0 : if (status == DB_SUCCESS) {
4789 0 : size = sizeof(sequence_number);
4790 0 : status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4791 :
4792 : /* if registered for deferred transition, set flag in ODB and return */
4793 0 : if (status == DB_SUCCESS) {
4794 : char str[256];
4795 0 : size = NAME_LENGTH;
4796 0 : db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4797 :
4798 0 : if (debug_flag == 1)
4799 0 : printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4800 0 : if (debug_flag == 2)
4801 0 : cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4802 :
4803 0 : if (debug_flag == 1)
4804 0 : printf("Setting /Runinfo/Requested transition\n");
4805 0 : if (debug_flag == 2)
4806 0 : cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4807 :
4808 : /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4809 : * will activate the deferred transition code in the frontend.
4810 : * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4811 :
4812 0 : db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4813 :
4814 0 : db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4815 :
4816 0 : if (errstr)
4817 0 : sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4818 :
4819 0 : return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4820 : }
4821 : }
4822 0 : }
4823 0 : }
4824 :
4825 : /* execute programs on start */
4826 0 : if (transition == TR_START) {
4827 : char str[256];
4828 0 : str[0] = 0;
4829 0 : size = sizeof(str);
4830 0 : db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4831 0 : if (str[0])
4832 0 : ss_system(str);
4833 :
4834 0 : db_find_key(hDB, 0, "/Programs", &hRootKey);
4835 0 : if (hRootKey) {
4836 0 : for (i = 0;; i++) {
4837 0 : BOOL program_info_auto_start = FALSE;
4838 0 : status = db_enum_key(hDB, hRootKey, i, &hKey);
4839 0 : if (status == DB_NO_MORE_SUBKEYS)
4840 0 : break;
4841 :
4842 0 : db_get_key(hDB, hKey, &key);
4843 :
4844 : /* don't check "execute on xxx" */
4845 0 : if (key.type != TID_KEY)
4846 0 : continue;
4847 :
4848 0 : size = sizeof(program_info_auto_start);
4849 0 : status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4850 0 : if (status != DB_SUCCESS) {
4851 0 : cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4852 0 : continue;
4853 : }
4854 :
4855 0 : if (program_info_auto_start) {
4856 : char start_command[MAX_STRING_LENGTH];
4857 0 : start_command[0] = 0;
4858 :
4859 0 : size = sizeof(start_command);
4860 0 : status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4861 0 : if (status != DB_SUCCESS) {
4862 0 : cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4863 0 : continue;
4864 : }
4865 :
4866 0 : if (start_command[0]) {
4867 0 : cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4868 : start_command);
4869 0 : ss_system(start_command);
4870 : }
4871 : }
4872 0 : }
4873 : }
4874 : }
4875 :
4876 : /* execute programs on startabort */
4877 0 : if (transition == TR_STARTABORT) {
4878 : /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4879 0 : std::string cmd;
4880 0 : db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4881 :
4882 0 : if (!cmd.empty())
4883 0 : ss_system(cmd.c_str());
4884 0 : }
4885 :
4886 : /* set new start time in database */
4887 0 : if (transition == TR_START) {
4888 : /* ASCII format */
4889 0 : std::string now = cm_asctime();
4890 0 : now.reserve(32);
4891 0 : db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4892 :
4893 : /* reset stop time */
4894 0 : seconds = 0;
4895 0 : db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4896 :
4897 : /* Seconds since 1.1.1970 */
4898 0 : cm_time(&seconds);
4899 0 : db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4900 0 : }
4901 :
4902 0 : size = sizeof(state);
4903 0 : status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4904 :
4905 : /* set stop time in database */
4906 0 : if (transition == TR_STOP) {
4907 0 : if (status != DB_SUCCESS)
4908 0 : cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4909 :
4910 0 : if (state != STATE_STOPPED) {
4911 : /* stop time binary */
4912 0 : cm_time(&seconds);
4913 0 : status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4914 0 : if (status != DB_SUCCESS)
4915 0 : cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4916 :
4917 : /* stop time ascii */
4918 0 : std::string now = cm_asctime();
4919 0 : now.reserve(32);
4920 0 : status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4921 0 : if (status != DB_SUCCESS)
4922 0 : cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4923 0 : }
4924 : }
4925 :
4926 0 : status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4927 0 : if (status != DB_SUCCESS) {
4928 0 : cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4929 0 : if (errstr)
4930 0 : mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4931 0 : return tr_finish(hDB, &s, transition, status, errstr);
4932 : }
4933 :
4934 0 : std::string trname = cm_transition_name(transition);
4935 :
4936 : /* check that all transition clients are alive */
4937 0 : for (int i = 0;;) {
4938 0 : status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4939 0 : if (status != DB_SUCCESS)
4940 0 : break;
4941 :
4942 0 : status = cm_check_client(hDB, hSubkey);
4943 :
4944 0 : if (status == DB_SUCCESS) {
4945 : /* this client is alive. Check next one! */
4946 0 : i++;
4947 0 : continue;
4948 : }
4949 :
4950 0 : assert(status == CM_NO_CLIENT);
4951 :
4952 : /* start from scratch: removing odb entries as we iterate over them
4953 : * does strange things to db_enum_key() */
4954 0 : i = 0;
4955 : }
4956 :
4957 : /* check for broken RPC connections */
4958 0 : rpc_client_check();
4959 :
4960 0 : if (debug_flag == 1)
4961 0 : printf("---- Transition %s started ----\n", trname.c_str());
4962 0 : if (debug_flag == 2)
4963 0 : cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4964 :
4965 0 : sprintf(tr_key_name, "Transition %s", trname.c_str());
4966 :
4967 : /* search database for clients which registered for transition */
4968 :
4969 0 : for (int i = 0, status = 0;; i++) {
4970 : KEY subkey;
4971 0 : status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4972 0 : if (status == DB_NO_MORE_SUBKEYS)
4973 0 : break;
4974 :
4975 0 : status = db_get_key(hDB, hSubkey, &subkey);
4976 0 : assert(status == DB_SUCCESS);
4977 :
4978 0 : if (status == DB_SUCCESS) {
4979 0 : status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
4980 :
4981 0 : if (status == DB_SUCCESS) {
4982 :
4983 0 : db_get_key(hDB, hKeyTrans, &key);
4984 :
4985 0 : for (int j = 0; j < key.num_values; j++) {
4986 0 : size = sizeof(sequence_number);
4987 0 : status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4988 0 : assert(status == DB_SUCCESS);
4989 :
4990 0 : TrClient *c = new TrClient;
4991 :
4992 0 : c->init_time = ss_millitime();
4993 0 : c->transition = transition;
4994 0 : c->run_number = run_number;
4995 0 : c->async_flag = async_flag;
4996 0 : c->debug_flag = debug_flag;
4997 0 : c->sequence_number = sequence_number;
4998 0 : c->status = 0;
4999 0 : c->key_name = subkey.name;
5000 :
5001 : /* get client info */
5002 : char client_name[NAME_LENGTH];
5003 0 : size = sizeof(client_name);
5004 0 : db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
5005 0 : c->client_name = client_name;
5006 :
5007 : char host_name[HOST_NAME_LENGTH];
5008 0 : size = sizeof(host_name);
5009 0 : db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
5010 0 : c->host_name = host_name;
5011 :
5012 : //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
5013 :
5014 0 : if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5015 : /* remember own client */
5016 0 : c->port = 0;
5017 : } else {
5018 0 : size = sizeof(port);
5019 0 : db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5020 0 : c->port = port;
5021 : }
5022 :
5023 : /* check for duplicates */
5024 :
5025 0 : bool found = false;
5026 0 : for (size_t k=0; k<s.clients.size(); k++) {
5027 0 : TrClient* cc = s.clients[k].get();
5028 0 : if (cc->client_name == c->client_name)
5029 0 : if (cc->host_name == c->host_name)
5030 0 : if (cc->port == c->port)
5031 0 : if (cc->sequence_number == c->sequence_number)
5032 0 : found = true;
5033 : }
5034 :
5035 0 : if (!found) {
5036 0 : s.clients.push_back(std::unique_ptr<TrClient>(c));
5037 0 : c = NULL;
5038 : } else {
5039 0 : cm_msg(MERROR, "cm_transition", "transition %s: client \"%s\" is registered with sequence number %d more than once", trname.c_str(), c->client_name.c_str(), c->sequence_number);
5040 0 : delete c;
5041 0 : c = NULL;
5042 : }
5043 : }
5044 : }
5045 : }
5046 0 : }
5047 :
5048 0 : std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5049 :
5050 : /* set predecessor for multi-threaded transitions */
5051 0 : for (size_t idx = 0; idx < s.clients.size(); idx++) {
5052 0 : if (s.clients[idx]->sequence_number == 0) {
5053 : // sequence number 0 means "don't care"
5054 : } else {
5055 : /* find clients with smaller sequence number */
5056 0 : if (idx > 0) {
5057 0 : for (size_t i = idx - 1; ; i--) {
5058 0 : if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5059 0 : if (s.clients[i]->sequence_number > 0) {
5060 0 : s.clients[idx]->wait_for_index.push_back(i);
5061 : }
5062 : }
5063 0 : if (i==0)
5064 0 : break;
5065 : }
5066 : }
5067 : }
5068 : }
5069 :
5070 0 : for (size_t idx = 0; idx < s.clients.size(); idx++) {
5071 0 : write_tr_client_to_odb(hDB, s.clients[idx].get());
5072 : }
5073 :
5074 : #if 0
5075 : for (size_t idx = 0; idx < s.clients.size(); idx++) {
5076 : printf("TrClient[%d]: ", int(idx));
5077 : s.clients[idx]->Print();
5078 : printf("\n");
5079 : }
5080 : #endif
5081 :
5082 : /* contact ordered clients for transition -----------------------*/
5083 0 : status = CM_SUCCESS;
5084 0 : for (size_t idx = 0; idx < s.clients.size(); idx++) {
5085 0 : if (debug_flag == 1)
5086 0 : printf("\n==== Found client \"%s\" with sequence number %d\n",
5087 0 : s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5088 0 : if (debug_flag == 2)
5089 0 : cm_msg(MINFO, "cm_transition",
5090 : "cm_transition: ==== Found client \"%s\" with sequence number %d",
5091 0 : s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5092 :
5093 0 : if (async_flag & TR_MTHREAD) {
5094 0 : status = CM_SUCCESS;
5095 0 : assert(s.clients[idx]->thread == NULL);
5096 0 : s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5097 : } else {
5098 0 : if (s.clients[idx]->port == 0) {
5099 : /* if own client call transition callback directly */
5100 0 : status = cm_transition_call_direct(s.clients[idx].get());
5101 : } else {
5102 : /* if other client call transition via RPC layer */
5103 0 : status = cm_transition_call(&s, idx);
5104 : }
5105 :
5106 0 : if (status == CM_SUCCESS && transition != TR_STOP)
5107 0 : if (s.clients[idx]->status != SUCCESS) {
5108 0 : cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5109 0 : s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5110 0 : break;
5111 : }
5112 : }
5113 :
5114 0 : if (status != CM_SUCCESS)
5115 0 : break;
5116 : }
5117 :
5118 : /* wait until all threads have finished */
5119 0 : for (size_t idx = 0; idx < s.clients.size(); idx++) {
5120 0 : if (s.clients[idx]->thread) {
5121 : // join() will wait forever until thread finishes
5122 0 : s.clients[idx]->thread->join();
5123 0 : delete s.clients[idx]->thread;
5124 0 : s.clients[idx]->thread = NULL;
5125 : }
5126 : }
5127 :
5128 : /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5129 :
5130 0 : i = 0;
5131 0 : size = sizeof(i);
5132 0 : status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5133 :
5134 0 : if (status == DB_SUCCESS && i == 0) {
5135 0 : cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5136 :
5137 0 : if (errstr != NULL)
5138 0 : mstrlcpy(errstr, "Canceled", errstr_size);
5139 :
5140 0 : return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5141 : }
5142 :
5143 : /* search for any error */
5144 0 : for (size_t idx = 0; idx < s.clients.size(); idx++)
5145 0 : if (s.clients[idx]->status != CM_SUCCESS) {
5146 0 : status = s.clients[idx]->status;
5147 0 : if (errstr)
5148 0 : mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5149 0 : s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5150 0 : break;
5151 : }
5152 :
5153 0 : if (transition != TR_STOP && status != CM_SUCCESS) {
5154 : /* indicate abort */
5155 0 : i = 1;
5156 0 : db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5157 0 : i = 0;
5158 0 : db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5159 :
5160 0 : return tr_finish(hDB, &s, transition, status, errstr);
5161 : }
5162 :
5163 0 : if (debug_flag == 1)
5164 0 : printf("\n---- Transition %s finished ----\n", trname.c_str());
5165 0 : if (debug_flag == 2)
5166 0 : cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5167 :
5168 : /* set new run state in database */
5169 0 : if (transition == TR_START || transition == TR_RESUME)
5170 0 : state = STATE_RUNNING;
5171 :
5172 0 : if (transition == TR_PAUSE)
5173 0 : state = STATE_PAUSED;
5174 :
5175 0 : if (transition == TR_STOP)
5176 0 : state = STATE_STOPPED;
5177 :
5178 0 : if (transition == TR_STARTABORT)
5179 0 : state = STATE_STOPPED;
5180 :
5181 0 : size = sizeof(state);
5182 0 : status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5183 0 : if (status != DB_SUCCESS)
5184 0 : cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5185 :
5186 : /* send notification message */
5187 0 : if (transition == TR_START)
5188 0 : cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5189 0 : if (transition == TR_STOP)
5190 0 : cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5191 0 : if (transition == TR_PAUSE)
5192 0 : cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5193 0 : if (transition == TR_RESUME)
5194 0 : cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5195 0 : if (transition == TR_STARTABORT)
5196 0 : cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5197 :
5198 : /* lock/unlock ODB values if present */
5199 0 : db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5200 0 : if (hKey) {
5201 0 : if (state == STATE_STOPPED)
5202 0 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE | MODE_DELETE, TRUE);
5203 : else
5204 0 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
5205 : }
5206 :
5207 : /* flush online database */
5208 0 : if (transition == TR_STOP)
5209 0 : db_flush_database(hDB);
5210 :
5211 : /* execute/stop programs on stop */
5212 0 : if (transition == TR_STOP) {
5213 0 : std::string cmd;
5214 0 : db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5215 0 : if (!cmd.empty())
5216 0 : ss_system(cmd.c_str());
5217 :
5218 0 : db_find_key(hDB, 0, "/Programs", &hRootKey);
5219 0 : if (hRootKey) {
5220 0 : for (i = 0;; i++) {
5221 0 : BOOL program_info_auto_stop = FALSE;
5222 0 : status = db_enum_key(hDB, hRootKey, i, &hKey);
5223 0 : if (status == DB_NO_MORE_SUBKEYS)
5224 0 : break;
5225 :
5226 0 : db_get_key(hDB, hKey, &key);
5227 :
5228 : /* don't check "execute on xxx" */
5229 0 : if (key.type != TID_KEY)
5230 0 : continue;
5231 :
5232 0 : size = sizeof(program_info_auto_stop);
5233 0 : status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5234 0 : if (status != DB_SUCCESS) {
5235 0 : cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5236 0 : continue;
5237 : }
5238 :
5239 0 : if (program_info_auto_stop) {
5240 0 : cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5241 0 : cm_shutdown(key.name, FALSE);
5242 : }
5243 0 : }
5244 : }
5245 0 : }
5246 :
5247 :
5248 : /* indicate success */
5249 0 : i = 0;
5250 0 : db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5251 :
5252 0 : if (errstr != NULL)
5253 0 : mstrlcpy(errstr, "Success", errstr_size);
5254 :
5255 0 : return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5256 0 : }
5257 :
5258 : /*------------------------------------------------------------------*/
5259 :
5260 : /* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5261 0 : static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5262 : int status;
5263 :
5264 0 : status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5265 :
5266 0 : if (transition == TR_START && status != CM_SUCCESS) {
5267 0 : cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5268 : errstr);
5269 0 : cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5270 : }
5271 :
5272 0 : return status;
5273 : }
5274 :
5275 : /*------------------------------------------------------------------*/
5276 :
5277 0 : static INT tr_main_thread(void *param) {
5278 : INT status;
5279 : TR_PARAM *trp;
5280 :
5281 0 : trp = (TR_PARAM *) param;
5282 0 : status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5283 :
5284 0 : trp->status = status;
5285 0 : trp->finished = TRUE;
5286 :
5287 0 : return 0;
5288 : }
5289 :
5290 0 : INT cm_transition_cleanup()
5291 : {
5292 0 : if (_trp.thread && !_trp.finished) {
5293 : //printf("main transition thread did not finish yet!\n");
5294 0 : return CM_TRANSITION_IN_PROGRESS;
5295 : }
5296 :
5297 0 : std::thread* t = _trp.thread.exchange(NULL);
5298 :
5299 0 : if (t) {
5300 0 : t->join();
5301 0 : delete t;
5302 0 : t = NULL;
5303 : }
5304 :
5305 0 : return CM_SUCCESS;
5306 : }
5307 :
5308 : /* wrapper around cm_transition1() for detached multi-threaded transitions */
5309 0 : INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5310 0 : int mflag = async_flag & TR_MTHREAD;
5311 0 : int sflag = async_flag & TR_SYNC;
5312 :
5313 0 : int status = cm_transition_cleanup();
5314 :
5315 0 : if (status != CM_SUCCESS) {
5316 0 : cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5317 0 : return CM_TRANSITION_IN_PROGRESS;
5318 : }
5319 :
5320 : /* get key of local client */
5321 : HNDLE hDB;
5322 0 : cm_get_experiment_database(&hDB, NULL);
5323 :
5324 0 : bool deferred = (transition & TR_DEFERRED) > 0;
5325 0 : INT trans_raw = (transition & ~TR_DEFERRED);
5326 :
5327 : /* check for valid transition */
5328 0 : if (trans_raw != TR_START && trans_raw != TR_STOP && trans_raw != TR_PAUSE && trans_raw != TR_RESUME && trans_raw != TR_STARTABORT) {
5329 0 : cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5330 0 : if (errstr) {
5331 0 : mstrlcpy(errstr, "Invalid transition request", errstr_size);
5332 : }
5333 0 : return CM_INVALID_TRANSITION;
5334 : }
5335 :
5336 : /* check if transition in progress */
5337 0 : if (!deferred) {
5338 0 : int i = 0;
5339 0 : int size = sizeof(i);
5340 0 : db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5341 0 : if (i == 1) {
5342 0 : if (errstr) {
5343 0 : sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5344 0 : mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5345 : }
5346 0 : cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5347 0 : return CM_TRANSITION_IN_PROGRESS;
5348 : }
5349 : }
5350 :
5351 0 : if (mflag) {
5352 0 : _trp.transition = transition;
5353 0 : _trp.run_number = run_number;
5354 0 : if (sflag) {
5355 : /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5356 0 : _trp.errstr = errstr;
5357 0 : _trp.errstr_size = errstr_size;
5358 : } else {
5359 : /* in normal MTHREAD mode, we return right away and
5360 : * if errstr is a local variable in the caller and they return too,
5361 : * errstr becomes a stale reference and writing into it will corrupt the stack
5362 : * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5363 : * and we definitely corrupt mlogger memory with out this: */
5364 0 : _trp.errstr = NULL;
5365 0 : _trp.errstr_size = 0;
5366 : }
5367 0 : _trp.async_flag = async_flag;
5368 0 : _trp.debug_flag = debug_flag;
5369 0 : _trp.status = 0;
5370 0 : _trp.finished = FALSE;
5371 :
5372 0 : if (errstr)
5373 0 : *errstr = 0; // null error string
5374 :
5375 : //ss_thread_create(tr_main_thread, &_trp);
5376 :
5377 0 : std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5378 :
5379 0 : assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5380 :
5381 0 : if (sflag) {
5382 :
5383 : /* wait until main thread has finished */
5384 : do {
5385 0 : ss_sleep(10);
5386 0 : } while (!_trp.finished);
5387 :
5388 0 : std::thread* t = _trp.thread.exchange(NULL);
5389 :
5390 0 : if (t) {
5391 0 : t->join();
5392 0 : delete t;
5393 0 : t = NULL;
5394 : }
5395 :
5396 0 : return _trp.status;
5397 : }
5398 : } else
5399 0 : return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5400 :
5401 0 : return CM_SUCCESS;
5402 : }
5403 :
5404 : /**dox***************************************************************/
5405 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
5406 :
5407 : /********************************************************************/
5408 0 : INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
5409 : /********************************************************************\
5410 :
5411 : Routine: cm_dispatch_ipc
5412 :
5413 : Purpose: Called from ss_suspend if an IPC message arrives
5414 :
5415 : Input:
5416 : INT msg IPC message we got, MSG_ODB/MSG_BM
5417 : INT p1, p2 Optional parameters
5418 : int s Optional server socket
5419 :
5420 : Output:
5421 : none
5422 :
5423 : Function value:
5424 : CM_SUCCESS Successful completion
5425 :
5426 : \********************************************************************/
5427 : {
5428 0 : if (message[0] == 'O') {
5429 : HNDLE hDB, hKey, hKeyRoot;
5430 : INT index;
5431 0 : index = 0;
5432 0 : sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5433 0 : if (client_socket) {
5434 0 : return db_update_record_mserver(hDB, hKeyRoot, hKey, index, client_socket);
5435 : } else {
5436 0 : return db_update_record_local(hDB, hKeyRoot, hKey, index);
5437 : }
5438 : }
5439 :
5440 : /* message == "B" means "resume event sender" */
5441 0 : if (message[0] == 'B' && message[2] != ' ') {
5442 : char str[NAME_LENGTH];
5443 :
5444 : //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5445 :
5446 0 : mstrlcpy(str, message + 2, sizeof(str));
5447 0 : if (strchr(str, ' '))
5448 0 : *strchr(str, ' ') = 0;
5449 :
5450 0 : if (client_socket)
5451 0 : return bm_notify_client(str, client_socket);
5452 : else
5453 0 : return bm_push_event(str);
5454 : }
5455 :
5456 : //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5457 :
5458 0 : return CM_SUCCESS;
5459 : }
5460 :
5461 : /********************************************************************/
5462 : static BOOL _ctrlc_pressed = FALSE;
5463 :
5464 0 : void cm_ctrlc_handler(int sig) {
5465 0 : if (_ctrlc_pressed) {
5466 0 : printf("Received 2nd Ctrl-C, hard abort\n");
5467 0 : exit(0);
5468 : }
5469 0 : printf("Received Ctrl-C, aborting...\n");
5470 0 : _ctrlc_pressed = TRUE;
5471 :
5472 0 : ss_ctrlc_handler(cm_ctrlc_handler);
5473 0 : }
5474 :
5475 0 : BOOL cm_is_ctrlc_pressed() {
5476 0 : return _ctrlc_pressed;
5477 : }
5478 :
5479 0 : void cm_ack_ctrlc_pressed() {
5480 0 : _ctrlc_pressed = FALSE;
5481 0 : }
5482 :
5483 : /********************************************************************/
5484 0 : int cm_exec_script(const char *odb_path_to_script)
5485 : /********************************************************************\
5486 :
5487 : Routine: cm_exec_script
5488 :
5489 : Purpose: Execute script from /Script tree
5490 :
5491 : exec_script is enabled by the tree /Script
5492 : The /Script struct is composed of list of keys
5493 : from which the name of the key is the button name
5494 : and the sub-structure is a record as follow:
5495 :
5496 : /Script/<button_name> = <script command> (TID_STRING)
5497 :
5498 : The "Script command", containing possible arguements,
5499 : is directly executed.
5500 :
5501 : /Script/<button_name>/<script command>
5502 : <soft link1>|<arg1>
5503 : <soft link2>|<arg2>
5504 : ...
5505 :
5506 : The arguments for the script are derived from the
5507 : subtree below <button_name>, where <button_name> must be
5508 : TID_KEY. The subtree may then contain arguments or links
5509 : to other values in the ODB, like run number etc.
5510 :
5511 : \********************************************************************/
5512 : {
5513 : HNDLE hDB, hkey;
5514 : KEY key;
5515 : int status;
5516 :
5517 0 : status = cm_get_experiment_database(&hDB, NULL);
5518 0 : if (status != DB_SUCCESS)
5519 0 : return status;
5520 :
5521 0 : status = db_find_key(hDB, 0, odb_path_to_script, &hkey);
5522 0 : if (status != DB_SUCCESS)
5523 0 : return status;
5524 :
5525 0 : status = db_get_key(hDB, hkey, &key);
5526 0 : if (status != DB_SUCCESS)
5527 0 : return status;
5528 :
5529 0 : std::string command;
5530 :
5531 0 : if (key.type == TID_STRING) {
5532 0 : int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5533 0 : if (status != DB_SUCCESS) {
5534 0 : cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5535 : odb_path_to_script, status);
5536 0 : return status;
5537 : }
5538 0 : } else if (key.type == TID_KEY) {
5539 0 : for (int i = 0;; i++) {
5540 : HNDLE hsubkey;
5541 : KEY subkey;
5542 0 : db_enum_key(hDB, hkey, i, &hsubkey);
5543 0 : if (!hsubkey)
5544 0 : break;
5545 0 : db_get_key(hDB, hsubkey, &subkey);
5546 :
5547 0 : if (i > 0)
5548 0 : command += " ";
5549 :
5550 0 : if (subkey.type == TID_KEY) {
5551 0 : cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5552 : subkey.name);
5553 0 : return DB_TYPE_MISMATCH;
5554 : } else {
5555 0 : int size = subkey.item_size;
5556 0 : char *buf = (char *) malloc(size);
5557 0 : assert(buf != NULL);
5558 0 : int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5559 0 : if (status != DB_SUCCESS) {
5560 0 : cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5561 : odb_path_to_script, subkey.name, subkey.type, status);
5562 0 : free(buf);
5563 0 : return status;
5564 : }
5565 0 : if (subkey.type == TID_STRING) {
5566 0 : command += buf;
5567 : } else {
5568 0 : command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5569 : }
5570 0 : free(buf);
5571 : }
5572 0 : }
5573 : } else {
5574 0 : cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5575 : odb_path_to_script, key.type);
5576 0 : return DB_TYPE_MISMATCH;
5577 : }
5578 :
5579 : // printf("exec_script: %s\n", command.c_str());
5580 :
5581 0 : if (command.length() > 0) {
5582 0 : cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5583 0 : ss_system(command.c_str());
5584 : }
5585 :
5586 0 : return SUCCESS;
5587 0 : }
5588 :
5589 : /**dox***************************************************************/
5590 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
5591 :
5592 : static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5593 :
5594 : /********************************************************************/
5595 : /**
5596 : Perform midas periodic tasks - check alarms, update and check
5597 : timeouts on odb and on event buffers, etc. Normally called by cm_yield().
5598 : Programs that do not use cm_yield(), i.e. the mserver,
5599 : should call this function periodically, every 1 or 2 seconds.
5600 : @return CM_SUCCESS
5601 : */
5602 0 : INT cm_periodic_tasks() {
5603 : static DWORD alarm_last_checked_sec = 0;
5604 0 : DWORD now_sec = ss_time();
5605 :
5606 0 : DWORD now_millitime = ss_millitime();
5607 : static DWORD last_millitime = 0;
5608 0 : DWORD tdiff_millitime = now_millitime - last_millitime;
5609 0 : const DWORD kPeriod = 1000;
5610 0 : if (last_millitime == 0) {
5611 0 : last_millitime = now_millitime;
5612 0 : tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5613 : }
5614 :
5615 : //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5616 :
5617 : //if (now_millitime < last_millitime) {
5618 : // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5619 : //}
5620 :
5621 : /* check alarms once every 10 seconds */
5622 0 : if (now_sec - alarm_last_checked_sec > 10) {
5623 0 : al_check();
5624 0 : alarm_last_checked_sec = now_sec;
5625 : }
5626 :
5627 : /* run periodic checks previously done by cm_watchdog */
5628 :
5629 0 : if (tdiff_millitime >= kPeriod) {
5630 0 : BOOL wrong_interval = FALSE;
5631 0 : if (tdiff_millitime > 60000)
5632 0 : wrong_interval = TRUE;
5633 :
5634 : //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5635 :
5636 0 : bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5637 0 : db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5638 :
5639 0 : bm_write_statistics_to_odb();
5640 :
5641 0 : last_millitime = now_millitime;
5642 : }
5643 :
5644 : /* reap transition thread */
5645 :
5646 0 : cm_transition_cleanup();
5647 :
5648 0 : return CM_SUCCESS;
5649 : }
5650 :
5651 : /********************************************************************/
5652 : /**
5653 : Central yield functions for clients. This routine should
5654 : be called in an infinite loop by a client in order to
5655 : give the MIDAS system the opportunity to receive commands
5656 : over RPC channels, update database records and receive
5657 : events.
5658 : @param millisec Timeout in millisec. If no message is
5659 : received during the specified timeout,
5660 : the routine returns. If millisec=-1,
5661 : it only returns when receiving an
5662 : RPC_SHUTDOWN message.
5663 : @return CM_SUCCESS, RPC_SHUTDOWN
5664 : */
5665 0 : INT cm_yield(INT millisec) {
5666 : INT status;
5667 : INT bMore;
5668 : //static DWORD last_yield = 0;
5669 : //static DWORD last_yield_time = 0;
5670 : //DWORD start_yield = ss_millitime();
5671 :
5672 : /* check for ctrl-c */
5673 0 : if (_ctrlc_pressed)
5674 0 : return RPC_SHUTDOWN;
5675 :
5676 : /* flush the cm_msg buffer */
5677 0 : cm_msg_flush_buffer();
5678 :
5679 0 : if (!rpc_is_remote()) {
5680 : /* flush the ODB to its binary file */
5681 : /* for remote clients, ODB is flushed by the mserver */
5682 : HNDLE hDB;
5683 0 : cm_get_experiment_database(&hDB, NULL);
5684 0 : db_flush_database(hDB);
5685 : }
5686 :
5687 : /* check for available events */
5688 0 : if (rpc_is_remote()) {
5689 : //printf("cm_yield() calling bm_poll_event()\n");
5690 0 : status = bm_poll_event();
5691 :
5692 0 : if (status == SS_ABORT) {
5693 0 : return status;
5694 : }
5695 :
5696 0 : if (status == BM_SUCCESS) {
5697 : /* one or more events received by bm_poll_event() */
5698 0 : status = ss_suspend(0, 0);
5699 : } else {
5700 0 : status = ss_suspend(millisec, 0);
5701 : }
5702 :
5703 0 : return status;
5704 : }
5705 :
5706 0 : status = cm_periodic_tasks();
5707 :
5708 0 : if (status != CM_SUCCESS)
5709 0 : return status;
5710 :
5711 : //DWORD start_check = ss_millitime();
5712 :
5713 0 : bMore = bm_check_buffers();
5714 :
5715 : //DWORD end_check = ss_millitime();
5716 : //printf("cm_yield: timeout %4d, yield period %4d, last yield time %4d, bm_check_buffers() elapsed %4d, returned %d\n", millisec, start_yield - last_yield, last_yield_time, end_check - start_check, bMore);
5717 : //fflush(stdout);
5718 :
5719 0 : if (bMore == BM_CORRUPTED) {
5720 0 : status = SS_ABORT;
5721 0 : } else if (bMore) {
5722 : /* if events available, quickly check other IPC channels */
5723 0 : status = ss_suspend(0, 0);
5724 : } else {
5725 0 : status = ss_suspend(millisec, 0);
5726 : }
5727 :
5728 : /* flush the cm_msg buffer */
5729 0 : cm_msg_flush_buffer();
5730 :
5731 : //DWORD end_yield = ss_millitime();
5732 : //last_yield_time = end_yield - start_yield;
5733 : //last_yield = start_yield;
5734 :
5735 0 : return status;
5736 : }
5737 :
5738 : /********************************************************************/
5739 : /**
5740 : Executes command via system() call
5741 : @param command Command string to execute
5742 : @param result stdout of command
5743 : @param bufsize string size in byte
5744 : @return CM_SUCCESS
5745 : */
5746 0 : INT cm_execute(const char *command, char *result, INT bufsize) {
5747 : char str[256];
5748 : INT n;
5749 : int fh;
5750 0 : int status = 0;
5751 : static int check_cm_execute = 1;
5752 : static int enable_cm_execute = 0;
5753 :
5754 0 : if (rpc_is_remote())
5755 0 : return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5756 :
5757 0 : if (check_cm_execute) {
5758 : int status;
5759 : int size;
5760 : HNDLE hDB;
5761 0 : check_cm_execute = 0;
5762 :
5763 0 : status = cm_get_experiment_database(&hDB, NULL);
5764 0 : assert(status == DB_SUCCESS);
5765 :
5766 0 : size = sizeof(enable_cm_execute);
5767 0 : status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5768 0 : assert(status == DB_SUCCESS);
5769 :
5770 : //printf("enable_cm_execute %d\n", enable_cm_execute);
5771 : }
5772 :
5773 0 : if (!enable_cm_execute) {
5774 : char buf[32];
5775 0 : mstrlcpy(buf, command, sizeof(buf));
5776 0 : cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5777 0 : return CM_WRONG_PASSWORD;
5778 : }
5779 :
5780 0 : if (bufsize > 0) {
5781 0 : strcpy(str, command);
5782 0 : sprintf(str, "%s > %d.tmp", command, ss_getpid());
5783 :
5784 0 : status = system(str);
5785 :
5786 0 : sprintf(str, "%d.tmp", ss_getpid());
5787 0 : fh = open(str, O_RDONLY, 0644);
5788 0 : result[0] = 0;
5789 0 : if (fh) {
5790 0 : n = read(fh, result, bufsize - 1);
5791 0 : result[MAX(0, n)] = 0;
5792 0 : close(fh);
5793 : }
5794 0 : remove(str);
5795 : } else {
5796 0 : status = system(command);
5797 : }
5798 :
5799 0 : if (status < 0) {
5800 0 : cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5801 0 : return CM_SET_ERROR;
5802 : }
5803 :
5804 0 : return CM_SUCCESS;
5805 : }
5806 :
5807 :
5808 :
5809 : /**dox***************************************************************/
5810 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
5811 :
5812 : /********************************************************************/
5813 2 : INT cm_register_function(INT id, INT(*func)(INT, void **))
5814 : /********************************************************************\
5815 :
5816 : Routine: cm_register_function
5817 :
5818 : Purpose: Call rpc_register_function and publish the registered
5819 : function under system/clients/<pid>/RPC
5820 :
5821 : Input:
5822 : INT id RPC ID
5823 : INT *func New dispatch function
5824 :
5825 : Output:
5826 : <implicit: func gets copied to rpc_list>
5827 :
5828 : Function value:
5829 : CM_SUCCESS Successful completion
5830 : RPC_INVALID_ID RPC ID not found
5831 :
5832 : \********************************************************************/
5833 : {
5834 : HNDLE hDB, hKey;
5835 : INT status;
5836 : char str[80];
5837 :
5838 2 : status = rpc_register_function(id, func);
5839 2 : if (status != RPC_SUCCESS)
5840 0 : return status;
5841 :
5842 2 : cm_get_experiment_database(&hDB, &hKey);
5843 :
5844 : /* create new key for this id */
5845 2 : status = 1;
5846 2 : sprintf(str, "RPC/%d", id);
5847 :
5848 2 : db_set_mode(hDB, hKey, MODE_READ | MODE_WRITE, TRUE);
5849 2 : status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5850 2 : db_set_mode(hDB, hKey, MODE_READ, TRUE);
5851 :
5852 2 : if (status != DB_SUCCESS)
5853 0 : return status;
5854 :
5855 2 : return CM_SUCCESS;
5856 : }
5857 :
5858 :
5859 : /**dox***************************************************************/
5860 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
5861 :
5862 : //
5863 : // Return "/"-terminated file path for given history channel
5864 : //
5865 :
5866 0 : std::string cm_get_history_path(const char* history_channel)
5867 : {
5868 : int status;
5869 : HNDLE hDB;
5870 0 : std::string path;
5871 :
5872 0 : cm_get_experiment_database(&hDB, NULL);
5873 :
5874 0 : if (history_channel && (strlen(history_channel) > 0)) {
5875 0 : std::string p;
5876 0 : p += "/Logger/History/";
5877 0 : p += history_channel;
5878 0 : p += "/History dir";
5879 :
5880 : // NB: be careful to avoid creating odb entries under /logger
5881 : // for whatever values of "history_channel" we get called with!
5882 0 : status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5883 0 : if (status == DB_SUCCESS && path.length() > 0) {
5884 : // if not absolute path, prepend with experiment directory
5885 0 : if (path[0] != DIR_SEPARATOR)
5886 0 : path = cm_get_path() + path;
5887 : // append directory separator
5888 0 : if (path.back() != DIR_SEPARATOR)
5889 0 : path += DIR_SEPARATOR_STR;
5890 : //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5891 0 : return path;
5892 : }
5893 0 : }
5894 :
5895 0 : status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5896 0 : if (status == DB_SUCCESS && path.length() > 0) {
5897 : // if not absolute path, prepend with experiment directory
5898 0 : if (path[0] != DIR_SEPARATOR)
5899 0 : path = cm_get_path() + path;
5900 : // append directory separator
5901 0 : if (path.back() != DIR_SEPARATOR)
5902 0 : path += DIR_SEPARATOR_STR;
5903 : //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5904 0 : return path;
5905 : }
5906 :
5907 0 : status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5908 0 : if (status == DB_SUCCESS && path.length() > 0) {
5909 : // if not absolute path, prepend with experiment directory
5910 0 : if (path[0] != DIR_SEPARATOR)
5911 0 : path = cm_get_path() + path;
5912 : // append directory separator
5913 0 : if (path.back() != DIR_SEPARATOR)
5914 0 : path += DIR_SEPARATOR_STR;
5915 : //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5916 0 : return path;
5917 : }
5918 :
5919 : //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5920 0 : return cm_get_path();
5921 0 : }
5922 :
5923 : /**dox***************************************************************/
5924 : /** @} *//* end of cmfunctionc */
5925 :
5926 : /**dox***************************************************************/
5927 : /** @addtogroup bmfunctionc
5928 : *
5929 : * @{ */
5930 :
5931 : /********************************************************************\
5932 : * *
5933 : * bm_xxx - Buffer Manager Functions *
5934 : * *
5935 : \********************************************************************/
5936 :
5937 : static DWORD _bm_max_event_size = 0;
5938 :
5939 : #ifdef LOCAL_ROUTINES
5940 :
5941 : // see locking code in xbm_lock_buffer()
5942 : static int _bm_lock_timeout = 5 * 60 * 1000;
5943 : static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5944 :
5945 6 : static int bm_validate_client_index_locked(bm_lock_buffer_guard& pbuf_guard)
5946 : {
5947 6 : const BUFFER *pbuf = pbuf_guard.get_pbuf();
5948 :
5949 6 : bool badindex = false;
5950 6 : bool badclient = false;
5951 :
5952 6 : int idx = pbuf->client_index;
5953 :
5954 6 : if (idx < 0) {
5955 0 : badindex = true;
5956 6 : } else if (idx > pbuf->buffer_header->max_client_index) {
5957 0 : badindex = true;
5958 : } else {
5959 6 : BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5960 6 : if (pclient->name[0] == 0)
5961 0 : badclient = true;
5962 6 : else if (pclient->pid != ss_getpid())
5963 0 : badclient = true;
5964 :
5965 : //if (strcmp(pclient->name,"mdump")==0) {
5966 : // for (int i=0; i<15; i++) {
5967 : // printf("sleep %d\n", i);
5968 : // ::sleep(1);
5969 : // }
5970 : //}
5971 : }
5972 :
5973 : #if 0
5974 : if (badindex) {
5975 : printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5976 : pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5977 : badindex, ss_getpid());
5978 : } else if (badclient) {
5979 : printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d, badclient %d\n",
5980 : pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5981 : pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5982 : ss_getpid(), badclient);
5983 : } else {
5984 : printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d, goodclient\n",
5985 : pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5986 : pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5987 : ss_getpid());
5988 : }
5989 : #endif
5990 :
5991 6 : if (badindex || badclient) {
5992 : static int prevent_recursion = 1;
5993 :
5994 0 : if (prevent_recursion) {
5995 0 : prevent_recursion = 0;
5996 :
5997 0 : if (badindex) {
5998 0 : cm_msg(MERROR, "bm_validate_client_index", "My client index %d in buffer \'%s\' is invalid, max_client_index %d, my pid %d", idx, pbuf->buffer_header->name, pbuf->buffer_header->max_client_index, ss_getpid());
5999 : } else {
6000 0 : cm_msg(MERROR, "bm_validate_client_index", "My client index %d in buffer \'%s\' is invalid: client name \'%s\', pid %d should be my pid %d", idx, pbuf->buffer_header->name, pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid, ss_getpid());
6001 : }
6002 :
6003 0 : cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
6004 : }
6005 :
6006 0 : if (badindex) {
6007 0 : fprintf(stderr, "bm_validate_client_index: My client index %d in buffer \'%s\' is invalid, max_client_index %d, my pid %d\n", idx, pbuf->buffer_header->name, pbuf->buffer_header->max_client_index, ss_getpid());
6008 : } else {
6009 0 : fprintf(stderr, "bm_validate_client_index: My client index %d in buffer \'%s\' is invalid: client name \'%s\', pid %d should be my pid %d\n", idx, pbuf->buffer_header->name, pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid, ss_getpid());
6010 : }
6011 :
6012 0 : fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
6013 :
6014 0 : pbuf_guard.unlock();
6015 :
6016 0 : abort();
6017 : }
6018 :
6019 6 : return idx;
6020 : }
6021 :
6022 6 : static BUFFER_CLIENT *bm_get_my_client_locked(bm_lock_buffer_guard& pbuf_guard) {
6023 6 : int my_client_index = bm_validate_client_index_locked(pbuf_guard);
6024 6 : return pbuf_guard.get_pbuf()->buffer_header->client + my_client_index;
6025 : }
6026 :
6027 : #endif // LOCAL_ROUTINES
6028 :
6029 : /********************************************************************/
6030 : /**
6031 : Check if an event matches a given event request by the
6032 : event id and trigger mask
6033 : @param event_id Event ID of request
6034 : @param trigger_mask Trigger mask of request
6035 : @param pevent Pointer to event to check
6036 : @return TRUE if event matches request
6037 : */
6038 0 : INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6039 : // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6040 : // because of mismatch in sign-extension between signed 16-bit event_id and
6041 : // unsigned 16-bit constants. K.O.
6042 :
6043 0 : if (((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) || ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG)))
6044 : /* fragmented event */
6045 0 : return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6046 0 : && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6047 :
6048 0 : return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6049 0 : && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6050 : }
6051 :
6052 : #ifdef LOCAL_ROUTINES
6053 :
6054 : /********************************************************************/
6055 : /**
6056 : Called to forcibly disconnect given client from a data buffer
6057 : */
6058 0 : void bm_remove_client_locked(BUFFER_HEADER *pheader, int j) {
6059 : int k, nc;
6060 : BUFFER_CLIENT *pbctmp;
6061 :
6062 : /* clear entry from client structure in buffer header */
6063 0 : memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6064 :
6065 : /* calculate new max_client_index entry */
6066 0 : for (k = MAX_CLIENTS - 1; k >= 0; k--)
6067 0 : if (pheader->client[k].pid != 0)
6068 0 : break;
6069 0 : pheader->max_client_index = k + 1;
6070 :
6071 : /* count new number of clients */
6072 0 : for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6073 0 : if (pheader->client[k].pid != 0)
6074 0 : nc++;
6075 0 : pheader->num_clients = nc;
6076 :
6077 : /* check if anyone is waiting and wake him up */
6078 0 : pbctmp = pheader->client;
6079 :
6080 0 : for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6081 0 : if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6082 0 : ss_resume(pbctmp->port, "B ");
6083 0 : }
6084 :
6085 : /********************************************************************/
6086 : /**
6087 : Check all clients on buffer, remove invalid clients
6088 : */
6089 4 : static void bm_cleanup_buffer_locked(BUFFER* pbuf, const char *who, DWORD actual_time) {
6090 : BUFFER_HEADER *pheader;
6091 : BUFFER_CLIENT *pbclient;
6092 : int j;
6093 :
6094 4 : pheader = pbuf->buffer_header;
6095 4 : pbclient = pheader->client;
6096 :
6097 : /* now check other clients */
6098 6 : for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6099 2 : if (pbclient->pid) {
6100 2 : if (!ss_pid_exists(pbclient->pid)) {
6101 0 : cm_msg(MINFO, "bm_cleanup",
6102 0 : "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6103 0 : pheader->name, who, pbclient->pid);
6104 :
6105 0 : bm_remove_client_locked(pheader, j);
6106 0 : continue;
6107 : }
6108 : }
6109 :
6110 : /* If client process has no activity, clear its buffer entry. */
6111 2 : if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6112 2 : DWORD tdiff = actual_time - pbclient->last_activity;
6113 : #if 0
6114 : printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6115 : pheader->name,
6116 : pbclient->name,
6117 : pbclient->last_activity,
6118 : actual_time,
6119 : tdiff,
6120 : tdiff,
6121 : pbclient->watchdog_timeout);
6122 : #endif
6123 2 : if (actual_time > pbclient->last_activity &&
6124 0 : tdiff > pbclient->watchdog_timeout) {
6125 :
6126 0 : cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6127 0 : pbclient->name, pheader->name, who,
6128 : tdiff / 1000.0,
6129 0 : pbclient->watchdog_timeout / 1000.0);
6130 :
6131 0 : bm_remove_client_locked(pheader, j);
6132 : }
6133 : }
6134 : }
6135 4 : }
6136 :
6137 : /**
6138 : Update last activity time
6139 : */
6140 0 : static void bm_update_last_activity(DWORD millitime) {
6141 0 : int pid = ss_getpid();
6142 :
6143 0 : std::vector<BUFFER*> mybuffers;
6144 :
6145 0 : gBuffersMutex.lock();
6146 0 : mybuffers = gBuffers;
6147 0 : gBuffersMutex.unlock();
6148 :
6149 0 : for (BUFFER* pbuf : mybuffers) {
6150 0 : if (!pbuf)
6151 0 : continue;
6152 0 : if (pbuf->attached) {
6153 :
6154 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
6155 :
6156 0 : if (!pbuf_guard.is_locked())
6157 0 : continue;
6158 :
6159 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
6160 0 : for (int j = 0; j < pheader->max_client_index; j++) {
6161 0 : BUFFER_CLIENT *pclient = pheader->client + j;
6162 0 : if (pclient->pid == pid) {
6163 0 : pclient->last_activity = millitime;
6164 : }
6165 : }
6166 0 : }
6167 : }
6168 0 : }
6169 :
6170 : #endif // LOCAL_ROUTINES
6171 :
6172 : /**
6173 : Check all clients on all buffers, remove invalid clients
6174 : */
6175 4 : static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
6176 : {
6177 : #ifdef LOCAL_ROUTINES
6178 :
6179 : //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6180 :
6181 4 : std::vector<BUFFER*> mybuffers;
6182 :
6183 4 : gBuffersMutex.lock();
6184 4 : mybuffers = gBuffers;
6185 4 : gBuffersMutex.unlock();
6186 :
6187 : /* check buffers */
6188 6 : for (BUFFER* pbuf : mybuffers) {
6189 2 : if (!pbuf)
6190 0 : continue;
6191 2 : if (pbuf->attached) {
6192 : /* update the last_activity entry to show that we are alive */
6193 :
6194 2 : bm_lock_buffer_guard pbuf_guard(pbuf);
6195 :
6196 2 : if (!pbuf_guard.is_locked())
6197 0 : continue;
6198 :
6199 2 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
6200 2 : pclient->last_activity = actual_time;
6201 :
6202 : /* don't check other clients if interval is strange */
6203 2 : if (!wrong_interval)
6204 2 : bm_cleanup_buffer_locked(pbuf, who, actual_time);
6205 2 : }
6206 : }
6207 : #endif // LOCAL_ROUTINES
6208 4 : }
6209 :
6210 : #ifdef LOCAL_ROUTINES
6211 :
6212 2 : static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6213 2 : if (rp < 0 || rp > pheader->size) {
6214 0 : cm_msg(MERROR, "bm_validate_rp",
6215 : "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6216 0 : pheader->name,
6217 : rp,
6218 0 : pheader->read_pointer,
6219 0 : pheader->write_pointer,
6220 0 : pheader->size,
6221 : who);
6222 0 : return FALSE;
6223 : }
6224 :
6225 2 : if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6226 : // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6227 0 : cm_msg(MERROR, "bm_validate_rp",
6228 : "error: buffer \"%s\" is corrupted: rp %d plus event header point beyond the end of buffer by %d bytes. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6229 0 : pheader->name,
6230 : rp,
6231 0 : (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6232 0 : pheader->read_pointer,
6233 0 : pheader->write_pointer,
6234 0 : pheader->size,
6235 : who);
6236 0 : return FALSE;
6237 : }
6238 :
6239 2 : return TRUE;
6240 : }
6241 :
6242 : #if 0
6243 : static FILE* gRpLog = NULL;
6244 : #endif
6245 :
6246 0 : static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6247 : {
6248 : #if 0
6249 : if (gRpLog == NULL) {
6250 : gRpLog = fopen("rp.log", "a");
6251 : }
6252 : if (gRpLog && (total_size < 16)) {
6253 : const char *pdata = (const char *) (pheader + 1);
6254 : const DWORD *pevent = (const DWORD*) (pdata + rp);
6255 : fprintf(gRpLog, "%s: rp %d, total_size %d, at rp 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", pheader->name, rp, total_size,
6256 : pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6257 : }
6258 : #endif
6259 :
6260 : // these checks are already done before we come here.
6261 : // but we check again as last-ressort protection. K.O.
6262 0 : assert(total_size > 0);
6263 0 : assert(total_size >= (int)sizeof(EVENT_HEADER));
6264 :
6265 0 : rp += total_size;
6266 0 : if (rp >= pheader->size) {
6267 0 : rp -= pheader->size;
6268 0 : } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6269 : // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6270 : // if at the end of the buffer, the remaining free space is exactly
6271 : // equal to the size of an event header, the event header
6272 : // is written there, the pointer is wrapped and the event data
6273 : // is written to the beginning of the buffer.
6274 0 : rp = 0;
6275 : }
6276 0 : return rp;
6277 : }
6278 :
6279 0 : static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6280 0 : const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6281 0 : int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6282 0 : int total_size = ALIGN8(event_size);
6283 :
6284 0 : if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6285 0 : cm_msg(MERROR, "bm_next_rp",
6286 : "error: buffer \"%s\" is corrupted: rp %d points to an invalid event: data_size %d, event size %d, total_size %d, buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6287 0 : pheader->name,
6288 : rp,
6289 0 : pevent->data_size,
6290 : event_size,
6291 : total_size,
6292 0 : pheader->read_pointer,
6293 0 : pheader->write_pointer,
6294 0 : pheader->size,
6295 : who);
6296 0 : return -1;
6297 : }
6298 :
6299 0 : int remaining = 0;
6300 0 : if (rp < pheader->write_pointer) {
6301 0 : remaining = pheader->write_pointer - rp;
6302 : } else {
6303 0 : remaining = pheader->size - rp;
6304 0 : remaining += pheader->write_pointer;
6305 : }
6306 :
6307 : //printf("bm_next_rp: total_size %d, remaining %d, rp %d, wp %d, size %d\n", total_size, remaining, rp, pheader->write_pointer, pheader->size);
6308 :
6309 0 : if (total_size > remaining) {
6310 0 : cm_msg(MERROR, "bm_next_rp",
6311 : "error: buffer \"%s\" is corrupted: rp %d points to an invalid event: data_size %d, event size %d, total_size %d, buffer read_pointer %d, write_pointer %d, size %d, remaining %d, called from %s",
6312 0 : pheader->name,
6313 : rp,
6314 0 : pevent->data_size,
6315 : event_size,
6316 : total_size,
6317 0 : pheader->read_pointer,
6318 0 : pheader->write_pointer,
6319 0 : pheader->size,
6320 : remaining,
6321 : who);
6322 0 : return -1;
6323 : }
6324 :
6325 0 : rp = bm_incr_rp_no_check(pheader, rp, total_size);
6326 :
6327 0 : return rp;
6328 : }
6329 :
6330 2 : static int bm_validate_buffer_locked(const BUFFER *pbuf) {
6331 2 : const BUFFER_HEADER *pheader = pbuf->buffer_header;
6332 2 : const char *pdata = (const char *) (pheader + 1);
6333 :
6334 : //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6335 :
6336 : //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6337 :
6338 : //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6339 :
6340 2 : if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6341 0 : cm_msg(MERROR, "bm_validate_buffer",
6342 0 : "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6343 0 : pheader->read_pointer, pheader->size, pheader->write_pointer);
6344 0 : return BM_CORRUPTED;
6345 : }
6346 :
6347 2 : if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6348 0 : cm_msg(MERROR, "bm_validate_buffer",
6349 0 : "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6350 0 : pheader->write_pointer, pheader->size, pheader->read_pointer);
6351 0 : return BM_CORRUPTED;
6352 : }
6353 :
6354 2 : if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6355 0 : cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6356 0 : pheader->read_pointer);
6357 0 : return BM_CORRUPTED;
6358 : }
6359 :
6360 2 : int rp = pheader->read_pointer;
6361 2 : int rp0 = -1;
6362 2 : while (rp != pheader->write_pointer) {
6363 0 : if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6364 0 : cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6365 0 : pheader->name, rp, rp0);
6366 0 : return BM_CORRUPTED;
6367 : }
6368 : //bm_print_event(pdata, rp);
6369 0 : int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6370 0 : if (rp1 < 0) {
6371 0 : cm_msg(MERROR, "bm_validate_buffer",
6372 0 : "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6373 0 : return BM_CORRUPTED;
6374 : }
6375 0 : rp0 = rp;
6376 0 : rp = rp1;
6377 : }
6378 :
6379 : int i;
6380 130 : for (i = 0; i < MAX_CLIENTS; i++) {
6381 128 : const BUFFER_CLIENT *c = &pheader->client[i];
6382 128 : if (c->pid == 0)
6383 128 : continue;
6384 0 : BOOL get_all = FALSE;
6385 : int j;
6386 0 : for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6387 0 : const EVENT_REQUEST *r = &c->event_request[j];
6388 0 : if (!r->valid)
6389 0 : continue;
6390 0 : BOOL xget_all = r->sampling_type == GET_ALL;
6391 0 : get_all = (get_all || xget_all);
6392 : //printf("client slot %d: pid %d, name \"%s\", request %d: id %d, valid %d, sampling_type %d, get_all %d\n", i, c->pid, c->name, j, r->id, r->valid, r->sampling_type, xget_all);
6393 : }
6394 :
6395 0 : int rp = c->read_pointer;
6396 0 : int rp0 = -1;
6397 0 : while (rp != pheader->write_pointer) {
6398 : //bm_print_event(pdata, rp);
6399 0 : int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6400 0 : if (rp1 < 0) {
6401 0 : cm_msg(MERROR, "bm_validate_buffer",
6402 : "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6403 0 : pheader->name, c->name, c->read_pointer, rp, rp0);
6404 0 : return BM_CORRUPTED;
6405 : }
6406 0 : rp0 = rp;
6407 0 : rp = rp1;
6408 : }
6409 : }
6410 :
6411 2 : return BM_SUCCESS;
6412 : }
6413 :
6414 0 : static void bm_reset_buffer_locked(BUFFER *pbuf) {
6415 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
6416 :
6417 : //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6418 :
6419 0 : pheader->read_pointer = 0;
6420 0 : pheader->write_pointer = 0;
6421 :
6422 : int i;
6423 0 : for (i = 0; i < pheader->max_client_index; i++) {
6424 0 : BUFFER_CLIENT *pc = pheader->client + i;
6425 0 : if (pc->pid) {
6426 0 : pc->read_pointer = 0;
6427 : }
6428 : }
6429 0 : }
6430 :
6431 2 : static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf) {
6432 : HNDLE hKey;
6433 : int status;
6434 :
6435 : char str[256 + 2 * NAME_LENGTH];
6436 2 : sprintf(str, "/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6437 : //printf("delete [%s]\n", str);
6438 2 : status = db_find_key(hDB, 0, str, &hKey);
6439 2 : if (status == DB_SUCCESS) {
6440 0 : status = db_delete_key(hDB, hKey, FALSE);
6441 : }
6442 2 : }
6443 :
6444 : struct BUFFER_INFO
6445 : {
6446 : BOOL get_all_flag = false; /**< this is a get_all reader */
6447 :
6448 : /* buffer statistics */
6449 : int count_lock = 0; /**< count how many times we locked the buffer */
6450 : int count_sent = 0; /**< count how many events we sent */
6451 : double bytes_sent = 0; /**< count how many bytes we sent */
6452 : int count_write_wait = 0; /**< count how many times we waited for free space */
6453 : DWORD time_write_wait = 0; /**< count for how long we waited for free space, in units of ss_millitime() */
6454 : int last_count_lock = 0; /**< avoid writing statistics to odb if lock count did not change */
6455 : DWORD wait_start_time = 0; /**< time when we started the wait */
6456 : int wait_client_index = 0; /**< waiting for which client */
6457 : int max_requested_space = 0; /**< waiting for this many bytes of free space */
6458 : int count_read = 0; /**< count how many events we read */
6459 : double bytes_read = 0; /**< count how many bytes we read */
6460 : int client_count_write_wait[MAX_CLIENTS]; /**< per-client count_write_wait */
6461 : DWORD client_time_write_wait[MAX_CLIENTS]; /**< per-client time_write_wait */
6462 :
6463 4 : BUFFER_INFO(BUFFER* pbuf)
6464 4 : {
6465 4 : get_all_flag = pbuf->get_all_flag;
6466 :
6467 : /* buffer statistics */
6468 4 : count_lock = pbuf->count_lock;
6469 4 : count_sent = pbuf->count_sent;
6470 4 : bytes_sent = pbuf->bytes_sent;
6471 4 : count_write_wait = pbuf->count_write_wait;
6472 4 : time_write_wait = pbuf->time_write_wait;
6473 4 : last_count_lock = pbuf->last_count_lock;
6474 4 : wait_start_time = pbuf->wait_start_time;
6475 4 : wait_client_index = pbuf->wait_client_index;
6476 4 : max_requested_space = pbuf->max_requested_space;
6477 4 : count_read = pbuf->count_read;
6478 4 : bytes_read = pbuf->bytes_read;
6479 :
6480 260 : for (int i=0; i<MAX_CLIENTS; i++) {
6481 256 : client_count_write_wait[i] = pbuf->client_count_write_wait[i];
6482 256 : client_time_write_wait[i] = pbuf->client_time_write_wait[i];
6483 : }
6484 4 : };
6485 : };
6486 :
6487 4 : static void bm_write_buffer_statistics_to_odb_copy(HNDLE hDB, const char* buffer_name, const char* client_name, int client_index, BUFFER_INFO *pbuf, BUFFER_HEADER* pheader)
6488 : {
6489 : int status;
6490 :
6491 4 : DWORD now = ss_millitime();
6492 :
6493 : HNDLE hKey;
6494 4 : status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6495 4 : if (status != DB_SUCCESS) {
6496 1 : db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6497 1 : status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6498 1 : if (status != DB_SUCCESS)
6499 0 : return;
6500 : }
6501 :
6502 : HNDLE hKeyBuffer;
6503 4 : status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6504 4 : if (status != DB_SUCCESS) {
6505 1 : db_create_key(hDB, hKey, buffer_name, TID_KEY);
6506 1 : status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6507 1 : if (status != DB_SUCCESS)
6508 0 : return;
6509 : }
6510 :
6511 4 : double buf_size = pheader->size;
6512 4 : double buf_rptr = pheader->read_pointer;
6513 4 : double buf_wptr = pheader->write_pointer;
6514 :
6515 4 : double buf_fill = 0;
6516 4 : double buf_cptr = 0;
6517 4 : double buf_cused = 0;
6518 4 : double buf_cused_pct = 0;
6519 :
6520 4 : if (client_index >= 0 && client_index <= pheader->max_client_index) {
6521 4 : buf_cptr = pheader->client[client_index].read_pointer;
6522 :
6523 4 : if (buf_wptr == buf_cptr) {
6524 3 : buf_cused = 0;
6525 1 : } else if (buf_wptr > buf_cptr) {
6526 1 : buf_cused = buf_wptr - buf_cptr;
6527 : } else {
6528 0 : buf_cused = (buf_size - buf_cptr) + buf_wptr;
6529 : }
6530 :
6531 4 : buf_cused_pct = buf_cused / buf_size * 100.0;
6532 :
6533 : // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6534 : // because some other GET_ALL client may have different buf_cused & etc,
6535 : // so they must be written into the per-client statistics
6536 : // and the web page should look at all the GET_ALL clients and used
6537 : // the biggest buf_cused as the whole-buffer "bytes used" value.
6538 : }
6539 :
6540 4 : if (buf_wptr == buf_rptr) {
6541 3 : buf_fill = 0;
6542 1 : } else if (buf_wptr > buf_rptr) {
6543 1 : buf_fill = buf_wptr - buf_rptr;
6544 : } else {
6545 0 : buf_fill = (buf_size - buf_rptr) + buf_wptr;
6546 : }
6547 :
6548 4 : double buf_fill_pct = buf_fill / buf_size * 100.0;
6549 :
6550 4 : db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6551 4 : db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6552 4 : db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6553 4 : db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6554 4 : db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6555 :
6556 4 : status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6557 4 : if (status != DB_SUCCESS) {
6558 1 : db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6559 1 : status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6560 1 : if (status != DB_SUCCESS)
6561 0 : return;
6562 : }
6563 :
6564 : HNDLE hKeyClient;
6565 4 : status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6566 4 : if (status != DB_SUCCESS) {
6567 2 : db_create_key(hDB, hKey, client_name, TID_KEY);
6568 2 : status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6569 2 : if (status != DB_SUCCESS)
6570 0 : return;
6571 : }
6572 :
6573 4 : db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6574 4 : db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6575 4 : db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6576 4 : db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6577 4 : db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6578 4 : db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6579 4 : db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6580 4 : db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6581 4 : db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6582 4 : db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6583 4 : db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6584 4 : db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6585 :
6586 260 : for (int i = 0; i < MAX_CLIENTS; i++) {
6587 256 : if (!pbuf->client_count_write_wait[i])
6588 256 : continue;
6589 :
6590 0 : if (pheader->client[i].pid == 0)
6591 0 : continue;
6592 :
6593 0 : if (pheader->client[i].name[0] == 0)
6594 0 : continue;
6595 :
6596 : char str[100 + NAME_LENGTH];
6597 :
6598 0 : sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6599 0 : db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6600 :
6601 0 : sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6602 0 : db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6603 : }
6604 :
6605 4 : db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6606 4 : db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6607 : }
6608 :
6609 4 : static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
6610 : {
6611 : //printf("bm_buffer_write_statistics_to_odb: buffer [%s] client [%s], lock count %d -> %d, force %d\n", pbuf->buffer_name, pbuf->client_name, pbuf->last_count_lock, pbuf->count_lock, force);
6612 :
6613 4 : bm_lock_buffer_guard pbuf_guard(pbuf);
6614 :
6615 4 : if (!pbuf_guard.is_locked())
6616 0 : return;
6617 :
6618 4 : if (!force) {
6619 0 : if (pbuf->count_lock == pbuf->last_count_lock) {
6620 0 : return;
6621 : }
6622 : }
6623 :
6624 8 : std::string buffer_name = pbuf->buffer_name;
6625 4 : std::string client_name = pbuf->client_name;
6626 :
6627 4 : if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6628 : // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6629 0 : pbuf_guard.unlock(); // unlock before cm_msg()
6630 0 : cm_msg(MERROR, "bm_write_buffer_statistics_to_odb", "Invalid empty buffer name \"%s\" or client name \"%s\"", buffer_name.c_str(), client_name.c_str());
6631 0 : return;
6632 : }
6633 :
6634 4 : pbuf->last_count_lock = pbuf->count_lock;
6635 :
6636 4 : BUFFER_INFO xbuf(pbuf);
6637 4 : BUFFER_HEADER xheader = *pbuf->buffer_header;
6638 4 : int client_index = pbuf->client_index;
6639 :
6640 4 : pbuf_guard.unlock();
6641 :
6642 4 : bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6643 4 : }
6644 :
6645 17 : static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6646 : {
6647 17 : size_t sbuffer_handle = buffer_handle;
6648 :
6649 17 : size_t nbuf = 0;
6650 17 : BUFFER* pbuf = NULL;
6651 :
6652 17 : gBuffersMutex.lock();
6653 :
6654 17 : nbuf = gBuffers.size();
6655 17 : if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6656 17 : pbuf = gBuffers[buffer_handle-1];
6657 : }
6658 :
6659 17 : gBuffersMutex.unlock();
6660 :
6661 17 : if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6662 0 : if (who)
6663 0 : cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6664 0 : if (pstatus)
6665 0 : *pstatus = BM_INVALID_HANDLE;
6666 0 : return NULL;
6667 : }
6668 :
6669 17 : if (!pbuf) {
6670 0 : if (who)
6671 0 : cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6672 0 : if (pstatus)
6673 0 : *pstatus = BM_INVALID_HANDLE;
6674 0 : return NULL;
6675 : }
6676 :
6677 17 : if (!pbuf->attached) {
6678 2 : if (who)
6679 0 : cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6680 2 : if (pstatus)
6681 2 : *pstatus = BM_INVALID_HANDLE;
6682 2 : return NULL;
6683 : }
6684 :
6685 15 : if (pstatus)
6686 15 : *pstatus = BM_SUCCESS;
6687 :
6688 15 : return pbuf;
6689 : }
6690 :
6691 : #endif // LOCAL_ROUTINES
6692 :
6693 : /********************************************************************/
6694 : /**
6695 : Open an event buffer.
6696 : Two default buffers are created by the system.
6697 : The "SYSTEM" buffer is used to
6698 : exchange events and the "SYSMSG" buffer is used to exchange system messages.
6699 : The name and size of the event buffers is defined in midas.h as
6700 : EVENT_BUFFER_NAME and DEFAULT_BUFFER_SIZE.
6701 : Following example opens the "SYSTEM" buffer, requests events with ID 1 and
6702 : enters a main loop. Events are then received in process_event()
6703 : \code
6704 : #include <stdio.h>
6705 : #include "midas.h"
6706 : void process_event(HNDLE hbuf, HNDLE request_id, EVENT_HEADER *pheader, void *pevent)
6707 : {
6708 : printf("Received event #%d\r",
6709 : pheader->serial_number);
6710 : }
6711 : main()
6712 : {
6713 : INT status, request_id;
6714 : HNDLE hbuf;
6715 : status = cm_connect_experiment("pc810", "Sample", "Simple Analyzer", NULL);
6716 : if (status != CM_SUCCESS)
6717 : return 1;
6718 : bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
6719 : bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, process_event);
6720 :
6721 : do
6722 : {
6723 : status = cm_yield(1000);
6724 : } while (status != RPC_SHUTDOWN && status != SS_ABORT);
6725 : cm_disconnect_experiment();
6726 : return 0;
6727 : }
6728 : \endcode
6729 : @param buffer_name Name of buffer
6730 : @param buffer_size Default size of buffer in bytes. Can by overwritten with ODB value
6731 : @param buffer_handle Buffer handle returned by function
6732 : @return BM_SUCCESS, BM_CREATED <br>
6733 : BM_NO_SHM Shared memory cannot be created <br>
6734 : BM_NO_SEMAPHORE Semaphore cannot be created <br>
6735 : BM_NO_MEMORY Not enough memory to create buffer descriptor <br>
6736 : BM_MEMSIZE_MISMATCH Buffer size conflicts with an existing buffer of
6737 : different size <br>
6738 : BM_INVALID_PARAM Invalid parameter
6739 : */
6740 2 : INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6741 : INT status;
6742 :
6743 2 : if (rpc_is_remote()) {
6744 0 : status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6745 :
6746 : HNDLE hDB;
6747 0 : status = cm_get_experiment_database(&hDB, NULL);
6748 0 : if (status != SUCCESS || hDB == 0) {
6749 0 : cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6750 0 : return BM_NO_SHM;
6751 : }
6752 :
6753 0 : _bm_max_event_size = DEFAULT_MAX_EVENT_SIZE;
6754 :
6755 0 : int size = sizeof(INT);
6756 0 : status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6757 :
6758 0 : if (status != DB_SUCCESS) {
6759 0 : cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6760 : status);
6761 0 : return status;
6762 : }
6763 :
6764 0 : return status;
6765 : }
6766 : #ifdef LOCAL_ROUTINES
6767 : {
6768 : HNDLE shm_handle;
6769 : size_t shm_size;
6770 : HNDLE hDB;
6771 2 : const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6772 :
6773 2 : bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6774 :
6775 2 : if (!buffer_name || !buffer_name[0]) {
6776 0 : cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6777 2 : return BM_INVALID_PARAM;
6778 : }
6779 :
6780 2 : if (strlen(buffer_name) >= NAME_LENGTH) {
6781 0 : cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6782 0 : return BM_INVALID_PARAM;
6783 : }
6784 :
6785 2 : status = cm_get_experiment_database(&hDB, NULL);
6786 :
6787 2 : if (status != SUCCESS || hDB == 0) {
6788 : //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6789 0 : return BM_NO_SHM;
6790 : }
6791 :
6792 : /* get buffer size from ODB, user parameter as default if not present in ODB */
6793 2 : std::string odb_path;
6794 2 : odb_path += "/Experiment/Buffer sizes/";
6795 2 : odb_path += buffer_name;
6796 :
6797 2 : int size = sizeof(INT);
6798 2 : status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6799 :
6800 2 : if (buffer_size <= 0 || buffer_size > max_buffer_size) {
6801 0 : cm_msg(MERROR, "bm_open_buffer",
6802 : "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6803 : buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6804 0 : return BM_INVALID_PARAM;
6805 : }
6806 :
6807 2 : _bm_max_event_size = DEFAULT_MAX_EVENT_SIZE;
6808 :
6809 2 : size = sizeof(INT);
6810 2 : status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6811 :
6812 2 : if (status != DB_SUCCESS) {
6813 0 : cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6814 : status);
6815 0 : return status;
6816 : }
6817 :
6818 : /* check if buffer already is open */
6819 2 : gBuffersMutex.lock();
6820 2 : for (size_t i = 0; i < gBuffers.size(); i++) {
6821 0 : BUFFER* pbuf = gBuffers[i];
6822 0 : if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6823 0 : *buffer_handle = i + 1;
6824 0 : gBuffersMutex.unlock();
6825 0 : return BM_SUCCESS;
6826 : }
6827 : }
6828 2 : gBuffersMutex.unlock();
6829 :
6830 : // only one thread at a time should create new buffers
6831 :
6832 : static std::mutex gNewBufferMutex;
6833 2 : std::lock_guard<std::mutex> guard(gNewBufferMutex);
6834 :
6835 : // if we had a race against another thread
6836 : // and while we were waiting for gNewBufferMutex
6837 : // the other thread created this buffer, we return it.
6838 :
6839 2 : gBuffersMutex.lock();
6840 2 : for (size_t i = 0; i < gBuffers.size(); i++) {
6841 0 : BUFFER* pbuf = gBuffers[i];
6842 0 : if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6843 0 : *buffer_handle = i + 1;
6844 0 : gBuffersMutex.unlock();
6845 0 : return BM_SUCCESS;
6846 : }
6847 : }
6848 2 : gBuffersMutex.unlock();
6849 :
6850 : /* allocate new BUFFER object */
6851 :
6852 2 : BUFFER* pbuf = new BUFFER;
6853 :
6854 : /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6855 :
6856 130 : for (int i=0; i<MAX_CLIENTS; i++) {
6857 128 : pbuf->client_count_write_wait[i] = 0;
6858 128 : pbuf->client_time_write_wait[i] = 0;
6859 : }
6860 :
6861 : /* create buffer semaphore */
6862 :
6863 2 : status = ss_semaphore_create(buffer_name, &(pbuf->semaphore));
6864 :
6865 2 : if (status != SS_CREATED && status != SS_SUCCESS) {
6866 0 : *buffer_handle = 0;
6867 0 : delete pbuf;
6868 0 : return BM_NO_SEMAPHORE;
6869 : }
6870 :
6871 2 : std::string client_name = cm_get_client_name();
6872 :
6873 : /* store client name */
6874 2 : mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6875 :
6876 : /* store buffer name */
6877 2 : mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6878 :
6879 : /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6880 :
6881 2 : pbuf->attached = true; // required by bm_lock_buffer()
6882 :
6883 2 : bm_lock_buffer_guard pbuf_guard(pbuf);
6884 :
6885 2 : if (!pbuf_guard.is_locked()) {
6886 : // cannot happen, no other thread can see this pbuf
6887 0 : abort();
6888 : return BM_NO_SEMAPHORE;
6889 : }
6890 :
6891 : /* open shared memory */
6892 :
6893 2 : void *p = NULL;
6894 2 : status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6895 :
6896 2 : if (status != SS_SUCCESS && status != SS_CREATED) {
6897 0 : *buffer_handle = 0;
6898 0 : pbuf_guard.unlock();
6899 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6900 0 : delete pbuf;
6901 0 : return BM_NO_SHM;
6902 : }
6903 :
6904 2 : pbuf->buffer_header = (BUFFER_HEADER *) p;
6905 :
6906 2 : BUFFER_HEADER *pheader = pbuf->buffer_header;
6907 :
6908 2 : bool shm_created = (status == SS_CREATED);
6909 :
6910 2 : if (shm_created) {
6911 : /* initialize newly created shared memory */
6912 :
6913 2 : memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6914 :
6915 2 : mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6916 2 : pheader->size = buffer_size;
6917 :
6918 : } else {
6919 : /* validate existing shared memory */
6920 :
6921 0 : if (!equal_ustring(pheader->name, buffer_name)) {
6922 : // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6923 0 : pbuf_guard.unlock();
6924 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6925 0 : cm_msg(MERROR, "bm_open_buffer",
6926 : "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6927 0 : pheader->name);
6928 0 : *buffer_handle = 0;
6929 0 : delete pbuf;
6930 0 : return BM_CORRUPTED;
6931 : }
6932 :
6933 0 : if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6934 : // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6935 0 : pbuf_guard.unlock();
6936 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6937 0 : cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6938 : buffer_name, pheader->num_clients, MAX_CLIENTS);
6939 0 : *buffer_handle = 0;
6940 0 : delete pbuf;
6941 0 : return BM_CORRUPTED;
6942 : }
6943 :
6944 0 : if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6945 : // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6946 0 : pbuf_guard.unlock();
6947 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6948 0 : cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6949 : buffer_name, pheader->max_client_index, MAX_CLIENTS);
6950 0 : *buffer_handle = 0;
6951 0 : delete pbuf;
6952 0 : return BM_CORRUPTED;
6953 : }
6954 :
6955 : /* check if buffer size is identical */
6956 0 : if (pheader->size != buffer_size) {
6957 0 : cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6958 : buffer_name, buffer_size, pheader->size);
6959 :
6960 0 : buffer_size = pheader->size;
6961 :
6962 0 : ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6963 :
6964 0 : status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6965 :
6966 0 : if (status != SS_SUCCESS) {
6967 0 : *buffer_handle = 0;
6968 0 : pbuf_guard.unlock();
6969 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6970 0 : delete pbuf;
6971 0 : return BM_NO_SHM;
6972 : }
6973 :
6974 0 : pbuf->buffer_header = (BUFFER_HEADER *) p;
6975 0 : pheader = pbuf->buffer_header;
6976 : }
6977 : }
6978 :
6979 : /* shared memory is good from here down */
6980 :
6981 2 : pbuf->attached = true;
6982 :
6983 2 : pbuf->shm_handle = shm_handle;
6984 2 : pbuf->shm_size = shm_size;
6985 2 : pbuf->callback = FALSE;
6986 :
6987 2 : bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6988 :
6989 2 : status = bm_validate_buffer_locked(pbuf);
6990 2 : if (status != BM_SUCCESS) {
6991 0 : cm_msg(MERROR, "bm_open_buffer",
6992 : "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6993 : status);
6994 0 : bm_reset_buffer_locked(pbuf);
6995 0 : cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6996 : }
6997 :
6998 : /* add our client BUFFER_HEADER */
6999 :
7000 2 : int iclient = 0;
7001 2 : for (; iclient < MAX_CLIENTS; iclient++)
7002 2 : if (pheader->client[iclient].pid == 0)
7003 2 : break;
7004 :
7005 2 : if (iclient == MAX_CLIENTS) {
7006 0 : *buffer_handle = 0;
7007 : // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
7008 0 : pbuf_guard.unlock();
7009 0 : pbuf_guard.invalidate(); // destructor will see a deleted pbuf
7010 0 : delete pbuf;
7011 0 : cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
7012 0 : return BM_NO_SLOT;
7013 : }
7014 :
7015 : /* store slot index in _buffer structure */
7016 2 : pbuf->client_index = iclient;
7017 :
7018 : /*
7019 : Save the index of the last client of that buffer so that later only
7020 : the clients 0..max_client_index-1 have to be searched through.
7021 : */
7022 2 : pheader->num_clients++;
7023 2 : if (iclient + 1 > pheader->max_client_index)
7024 2 : pheader->max_client_index = iclient + 1;
7025 :
7026 : /* setup buffer header and client structure */
7027 2 : BUFFER_CLIENT *pclient = &pheader->client[iclient];
7028 :
7029 2 : memset(pclient, 0, sizeof(BUFFER_CLIENT));
7030 :
7031 2 : mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7032 :
7033 2 : pclient->pid = ss_getpid();
7034 :
7035 2 : ss_suspend_get_buffer_port(ss_gettid(), &pclient->port);
7036 :
7037 2 : pclient->read_pointer = pheader->write_pointer;
7038 2 : pclient->last_activity = ss_millitime();
7039 :
7040 2 : cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
7041 :
7042 2 : pbuf_guard.unlock();
7043 :
7044 : /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7045 :
7046 2 : pheader = NULL;
7047 :
7048 : /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7049 :
7050 2 : bm_clear_buffer_statistics(hDB, pbuf);
7051 2 : bm_write_buffer_statistics_to_odb(hDB, pbuf, true);
7052 :
7053 : /* add pbuf to buffer list */
7054 :
7055 2 : gBuffersMutex.lock();
7056 :
7057 2 : bool added = false;
7058 2 : for (size_t i=0; i<gBuffers.size(); i++) {
7059 0 : if (gBuffers[i] == NULL) {
7060 0 : gBuffers[i] = pbuf;
7061 0 : added = true;
7062 0 : *buffer_handle = i+1;
7063 0 : break;
7064 : }
7065 : }
7066 2 : if (!added) {
7067 2 : *buffer_handle = gBuffers.size() + 1;
7068 2 : gBuffers.push_back(pbuf);
7069 : }
7070 :
7071 : /* from here down we should not touch pbuf without locking it */
7072 :
7073 2 : pbuf = NULL;
7074 :
7075 2 : gBuffersMutex.unlock();
7076 :
7077 : /* new buffer is now ready for use */
7078 :
7079 : /* initialize buffer counters */
7080 2 : bm_init_buffer_counters(*buffer_handle);
7081 :
7082 2 : bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7083 :
7084 2 : if (shm_created)
7085 2 : return BM_CREATED;
7086 8 : }
7087 : #endif /* LOCAL_ROUTINES */
7088 :
7089 0 : return BM_SUCCESS;
7090 : }
7091 :
7092 : /********************************************************************/
7093 : /**
7094 : If buffer is already open, return it's handle
7095 : @param buffer_name buffer name
7096 : @return BM_SUCCESS, BM_NOT_FOUND
7097 : */
7098 0 : INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7099 : {
7100 0 : gBuffersMutex.lock();
7101 0 : for (size_t i = 0; i < gBuffers.size(); i++) {
7102 0 : BUFFER* pbuf = gBuffers[i];
7103 0 : if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7104 0 : *buffer_handle = i + 1;
7105 0 : gBuffersMutex.unlock();
7106 0 : return BM_SUCCESS;
7107 : }
7108 : }
7109 0 : gBuffersMutex.unlock();
7110 0 : return BM_NOT_FOUND;
7111 : }
7112 :
7113 : /********************************************************************/
7114 : /**
7115 : Closes an event buffer previously opened with bm_open_buffer().
7116 : @param buffer_handle buffer handle
7117 : @return BM_SUCCESS, BM_INVALID_HANDLE
7118 : */
7119 4 : INT bm_close_buffer(INT buffer_handle) {
7120 : //printf("bm_close_buffer: handle %d\n", buffer_handle);
7121 :
7122 4 : if (rpc_is_remote())
7123 0 : return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7124 :
7125 : #ifdef LOCAL_ROUTINES
7126 : {
7127 4 : int status = 0;
7128 :
7129 4 : BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7130 :
7131 4 : if (!pbuf)
7132 2 : return status;
7133 :
7134 : //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7135 :
7136 : int i;
7137 :
7138 : { /* delete all requests for this buffer */
7139 2 : _request_list_mutex.lock();
7140 2 : std::vector<EventRequest> request_list_copy = _request_list;
7141 2 : _request_list_mutex.unlock();
7142 2 : for (size_t i = 0; i < request_list_copy.size(); i++) {
7143 0 : if (request_list_copy[i].buffer_handle == buffer_handle) {
7144 0 : bm_delete_request(i);
7145 : }
7146 : }
7147 2 : }
7148 :
7149 : HNDLE hDB;
7150 2 : cm_get_experiment_database(&hDB, NULL);
7151 :
7152 2 : if (hDB) {
7153 : /* write statistics to odb */
7154 2 : bm_write_buffer_statistics_to_odb(hDB, pbuf, TRUE);
7155 : }
7156 :
7157 : /* lock buffer in correct order */
7158 :
7159 2 : status = bm_lock_buffer_read_cache(pbuf);
7160 :
7161 2 : if (status != BM_SUCCESS) {
7162 0 : return status;
7163 : }
7164 :
7165 2 : status = bm_lock_buffer_write_cache(pbuf);
7166 :
7167 2 : if (status != BM_SUCCESS) {
7168 0 : pbuf->read_cache_mutex.unlock();
7169 0 : return status;
7170 : }
7171 :
7172 2 : bm_lock_buffer_guard pbuf_guard(pbuf);
7173 :
7174 2 : if (!pbuf_guard.is_locked()) {
7175 0 : pbuf->write_cache_mutex.unlock();
7176 0 : pbuf->read_cache_mutex.unlock();
7177 0 : return pbuf_guard.get_status();
7178 : }
7179 :
7180 2 : BUFFER_HEADER *pheader = pbuf->buffer_header;
7181 :
7182 : /* mark entry in _buffer as empty */
7183 2 : pbuf->attached = false;
7184 :
7185 2 : BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
7186 :
7187 2 : if (pclient) {
7188 : /* clear entry from client structure in buffer header */
7189 2 : memset(pclient, 0, sizeof(BUFFER_CLIENT));
7190 : }
7191 :
7192 : /* calculate new max_client_index entry */
7193 130 : for (i = MAX_CLIENTS - 1; i >= 0; i--)
7194 128 : if (pheader->client[i].pid != 0)
7195 0 : break;
7196 2 : pheader->max_client_index = i + 1;
7197 :
7198 : /* count new number of clients */
7199 2 : int j = 0;
7200 130 : for (i = MAX_CLIENTS - 1; i >= 0; i--)
7201 128 : if (pheader->client[i].pid != 0)
7202 0 : j++;
7203 2 : pheader->num_clients = j;
7204 :
7205 2 : int destroy_flag = (pheader->num_clients == 0);
7206 :
7207 : // we hold the locks on the read cache and the write cache.
7208 :
7209 : /* free cache */
7210 2 : if (pbuf->read_cache_size > 0) {
7211 0 : free(pbuf->read_cache);
7212 0 : pbuf->read_cache = NULL;
7213 0 : pbuf->read_cache_size = 0;
7214 0 : pbuf->read_cache_rp = 0;
7215 0 : pbuf->read_cache_wp = 0;
7216 : }
7217 :
7218 2 : if (pbuf->write_cache_size > 0) {
7219 0 : free(pbuf->write_cache);
7220 0 : pbuf->write_cache = NULL;
7221 0 : pbuf->write_cache_size = 0;
7222 0 : pbuf->write_cache_rp = 0;
7223 0 : pbuf->write_cache_wp = 0;
7224 : }
7225 :
7226 : /* check if anyone is waiting and wake him up */
7227 :
7228 2 : for (int i = 0; i < pheader->max_client_index; i++) {
7229 0 : BUFFER_CLIENT *pclient = pheader->client + i;
7230 0 : if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7231 0 : ss_resume(pclient->port, "B ");
7232 : }
7233 :
7234 : /* unmap shared memory, delete it if we are the last */
7235 :
7236 2 : ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7237 :
7238 : /* after ss_shm_close() these are invalid: */
7239 :
7240 2 : pheader = NULL;
7241 2 : pbuf->buffer_header = NULL;
7242 2 : pbuf->shm_size = 0;
7243 2 : pbuf->shm_handle = 0;
7244 :
7245 : /* unlock buffer in correct order */
7246 :
7247 2 : pbuf_guard.unlock();
7248 :
7249 2 : pbuf->write_cache_mutex.unlock();
7250 2 : pbuf->read_cache_mutex.unlock();
7251 :
7252 : /* delete semaphore */
7253 :
7254 2 : ss_semaphore_delete(pbuf->semaphore, destroy_flag);
7255 2 : }
7256 : #endif /* LOCAL_ROUTINES */
7257 :
7258 2 : return BM_SUCCESS;
7259 : }
7260 :
7261 : /********************************************************************/
7262 : /**
7263 : Close all open buffers
7264 : @return BM_SUCCESS
7265 : */
7266 2 : INT bm_close_all_buffers(void) {
7267 2 : if (rpc_is_remote())
7268 0 : return rpc_call(RPC_BM_CLOSE_ALL_BUFFERS);
7269 :
7270 : #ifdef LOCAL_ROUTINES
7271 : {
7272 2 : cm_msg_close_buffer();
7273 :
7274 2 : gBuffersMutex.lock();
7275 2 : size_t nbuf = gBuffers.size();
7276 2 : gBuffersMutex.unlock();
7277 :
7278 4 : for (size_t i = nbuf; i > 0; i--) {
7279 2 : bm_close_buffer(i);
7280 : }
7281 :
7282 2 : gBuffersMutex.lock();
7283 4 : for (size_t i=0; i< gBuffers.size(); i++) {
7284 2 : BUFFER* pbuf = gBuffers[i];
7285 2 : if (!pbuf)
7286 0 : continue;
7287 2 : delete pbuf;
7288 2 : pbuf = NULL;
7289 2 : gBuffers[i] = NULL;
7290 : }
7291 2 : gBuffersMutex.unlock();
7292 : }
7293 : #endif /* LOCAL_ROUTINES */
7294 :
7295 2 : return BM_SUCCESS;
7296 : }
7297 :
7298 : /********************************************************************/
7299 : /**
7300 : Close all open buffers
7301 : @return BM_SUCCESS
7302 : */
7303 0 : INT bm_write_statistics_to_odb(void) {
7304 : #ifdef LOCAL_ROUTINES
7305 : {
7306 : int status;
7307 : HNDLE hDB;
7308 :
7309 0 : status = cm_get_experiment_database(&hDB, NULL);
7310 :
7311 0 : if (status != CM_SUCCESS) {
7312 : //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7313 0 : return BM_SUCCESS;
7314 : }
7315 :
7316 0 : std::vector<BUFFER*> mybuffers;
7317 :
7318 0 : gBuffersMutex.lock();
7319 0 : mybuffers = gBuffers;
7320 0 : gBuffersMutex.unlock();
7321 :
7322 0 : for (BUFFER* pbuf : mybuffers) {
7323 0 : if (!pbuf || !pbuf->attached)
7324 0 : continue;
7325 0 : bm_write_buffer_statistics_to_odb(hDB, pbuf, FALSE);
7326 : }
7327 0 : }
7328 : #endif /* LOCAL_ROUTINES */
7329 :
7330 0 : return BM_SUCCESS;
7331 : }
7332 :
7333 : /**dox***************************************************************/
7334 : /** @} *//* end of bmfunctionc */
7335 :
7336 : /**dox***************************************************************/
7337 : /** @addtogroup cmfunctionc
7338 : *
7339 : * @{ */
7340 :
7341 : /*-- Watchdog routines ---------------------------------------------*/
7342 : #ifdef LOCAL_ROUTINES
7343 :
7344 : static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7345 : static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7346 : static std::atomic<std::thread*> _watchdog_thread{NULL};
7347 :
7348 : /********************************************************************/
7349 : /**
7350 : Watchdog thread to maintain the watchdog timeout timestamps for this client
7351 : */
7352 0 : INT cm_watchdog_thread(void *unused) {
7353 0 : _watchdog_thread_is_running = true;
7354 : //printf("cm_watchdog_thread started!\n");
7355 0 : while (_watchdog_thread_run) {
7356 : //printf("cm_watchdog_thread runs!\n");
7357 0 : DWORD now = ss_millitime();
7358 0 : bm_update_last_activity(now);
7359 0 : db_update_last_activity(now);
7360 : int i;
7361 0 : for (i = 0; i < 20; i++) {
7362 0 : ss_sleep(100);
7363 0 : if (!_watchdog_thread_run)
7364 0 : break;
7365 : }
7366 : }
7367 : //printf("cm_watchdog_thread stopped!\n");
7368 0 : _watchdog_thread_is_running = false;
7369 0 : return 0;
7370 : }
7371 :
7372 0 : static void xcm_watchdog_thread() {
7373 0 : cm_watchdog_thread(NULL);
7374 0 : }
7375 :
7376 : #endif
7377 :
7378 0 : INT cm_start_watchdog_thread() {
7379 : /* watchdog does not run inside remote clients.
7380 : * watchdog timeout timers are maintained by the mserver */
7381 0 : if (rpc_is_remote())
7382 0 : return CM_SUCCESS;
7383 : #ifdef LOCAL_ROUTINES
7384 : /* only start once */
7385 0 : if (_watchdog_thread)
7386 0 : return CM_SUCCESS;
7387 0 : _watchdog_thread_run = true;
7388 0 : _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7389 : #endif
7390 0 : return CM_SUCCESS;
7391 : }
7392 :
7393 2 : INT cm_stop_watchdog_thread() {
7394 : /* watchdog does not run inside remote clients.
7395 : * watchdog timeout timers are maintained by the mserver */
7396 2 : if (rpc_is_remote())
7397 0 : return CM_SUCCESS;
7398 : #ifdef LOCAL_ROUTINES
7399 2 : _watchdog_thread_run = false;
7400 2 : while (_watchdog_thread_is_running) {
7401 : //printf("waiting for watchdog thread to shut down\n");
7402 0 : ss_sleep(10);
7403 : }
7404 2 : if (_watchdog_thread != NULL) {
7405 0 : _watchdog_thread.load()->join();
7406 0 : delete static_cast<std::thread *>(_watchdog_thread);
7407 0 : _watchdog_thread = NULL;
7408 : }
7409 : #endif
7410 2 : return CM_SUCCESS;
7411 : }
7412 :
7413 : /********************************************************************/
7414 : /**
7415 : Shutdown (exit) other MIDAS client
7416 : @param name Client name or "all" for all clients
7417 : @param bUnique If true, look for the exact client name.
7418 : If false, look for namexxx where xxx is
7419 : a any number.
7420 :
7421 : @return CM_SUCCESS, CM_NO_CLIENT, DB_NO_KEY
7422 : */
7423 0 : INT cm_shutdown(const char *name, BOOL bUnique) {
7424 : INT status, return_status, i, size;
7425 : HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
7426 : KEY key;
7427 : char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7428 : INT port;
7429 : DWORD start_time;
7430 : DWORD timeout;
7431 : DWORD last;
7432 :
7433 0 : cm_get_experiment_database(&hDB, &hKeyClient);
7434 :
7435 0 : status = db_find_key(hDB, 0, "System/Clients", &hKey);
7436 0 : if (status != DB_SUCCESS)
7437 0 : return DB_NO_KEY;
7438 :
7439 0 : return_status = CM_NO_CLIENT;
7440 :
7441 : /* loop over all clients */
7442 0 : for (i = 0;; i++) {
7443 0 : status = db_enum_key(hDB, hKey, i, &hSubkey);
7444 0 : if (status == DB_NO_MORE_SUBKEYS)
7445 0 : break;
7446 :
7447 : /* don't shutdown ourselves */
7448 0 : if (hSubkey == hKeyClient)
7449 0 : continue;
7450 :
7451 0 : if (status == DB_SUCCESS) {
7452 0 : db_get_key(hDB, hSubkey, &key);
7453 :
7454 : /* contact client */
7455 0 : size = sizeof(client_name);
7456 0 : status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7457 0 : if (status != DB_SUCCESS)
7458 0 : continue;
7459 :
7460 0 : if (!bUnique)
7461 0 : client_name[strlen(name)] = 0; /* strip number */
7462 :
7463 : /* check if individual client */
7464 0 : if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7465 0 : continue;
7466 :
7467 0 : size = sizeof(port);
7468 0 : db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7469 :
7470 0 : size = sizeof(remote_host);
7471 0 : db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7472 :
7473 0 : cm_get_watchdog_info(hDB, name, &timeout, &last);
7474 0 : if (timeout == 0)
7475 0 : timeout = 5000;
7476 :
7477 : /* client found -> connect to its server port */
7478 0 : status = rpc_client_connect(remote_host, port, client_name, &hConn);
7479 0 : if (status != RPC_SUCCESS) {
7480 0 : int client_pid = atoi(key.name);
7481 0 : return_status = CM_NO_CLIENT;
7482 0 : cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7483 : client_name, remote_host, port);
7484 : #ifdef SIGKILL
7485 0 : cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7486 : client_pid);
7487 0 : kill(client_pid, SIGKILL);
7488 0 : return_status = CM_SUCCESS;
7489 0 : status = cm_delete_client_info(hDB, client_pid);
7490 0 : if (status != CM_SUCCESS)
7491 0 : cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7492 : name, client_pid, status);
7493 : #endif
7494 : } else {
7495 : /* call disconnect with shutdown=TRUE */
7496 0 : rpc_client_disconnect(hConn, TRUE);
7497 :
7498 : /* wait until client has shut down */
7499 0 : start_time = ss_millitime();
7500 : do {
7501 0 : ss_sleep(100);
7502 0 : status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
7503 0 : } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7504 :
7505 0 : if (status == DB_SUCCESS) {
7506 0 : int client_pid = atoi(key.name);
7507 0 : return_status = CM_NO_CLIENT;
7508 0 : cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7509 : #ifdef SIGKILL
7510 0 : cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7511 : client_pid);
7512 0 : kill(client_pid, SIGKILL);
7513 0 : status = cm_delete_client_info(hDB, client_pid);
7514 0 : if (status != CM_SUCCESS)
7515 0 : cm_msg(MERROR, "cm_shutdown",
7516 : "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7517 : status);
7518 : #endif
7519 0 : return_status = CM_NO_CLIENT;
7520 : } else {
7521 0 : return_status = CM_SUCCESS;
7522 0 : i--;
7523 : }
7524 : }
7525 : }
7526 :
7527 : /* display any message created during each shutdown */
7528 0 : cm_msg_flush_buffer();
7529 0 : }
7530 :
7531 0 : return return_status;
7532 : }
7533 :
7534 : /********************************************************************/
7535 : /**
7536 : Check if a MIDAS client exists in current experiment
7537 : @param name Client name
7538 : @param bUnique If true, look for the exact client name.
7539 : If false, look for namexxx where xxx is
7540 : a any number
7541 : @return CM_SUCCESS, CM_NO_CLIENT
7542 : */
7543 0 : INT cm_exist(const char *name, BOOL bUnique) {
7544 : INT status, i, size;
7545 : HNDLE hDB, hKeyClient, hKey, hSubkey;
7546 : char client_name[NAME_LENGTH];
7547 :
7548 0 : if (rpc_is_remote())
7549 0 : return rpc_call(RPC_CM_EXIST, name, bUnique);
7550 :
7551 0 : cm_get_experiment_database(&hDB, &hKeyClient);
7552 :
7553 0 : status = db_find_key(hDB, 0, "System/Clients", &hKey);
7554 0 : if (status != DB_SUCCESS)
7555 0 : return DB_NO_KEY;
7556 :
7557 0 : db_lock_database(hDB);
7558 :
7559 : /* loop over all clients */
7560 0 : for (i = 0;; i++) {
7561 0 : status = db_enum_key(hDB, hKey, i, &hSubkey);
7562 0 : if (status == DB_NO_MORE_SUBKEYS)
7563 0 : break;
7564 :
7565 0 : if (hSubkey == hKeyClient)
7566 0 : continue;
7567 :
7568 0 : if (status == DB_SUCCESS) {
7569 : /* get client name */
7570 0 : size = sizeof(client_name);
7571 0 : status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7572 :
7573 0 : if (status != DB_SUCCESS) {
7574 : //fprintf(stderr, "cm_exist: name %s, i=%d, hSubkey=%d, status %d, client_name %s, my name %s\n", name, i, hSubkey, status, client_name, _client_name);
7575 0 : continue;
7576 : }
7577 :
7578 0 : if (equal_ustring(client_name, name)) {
7579 0 : db_unlock_database(hDB);
7580 0 : return CM_SUCCESS;
7581 : }
7582 :
7583 0 : if (!bUnique) {
7584 0 : client_name[strlen(name)] = 0; /* strip number */
7585 0 : if (equal_ustring(client_name, name)) {
7586 0 : db_unlock_database(hDB);
7587 0 : return CM_SUCCESS;
7588 : }
7589 : }
7590 : }
7591 : }
7592 :
7593 0 : db_unlock_database(hDB);
7594 :
7595 0 : return CM_NO_CLIENT;
7596 : }
7597 :
7598 : /********************************************************************/
7599 : /**
7600 : Remove hanging clients independent of their watchdog
7601 : timeout.
7602 :
7603 : Since this function does not obey the client watchdog
7604 : timeout, it should be only called to remove clients which
7605 : have their watchdog checking turned off or which are
7606 : known to be dead. The normal client removement is done
7607 : via cm_watchdog().
7608 :
7609 : Currently (Sept. 02) there are two applications for that:
7610 : -# The ODBEdit command "cleanup", which can be used to
7611 : remove clients which have their watchdog checking off,
7612 : like the analyzer started with the "-d" flag for a
7613 : debugging session.
7614 : -# The frontend init code to remove previous frontends.
7615 : This can be helpful if a frontend dies. Normally,
7616 : one would have to wait 60 sec. for a crashed frontend
7617 : to be removed. Only then one can start again the
7618 : frontend. Since the frontend init code contains a
7619 : call to cm_cleanup(<frontend_name>), one can restart
7620 : a frontend immediately.
7621 :
7622 : Added ignore_timeout on Nov.03. A logger might have an
7623 : increased tiemout of up to 60 sec. because of tape
7624 : operations. If ignore_timeout is FALSE, the logger is
7625 : then not killed if its inactivity is less than 60 sec.,
7626 : while in the previous implementation it was always
7627 : killed after 2*WATCHDOG_INTERVAL.
7628 : @param client_name Client name, if zero check all clients
7629 : @param ignore_timeout If TRUE, ignore a possible increased
7630 : timeout defined by each client.
7631 : @return CM_SUCCESS
7632 : */
7633 0 : INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7634 0 : if (rpc_is_remote())
7635 0 : return rpc_call(RPC_CM_CLEANUP, client_name);
7636 :
7637 : #ifdef LOCAL_ROUTINES
7638 : {
7639 : DWORD interval;
7640 0 : DWORD now = ss_millitime();
7641 :
7642 0 : std::vector<BUFFER*> mybuffers;
7643 :
7644 0 : gBuffersMutex.lock();
7645 0 : mybuffers = gBuffers;
7646 0 : gBuffersMutex.unlock();
7647 :
7648 : /* check buffers */
7649 0 : for (BUFFER* pbuf : mybuffers) {
7650 0 : if (!pbuf)
7651 0 : continue;
7652 0 : if (pbuf->attached) {
7653 0 : std::string msg;
7654 :
7655 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
7656 :
7657 0 : if (!pbuf_guard.is_locked())
7658 0 : continue;
7659 :
7660 : /* update the last_activity entry to show that we are alive */
7661 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
7662 0 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7663 0 : pclient->last_activity = ss_millitime();
7664 :
7665 : /* now check other clients */
7666 0 : for (int j = 0; j < pheader->max_client_index; j++) {
7667 0 : BUFFER_CLIENT *pbclient = &pheader->client[j];
7668 0 : if (j != pbuf->client_index && pbclient->pid &&
7669 0 : (client_name == NULL || client_name[0] == 0
7670 0 : || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7671 0 : if (ignore_timeout)
7672 0 : interval = 2 * WATCHDOG_INTERVAL;
7673 : else
7674 0 : interval = pbclient->watchdog_timeout;
7675 :
7676 : /* If client process has no activity, clear its buffer entry. */
7677 0 : if (interval > 0
7678 0 : && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7679 :
7680 : /* now make again the check with the buffer locked */
7681 0 : if (interval > 0
7682 0 : && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7683 0 : msg = msprintf(
7684 : "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7685 0 : pbclient->name, pheader->name,
7686 0 : (ss_millitime() - pbclient->last_activity) / 1000.0,
7687 0 : interval / 1000.0);
7688 :
7689 0 : bm_remove_client_locked(pheader, j);
7690 : }
7691 :
7692 : /* go again through whole list */
7693 0 : j = 0;
7694 : }
7695 : }
7696 : }
7697 :
7698 : // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7699 0 : pbuf_guard.unlock();
7700 :
7701 : /* display info message after unlocking buffer */
7702 0 : if (!msg.empty())
7703 0 : cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7704 0 : }
7705 : }
7706 :
7707 0 : db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7708 0 : }
7709 : #endif /* LOCAL_ROUTINES */
7710 :
7711 0 : return CM_SUCCESS;
7712 : }
7713 :
7714 : /********************************************************************/
7715 : /**
7716 : Expand environment variables in filesystem file path names
7717 :
7718 : Examples of expansion: $FOO=foo, $BAR=bar, $UNDEF is undefined (undefined, not empty)
7719 :
7720 : ok &= test_cm_expand_env1("aaa", "aaa");
7721 : ok &= test_cm_expand_env1("$FOO", "foo");
7722 : ok &= test_cm_expand_env1("/$FOO", "/foo");
7723 : ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7724 : ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7725 : ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7726 : ok &= test_cm_expand_env1("1$FOO", "1foo");
7727 : ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7728 : ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7729 :
7730 : @param str Input file path
7731 : @return expanded file path
7732 : */
7733 0 : std::string cm_expand_env(const char *str) {
7734 0 : const char *s = str;
7735 0 : std::string r;
7736 0 : for (; *s;) {
7737 0 : if (*s == '$') {
7738 0 : s++;
7739 0 : std::string envname;
7740 0 : for (; *s;) {
7741 0 : if (*s == DIR_SEPARATOR)
7742 0 : break;
7743 0 : envname += *s;
7744 0 : s++;
7745 : }
7746 0 : const char *e = getenv(envname.c_str());
7747 : //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7748 0 : if (!e) {
7749 : //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7750 0 : r += '$';
7751 0 : r += envname;
7752 : } else {
7753 0 : r += e;
7754 : //if (r[r.length()-1] != DIR_SEPARATOR)
7755 : //r += DIR_SEPARATOR_STR;
7756 : }
7757 0 : } else {
7758 0 : r += *s;
7759 0 : s++;
7760 : }
7761 : }
7762 0 : return r;
7763 0 : }
7764 :
7765 0 : static bool test_cm_expand_env1(const char *str, const char *expected) {
7766 0 : std::string s = cm_expand_env(str);
7767 0 : printf("test_expand_env: [%s] -> [%s] expected [%s]",
7768 : str,
7769 : s.c_str(),
7770 : expected);
7771 0 : if (s != expected) {
7772 0 : printf(", MISMATCH!\n");
7773 0 : return false;
7774 : }
7775 :
7776 0 : printf("\n");
7777 0 : return true;
7778 0 : }
7779 :
7780 0 : void cm_test_expand_env() {
7781 0 : printf("Test expand_end()\n");
7782 0 : setenv("FOO", "foo", 1);
7783 0 : setenv("BAR", "bar", 1);
7784 0 : setenv("EMPTY", "", 1);
7785 0 : unsetenv("UNDEF");
7786 :
7787 0 : bool ok = true;
7788 :
7789 0 : ok &= test_cm_expand_env1("aaa", "aaa");
7790 0 : ok &= test_cm_expand_env1("$FOO", "foo");
7791 0 : ok &= test_cm_expand_env1("/$FOO", "/foo");
7792 0 : ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7793 0 : ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7794 0 : ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7795 0 : ok &= test_cm_expand_env1("1$FOO", "1foo");
7796 0 : ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7797 0 : ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7798 :
7799 0 : if (ok) {
7800 0 : printf("test_expand_env: all tests passed!\n");
7801 : } else {
7802 0 : printf("test_expand_env: test FAILED!\n");
7803 : }
7804 0 : }
7805 :
7806 : /**dox***************************************************************/
7807 :
7808 : /** @} *//* end of cmfunctionc */
7809 :
7810 : /**dox***************************************************************/
7811 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
7812 :
7813 : /********************************************************************/
7814 0 : INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7815 : /********************************************************************\
7816 :
7817 : Routine: bm_buffer_info
7818 :
7819 : Purpose: Copies the current buffer header referenced by buffer_handle
7820 : into the *buffer_header structure which must be supplied
7821 : by the calling routine.
7822 :
7823 : Input:
7824 : INT buffer_handle Handle of the buffer to get the header from
7825 :
7826 : Output:
7827 : BUFFER_HEADER *buffer_header Destination address which gets a copy
7828 : of the buffer header structure.
7829 :
7830 : Function value:
7831 : BM_SUCCESS Successful completion
7832 : BM_INVALID_HANDLE Buffer handle is invalid
7833 : RPC_NET_ERROR Network error
7834 :
7835 : \********************************************************************/
7836 : {
7837 0 : if (rpc_is_remote())
7838 0 : return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7839 :
7840 : #ifdef LOCAL_ROUTINES
7841 :
7842 0 : int status = 0;
7843 0 : BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7844 :
7845 0 : if (!pbuf)
7846 0 : return status;
7847 :
7848 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
7849 :
7850 0 : if (!pbuf_guard.is_locked())
7851 0 : return pbuf_guard.get_status();
7852 :
7853 0 : memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7854 :
7855 : #endif /* LOCAL_ROUTINES */
7856 :
7857 0 : return BM_SUCCESS;
7858 0 : }
7859 :
7860 : /********************************************************************/
7861 0 : INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7862 : /********************************************************************\
7863 :
7864 : Routine: bm_get_buffer_level
7865 :
7866 : Purpose: Return number of bytes in buffer or in cache
7867 :
7868 : Input:
7869 : INT buffer_handle Handle of the buffer to get the info
7870 :
7871 : Output:
7872 : INT *n_bytes Number of bytes in buffer
7873 :
7874 : Function value:
7875 : BM_SUCCESS Successful completion
7876 : BM_INVALID_HANDLE Buffer handle is invalid
7877 : RPC_NET_ERROR Network error
7878 :
7879 : \********************************************************************/
7880 : {
7881 0 : if (rpc_is_remote())
7882 0 : return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7883 :
7884 : #ifdef LOCAL_ROUTINES
7885 : {
7886 0 : int status = 0;
7887 :
7888 0 : BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7889 :
7890 0 : if (!pbuf)
7891 0 : return status;
7892 :
7893 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
7894 :
7895 0 : if (!pbuf_guard.is_locked())
7896 0 : return pbuf_guard.get_status();
7897 :
7898 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
7899 :
7900 0 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7901 :
7902 0 : *n_bytes = pheader->write_pointer - pclient->read_pointer;
7903 0 : if (*n_bytes < 0)
7904 0 : *n_bytes += pheader->size;
7905 :
7906 0 : pbuf_guard.unlock();
7907 :
7908 0 : if (pbuf->read_cache_size) {
7909 0 : status = bm_lock_buffer_read_cache(pbuf);
7910 0 : if (status == BM_SUCCESS) {
7911 : /* add bytes in cache */
7912 0 : if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7913 0 : *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7914 0 : pbuf->read_cache_mutex.unlock();
7915 : }
7916 : }
7917 0 : }
7918 : #endif /* LOCAL_ROUTINES */
7919 :
7920 0 : return BM_SUCCESS;
7921 : }
7922 :
7923 :
7924 : #ifdef LOCAL_ROUTINES
7925 :
7926 : /********************************************************************/
7927 2 : static int bm_lock_buffer_read_cache(BUFFER *pbuf)
7928 : {
7929 2 : bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7930 :
7931 2 : if (!locked) {
7932 0 : fprintf(stderr, "bm_lock_buffer_read_cache: Error: Cannot lock read cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7933 0 : cm_msg(MERROR, "bm_lock_buffer_read_cache", "Cannot lock read cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7934 0 : abort();
7935 : /* DOES NOT RETURN */
7936 : }
7937 :
7938 2 : if (!pbuf->attached) {
7939 0 : pbuf->read_cache_mutex.unlock();
7940 0 : fprintf(stderr, "bm_lock_buffer_read_cache: Error: Cannot lock read cache of buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
7941 0 : return BM_INVALID_HANDLE;
7942 : }
7943 :
7944 2 : return BM_SUCCESS;
7945 : }
7946 :
7947 : /********************************************************************/
7948 2 : static int bm_lock_buffer_write_cache(BUFFER *pbuf)
7949 : {
7950 2 : bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7951 :
7952 2 : if (!locked) {
7953 0 : fprintf(stderr, "bm_lock_buffer_write_cache: Error: Cannot lock write cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7954 0 : cm_msg(MERROR, "bm_lock_buffer_write_cache", "Cannot lock write cache of buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7955 0 : abort();
7956 : /* DOES NOT RETURN */
7957 : }
7958 :
7959 2 : if (!pbuf->attached) {
7960 0 : pbuf->write_cache_mutex.unlock();
7961 0 : fprintf(stderr, "bm_lock_buffer_write_cache: Error: Cannot lock write cache of buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
7962 0 : return BM_INVALID_HANDLE;
7963 : }
7964 :
7965 2 : return BM_SUCCESS;
7966 : }
7967 :
7968 : /********************************************************************/
7969 25 : static int bm_lock_buffer_mutex(BUFFER *pbuf)
7970 : {
7971 : //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7972 :
7973 25 : bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7974 :
7975 25 : if (!locked) {
7976 0 : fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7977 0 : cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7978 0 : abort();
7979 : /* DOES NOT RETURN */
7980 : }
7981 :
7982 25 : if (!pbuf->attached) {
7983 0 : pbuf->buffer_mutex.unlock();
7984 0 : fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", buffer was closed while we waited for the buffer_mutex\n", pbuf->buffer_name);
7985 0 : return BM_INVALID_HANDLE;
7986 : }
7987 :
7988 : //static int counter = 0;
7989 : //counter++;
7990 : //printf("locked %d!\n", counter);
7991 : //if (counter > 50)
7992 : // ::sleep(3);
7993 :
7994 25 : return BM_SUCCESS;
7995 : }
7996 :
7997 : /********************************************************************/
7998 25 : static int xbm_lock_buffer(BUFFER *pbuf)
7999 : {
8000 : int status;
8001 :
8002 : // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8003 :
8004 : //if (pbuf->locked) {
8005 : // fprintf(stderr, "double lock, abort!\n");
8006 : // abort();
8007 : //}
8008 :
8009 25 : status = bm_lock_buffer_mutex(pbuf);
8010 :
8011 25 : if (status != BM_SUCCESS)
8012 0 : return status;
8013 :
8014 25 : status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8015 :
8016 25 : if (status != SS_SUCCESS) {
8017 0 : fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8018 :
8019 0 : status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8020 :
8021 0 : if (status != SS_SUCCESS) {
8022 0 : fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 10 seconds, buffer semaphore is probably stuck, delete %s.SHM and try again!\n", pbuf->buffer_name, pbuf->buffer_name);
8023 :
8024 0 : if (pbuf->buffer_header) {
8025 0 : for (int i=0; i<MAX_CLIENTS; i++) {
8026 0 : fprintf(stderr, "bm_lock_buffer: Buffer \"%s\" client %d \"%s\" pid %d\n", pbuf->buffer_name, i, pbuf->buffer_header->client[i].name, pbuf->buffer_header->client[i].pid);
8027 : }
8028 : }
8029 :
8030 0 : status = ss_semaphore_wait_for(pbuf->semaphore, _bm_lock_timeout);
8031 :
8032 0 : if (status != SS_SUCCESS) {
8033 0 : fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8034 0 : cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8035 0 : abort();
8036 : /* DOES NOT RETURN */
8037 : }
8038 : }
8039 : }
8040 :
8041 : // protect against double lock
8042 25 : assert(!pbuf->locked);
8043 25 : pbuf->locked = TRUE;
8044 :
8045 : #if 0
8046 : int x = MAX_CLIENTS - 1;
8047 : if (pbuf->buffer_header->client[x].unused1 != 0) {
8048 : printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8049 : }
8050 : //assert(pbuf->buffer_header->client[x].unused1 == 0);
8051 : pbuf->buffer_header->client[x].unused1 = getpid();
8052 : #endif
8053 :
8054 25 : pbuf->count_lock++;
8055 :
8056 25 : return BM_SUCCESS;
8057 : }
8058 :
8059 : /********************************************************************/
8060 25 : static void xbm_unlock_buffer(BUFFER *pbuf) {
8061 : // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8062 :
8063 : #if 0
8064 : int x = MAX_CLIENTS-1;
8065 : if (pbuf->attached) {
8066 : if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8067 : printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8068 : }
8069 : pbuf->buffer_header->client[x].unused1 = 0;
8070 : } else {
8071 : printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8072 : }
8073 : #endif
8074 :
8075 : // protect against double unlock
8076 25 : assert(pbuf->locked);
8077 25 : pbuf->locked = FALSE;
8078 :
8079 25 : ss_semaphore_release(pbuf->semaphore);
8080 25 : pbuf->buffer_mutex.unlock();
8081 25 : }
8082 :
8083 : #endif /* LOCAL_ROUTINES */
8084 :
8085 : /********************************************************************/
8086 2 : INT bm_init_buffer_counters(INT buffer_handle)
8087 : /********************************************************************\
8088 :
8089 : Routine: bm_init_event_counters
8090 :
8091 : Purpose: Initialize counters for a specific buffer. This routine
8092 : should be called at the beginning of a run.
8093 :
8094 : Input:
8095 : INT buffer_handle Handle to the buffer to be
8096 : initialized.
8097 : Output:
8098 : none
8099 :
8100 : Function value:
8101 : BM_SUCCESS Successful completion
8102 : BM_INVALID_HANDLE Buffer handle is invalid
8103 :
8104 : \********************************************************************/
8105 : {
8106 2 : if (rpc_is_remote())
8107 0 : return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8108 :
8109 : #ifdef LOCAL_ROUTINES
8110 :
8111 2 : int status = 0;
8112 :
8113 2 : BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8114 :
8115 2 : if (!pbuf)
8116 0 : return status;
8117 :
8118 2 : bm_lock_buffer_guard pbuf_guard(pbuf);
8119 :
8120 2 : if (!pbuf_guard.is_locked())
8121 0 : return pbuf_guard.get_status();
8122 :
8123 2 : pbuf->buffer_header->num_in_events = 0;
8124 2 : pbuf->buffer_header->num_out_events = 0;
8125 :
8126 : #endif /* LOCAL_ROUTINES */
8127 :
8128 2 : return BM_SUCCESS;
8129 2 : }
8130 :
8131 : /**dox***************************************************************/
8132 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
8133 :
8134 : /**dox***************************************************************/
8135 : /** @addtogroup bmfunctionc
8136 : *
8137 : * @{ */
8138 :
8139 : /********************************************************************/
8140 : /**
8141 : Modifies buffer cache size.
8142 : Without a buffer cache, events are copied to/from the shared
8143 : memory event by event.
8144 :
8145 : To protect processed from accessing the shared memory simultaneously,
8146 : semaphores are used. Since semaphore operations are CPU consuming (typically
8147 : 50-100us) this can slow down the data transfer especially for small events.
8148 : By using a cache the number of semaphore operations is reduced dramatically.
8149 : Instead writing directly to the shared memory, the events are copied to a
8150 : local cache buffer. When this buffer is full, it is copied to the shared
8151 : memory in one operation. The same technique can be used when receiving events.
8152 :
8153 : The drawback of this method is that the events have to be copied twice, once to the
8154 : cache and once from the cache to the shared memory. Therefore it can happen that the
8155 : usage of a cache even slows down data throughput on a given environment (computer
8156 : type, OS type, event size).
8157 : The cache size has therefore be optimized manually to maximize data throughput.
8158 : @param buffer_handle buffer handle obtained via bm_open_buffer()
8159 : @param read_size cache size for reading events in bytes, zero for no cache
8160 : @param write_size cache size for writing events in bytes, zero for no cache
8161 : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NO_MEMORY, BM_INVALID_PARAM
8162 : */
8163 0 : INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8164 : /*------------------------------------------------------------------*/
8165 : {
8166 0 : if (rpc_is_remote())
8167 0 : return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8168 :
8169 : #ifdef LOCAL_ROUTINES
8170 : {
8171 0 : int status = 0;
8172 :
8173 0 : BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8174 :
8175 0 : if (!pbuf)
8176 0 : return status;
8177 :
8178 : /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8179 :
8180 0 : status = bm_lock_buffer_mutex(pbuf);
8181 :
8182 0 : if (status != BM_SUCCESS)
8183 0 : return status;
8184 :
8185 : if (write_size < 0)
8186 : write_size = 0;
8187 :
8188 0 : if (write_size > 0) {
8189 0 : if (write_size < MIN_WRITE_CACHE_SIZE) {
8190 0 : cm_msg(MERROR, "bm_set_cache_size", "requested write cache size %zu on buffer \"%s\" too small, will use minimum size %d", write_size, pbuf->buffer_name, MIN_WRITE_CACHE_SIZE);
8191 0 : write_size = MIN_WRITE_CACHE_SIZE;
8192 : }
8193 : }
8194 :
8195 0 : size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8196 :
8197 0 : if (write_size > max_write_size) {
8198 0 : size_t new_write_size = max_write_size;
8199 0 : cm_msg(MERROR, "bm_set_cache_size", "requested write cache size %zu on buffer \"%s\" is too big: buffer size is %d, write cache size will be %zu bytes", write_size, pbuf->buffer_name, pbuf->buffer_header->size, new_write_size);
8200 0 : write_size = new_write_size;
8201 : }
8202 :
8203 0 : pbuf->buffer_mutex.unlock();
8204 :
8205 : /* resize read cache */
8206 :
8207 0 : status = bm_lock_buffer_read_cache(pbuf);
8208 :
8209 0 : if (status != BM_SUCCESS) {
8210 0 : return status;
8211 : }
8212 :
8213 0 : if (pbuf->read_cache_size > 0) {
8214 0 : free(pbuf->read_cache);
8215 0 : pbuf->read_cache = NULL;
8216 : }
8217 :
8218 0 : if (read_size > 0) {
8219 0 : pbuf->read_cache = (char *) malloc(read_size);
8220 0 : if (pbuf->read_cache == NULL) {
8221 0 : pbuf->read_cache_size = 0;
8222 0 : pbuf->read_cache_rp = 0;
8223 0 : pbuf->read_cache_wp = 0;
8224 0 : pbuf->read_cache_mutex.unlock();
8225 0 : cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate read cache for buffer \"%s\", malloc(%zu) failed", pbuf->buffer_name, read_size);
8226 0 : return BM_NO_MEMORY;
8227 : }
8228 : }
8229 :
8230 0 : pbuf->read_cache_size = read_size;
8231 0 : pbuf->read_cache_rp = 0;
8232 0 : pbuf->read_cache_wp = 0;
8233 :
8234 0 : pbuf->read_cache_mutex.unlock();
8235 :
8236 : /* resize the write cache */
8237 :
8238 0 : status = bm_lock_buffer_write_cache(pbuf);
8239 :
8240 0 : if (status != BM_SUCCESS)
8241 0 : return status;
8242 :
8243 : // FIXME: should flush the write cache!
8244 0 : if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8245 0 : cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8246 : }
8247 :
8248 : /* manage write cache */
8249 0 : if (pbuf->write_cache_size > 0) {
8250 0 : free(pbuf->write_cache);
8251 0 : pbuf->write_cache = NULL;
8252 : }
8253 :
8254 0 : if (write_size > 0) {
8255 0 : pbuf->write_cache = (char *) M_MALLOC(write_size);
8256 0 : if (pbuf->write_cache == NULL) {
8257 0 : pbuf->write_cache_size = 0;
8258 0 : pbuf->write_cache_rp = 0;
8259 0 : pbuf->write_cache_wp = 0;
8260 0 : pbuf->write_cache_mutex.unlock();
8261 0 : cm_msg(MERROR, "bm_set_cache_size", "not enough memory to allocate write cache for buffer \"%s\", malloc(%zu) failed", pbuf->buffer_name, write_size);
8262 0 : return BM_NO_MEMORY;
8263 : }
8264 : }
8265 :
8266 0 : pbuf->write_cache_size = write_size;
8267 0 : pbuf->write_cache_rp = 0;
8268 0 : pbuf->write_cache_wp = 0;
8269 :
8270 0 : pbuf->write_cache_mutex.unlock();
8271 : }
8272 : #endif /* LOCAL_ROUTINES */
8273 :
8274 0 : return BM_SUCCESS;
8275 : }
8276 :
8277 : /********************************************************************/
8278 : /**
8279 : Compose a Midas event header.
8280 : An event header can usually be set-up manually or
8281 : through this routine. If the data size of the event is not known when
8282 : the header is composed, it can be set later with event_header->data-size = <...>
8283 : Following structure is created at the beginning of an event
8284 : \code
8285 : typedef struct {
8286 : short int event_id;
8287 : short int trigger_mask;
8288 : DWORD serial_number;
8289 : DWORD time_stamp;
8290 : DWORD data_size;
8291 : } EVENT_HEADER;
8292 :
8293 : char event[1000];
8294 : bm_compose_event((EVENT_HEADER *)event, 1, 0, 100, 1);
8295 : *(event+sizeof(EVENT_HEADER)) = <...>
8296 : \endcode
8297 : @param event_header pointer to the event header
8298 : @param event_id event ID of the event
8299 : @param trigger_mask trigger mask of the event
8300 : @param data_size size if the data part of the event in bytes
8301 : @param serial serial number
8302 : @return BM_SUCCESS
8303 : */
8304 11 : INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
8305 : {
8306 11 : event_header->event_id = event_id;
8307 11 : event_header->trigger_mask = trigger_mask;
8308 11 : event_header->data_size = data_size;
8309 11 : event_header->time_stamp = ss_time();
8310 11 : event_header->serial_number = serial;
8311 :
8312 11 : return BM_SUCCESS;
8313 : }
8314 :
8315 0 : INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
8316 : {
8317 : static std::mutex mutex;
8318 :
8319 0 : event_header->event_id = event_id;
8320 0 : event_header->trigger_mask = trigger_mask;
8321 0 : event_header->data_size = data_size;
8322 0 : event_header->time_stamp = ss_time();
8323 : {
8324 0 : std::lock_guard<std::mutex> lock(mutex);
8325 0 : event_header->serial_number = *serial;
8326 0 : *serial = *serial + 1;
8327 : // implicit unlock
8328 0 : }
8329 :
8330 0 : return BM_SUCCESS;
8331 : }
8332 :
8333 : /**dox***************************************************************/
8334 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
8335 :
8336 : /********************************************************************/
8337 0 : INT bm_add_event_request(INT buffer_handle, short int event_id,
8338 : short int trigger_mask,
8339 : INT sampling_type,
8340 : EVENT_HANDLER *func,
8341 : INT request_id)
8342 : /********************************************************************\
8343 :
8344 : Routine: bm_add_event_request
8345 :
8346 : Purpose: Place a request for a specific event type in the client
8347 : structure of the buffer refereced by buffer_handle.
8348 :
8349 : Input:
8350 : INT buffer_handle Handle to the buffer where the re-
8351 : quest should be placed in
8352 :
8353 : short int event_id Event ID \
8354 : short int trigger_mask Trigger mask / Event specification
8355 :
8356 : INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8357 :
8358 :
8359 : Note: to request all types of events, use
8360 : event_id = 0 (all others should be !=0 !)
8361 : trigger_mask = TRIGGER_ALL
8362 : sampling_typ = GET_ALL
8363 :
8364 :
8365 : void *func Callback function
8366 : INT request_id Request id (unique number assigned
8367 : by bm_request_event)
8368 :
8369 : Output:
8370 : none
8371 :
8372 : Function value:
8373 : BM_SUCCESS Successful completion
8374 : BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8375 : MIDAS.H should be increased.
8376 : BM_INVALID_HANDLE Buffer handle is invalid
8377 : BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8378 : RPC_NET_ERROR Network error
8379 :
8380 : \********************************************************************/
8381 : {
8382 0 : if (rpc_is_remote())
8383 0 : return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8384 0 : trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8385 :
8386 : #ifdef LOCAL_ROUTINES
8387 : {
8388 0 : int status = 0;
8389 :
8390 0 : BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8391 :
8392 0 : if (!pbuf)
8393 0 : return status;
8394 :
8395 : /* lock buffer */
8396 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
8397 :
8398 0 : if (!pbuf_guard.is_locked())
8399 0 : return pbuf_guard.get_status();
8400 :
8401 : /* avoid callback/non callback requests */
8402 0 : if (func == NULL && pbuf->callback) {
8403 0 : pbuf_guard.unlock(); // unlock before cm_msg()
8404 0 : cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8405 0 : return BM_INVALID_MIXING;
8406 : }
8407 :
8408 : /* do not allow GET_RECENT with nonzero cache size */
8409 0 : if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8410 0 : pbuf_guard.unlock(); // unlock before cm_msg()
8411 0 : cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8412 0 : return BM_INVALID_PARAM;
8413 : }
8414 :
8415 : /* get a pointer to the proper client structure */
8416 0 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8417 :
8418 : /* look for a empty request entry */
8419 : int i;
8420 0 : for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8421 0 : if (!pclient->event_request[i].valid)
8422 0 : break;
8423 :
8424 0 : if (i == MAX_EVENT_REQUESTS) {
8425 : // implicit unlock
8426 0 : return BM_NO_MEMORY;
8427 : }
8428 :
8429 : /* setup event_request structure */
8430 0 : pclient->event_request[i].id = request_id;
8431 0 : pclient->event_request[i].valid = TRUE;
8432 0 : pclient->event_request[i].event_id = event_id;
8433 0 : pclient->event_request[i].trigger_mask = trigger_mask;
8434 0 : pclient->event_request[i].sampling_type = sampling_type;
8435 :
8436 0 : pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8437 :
8438 0 : pbuf->get_all_flag = pclient->all_flag;
8439 :
8440 : /* set callback flag in buffer structure */
8441 0 : if (func != NULL)
8442 0 : pbuf->callback = TRUE;
8443 :
8444 : /*
8445 : Save the index of the last request in the list so that later only the
8446 : requests 0..max_request_index-1 have to be searched through.
8447 : */
8448 :
8449 0 : if (i + 1 > pclient->max_request_index)
8450 0 : pclient->max_request_index = i + 1;
8451 0 : }
8452 : #endif /* LOCAL_ROUTINES */
8453 :
8454 0 : return BM_SUCCESS;
8455 : }
8456 :
8457 : /**dox***************************************************************/
8458 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
8459 :
8460 : /********************************************************************/
8461 : /**
8462 : Place an event request based on certain characteristics.
8463 : Multiple event requests can be placed for each buffer, which
8464 : are later identified by their request ID. They can contain different callback
8465 : routines. Example see bm_open_buffer() and bm_receive_event()
8466 : @param buffer_handle buffer handle obtained via bm_open_buffer()
8467 : @param event_id event ID for requested events. Use EVENTID_ALL
8468 : to receive events with any ID.
8469 : @param trigger_mask trigger mask for requested events.
8470 : The requested events must have at least one bit in its
8471 : trigger mask common with the requested trigger mask. Use TRIGGER_ALL to
8472 : receive events with any trigger mask.
8473 : @param sampling_type specifies how many events to receive.
8474 : A value of GET_ALL receives all events which
8475 : match the specified event ID and trigger mask. If the events are consumed slower
8476 : than produced, the producer is automatically slowed down. A value of GET_NONBLOCKING
8477 : receives as much events as possible without slowing down the producer. GET_ALL is
8478 : typically used by the logger, while GET_NONBLOCKING is typically used by analyzers.
8479 : @param request_id request ID returned by the function.
8480 : This ID is passed to the callback routine and must
8481 : be used in the bm_delete_request() routine.
8482 : @param func allback routine which gets called when an event of the
8483 : specified type is received.
8484 : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
8485 : BM_NO_MEMORY too many requests. The value MAX_EVENT_REQUESTS in midas.h
8486 : should be increased.
8487 : */
8488 0 : INT bm_request_event(HNDLE buffer_handle, short int event_id,
8489 : short int trigger_mask,
8490 : INT sampling_type, HNDLE *request_id,
8491 : EVENT_HANDLER *func)
8492 : {
8493 0 : assert(request_id != NULL);
8494 :
8495 0 : EventRequest r;
8496 0 : r.buffer_handle = buffer_handle;
8497 0 : r.event_id = event_id;
8498 0 : r.trigger_mask = trigger_mask;
8499 0 : r.dispatcher = func;
8500 :
8501 : {
8502 0 : std::lock_guard<std::mutex> guard(_request_list_mutex);
8503 :
8504 0 : bool found = false;
8505 :
8506 : // find deleted entry
8507 0 : for (size_t i = 0; i < _request_list.size(); i++) {
8508 0 : if (_request_list[i].buffer_handle == 0) {
8509 0 : _request_list[i] = r;
8510 0 : *request_id = i;
8511 0 : found = true;
8512 0 : break;
8513 : }
8514 : }
8515 :
8516 0 : if (!found) { // not found
8517 0 : *request_id = _request_list.size();
8518 0 : _request_list.push_back(r);
8519 : }
8520 :
8521 : // implicit unlock()
8522 0 : }
8523 :
8524 : /* add request in buffer structure */
8525 0 : int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8526 0 : if (status != BM_SUCCESS)
8527 0 : return status;
8528 :
8529 0 : return BM_SUCCESS;
8530 : }
8531 :
8532 : /********************************************************************/
8533 : /**
8534 : Delete a previously placed request for a specific event
8535 : type in the client structure of the buffer refereced by buffer_handle.
8536 : @param buffer_handle Handle to the buffer where the re-
8537 : quest should be placed in
8538 : @param request_id Request id returned by bm_request_event
8539 : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_NOT_FOUND, RPC_NET_ERROR
8540 : */
8541 0 : INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8542 0 : if (rpc_is_remote())
8543 0 : return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8544 :
8545 : #ifdef LOCAL_ROUTINES
8546 : {
8547 0 : int status = 0;
8548 :
8549 0 : BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8550 :
8551 0 : if (!pbuf)
8552 0 : return status;
8553 :
8554 : /* lock buffer */
8555 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
8556 :
8557 0 : if (!pbuf_guard.is_locked())
8558 0 : return pbuf_guard.get_status();
8559 :
8560 : INT i, deleted;
8561 :
8562 : /* get a pointer to the proper client structure */
8563 0 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8564 :
8565 : /* check all requests and set to zero if matching */
8566 0 : for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8567 0 : if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8568 0 : memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8569 0 : deleted++;
8570 : }
8571 :
8572 : /* calculate new max_request_index entry */
8573 0 : for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8574 0 : if (pclient->event_request[i].valid)
8575 0 : break;
8576 :
8577 0 : pclient->max_request_index = i + 1;
8578 :
8579 : /* calculate new all_flag */
8580 0 : pclient->all_flag = FALSE;
8581 :
8582 0 : for (i = 0; i < pclient->max_request_index; i++)
8583 0 : if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8584 0 : pclient->all_flag = TRUE;
8585 0 : break;
8586 : }
8587 :
8588 0 : pbuf->get_all_flag = pclient->all_flag;
8589 :
8590 0 : if (!deleted)
8591 0 : return BM_NOT_FOUND;
8592 0 : }
8593 : #endif /* LOCAL_ROUTINES */
8594 :
8595 0 : return BM_SUCCESS;
8596 : }
8597 :
8598 : /********************************************************************/
8599 : /**
8600 : Deletes an event request previously done with bm_request_event().
8601 : When an event request gets deleted, events of that requested type are
8602 : not received any more. When a buffer is closed via bm_close_buffer(), all
8603 : event requests from that buffer are deleted automatically
8604 : @param request_id request identifier given by bm_request_event()
8605 : @return BM_SUCCESS, BM_INVALID_HANDLE
8606 : */
8607 0 : INT bm_delete_request(INT request_id)
8608 : {
8609 0 : _request_list_mutex.lock();
8610 :
8611 0 : if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8612 0 : _request_list_mutex.unlock();
8613 0 : return BM_INVALID_HANDLE;
8614 : }
8615 :
8616 0 : int buffer_handle = _request_list[request_id].buffer_handle;
8617 :
8618 0 : _request_list[request_id].clear();
8619 :
8620 0 : _request_list_mutex.unlock();
8621 :
8622 : /* remove request entry from buffer */
8623 0 : return bm_remove_event_request(buffer_handle, request_id);
8624 : }
8625 :
8626 : #if 0 // currently not used
8627 : static void bm_show_pointers(const BUFFER_HEADER * pheader)
8628 : {
8629 : int i;
8630 : const BUFFER_CLIENT *pclient;
8631 :
8632 : pclient = pheader->client;
8633 :
8634 : printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8635 : pheader->write_pointer, pheader->size);
8636 : for (i = 0; i < pheader->max_client_index; i++)
8637 : if (pclient[i].pid) {
8638 : printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8639 : }
8640 :
8641 : printf("done\n");
8642 : }
8643 : #endif
8644 :
8645 0 : static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient) {
8646 0 : assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8647 0 : assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8648 :
8649 0 : if (pheader->read_pointer <= pheader->write_pointer) {
8650 :
8651 0 : if (pclient->read_pointer < pheader->read_pointer) {
8652 0 : cm_msg(MINFO, "bm_validate_client_pointers",
8653 : "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8654 0 : pclient->name,
8655 0 : pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8656 :
8657 0 : pclient->read_pointer = pheader->read_pointer;
8658 : }
8659 :
8660 0 : if (pclient->read_pointer > pheader->write_pointer) {
8661 0 : cm_msg(MINFO, "bm_validate_client_pointers",
8662 : "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8663 0 : pclient->name,
8664 0 : pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8665 :
8666 0 : pclient->read_pointer = pheader->write_pointer;
8667 : }
8668 :
8669 : } else {
8670 :
8671 0 : if (pclient->read_pointer < 0) {
8672 0 : cm_msg(MINFO, "bm_validate_client_pointers",
8673 : "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8674 0 : pclient->name,
8675 0 : pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8676 :
8677 0 : pclient->read_pointer = pheader->read_pointer;
8678 : }
8679 :
8680 0 : if (pclient->read_pointer >= pheader->size) {
8681 0 : cm_msg(MINFO, "bm_validate_client_pointers",
8682 : "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8683 0 : pclient->name,
8684 0 : pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8685 :
8686 0 : pclient->read_pointer = pheader->read_pointer;
8687 : }
8688 :
8689 0 : if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8690 0 : cm_msg(MINFO, "bm_validate_client_pointers",
8691 : "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8692 0 : pclient->name,
8693 0 : pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8694 :
8695 0 : pclient->read_pointer = pheader->read_pointer;
8696 : }
8697 : }
8698 0 : }
8699 :
8700 : #if 0 // currently not used
8701 : static void bm_validate_pointers(BUFFER_HEADER * pheader)
8702 : {
8703 : BUFFER_CLIENT *pclient = pheader->client;
8704 : int i;
8705 :
8706 : for (i = 0; i < pheader->max_client_index; i++)
8707 : if (pclient[i].pid) {
8708 : bm_validate_client_pointers(pheader, &pclient[i]);
8709 : }
8710 : }
8711 : #endif
8712 :
8713 : //
8714 : // Buffer pointers
8715 : //
8716 : // normal:
8717 : //
8718 : // zero -->
8719 : // ... free space
8720 : // read_pointer -->
8721 : // client1 rp -->
8722 : // client2 rp -->
8723 : // ... buffered data
8724 : // write_pointer -->
8725 : // ... free space
8726 : // pheader->size -->
8727 : //
8728 : // inverted:
8729 : //
8730 : // zero -->
8731 : // client3 rp -->
8732 : // ... buffered data
8733 : // client4 rp -->
8734 : // write_pointer -->
8735 : // ... free space
8736 : // read_pointer -->
8737 : // client1 rp -->
8738 : // client2 rp -->
8739 : // ... buffered data
8740 : // pheader->size -->
8741 : //
8742 :
8743 0 : static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
8744 0 : assert(caller_name);
8745 :
8746 : /* calculate global read pointer as "minimum" of client read pointers */
8747 0 : int min_rp = pheader->write_pointer;
8748 :
8749 : int i;
8750 0 : for (i = 0; i < pheader->max_client_index; i++) {
8751 0 : BUFFER_CLIENT *pc = pheader->client + i;
8752 0 : if (pc->pid) {
8753 0 : bm_validate_client_pointers_locked(pheader, pc);
8754 :
8755 : #if 0
8756 : printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8757 : pheader->name,
8758 : pheader->read_pointer,
8759 : pheader->write_pointer,
8760 : pheader->size,
8761 : min_rp,
8762 : pc->name,
8763 : pc->read_pointer);
8764 : #endif
8765 :
8766 0 : if (pheader->read_pointer <= pheader->write_pointer) {
8767 : // normal pointers
8768 0 : if (pc->read_pointer < min_rp)
8769 0 : min_rp = pc->read_pointer;
8770 : } else {
8771 : // inverted pointers
8772 0 : if (pc->read_pointer <= pheader->write_pointer) {
8773 : // clients 3 and 4
8774 0 : if (pc->read_pointer < min_rp)
8775 0 : min_rp = pc->read_pointer;
8776 : } else {
8777 : // clients 1 and 2
8778 0 : int xptr = pc->read_pointer - pheader->size;
8779 0 : if (xptr < min_rp)
8780 0 : min_rp = xptr;
8781 : }
8782 : }
8783 : }
8784 : }
8785 :
8786 0 : if (min_rp < 0)
8787 0 : min_rp += pheader->size;
8788 :
8789 0 : assert(min_rp >= 0);
8790 0 : assert(min_rp < pheader->size);
8791 :
8792 0 : if (min_rp == pheader->read_pointer) {
8793 0 : return FALSE;
8794 : }
8795 :
8796 : #if 0
8797 : printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8798 : pheader->name,
8799 : pheader->read_pointer,
8800 : pheader->write_pointer,
8801 : pheader->size,
8802 : min_rp);
8803 : #endif
8804 :
8805 0 : pheader->read_pointer = min_rp;
8806 :
8807 0 : return TRUE;
8808 : }
8809 :
8810 0 : static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8811 : int i;
8812 0 : int have_get_all_requests = 0;
8813 :
8814 0 : for (i = 0; i < pc->max_request_index; i++)
8815 0 : if (pc->event_request[i].valid)
8816 0 : have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8817 :
8818 : /* only GET_ALL requests actually free space in the event buffer */
8819 0 : if (!have_get_all_requests)
8820 0 : return;
8821 :
8822 : /*
8823 : If read pointer has been changed, it may have freed up some space
8824 : for waiting producers. So check if free space is now more than 50%
8825 : of the buffer size and wake waiting producers.
8826 : */
8827 :
8828 0 : int free_space = pc->read_pointer - pheader->write_pointer;
8829 0 : if (free_space <= 0)
8830 0 : free_space += pheader->size;
8831 :
8832 0 : if (free_space >= pheader->size * 0.5) {
8833 0 : for (i = 0; i < pheader->max_client_index; i++) {
8834 0 : const BUFFER_CLIENT *pc = pheader->client + i;
8835 0 : if (pc->pid && pc->write_wait) {
8836 0 : BOOL send_wakeup = (pc->write_wait < free_space);
8837 : //printf("bm_wakeup_producers: buffer [%s] client [%s] write_wait %d, free_space %d, sending wakeup message %d\n", pheader->name, pc->name, pc->write_wait, free_space, send_wakeup);
8838 0 : if (send_wakeup) {
8839 0 : ss_resume(pc->port, "B ");
8840 : }
8841 : }
8842 : }
8843 : }
8844 : }
8845 :
8846 0 : static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8847 : {
8848 0 : _request_list_mutex.lock();
8849 0 : bool locked = true;
8850 0 : size_t n = _request_list.size();
8851 : /* call dispatcher */
8852 0 : for (size_t i = 0; i < n; i++) {
8853 0 : if (!locked) {
8854 0 : _request_list_mutex.lock();
8855 0 : locked = true;
8856 : }
8857 0 : EventRequest r = _request_list[i];
8858 0 : if (r.buffer_handle != buffer_handle)
8859 0 : continue;
8860 0 : if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8861 0 : continue;
8862 : /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8863 0 : _request_list_mutex.unlock();
8864 0 : locked = false;
8865 : /* if event is fragmented, call defragmenter */
8866 0 : if (((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) || ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG))) {
8867 0 : bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8868 : } else {
8869 0 : r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8870 : }
8871 : }
8872 0 : if (locked)
8873 0 : _request_list_mutex.unlock();
8874 0 : }
8875 :
8876 : #ifdef LOCAL_ROUTINES
8877 :
8878 0 : static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8879 : /* increment read cache read pointer */
8880 0 : pbuf->read_cache_rp += total_size;
8881 :
8882 0 : if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8883 0 : pbuf->read_cache_rp = 0;
8884 0 : pbuf->read_cache_wp = 0;
8885 : }
8886 0 : }
8887 :
8888 0 : static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8889 : {
8890 0 : if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8891 0 : return FALSE;
8892 :
8893 0 : EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8894 0 : int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8895 0 : int total_size = ALIGN8(event_size);
8896 :
8897 0 : if (ppevent)
8898 0 : *ppevent = pevent;
8899 0 : if (pevent_size)
8900 0 : *pevent_size = event_size;
8901 0 : if (ptotal_size)
8902 0 : *ptotal_size = total_size;
8903 :
8904 0 : return TRUE;
8905 : }
8906 :
8907 : //
8908 : // return values:
8909 : // BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8910 : // BM_ASYNC_RETURN - buffer is empty
8911 : // BM_CORRUPTED - buffer is corrupted
8912 : //
8913 :
8914 0 : static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8915 : {
8916 0 : if (pc->read_pointer == pheader->write_pointer) {
8917 : /* no more events buffered for this client */
8918 0 : if (!pc->read_wait) {
8919 : //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8920 0 : pc->read_wait = TRUE;
8921 : }
8922 0 : return BM_ASYNC_RETURN;
8923 : }
8924 :
8925 0 : if (pc->read_wait) {
8926 : //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8927 0 : pc->read_wait = FALSE;
8928 : }
8929 :
8930 0 : if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8931 0 : cm_msg(MERROR, "bm_peek_buffer_locked", "event buffer \"%s\" is corrupted: client \"%s\" read pointer %d is invalid. buffer read pointer %d, write pointer %d, size %d", pheader->name, pc->name, pc->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8932 0 : return BM_CORRUPTED;
8933 : }
8934 :
8935 0 : char *pdata = (char *) (pheader + 1);
8936 :
8937 0 : EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8938 0 : int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8939 0 : int total_size = ALIGN8(event_size);
8940 :
8941 0 : if ((total_size <= 0) || (total_size > pheader->size)) {
8942 0 : cm_msg(MERROR, "bm_peek_buffer_locked", "event buffer \"%s\" is corrupted: client \"%s\" read pointer %d points to invalid event: data_size %d, event_size %d, total_size %d. buffer size: %d, read_pointer: %d, write_pointer: %d", pheader->name, pc->name, pc->read_pointer, pevent->data_size, event_size, total_size, pheader->size, pheader->read_pointer, pheader->write_pointer);
8943 0 : return BM_CORRUPTED;
8944 : }
8945 :
8946 0 : assert(total_size > 0);
8947 0 : assert(total_size <= pheader->size);
8948 :
8949 0 : if (ppevent)
8950 0 : *ppevent = pevent;
8951 0 : if (pevent_size)
8952 0 : *pevent_size = event_size;
8953 0 : if (ptotal_size)
8954 0 : *ptotal_size = total_size;
8955 :
8956 0 : return BM_SUCCESS;
8957 : }
8958 :
8959 0 : static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8960 : {
8961 0 : const char *pdata = (const char *) (pheader + 1);
8962 :
8963 0 : if (rp + event_size <= pheader->size) {
8964 : /* copy event to cache */
8965 0 : memcpy(buf, pdata + rp, event_size);
8966 : } else {
8967 : /* event is splitted */
8968 0 : int size = pheader->size - rp;
8969 0 : memcpy(buf, pdata + rp, size);
8970 0 : memcpy(buf + size, pdata, event_size - size);
8971 : }
8972 0 : }
8973 :
8974 0 : static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8975 : {
8976 0 : const char *pdata = (const char *) (pheader + 1);
8977 :
8978 0 : if (rp + event_size <= pheader->size) {
8979 : /* copy event to cache */
8980 0 : vecptr->assign(pdata + rp, pdata + rp + event_size);
8981 : } else {
8982 : /* event is splitted */
8983 0 : int size = pheader->size - rp;
8984 0 : vecptr->assign(pdata + rp, pdata + rp + size);
8985 0 : vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8986 : }
8987 0 : }
8988 :
8989 0 : static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8990 :
8991 0 : BOOL is_requested = FALSE;
8992 : int i;
8993 0 : for (i = 0; i < pc->max_request_index; i++) {
8994 0 : const EVENT_REQUEST *prequest = pc->event_request + i;
8995 0 : if (prequest->valid) {
8996 0 : if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8997 : /* check if this is a recent event */
8998 0 : if (prequest->sampling_type == GET_RECENT) {
8999 0 : if (ss_time() - pevent->time_stamp > 1) {
9000 : /* skip that event */
9001 0 : continue;
9002 : }
9003 : }
9004 :
9005 0 : is_requested = TRUE;
9006 0 : break;
9007 : }
9008 : }
9009 : }
9010 0 : return is_requested;
9011 : }
9012 :
9013 : static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
9014 :
9015 0 : static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
9016 : {
9017 0 : BUFFER* pbuf = pbuf_guard.get_pbuf();
9018 0 : BUFFER_HEADER* pheader = pbuf->buffer_header;
9019 0 : BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9020 0 : BOOL need_wakeup = FALSE;
9021 :
9022 : //printf("bm_fill_read_cache: [%s] timeout %d, size %d, rp %d, wp %d\n", pheader->name, timeout_msec, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
9023 :
9024 : /* loop over all events in the buffer */
9025 :
9026 : while (1) {
9027 0 : EVENT_HEADER *pevent = NULL;
9028 0 : int event_size = 3; // poison value
9029 0 : int total_size = 3; // poison value
9030 :
9031 0 : int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9032 0 : if (status == BM_CORRUPTED) {
9033 0 : return status;
9034 0 : } else if (status != BM_SUCCESS) {
9035 : /* event buffer is empty */
9036 0 : if (timeout_msec == BM_NO_WAIT) {
9037 0 : if (need_wakeup)
9038 0 : bm_wakeup_producers_locked(pheader, pc);
9039 0 : if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9040 : // read cache is empty
9041 0 : return BM_ASYNC_RETURN;
9042 : }
9043 0 : return BM_SUCCESS;
9044 : }
9045 :
9046 0 : int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
9047 :
9048 0 : if (status != BM_SUCCESS) {
9049 : // we only come here with SS_ABORT & co
9050 0 : return status;
9051 : }
9052 :
9053 : // make sure we wait for new event only once
9054 0 : timeout_msec = BM_NO_WAIT;
9055 : // go back to bm_peek_buffer_locked
9056 0 : continue;
9057 0 : }
9058 :
9059 : /* loop over all requests: if this event matches a request,
9060 : * copy it to the read cache */
9061 :
9062 0 : BOOL is_requested = bm_check_requests(pc, pevent);
9063 :
9064 0 : if (is_requested) {
9065 0 : if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9066 : /* read cache is full */
9067 0 : if (need_wakeup)
9068 0 : bm_wakeup_producers_locked(pheader, pc);
9069 0 : return BM_SUCCESS;
9070 : }
9071 :
9072 0 : bm_read_from_buffer_locked(pheader, pc->read_pointer, pbuf->read_cache + pbuf->read_cache_wp, event_size);
9073 :
9074 0 : pbuf->read_cache_wp += total_size;
9075 :
9076 : /* update statistics */
9077 0 : pheader->num_out_events++;
9078 0 : pbuf->count_read++;
9079 0 : pbuf->bytes_read += event_size;
9080 : }
9081 :
9082 : /* shift read pointer */
9083 :
9084 0 : int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9085 0 : pc->read_pointer = new_read_pointer;
9086 :
9087 0 : need_wakeup = TRUE;
9088 0 : }
9089 : /* NOT REACHED */
9090 : }
9091 :
9092 0 : static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9093 : /* now convert event header */
9094 0 : if (convert_flags) {
9095 0 : rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9096 0 : rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9097 0 : rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9098 0 : rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9099 0 : rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9100 : }
9101 0 : }
9102 :
9103 11 : static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
9104 : {
9105 : // return values:
9106 : // BM_SUCCESS - have "requested_space" bytes free in the buffer
9107 : // BM_CORRUPTED - shared memory is corrupted
9108 : // BM_NO_MEMORY - asked for more than buffer size
9109 : // BM_ASYNC_RETURN - timeout waiting for free space
9110 : // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9111 : // SS_ABORT - we are told to shutdown (locks releases)
9112 :
9113 : int status;
9114 11 : BUFFER* pbuf = pbuf_guard.get_pbuf();
9115 11 : BUFFER_HEADER *pheader = pbuf->buffer_header;
9116 11 : char *pdata = (char *) (pheader + 1);
9117 :
9118 : /* make sure the buffer never completely full:
9119 : * read pointer and write pointer would coincide
9120 : * and the code cannot tell if it means the
9121 : * buffer is 100% full or 100% empty. It will explode
9122 : * or lose events */
9123 11 : requested_space += 100;
9124 :
9125 11 : if (requested_space >= pheader->size)
9126 0 : return BM_NO_MEMORY;
9127 :
9128 11 : DWORD time_start = ss_millitime();
9129 11 : DWORD time_end = time_start + timeout_msec;
9130 :
9131 : //DWORD blocking_time = 0;
9132 : //int blocking_loops = 0;
9133 11 : int blocking_client_index = -1;
9134 : char blocking_client_name[NAME_LENGTH];
9135 11 : blocking_client_name[0] = 0;
9136 :
9137 : while (1) {
9138 : while (1) {
9139 : /* check if enough space in buffer */
9140 :
9141 11 : int free = pheader->read_pointer - pheader->write_pointer;
9142 11 : if (free <= 0)
9143 11 : free += pheader->size;
9144 :
9145 : //printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec);
9146 :
9147 11 : if (requested_space < free) { /* note the '<' to avoid 100% filling */
9148 : //if (blocking_loops) {
9149 : // DWORD wait_time = ss_millitime() - blocking_time;
9150 : // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9151 : //}
9152 :
9153 11 : if (pbuf->wait_start_time != 0) {
9154 0 : DWORD now = ss_millitime();
9155 0 : DWORD wait_time = now - pbuf->wait_start_time;
9156 0 : pbuf->time_write_wait += wait_time;
9157 0 : pbuf->wait_start_time = 0;
9158 0 : int iclient = pbuf->wait_client_index;
9159 : //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9160 0 : if (iclient >= 0 && iclient < MAX_CLIENTS) {
9161 0 : pbuf->client_count_write_wait[iclient] += 1;
9162 0 : pbuf->client_time_write_wait[iclient] += wait_time;
9163 : }
9164 : }
9165 :
9166 : //if (blocking_loops > 0) {
9167 : // printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d, found space after %d waits\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec, blocking_loops);
9168 : //}
9169 :
9170 11 : return BM_SUCCESS;
9171 : }
9172 :
9173 0 : if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9174 0 : cm_msg(MERROR, "bm_wait_for_free_space",
9175 : "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9176 0 : pheader->name,
9177 : pheader->read_pointer,
9178 : pheader->write_pointer,
9179 : pheader->size,
9180 : free,
9181 : requested_space);
9182 0 : return BM_CORRUPTED;
9183 : }
9184 :
9185 0 : const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9186 0 : int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9187 0 : int total_size = ALIGN8(event_size);
9188 :
9189 : #if 0
9190 : printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, blocking event size %d/%d\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, event_size, total_size);
9191 : #endif
9192 :
9193 0 : if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9194 0 : cm_msg(MERROR, "bm_wait_for_free_space",
9195 : "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer points to an invalid event: data_size %d, event size %d, total_size %d",
9196 0 : pheader->name,
9197 : pheader->read_pointer,
9198 : pheader->write_pointer,
9199 : pheader->size,
9200 : free,
9201 : requested_space,
9202 0 : pevent->data_size,
9203 : event_size,
9204 : total_size);
9205 0 : return BM_CORRUPTED;
9206 : }
9207 :
9208 0 : int blocking_client = -1;
9209 :
9210 : int i;
9211 0 : for (i = 0; i < pheader->max_client_index; i++) {
9212 0 : BUFFER_CLIENT *pc = pheader->client + i;
9213 0 : if (pc->pid) {
9214 0 : if (pc->read_pointer == pheader->read_pointer) {
9215 : /*
9216 : First assume that the client with the "minimum" read pointer
9217 : is not really blocking due to a GET_ALL request.
9218 : */
9219 0 : BOOL blocking = FALSE;
9220 : //int blocking_request_id = -1;
9221 :
9222 : int j;
9223 0 : for (j = 0; j < pc->max_request_index; j++) {
9224 0 : const EVENT_REQUEST *prequest = pc->event_request + j;
9225 0 : if (prequest->valid
9226 0 : && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9227 0 : if (prequest->sampling_type & GET_ALL) {
9228 0 : blocking = TRUE;
9229 : //blocking_request_id = prequest->id;
9230 0 : break;
9231 : }
9232 : }
9233 : }
9234 :
9235 : //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9236 :
9237 0 : if (blocking) {
9238 0 : blocking_client = i;
9239 0 : break;
9240 : }
9241 :
9242 0 : pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9243 : }
9244 : }
9245 : } /* client loop */
9246 :
9247 0 : if (blocking_client >= 0) {
9248 0 : blocking_client_index = blocking_client;
9249 0 : mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
9250 : //if (!blocking_time) {
9251 : // blocking_time = ss_millitime();
9252 : //}
9253 :
9254 : //printf("bm_wait_for_free_space: buffer pointers: read: %d, write: %d, free space: %d, bufsize: %d, event size: %d, timeout %d, must wait for more space!\n", pheader->read_pointer, pheader->write_pointer, free, pheader->size, requested_space, timeout_msec);
9255 :
9256 : // from this "break" we go into timeout check and sleep/wait.
9257 0 : break;
9258 : }
9259 :
9260 : /* no blocking clients. move the read pointer and again check for free space */
9261 :
9262 0 : BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9263 :
9264 0 : if (!moved) {
9265 0 : cm_msg(MERROR, "bm_wait_for_free_space",
9266 : "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer did not move as expected",
9267 0 : pheader->name,
9268 : pheader->read_pointer,
9269 : pheader->write_pointer,
9270 : pheader->size,
9271 : free,
9272 : requested_space);
9273 0 : return BM_CORRUPTED;
9274 : }
9275 :
9276 : /* we freed one event, loop back to the check for free space */
9277 0 : }
9278 :
9279 : //blocking_loops++;
9280 :
9281 : /* at least one client is blocking */
9282 :
9283 0 : BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9284 0 : pc->write_wait = requested_space;
9285 :
9286 0 : if (pbuf->wait_start_time == 0) {
9287 0 : pbuf->wait_start_time = ss_millitime();
9288 0 : pbuf->count_write_wait++;
9289 0 : if (requested_space > pbuf->max_requested_space)
9290 0 : pbuf->max_requested_space = requested_space;
9291 0 : pbuf->wait_client_index = blocking_client_index;
9292 : }
9293 :
9294 0 : DWORD now = ss_millitime();
9295 :
9296 : //printf("bm_wait_for_free_space: start 0x%08x, now 0x%08x, end 0x%08x, timeout %d, wait %d\n", time_start, now, time_end, timeout_msec, time_end - now);
9297 :
9298 0 : int sleep_time_msec = 1000;
9299 :
9300 0 : if (timeout_msec == BM_WAIT) {
9301 : // wait forever
9302 0 : } else if (timeout_msec == BM_NO_WAIT) {
9303 : // no wait
9304 0 : return BM_ASYNC_RETURN;
9305 : } else {
9306 : // check timeout
9307 0 : if (now >= time_end) {
9308 : // timeout!
9309 0 : return BM_ASYNC_RETURN;
9310 : }
9311 :
9312 0 : sleep_time_msec = time_end - now;
9313 :
9314 0 : if (sleep_time_msec <= 0) {
9315 0 : sleep_time_msec = 10;
9316 0 : } else if (sleep_time_msec > 1000) {
9317 0 : sleep_time_msec = 1000;
9318 : }
9319 : }
9320 :
9321 0 : ss_suspend_get_buffer_port(ss_gettid(), &pc->port);
9322 :
9323 : /* before waiting, unlock everything in the correct order */
9324 :
9325 0 : pbuf_guard.unlock();
9326 :
9327 0 : if (unlock_write_cache)
9328 0 : pbuf->write_cache_mutex.unlock();
9329 :
9330 : //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9331 :
9332 : #ifdef DEBUG_MSG
9333 : cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9334 : #endif
9335 :
9336 : ///* signal other clients wait mode */
9337 : //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9338 : //if (idx >= 0)
9339 : // pheader->client[idx].write_wait = requested_space;
9340 :
9341 : //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9342 :
9343 0 : status = ss_suspend(sleep_time_msec, MSG_BM);
9344 :
9345 : /* we are told to shutdown */
9346 0 : if (status == SS_ABORT) {
9347 : // NB: buffer is locked!
9348 0 : return SS_ABORT;
9349 : }
9350 :
9351 : /* make sure we do sleep in this loop:
9352 : * if we are the mserver receiving data on the event
9353 : * socket and the data buffer is full, ss_suspend() will
9354 : * never sleep: it will detect data on the event channel,
9355 : * call rpc_server_receive() (recursively, we already *are* in
9356 : * rpc_server_receive()) and return without sleeping. Result
9357 : * is a busy loop waiting for free space in data buffer */
9358 :
9359 : /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9360 : * the event socket, and should sleep now, so this sleep below
9361 : * maybe is not needed now. but for safety, I keep it. K.O. */
9362 :
9363 0 : if (status != SS_TIMEOUT) {
9364 : //printf("ss_suspend: status %d\n", status);
9365 0 : ss_sleep(1);
9366 : }
9367 :
9368 : /* we may be stuck in this loop for an arbitrary long time,
9369 : * depending on how other buffer clients read the accumulated data
9370 : * so we should update all the timeouts & etc. K.O. */
9371 :
9372 0 : cm_periodic_tasks();
9373 :
9374 : /* lock things again in the correct order */
9375 :
9376 0 : if (unlock_write_cache) {
9377 0 : status = bm_lock_buffer_write_cache(pbuf);
9378 :
9379 0 : if (status != BM_SUCCESS) {
9380 : // bail out with all locks released
9381 0 : return status;
9382 : }
9383 : }
9384 :
9385 0 : if (!pbuf_guard.relock()) {
9386 0 : if (unlock_write_cache) {
9387 0 : pbuf->write_cache_mutex.unlock();
9388 : }
9389 :
9390 : // bail out with all locks released
9391 0 : return pbuf_guard.get_status();
9392 : }
9393 :
9394 : /* revalidate the client index: we could have been removed from the buffer while sleeping */
9395 0 : pc = bm_get_my_client_locked(pbuf_guard);
9396 :
9397 0 : pc->write_wait = 0;
9398 :
9399 : ///* validate client index: we could have been removed from the buffer */
9400 : //idx = bm_validate_client_index_locked(pbuf, FALSE);
9401 : //if (idx >= 0)
9402 : // pheader->client[idx].write_wait = 0;
9403 : //else {
9404 : // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9405 : // status = SS_ABORT;
9406 : //}
9407 :
9408 : #ifdef DEBUG_MSG
9409 : cm_msg(MDEBUG, "Send woke up: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9410 : #endif
9411 :
9412 0 : }
9413 : }
9414 :
9415 0 : static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
9416 : {
9417 0 : BUFFER* pbuf = pbuf_guard.get_pbuf();
9418 0 : BUFFER_HEADER* pheader = pbuf->buffer_header;
9419 :
9420 : //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9421 :
9422 0 : if (pc->read_pointer != pheader->write_pointer) {
9423 : // buffer has data
9424 0 : return BM_SUCCESS;
9425 : }
9426 :
9427 0 : if (timeout_msec == BM_NO_WAIT) {
9428 : /* event buffer is empty and we are told to not wait */
9429 0 : if (!pc->read_wait) {
9430 : //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9431 0 : pc->read_wait = TRUE;
9432 : }
9433 0 : return BM_ASYNC_RETURN;
9434 : }
9435 :
9436 0 : DWORD time_start = ss_millitime();
9437 0 : DWORD time_wait = time_start + timeout_msec;
9438 0 : DWORD sleep_time = 1000;
9439 0 : if (timeout_msec == BM_NO_WAIT) {
9440 : // default sleep time
9441 0 : } else if (timeout_msec == BM_WAIT) {
9442 : // default sleep time
9443 : } else {
9444 0 : if (sleep_time > (DWORD)timeout_msec)
9445 0 : sleep_time = timeout_msec;
9446 : }
9447 :
9448 : //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9449 :
9450 0 : while (pc->read_pointer == pheader->write_pointer) {
9451 : /* wait until there is data in the buffer (write pointer moves) */
9452 :
9453 0 : if (!pc->read_wait) {
9454 : //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9455 0 : pc->read_wait = TRUE;
9456 : }
9457 :
9458 0 : pc->last_activity = ss_millitime();
9459 :
9460 0 : ss_suspend_get_buffer_port(ss_gettid(), &pc->port);
9461 :
9462 : // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9463 :
9464 0 : pbuf_guard.unlock();
9465 :
9466 0 : if (unlock_read_cache)
9467 0 : pbuf->read_cache_mutex.unlock();
9468 :
9469 0 : int status = ss_suspend(sleep_time, MSG_BM);
9470 :
9471 0 : if (timeout_msec == BM_NO_WAIT) {
9472 : // return immediately
9473 0 : } else if (timeout_msec == BM_WAIT) {
9474 : // wait forever
9475 : } else {
9476 0 : DWORD now = ss_millitime();
9477 : //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9478 0 : if (now >= time_wait) {
9479 0 : timeout_msec = BM_NO_WAIT; // cause immediate return
9480 : } else {
9481 0 : sleep_time = time_wait - now;
9482 0 : if (sleep_time > 1000)
9483 0 : sleep_time = 1000;
9484 : //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9485 : }
9486 : }
9487 :
9488 : // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9489 :
9490 0 : if (unlock_read_cache) {
9491 0 : status = bm_lock_buffer_read_cache(pbuf);
9492 0 : if (status != BM_SUCCESS) {
9493 : // bail out with all locks released
9494 0 : return status;
9495 : }
9496 : }
9497 :
9498 0 : if (!pbuf_guard.relock()) {
9499 0 : if (unlock_read_cache) {
9500 0 : pbuf->read_cache_mutex.unlock();
9501 : }
9502 : // bail out with all locks released
9503 0 : return pbuf_guard.get_status();
9504 : }
9505 :
9506 : /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9507 : * because we may have been removed from the buffer by bm_cleanup() & co
9508 : * due to a timeout or whatever. */
9509 0 : pc = bm_get_my_client_locked(pbuf_guard);
9510 :
9511 : /* return if TCP connection broken */
9512 0 : if (status == SS_ABORT)
9513 0 : return SS_ABORT;
9514 :
9515 0 : if (timeout_msec == BM_NO_WAIT)
9516 0 : return BM_ASYNC_RETURN;
9517 : }
9518 :
9519 0 : if (pc->read_wait) {
9520 : //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9521 0 : pc->read_wait = FALSE;
9522 : }
9523 :
9524 0 : return BM_SUCCESS;
9525 : }
9526 :
9527 11 : static void bm_write_to_buffer_locked(BUFFER_HEADER *pheader, int sg_n, const char* const sg_ptr[], const size_t sg_len[], size_t total_size)
9528 : {
9529 11 : char *pdata = (char *) (pheader + 1);
9530 :
9531 : //int old_write_pointer = pheader->write_pointer;
9532 :
9533 : /* new event fits into the remaining space? */
9534 11 : if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9535 : //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9536 11 : char* wptr = pdata + pheader->write_pointer;
9537 22 : for (int i=0; i<sg_n; i++) {
9538 : //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9539 11 : memcpy(wptr, sg_ptr[i], sg_len[i]);
9540 11 : wptr += sg_len[i];
9541 : }
9542 11 : pheader->write_pointer = pheader->write_pointer + total_size;
9543 11 : assert(pheader->write_pointer <= pheader->size);
9544 : /* remaining space is smaller than size of an event header? */
9545 11 : if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9546 : // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9547 : // equal to the event header size, we will write the next event header here,
9548 : // then wrap the pointer and write the event data at the beginning of the buffer.
9549 : //printf("bm_write_to_buffer_locked: truncate wp %d. buffer size %d, remaining %d, event header size %d, event size %d, total size %d\n", pheader->write_pointer, pheader->size, pheader->size-pheader->write_pointer, (int)sizeof(EVENT_HEADER), event_size, total_size);
9550 0 : pheader->write_pointer = 0;
9551 : }
9552 : } else {
9553 : /* split event */
9554 0 : size_t size = pheader->size - pheader->write_pointer;
9555 :
9556 : //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9557 :
9558 : //memcpy(pdata + pheader->write_pointer, pevent, size);
9559 : //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9560 :
9561 0 : char* wptr = pdata + pheader->write_pointer;
9562 0 : size_t count = 0;
9563 :
9564 : // copy first part
9565 :
9566 0 : int i = 0;
9567 0 : for (; i<sg_n; i++) {
9568 0 : if (count + sg_len[i] > size)
9569 0 : break;
9570 0 : memcpy(wptr, sg_ptr[i], sg_len[i]);
9571 0 : wptr += sg_len[i];
9572 0 : count += sg_len[i];
9573 : }
9574 :
9575 : //printf("wptr %d, count %d\n", wptr-pdata, count);
9576 :
9577 : // split segment
9578 :
9579 0 : size_t first = size - count;
9580 0 : size_t second = sg_len[i] - first;
9581 0 : assert(first + second == sg_len[i]);
9582 0 : assert(count + first == size);
9583 :
9584 : //printf("first %d, second %d\n", first, second);
9585 :
9586 0 : memcpy(wptr, sg_ptr[i], first);
9587 0 : wptr = pdata + 0;
9588 0 : count += first;
9589 0 : memcpy(wptr, sg_ptr[i] + first, second);
9590 0 : wptr += second;
9591 0 : count += second;
9592 0 : i++;
9593 :
9594 : // copy remaining
9595 :
9596 0 : for (; i<sg_n; i++) {
9597 0 : memcpy(wptr, sg_ptr[i], sg_len[i]);
9598 0 : wptr += sg_len[i];
9599 0 : count += sg_len[i];
9600 : }
9601 :
9602 : //printf("wptr %d, count %d\n", wptr-pdata, count);
9603 :
9604 : //printf("bm_write_to_buffer_locked: wrap wp %d -> %d. buffer size %d, available %d, wrote %d, remaining %d, event size %d, total size %d\n", pheader->write_pointer, total_size-size, pheader->size, pheader->size-pheader->write_pointer, size, pheader->size - (pheader->write_pointer+size), event_size, total_size);
9605 :
9606 0 : pheader->write_pointer = total_size - size;
9607 : }
9608 :
9609 : //printf("bm_write_to_buffer_locked: buf [%s] size %d, wrote %d/%d, wp %d -> %d\n", pheader->name, pheader->size, event_size, total_size, old_write_pointer, pheader->write_pointer);
9610 11 : }
9611 :
9612 11 : static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
9613 11 : if (pc->pid) {
9614 : int j;
9615 11 : for (j = 0; j < pc->max_request_index; j++) {
9616 0 : const EVENT_REQUEST *prequest = pc->event_request + j;
9617 0 : if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9618 0 : return prequest->id;
9619 : }
9620 : }
9621 : }
9622 :
9623 11 : return -1;
9624 : }
9625 :
9626 11 : static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9627 11 : if (request_id >= 0) {
9628 : /* if that client has a request and is suspended, wake it up */
9629 0 : if (pc->read_wait) {
9630 : char str[80];
9631 0 : sprintf(str, "B %s %d", pheader->name, request_id);
9632 0 : ss_resume(pc->port, str);
9633 : //printf("bm_notify_reader_locked: buffer [%s] client [%s] request_id %d, port %d, message [%s]\n", pheader->name, pc->name, request_id, pc->port, str);
9634 : //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9635 0 : pc->read_wait = FALSE;
9636 : }
9637 : }
9638 11 : }
9639 :
9640 : #endif // LOCAL_ROUTINES
9641 :
9642 : #if 0
9643 : INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9644 : {
9645 : //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9646 :
9647 : DWORD time_start = ss_millitime();
9648 : DWORD time_end = time_start + timeout_msec;
9649 :
9650 : int xtimeout_msec = timeout_msec;
9651 :
9652 : while (1) {
9653 : if (timeout_msec == BM_WAIT) {
9654 : xtimeout_msec = 1000;
9655 : } else if (timeout_msec == BM_NO_WAIT) {
9656 : xtimeout_msec = BM_NO_WAIT;
9657 : } else {
9658 : if (xtimeout_msec > 1000) {
9659 : xtimeout_msec = 1000;
9660 : }
9661 : }
9662 :
9663 : int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9664 :
9665 : //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9666 :
9667 : if (status == BM_ASYNC_RETURN) {
9668 : if (timeout_msec == BM_WAIT) {
9669 : // BM_WAIT means wait forever
9670 : continue;
9671 : } else if (timeout_msec == BM_NO_WAIT) {
9672 : // BM_NO_WAIT means do not wait
9673 : return status;
9674 : } else {
9675 : DWORD now = ss_millitime();
9676 : if (now >= time_end) {
9677 : // timeout, return BM_ASYNC_RETURN
9678 : return status;
9679 : }
9680 :
9681 : DWORD remain = time_end - now;
9682 :
9683 : if (remain < xtimeout_msec) {
9684 : xtimeout_msec = remain;
9685 : }
9686 :
9687 : // keep asking for event...
9688 : continue;
9689 : }
9690 : } else if (status == BM_SUCCESS) {
9691 : // success, return BM_SUCCESS
9692 : return status;
9693 : } else {
9694 : // error
9695 : return status;
9696 : }
9697 : }
9698 : }
9699 : #endif
9700 :
9701 11 : INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9702 : {
9703 11 : const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9704 11 : const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9705 :
9706 11 : if (data_size == 0) {
9707 0 : cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9708 0 : return BM_INVALID_SIZE;
9709 : }
9710 :
9711 11 : if (data_size > MAX_DATA_SIZE) {
9712 0 : cm_msg(MERROR, "bm_send_event", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
9713 0 : return BM_INVALID_SIZE;
9714 : }
9715 :
9716 11 : const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9717 :
9718 : //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9719 :
9720 11 : if (rpc_is_remote()) {
9721 : //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9722 0 : return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9723 : } else {
9724 11 : return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9725 : }
9726 : }
9727 :
9728 0 : int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9729 : {
9730 0 : const char* cptr = event.data();
9731 0 : size_t clen = event.size();
9732 0 : return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9733 : }
9734 :
9735 0 : int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9736 : {
9737 0 : int sg_n = event.size();
9738 0 : const char* sg_ptr[sg_n];
9739 0 : size_t sg_len[sg_n];
9740 0 : for (int i=0; i<sg_n; i++) {
9741 0 : sg_ptr[i] = event[i].data();
9742 0 : sg_len[i] = event[i].size();
9743 : }
9744 0 : return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9745 0 : }
9746 :
9747 : #ifdef LOCAL_ROUTINES
9748 : static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
9749 : #endif
9750 :
9751 : /********************************************************************/
9752 : /**
9753 : Sends an event to a buffer.
9754 : This function check if the buffer has enough space for the
9755 : event, then copies the event to the buffer in shared memory.
9756 : If clients have requests for the event, they are notified via an UDP packet.
9757 : \code
9758 : char event[1000];
9759 : // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
9760 : bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
9761 :
9762 : // set first byte of event
9763 : *(event+sizeof(EVENT_HEADER)) = <...>
9764 : #include <stdio.h>
9765 : #include "midas.h"
9766 : main()
9767 : {
9768 : INT status, i;
9769 : HNDLE hbuf;
9770 : char event[1000];
9771 : status = cm_connect_experiment("", "Sample", "Producer", NULL);
9772 : if (status != CM_SUCCESS)
9773 : return 1;
9774 : bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
9775 :
9776 : // create event with ID 1, trigger mask 0, size 100 bytes and serial number 1
9777 : bm_compose_event((EVENT_HEADER *) event, 1, 0, 100, 1);
9778 :
9779 : // set event data
9780 : for (i=0 ; i<100 ; i++)
9781 : *(event+sizeof(EVENT_HEADER)+i) = i;
9782 : // send event
9783 : bm_send_event(hbuf, event, 100+sizeof(EVENT_HEADER), BM_WAIT);
9784 : cm_disconnect_experiment();
9785 : return 0;
9786 : }
9787 : \endcode
9788 : @param buffer_handle Buffer handle obtained via bm_open_buffer()
9789 : @param source Address of event buffer
9790 : @param buf_size Size of event including event header in bytes
9791 : @param timeout_msec Timeout waiting for free space in the event buffer. If BM_WAIT, wait forever.
9792 : If BM_NO_WAIT, the function returns immediately with a
9793 : value of BM_ASYNC_RETURN without writing the event to the buffer
9794 : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_INVALID_PARAM<br>
9795 : BM_ASYNC_RETURN Routine called with timeout_msec == BM_NO_WAIT and
9796 : buffer has not enough space to receive event<br>
9797 : BM_NO_MEMORY Event is too large for network buffer or event buffer.
9798 : One has to increase the event buffer size "/Experiment/Buffer sizes/SYSTEM"
9799 : and/or /Experiment/MAX_EVENT_SIZE in ODB.
9800 : */
9801 11 : int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9802 : {
9803 11 : if (rpc_is_remote())
9804 0 : return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9805 :
9806 11 : if (sg_n < 1) {
9807 0 : cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9808 0 : return BM_INVALID_SIZE;
9809 : }
9810 :
9811 11 : if (sg_ptr[0] == NULL) {
9812 0 : cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9813 0 : return BM_INVALID_SIZE;
9814 : }
9815 :
9816 11 : if (sg_len[0] < sizeof(EVENT_HEADER)) {
9817 0 : cm_msg(MERROR, "bm_send_event", "invalid sg_len[0] value %d is smaller than event header size %d", (int)sg_len[0], (int)sizeof(EVENT_HEADER));
9818 0 : return BM_INVALID_SIZE;
9819 : }
9820 :
9821 11 : const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9822 :
9823 11 : const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9824 11 : const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9825 :
9826 11 : if (data_size == 0) {
9827 0 : cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9828 0 : return BM_INVALID_SIZE;
9829 : }
9830 :
9831 11 : if (data_size > MAX_DATA_SIZE) {
9832 0 : cm_msg(MERROR, "bm_send_event", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
9833 0 : return BM_INVALID_SIZE;
9834 : }
9835 :
9836 11 : const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9837 :
9838 11 : size_t count = 0;
9839 22 : for (int i=0; i<sg_n; i++) {
9840 11 : count += sg_len[i];
9841 : }
9842 :
9843 11 : if (count != event_size) {
9844 0 : cm_msg(MERROR, "bm_send_event", "data size mismatch: event data_size %d, event_size %d not same as sum of sg_len %d", (int)data_size, (int)event_size, (int)count);
9845 0 : return BM_INVALID_SIZE;
9846 : }
9847 :
9848 : //printf("bm_send_event_sg: pevent %p, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", pevent, pevent->event_id, pevent->serial_number, (int)pevent->data_size, (int)event_size, (int)total_size);
9849 :
9850 : #ifdef LOCAL_ROUTINES
9851 : {
9852 11 : int status = 0;
9853 11 : const size_t total_size = ALIGN8(event_size);
9854 :
9855 11 : BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9856 :
9857 11 : if (!pbuf)
9858 0 : return status;
9859 :
9860 : /* round up total_size to next DWORD boundary */
9861 : //int total_size = ALIGN8(event_size);
9862 :
9863 : /* check if write cache is enabled */
9864 11 : if (pbuf->write_cache_size) {
9865 0 : status = bm_lock_buffer_write_cache(pbuf);
9866 :
9867 0 : if (status != BM_SUCCESS)
9868 0 : return status;
9869 :
9870 : /* check if write cache is enabled */
9871 0 : if (pbuf->write_cache_size) {
9872 0 : size_t max_event_size = pbuf->write_cache_size/MAX_WRITE_CACHE_EVENT_SIZE_DIV;
9873 0 : bool too_big = event_size > max_event_size;
9874 :
9875 : //printf("bm_send_event: write %zu/%zu max %zu, cache size %zu, wp %zu\n", event_size, total_size, max_event_size, pbuf->write_cache_size.load(), pbuf->write_cache_wp);
9876 :
9877 : /* if this event does not fit into the write cache, flush the write cache */
9878 0 : if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9879 : //printf("bm_send_event: write %zu/%zu but cache is full, size %zu, wp %zu\n", event_size, total_size, pbuf->write_cache_size.load(), pbuf->write_cache_wp);
9880 :
9881 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
9882 :
9883 0 : if (!pbuf_guard.is_locked()) {
9884 0 : pbuf->write_cache_mutex.unlock();
9885 0 : return pbuf_guard.get_status();
9886 : }
9887 :
9888 0 : int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
9889 :
9890 0 : if (pbuf_guard.is_locked()) {
9891 : // check if bm_wait_for_free_space() failed to relock the buffer
9892 0 : pbuf_guard.unlock();
9893 : }
9894 :
9895 0 : if (status != BM_SUCCESS) {
9896 0 : pbuf->write_cache_mutex.unlock();
9897 : // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9898 0 : if (status == BM_NO_MEMORY)
9899 0 : cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9900 0 : return status;
9901 : }
9902 :
9903 : // write cache must be empty here
9904 0 : assert(pbuf->write_cache_wp == 0);
9905 0 : }
9906 :
9907 : /* write this event into the write cache, if it is not too big and if it fits */
9908 0 : if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9909 : //printf("bm_send_event: write %d/%d to cache size %d, wp %d\n", (int)event_size, (int)total_size, (int)pbuf->write_cache_size, (int)pbuf->write_cache_wp);
9910 :
9911 0 : char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9912 :
9913 0 : for (int i=0; i<sg_n; i++) {
9914 0 : memcpy(wptr, sg_ptr[i], sg_len[i]);
9915 0 : wptr += sg_len[i];
9916 : }
9917 :
9918 0 : pbuf->write_cache_wp += total_size;
9919 :
9920 0 : pbuf->write_cache_mutex.unlock();
9921 0 : return BM_SUCCESS;
9922 : }
9923 : }
9924 :
9925 : /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9926 0 : pbuf->write_cache_mutex.unlock();
9927 : }
9928 :
9929 : /* we come here only for events that are too big to fit into the cache */
9930 :
9931 : /* lock the buffer */
9932 11 : bm_lock_buffer_guard pbuf_guard(pbuf);
9933 :
9934 11 : if (!pbuf_guard.is_locked()) {
9935 0 : return pbuf_guard.get_status();
9936 : }
9937 :
9938 : /* calculate some shorthands */
9939 11 : BUFFER_HEADER *pheader = pbuf->buffer_header;
9940 :
9941 : #if 0
9942 : status = bm_validate_buffer_locked(pbuf);
9943 : if (status != BM_SUCCESS) {
9944 : printf("bm_send_event: corrupted 111!\n");
9945 : abort();
9946 : }
9947 : #endif
9948 :
9949 : /* check if buffer is large enough */
9950 11 : if (total_size >= (size_t)pheader->size) {
9951 0 : pbuf_guard.unlock(); // unlock before cm_msg()
9952 0 : cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9953 0 : return BM_NO_MEMORY;
9954 : }
9955 :
9956 11 : status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
9957 :
9958 11 : if (status != BM_SUCCESS) {
9959 : // implicit unlock
9960 0 : return status;
9961 : }
9962 :
9963 : #if 0
9964 : status = bm_validate_buffer_locked(pbuf);
9965 : if (status != BM_SUCCESS) {
9966 : printf("bm_send_event: corrupted 222!\n");
9967 : abort();
9968 : }
9969 : #endif
9970 :
9971 11 : int old_write_pointer = pheader->write_pointer;
9972 :
9973 11 : bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9974 :
9975 : /* write pointer was incremented, but there should
9976 : * always be some free space in the buffer and the
9977 : * write pointer should never cacth up to the read pointer:
9978 : * the rest of the code gets confused this happens (buffer 100% full)
9979 : * as it is write_pointer == read_pointer can be either
9980 : * 100% full or 100% empty. My solution: never fill
9981 : * the buffer to 100% */
9982 11 : assert(pheader->write_pointer != pheader->read_pointer);
9983 :
9984 : /* send wake up messages to all clients that want this event */
9985 : int i;
9986 22 : for (i = 0; i < pheader->max_client_index; i++) {
9987 11 : BUFFER_CLIENT *pc = pheader->client + i;
9988 11 : int request_id = bm_find_first_request_locked(pc, pevent);
9989 11 : bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9990 : }
9991 :
9992 : #if 0
9993 : status = bm_validate_buffer_locked(pbuf);
9994 : if (status != BM_SUCCESS) {
9995 : printf("bm_send_event: corrupted 333!\n");
9996 : abort();
9997 : }
9998 : #endif
9999 :
10000 : /* update statistics */
10001 11 : pheader->num_in_events++;
10002 11 : pbuf->count_sent += 1;
10003 11 : pbuf->bytes_sent += total_size;
10004 11 : }
10005 : #endif /* LOCAL_ROUTINES */
10006 :
10007 11 : return BM_SUCCESS;
10008 : }
10009 :
10010 0 : static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
10011 : {
10012 : //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10013 :
10014 0 : DWORD time_start = ss_millitime();
10015 0 : DWORD time_end = time_start + timeout_msec;
10016 :
10017 0 : int xtimeout_msec = timeout_msec;
10018 :
10019 : while (1) {
10020 0 : if (timeout_msec == BM_WAIT) {
10021 0 : xtimeout_msec = 1000;
10022 0 : } else if (timeout_msec == BM_NO_WAIT) {
10023 0 : xtimeout_msec = BM_NO_WAIT;
10024 : } else {
10025 0 : if (xtimeout_msec > 1000) {
10026 0 : xtimeout_msec = 1000;
10027 : }
10028 : }
10029 :
10030 0 : int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10031 :
10032 : //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10033 :
10034 0 : if (status == BM_ASYNC_RETURN) {
10035 0 : if (timeout_msec == BM_WAIT) {
10036 : // BM_WAIT means wait forever
10037 0 : continue;
10038 0 : } else if (timeout_msec == BM_NO_WAIT) {
10039 : // BM_NO_WAIT means do not wait
10040 0 : return status;
10041 : } else {
10042 0 : DWORD now = ss_millitime();
10043 0 : if (now >= time_end) {
10044 : // timeout, return BM_ASYNC_RETURN
10045 0 : return status;
10046 : }
10047 :
10048 0 : DWORD remain = time_end - now;
10049 :
10050 0 : if (remain < (DWORD)xtimeout_msec) {
10051 0 : xtimeout_msec = remain;
10052 : }
10053 :
10054 : // keep asking for event...
10055 0 : continue;
10056 0 : }
10057 0 : } else if (status == BM_SUCCESS) {
10058 : // success, return BM_SUCCESS
10059 0 : return status;
10060 : } else {
10061 : // error
10062 0 : return status;
10063 : }
10064 0 : }
10065 : }
10066 :
10067 : /********************************************************************/
10068 : /**
10069 : Empty write cache.
10070 : This function should be used if events in the write cache
10071 : should be visible to the consumers immediately. It should be called at the
10072 : end of each run, otherwise events could be kept in the write buffer and will
10073 : flow to the data of the next run.
10074 : @param buffer_handle Buffer handle obtained via bm_open_buffer() or 0 to flush data in the mserver event socket
10075 : @param timeout_msec Timeout waiting for free space in the event buffer.
10076 : If BM_WAIT, wait forever. If BM_NO_WAIT, the function returns
10077 : immediately with a value of BM_ASYNC_RETURN without writing the cache.
10078 : @return BM_SUCCESS, BM_INVALID_HANDLE<br>
10079 : BM_ASYNC_RETURN Routine called with async_flag == BM_NO_WAIT
10080 : and buffer has not enough space to receive cache<br>
10081 : BM_NO_MEMORY Event is too large for network buffer or event buffer.
10082 : One has to increase the event buffer size "/Experiment/Buffer sizes/SYSTEM"
10083 : and/or /Experiment/MAX_EVENT_SIZE in ODB.
10084 : */
10085 : #ifdef LOCAL_ROUTINES
10086 0 : static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
10087 : {
10088 : // NB we come here with write cache locked and buffer locked.
10089 :
10090 : {
10091 0 : INT status = 0;
10092 :
10093 : //printf("bm_flush_cache_locked!\n");
10094 :
10095 0 : BUFFER* pbuf = pbuf_guard.get_pbuf();
10096 0 : BUFFER_HEADER* pheader = pbuf->buffer_header;
10097 :
10098 : //printf("bm_flush_cache_locked: buffer %s, cache rp %zu, wp %zu, timeout %d msec\n", pbuf->buffer_name, pbuf->write_cache_rp, pbuf->write_cache_wp, timeout_msec);
10099 :
10100 0 : int old_write_pointer = pheader->write_pointer;
10101 :
10102 : int request_id[MAX_CLIENTS];
10103 0 : for (int i = 0; i < pheader->max_client_index; i++) {
10104 0 : request_id[i] = -1;
10105 : }
10106 :
10107 0 : size_t ask_rp = pbuf->write_cache_rp;
10108 0 : size_t ask_wp = pbuf->write_cache_wp;
10109 :
10110 0 : if (ask_wp == 0) { // nothing to do
10111 0 : return BM_SUCCESS;
10112 : }
10113 :
10114 0 : if (ask_rp == ask_wp) { // nothing to do
10115 0 : return BM_SUCCESS;
10116 : }
10117 :
10118 0 : assert(ask_rp < ask_wp);
10119 :
10120 0 : size_t ask_free = ALIGN8(ask_wp - ask_rp);
10121 :
10122 0 : if (ask_free == 0) { // nothing to do
10123 0 : return BM_SUCCESS;
10124 : }
10125 :
10126 : #if 0
10127 : status = bm_validate_buffer_locked(pbuf);
10128 : if (status != BM_SUCCESS) {
10129 : printf("bm_flush_cache: corrupted 111!\n");
10130 : abort();
10131 : }
10132 : #endif
10133 :
10134 0 : status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
10135 :
10136 0 : if (status != BM_SUCCESS) {
10137 0 : return status;
10138 : }
10139 :
10140 : // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10141 : //
10142 : // wait_for_free_space() will sleep with all locks released,
10143 : // during this time, another thread may call bm_send_event() that will
10144 : // add one or more events to the write cache and after wait_for_free_space()
10145 : // returns, size of data in cache will be bigger than the amount
10146 : // of free space we requested. so we need to keep track of how
10147 : // much data we write to the buffer and ask for more data
10148 : // if we run short. This is the reason for the big loop
10149 : // around wait_for_free_space(). We ask for slightly too little free
10150 : // space to make sure all this code is always used and does work. K.O.
10151 :
10152 0 : if (pbuf->write_cache_wp == 0) {
10153 : /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10154 0 : return BM_SUCCESS;
10155 : }
10156 :
10157 : //size_t written = 0;
10158 0 : while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10159 : /* loop over all events in cache */
10160 :
10161 0 : const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10162 0 : size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10163 0 : size_t total_size = ALIGN8(event_size);
10164 :
10165 : #if 0
10166 : printf("bm_flush_cache: cache size %d, wp %d, rp %d, event data_size %d, event_size %d, total_size %d, free %d, written %d\n",
10167 : int(pbuf->write_cache_size),
10168 : int(pbuf->write_cache_wp),
10169 : int(pbuf->write_cache_rp),
10170 : int(pevent->data_size),
10171 : int(event_size),
10172 : int(total_size),
10173 : int(ask_free),
10174 : int(written));
10175 : #endif
10176 :
10177 : // check for crazy event size
10178 0 : assert(total_size >= sizeof(EVENT_HEADER));
10179 0 : assert(total_size <= (size_t)pheader->size);
10180 :
10181 0 : bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10182 :
10183 : /* update statistics */
10184 0 : pheader->num_in_events++;
10185 0 : pbuf->count_sent += 1;
10186 0 : pbuf->bytes_sent += total_size;
10187 :
10188 : /* see comment for the same code in bm_send_event().
10189 : * We make sure the buffer is never 100% full */
10190 0 : assert(pheader->write_pointer != pheader->read_pointer);
10191 :
10192 : /* check if anybody has a request for this event */
10193 0 : for (int i = 0; i < pheader->max_client_index; i++) {
10194 0 : BUFFER_CLIENT *pc = pheader->client + i;
10195 0 : int r = bm_find_first_request_locked(pc, pevent);
10196 0 : if (r >= 0) {
10197 0 : request_id[i] = r;
10198 : }
10199 : }
10200 :
10201 : /* this loop does not loop forever because rp
10202 : * is monotonously incremented here. write_cache_wp does
10203 : * not change */
10204 :
10205 0 : pbuf->write_cache_rp += total_size;
10206 : //written += total_size;
10207 :
10208 0 : assert(pbuf->write_cache_rp > 0);
10209 0 : assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10210 0 : assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10211 : }
10212 :
10213 : /* the write cache is now empty */
10214 0 : assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10215 0 : pbuf->write_cache_wp = 0;
10216 0 : pbuf->write_cache_rp = 0;
10217 :
10218 : /* check which clients are waiting */
10219 0 : for (int i = 0; i < pheader->max_client_index; i++) {
10220 0 : BUFFER_CLIENT *pc = pheader->client + i;
10221 0 : bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10222 : }
10223 : }
10224 :
10225 0 : return BM_SUCCESS;
10226 : }
10227 :
10228 : #endif /* LOCAL_ROUTINES */
10229 :
10230 0 : INT bm_flush_cache(int buffer_handle, int timeout_msec)
10231 : {
10232 0 : if (rpc_is_remote()) {
10233 0 : return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10234 : }
10235 :
10236 : #ifdef LOCAL_ROUTINES
10237 : {
10238 0 : INT status = 0;
10239 :
10240 : //printf("bm_flush_cache!\n");
10241 :
10242 0 : BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10243 :
10244 0 : if (!pbuf)
10245 0 : return status;
10246 :
10247 0 : if (pbuf->write_cache_size == 0)
10248 0 : return BM_SUCCESS;
10249 :
10250 0 : status = bm_lock_buffer_write_cache(pbuf);
10251 :
10252 0 : if (status != BM_SUCCESS)
10253 0 : return status;
10254 :
10255 : /* check if anything needs to be flushed */
10256 0 : if (pbuf->write_cache_wp == 0) {
10257 0 : pbuf->write_cache_mutex.unlock();
10258 0 : return BM_SUCCESS;
10259 : }
10260 :
10261 : /* lock the buffer */
10262 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
10263 :
10264 0 : if (!pbuf_guard.is_locked())
10265 0 : return pbuf_guard.get_status();
10266 :
10267 0 : status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
10268 :
10269 : /* unlock in correct order */
10270 :
10271 0 : if (pbuf_guard.is_locked()) {
10272 : // check if bm_wait_for_free_space() failed to relock the buffer
10273 0 : pbuf_guard.unlock();
10274 : }
10275 :
10276 0 : pbuf->write_cache_mutex.unlock();
10277 :
10278 0 : return status;
10279 0 : }
10280 : #endif /* LOCAL_ROUTINES */
10281 :
10282 : return BM_SUCCESS;
10283 : }
10284 :
10285 : #ifdef LOCAL_ROUTINES
10286 :
10287 0 : static INT bm_read_buffer(BUFFER *pbuf, INT buffer_handle, void **bufptr, void *buf, INT *buf_size, std::vector<char> *vecptr, int timeout_msec, int convert_flags, BOOL dispatch) {
10288 0 : INT status = BM_SUCCESS;
10289 :
10290 0 : int max_size = 0;
10291 0 : if (buf_size) {
10292 0 : max_size = *buf_size;
10293 0 : *buf_size = 0;
10294 : }
10295 :
10296 : //printf("bm_read_buffer: [%s] timeout %d, conv %d, ptr %p, buf %p, disp %d\n", pbuf->buffer_name, timeout_msec, convert_flags, bufptr, buf, dispatch);
10297 :
10298 0 : bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10299 :
10300 : // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10301 :
10302 : /* look if there is anything in the cache */
10303 0 : if (pbuf->read_cache_size > 0) {
10304 :
10305 0 : status = bm_lock_buffer_read_cache(pbuf);
10306 :
10307 0 : if (status != BM_SUCCESS)
10308 0 : return status;
10309 :
10310 0 : if (pbuf->read_cache_wp == 0) {
10311 :
10312 : // lock buffer for the first time
10313 :
10314 0 : if (!pbuf_guard.relock()) {
10315 0 : pbuf->read_cache_mutex.unlock();
10316 0 : return pbuf_guard.get_status();
10317 : }
10318 :
10319 0 : status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
10320 0 : if (status != BM_SUCCESS) {
10321 : // unlock in correct order
10322 0 : if (pbuf_guard.is_locked()) {
10323 : // check if bm_wait_for_more_events() failed to relock the buffer
10324 0 : pbuf_guard.unlock();
10325 : }
10326 0 : pbuf->read_cache_mutex.unlock();
10327 0 : return status;
10328 : }
10329 :
10330 : // buffer remains locked here
10331 : }
10332 : EVENT_HEADER *pevent;
10333 : int event_size;
10334 : int total_size;
10335 0 : if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10336 0 : if (pbuf_guard.is_locked()) {
10337 : // do not need to keep the event buffer locked
10338 : // when reading from the read cache
10339 0 : pbuf_guard.unlock();
10340 : }
10341 : //printf("bm_read_buffer: [%s] async %d, conv %d, ptr %p, buf %p, disp %d, total_size %d, read from cache %d %d %d\n", pbuf->buffer_name, async_flag, convert_flags, bufptr, buf, dispatch, total_size, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
10342 0 : status = BM_SUCCESS;
10343 0 : if (buf) {
10344 0 : if (event_size > max_size) {
10345 0 : cm_msg(MERROR, "bm_read_buffer", "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size, event_size, pbuf->buffer_name);
10346 0 : event_size = max_size;
10347 0 : status = BM_TRUNCATED;
10348 : }
10349 :
10350 0 : memcpy(buf, pevent, event_size);
10351 :
10352 0 : if (buf_size) {
10353 0 : *buf_size = event_size;
10354 : }
10355 0 : if (convert_flags) {
10356 0 : bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10357 : }
10358 0 : } else if (bufptr) {
10359 0 : *bufptr = malloc(event_size);
10360 0 : memcpy(*bufptr, pevent, event_size);
10361 0 : status = BM_SUCCESS;
10362 0 : } else if (vecptr) {
10363 0 : vecptr->resize(0);
10364 0 : char* cptr = (char*)pevent;
10365 0 : vecptr->assign(cptr, cptr+event_size);
10366 : }
10367 0 : bm_incr_read_cache_locked(pbuf, total_size);
10368 0 : pbuf->read_cache_mutex.unlock();
10369 0 : if (dispatch) {
10370 : // FIXME need to protect currently dispatched event against
10371 : // another thread overwriting it by refilling the read cache
10372 0 : bm_dispatch_event(buffer_handle, pevent);
10373 0 : return BM_MORE_EVENTS;
10374 : }
10375 : // buffer is unlocked here
10376 0 : return status;
10377 : }
10378 0 : pbuf->read_cache_mutex.unlock();
10379 : }
10380 :
10381 : /* we come here if the read cache is disabled */
10382 : /* we come here if the next event is too big to fit into the read cache */
10383 :
10384 0 : if (!pbuf_guard.is_locked()) {
10385 0 : if (!pbuf_guard.relock())
10386 0 : return pbuf_guard.get_status();
10387 : }
10388 :
10389 0 : EVENT_HEADER *event_buffer = NULL;
10390 :
10391 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
10392 :
10393 0 : BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
10394 :
10395 : while (1) {
10396 : /* loop over events in the event buffer */
10397 :
10398 0 : status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
10399 :
10400 0 : if (status != BM_SUCCESS) {
10401 : // implicit unlock
10402 0 : return status;
10403 : }
10404 :
10405 : /* check if event at current read pointer matches a request */
10406 :
10407 : EVENT_HEADER *pevent;
10408 : int event_size;
10409 : int total_size;
10410 :
10411 0 : status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10412 0 : if (status == BM_CORRUPTED) {
10413 : // implicit unlock
10414 0 : return status;
10415 0 : } else if (status != BM_SUCCESS) {
10416 : /* event buffer is empty */
10417 0 : break;
10418 : }
10419 :
10420 0 : BOOL is_requested = bm_check_requests(pc, pevent);
10421 :
10422 0 : if (is_requested) {
10423 : //printf("bm_read_buffer: [%s] async %d, conv %d, ptr %p, buf %p, disp %d, total_size %d, read from buffer, cache %d %d %d\n", pheader->name, async_flag, convert_flags, bufptr, buf, dispatch, total_size, pbuf->read_cache_size, pbuf->read_cache_rp, pbuf->read_cache_wp);
10424 :
10425 0 : status = BM_SUCCESS;
10426 :
10427 0 : if (buf) {
10428 0 : if (event_size > max_size) {
10429 0 : cm_msg(MERROR, "bm_read_buffer",
10430 : "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10431 0 : event_size, pheader->name);
10432 0 : event_size = max_size;
10433 0 : status = BM_TRUNCATED;
10434 : }
10435 :
10436 0 : bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10437 :
10438 0 : if (buf_size) {
10439 0 : *buf_size = event_size;
10440 : }
10441 :
10442 0 : if (convert_flags) {
10443 0 : bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10444 : }
10445 :
10446 0 : pbuf->count_read++;
10447 0 : pbuf->bytes_read += event_size;
10448 0 : } else if (dispatch || bufptr) {
10449 0 : assert(event_buffer == NULL); // make sure we only come here once
10450 0 : event_buffer = (EVENT_HEADER *) malloc(event_size);
10451 0 : bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) event_buffer, event_size);
10452 0 : pbuf->count_read++;
10453 0 : pbuf->bytes_read += event_size;
10454 0 : } else if (vecptr) {
10455 0 : bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
10456 0 : pbuf->count_read++;
10457 0 : pbuf->bytes_read += event_size;
10458 : }
10459 :
10460 0 : int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10461 0 : pc->read_pointer = new_read_pointer;
10462 :
10463 0 : pheader->num_out_events++;
10464 : /* exit loop over events */
10465 0 : break;
10466 : }
10467 :
10468 0 : int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10469 0 : pc->read_pointer = new_read_pointer;
10470 0 : pheader->num_out_events++;
10471 0 : }
10472 :
10473 : /*
10474 : If read pointer has been changed, it may have freed up some space
10475 : for waiting producers. So check if free space is now more than 50%
10476 : of the buffer size and wake waiting producers.
10477 : */
10478 :
10479 0 : bm_wakeup_producers_locked(pheader, pc);
10480 :
10481 0 : pbuf_guard.unlock();
10482 :
10483 0 : if (dispatch && event_buffer) {
10484 0 : bm_dispatch_event(buffer_handle, event_buffer);
10485 0 : free(event_buffer);
10486 0 : event_buffer = NULL;
10487 0 : return BM_MORE_EVENTS;
10488 : }
10489 :
10490 0 : if (bufptr && event_buffer) {
10491 0 : *bufptr = event_buffer;
10492 0 : event_buffer = NULL;
10493 0 : status = BM_SUCCESS;
10494 : }
10495 :
10496 0 : if (event_buffer) {
10497 0 : free(event_buffer);
10498 0 : event_buffer = NULL;
10499 : }
10500 :
10501 0 : return status;
10502 0 : }
10503 :
10504 : #endif
10505 :
10506 0 : static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10507 : {
10508 : //printf("bm_receive_event_rpc: handle %d, buf %p, pevent %p, pvec %p, timeout %d, max_event_size %d\n", buffer_handle, buf, ppevent, pvec, timeout_msec, _bm_max_event_size);
10509 :
10510 0 : assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10511 :
10512 0 : void *xbuf = NULL;
10513 0 : int xbuf_size = 0;
10514 :
10515 0 : if (buf) {
10516 0 : xbuf = buf;
10517 0 : xbuf_size = *buf_size;
10518 0 : } else if (ppevent) {
10519 0 : *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
10520 0 : xbuf_size = _bm_max_event_size;
10521 0 : } else if (pvec) {
10522 0 : pvec->resize(_bm_max_event_size);
10523 0 : xbuf = pvec->data();
10524 0 : xbuf_size = pvec->size();
10525 : } else {
10526 0 : assert(!"incorrect call to bm_receivent_event_rpc()");
10527 : }
10528 :
10529 : int status;
10530 0 : DWORD time_start = ss_millitime();
10531 0 : DWORD time_end = time_start + timeout_msec;
10532 :
10533 0 : int xtimeout_msec = timeout_msec;
10534 :
10535 0 : int zbuf_size = xbuf_size;
10536 :
10537 : while (1) {
10538 0 : if (timeout_msec == BM_WAIT) {
10539 0 : xtimeout_msec = 1000;
10540 0 : } else if (timeout_msec == BM_NO_WAIT) {
10541 0 : xtimeout_msec = BM_NO_WAIT;
10542 : } else {
10543 0 : if (xtimeout_msec > 1000) {
10544 0 : xtimeout_msec = 1000;
10545 : }
10546 : }
10547 :
10548 0 : zbuf_size = xbuf_size;
10549 :
10550 0 : status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
10551 :
10552 : //printf("bm_receive_event_rpc: handle %d, timeout %d, status %d, size %d in, %d out, via RPC_BM_RECEIVE_EVENT\n", buffer_handle, xtimeout_msec, status, xbuf_size, zbuf_size);
10553 :
10554 0 : if (status == BM_ASYNC_RETURN) {
10555 0 : if (timeout_msec == BM_WAIT) {
10556 : // BM_WAIT means wait forever
10557 0 : continue;
10558 0 : } else if (timeout_msec == BM_NO_WAIT) {
10559 : // BM_NO_WAIT means do not wait
10560 0 : break;
10561 : } else {
10562 0 : DWORD now = ss_millitime();
10563 0 : if (now >= time_end) {
10564 : // timeout, return BM_ASYNC_RETURN
10565 0 : break;
10566 : }
10567 :
10568 0 : DWORD remain = time_end - now;
10569 :
10570 0 : if (remain < (DWORD)xtimeout_msec) {
10571 0 : xtimeout_msec = remain;
10572 : }
10573 :
10574 : // keep asking for event...
10575 0 : continue;
10576 0 : }
10577 0 : } else if (status == BM_SUCCESS) {
10578 : // success, return BM_SUCCESS
10579 0 : break;
10580 : }
10581 :
10582 : // RPC error
10583 :
10584 0 : if (buf) {
10585 0 : *buf_size = 0;
10586 0 : } else if (ppevent) {
10587 0 : free(*ppevent);
10588 0 : *ppevent = NULL;
10589 0 : } else if (pvec) {
10590 0 : pvec->resize(0);
10591 : } else {
10592 0 : assert(!"incorrect call to bm_receivent_event_rpc()");
10593 : }
10594 :
10595 0 : return status;
10596 0 : }
10597 :
10598 : // status is BM_SUCCESS or BM_ASYNC_RETURN
10599 :
10600 0 : if (buf) {
10601 0 : *buf_size = zbuf_size;
10602 0 : } else if (ppevent) {
10603 : // nothing to do
10604 : // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10605 0 : } else if (pvec) {
10606 0 : pvec->resize(zbuf_size);
10607 : } else {
10608 0 : assert(!"incorrect call to bm_receivent_event_rpc()");
10609 : }
10610 :
10611 0 : return status;
10612 : }
10613 :
10614 : /********************************************************************/
10615 : /**
10616 : Receives events directly.
10617 : This function is an alternative way to receive events without
10618 : a main loop.
10619 :
10620 : It can be used in analysis systems which actively receive events,
10621 : rather than using callbacks. A analysis package could for example contain its own
10622 : command line interface. A command
10623 : like "receive 1000 events" could make it necessary to call bm_receive_event()
10624 : 1000 times in a row to receive these events and then return back to the
10625 : command line prompt.
10626 : The according bm_request_event() call contains NULL as the
10627 : callback routine to indicate that bm_receive_event() is called to receive
10628 : events.
10629 : \code
10630 : #include <stdio.h>
10631 : #include "midas.h"
10632 : void process_event(EVENT_HEADER *pheader)
10633 : {
10634 : printf("Received event #%d\r",
10635 : pheader->serial_number);
10636 : }
10637 : main()
10638 : {
10639 : INT status, request_id;
10640 : HNDLE hbuf;
10641 : char event_buffer[1000];
10642 : status = cm_connect_experiment("", "Sample",
10643 : "Simple Analyzer", NULL);
10644 : if (status != CM_SUCCESS)
10645 : return 1;
10646 : bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
10647 : bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
10648 :
10649 : do
10650 : {
10651 : size = sizeof(event_buffer);
10652 : status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
10653 : if (status == CM_SUCCESS)
10654 : process_event((EVENT_HEADER *) event_buffer);
10655 : <...do something else...>
10656 : status = cm_yield(0);
10657 : } while (status != RPC_SHUTDOWN &&
10658 : status != SS_ABORT);
10659 : cm_disconnect_experiment();
10660 : return 0;
10661 : }
10662 : \endcode
10663 : @param buffer_handle buffer handle
10664 : @param destination destination address where event is written to
10665 : @param buf_size size of destination buffer on input, size of event plus
10666 : header on return.
10667 : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
10668 : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
10669 : BM_TRUNCATED The event is larger than the destination buffer and was
10670 : therefore truncated <br>
10671 : BM_ASYNC_RETURN No event available
10672 : */
10673 0 : INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10674 : //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10675 0 : if (rpc_is_remote()) {
10676 0 : return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10677 : }
10678 : #ifdef LOCAL_ROUTINES
10679 : {
10680 0 : INT status = BM_SUCCESS;
10681 :
10682 0 : BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10683 :
10684 0 : if (!pbuf)
10685 0 : return status;
10686 :
10687 0 : int convert_flags = rpc_get_convert_flags();
10688 :
10689 0 : status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10690 : //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10691 0 : return status;
10692 : }
10693 : #else /* LOCAL_ROUTINES */
10694 :
10695 : return BM_SUCCESS;
10696 : #endif
10697 : }
10698 :
10699 : /********************************************************************/
10700 : /**
10701 : Receives events directly.
10702 : This function is an alternative way to receive events without
10703 : a main loop.
10704 :
10705 : It can be used in analysis systems which actively receive events,
10706 : rather than using callbacks. A analysis package could for example contain its own
10707 : command line interface. A command
10708 : like "receive 1000 events" could make it necessary to call bm_receive_event()
10709 : 1000 times in a row to receive these events and then return back to the
10710 : command line prompt.
10711 : The according bm_request_event() call contains NULL as the
10712 : callback routine to indicate that bm_receive_event() is called to receive
10713 : events.
10714 : \code
10715 : #include <stdio.h>
10716 : #include "midas.h"
10717 : void process_event(EVENT_HEADER *pheader)
10718 : {
10719 : printf("Received event #%d\r",
10720 : pheader->serial_number);
10721 : }
10722 : main()
10723 : {
10724 : INT status, request_id;
10725 : HNDLE hbuf;
10726 : char event_buffer[1000];
10727 : status = cm_connect_experiment("", "Sample",
10728 : "Simple Analyzer", NULL);
10729 : if (status != CM_SUCCESS)
10730 : return 1;
10731 : bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
10732 : bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
10733 :
10734 : do
10735 : {
10736 : size = sizeof(event_buffer);
10737 : status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
10738 : if (status == CM_SUCCESS)
10739 : process_event((EVENT_HEADER *) event_buffer);
10740 : <...do something else...>
10741 : status = cm_yield(0);
10742 : } while (status != RPC_SHUTDOWN &&
10743 : status != SS_ABORT);
10744 : cm_disconnect_experiment();
10745 : return 0;
10746 : }
10747 : \endcode
10748 : @param buffer_handle buffer handle
10749 : @param ppevent pointer to the received event pointer, event pointer should be free()ed to avoid memory leak
10750 : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
10751 : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
10752 : BM_ASYNC_RETURN No event available
10753 : */
10754 0 : INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
10755 0 : if (rpc_is_remote()) {
10756 0 : return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10757 : }
10758 : #ifdef LOCAL_ROUTINES
10759 : {
10760 0 : INT status = BM_SUCCESS;
10761 :
10762 0 : BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10763 :
10764 0 : if (!pbuf)
10765 0 : return status;
10766 :
10767 0 : int convert_flags = rpc_get_convert_flags();
10768 :
10769 0 : return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10770 : }
10771 : #else /* LOCAL_ROUTINES */
10772 :
10773 : return BM_SUCCESS;
10774 : #endif
10775 : }
10776 :
10777 : /********************************************************************/
10778 : /**
10779 : Receives events directly.
10780 : This function is an alternative way to receive events without
10781 : a main loop.
10782 :
10783 : It can be used in analysis systems which actively receive events,
10784 : rather than using callbacks. A analysis package could for example contain its own
10785 : command line interface. A command
10786 : like "receive 1000 events" could make it necessary to call bm_receive_event()
10787 : 1000 times in a row to receive these events and then return back to the
10788 : command line prompt.
10789 : The according bm_request_event() call contains NULL as the
10790 : callback routine to indicate that bm_receive_event() is called to receive
10791 : events.
10792 : \code
10793 : #include <stdio.h>
10794 : #include "midas.h"
10795 : void process_event(EVENT_HEADER *pheader)
10796 : {
10797 : printf("Received event #%d\r",
10798 : pheader->serial_number);
10799 : }
10800 : main()
10801 : {
10802 : INT status, request_id;
10803 : HNDLE hbuf;
10804 : char event_buffer[1000];
10805 : status = cm_connect_experiment("", "Sample",
10806 : "Simple Analyzer", NULL);
10807 : if (status != CM_SUCCESS)
10808 : return 1;
10809 : bm_open_buffer(EVENT_BUFFER_NAME, DEFAULT_BUFFER_SIZE, &hbuf);
10810 : bm_request_event(hbuf, 1, TRIGGER_ALL, GET_ALL, request_id, NULL);
10811 :
10812 : do
10813 : {
10814 : size = sizeof(event_buffer);
10815 : status = bm_receive_event(hbuf, event_buffer, &size, BM_NO_WAIT);
10816 : if (status == CM_SUCCESS)
10817 : process_event((EVENT_HEADER *) event_buffer);
10818 : <...do something else...>
10819 : status = cm_yield(0);
10820 : } while (status != RPC_SHUTDOWN &&
10821 : status != SS_ABORT);
10822 : cm_disconnect_experiment();
10823 : return 0;
10824 : }
10825 : \endcode
10826 : @param buffer_handle buffer handle
10827 : @param ppevent pointer to the received event pointer, event pointer should be free()ed to avoid memory leak
10828 : @param timeout_msec Wait so many millisecond for new data. Special values: BM_WAIT: wait forever, BM_NO_WAIT: do not wait, return BM_ASYNC_RETURN if no data is immediately available
10829 : @return BM_SUCCESS, BM_INVALID_HANDLE <br>
10830 : BM_ASYNC_RETURN No event available
10831 : */
10832 0 : INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10833 0 : if (rpc_is_remote()) {
10834 0 : return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10835 : }
10836 : #ifdef LOCAL_ROUTINES
10837 : {
10838 0 : INT status = BM_SUCCESS;
10839 :
10840 0 : BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10841 :
10842 0 : if (!pbuf)
10843 0 : return status;
10844 :
10845 0 : int convert_flags = rpc_get_convert_flags();
10846 :
10847 0 : return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10848 : }
10849 : #else /* LOCAL_ROUTINES */
10850 : return BM_SUCCESS;
10851 : #endif
10852 : }
10853 :
10854 : #ifdef LOCAL_ROUTINES
10855 :
10856 0 : static int bm_skip_event(BUFFER* pbuf)
10857 : {
10858 : /* clear read cache */
10859 0 : if (pbuf->read_cache_size > 0) {
10860 :
10861 0 : int status = bm_lock_buffer_read_cache(pbuf);
10862 :
10863 0 : if (status != BM_SUCCESS)
10864 0 : return status;
10865 :
10866 0 : pbuf->read_cache_rp = 0;
10867 0 : pbuf->read_cache_wp = 0;
10868 :
10869 0 : pbuf->read_cache_mutex.unlock();
10870 : }
10871 :
10872 0 : bm_lock_buffer_guard pbuf_guard(pbuf);
10873 :
10874 0 : if (!pbuf_guard.is_locked())
10875 0 : return pbuf_guard.get_status();
10876 :
10877 0 : BUFFER_HEADER *pheader = pbuf->buffer_header;
10878 :
10879 : /* forward read pointer to global write pointer */
10880 0 : BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
10881 0 : pclient->read_pointer = pheader->write_pointer;
10882 :
10883 0 : return BM_SUCCESS;
10884 0 : }
10885 :
10886 : #endif /* LOCAL_ROUTINES */
10887 :
10888 : /********************************************************************/
10889 : /**
10890 : Skip all events in current buffer.
10891 :
10892 : Useful for single event displays to see the newest events
10893 : @param buffer_handle Handle of the buffer. Must be obtained
10894 : via bm_open_buffer.
10895 : @return BM_SUCCESS, BM_INVALID_HANDLE, RPC_NET_ERROR
10896 : */
10897 0 : INT bm_skip_event(INT buffer_handle) {
10898 0 : if (rpc_is_remote())
10899 0 : return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
10900 :
10901 : #ifdef LOCAL_ROUTINES
10902 : {
10903 0 : int status = 0;
10904 :
10905 0 : BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
10906 :
10907 0 : if (!pbuf)
10908 0 : return status;
10909 :
10910 0 : return bm_skip_event(pbuf);
10911 : }
10912 : #endif
10913 :
10914 : return BM_SUCCESS;
10915 : }
10916 :
10917 : #ifdef LOCAL_ROUTINES
10918 : /********************************************************************/
10919 : /**
10920 : Check a buffer if an event is available and call the dispatch function if found.
10921 : @param buffer_name Name of buffer
10922 : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN,
10923 : RPC_NET_ERROR
10924 : */
10925 0 : static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
10926 : //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
10927 :
10928 : /* return immediately if no callback routine is defined */
10929 0 : if (!pbuf->callback)
10930 0 : return BM_SUCCESS;
10931 :
10932 0 : return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
10933 : }
10934 :
10935 : /********************************************************************/
10936 : /**
10937 : Check a buffer if an event is available and call the dispatch function if found.
10938 : @param buffer_name Name of buffer
10939 : @return BM_SUCCESS, BM_INVALID_HANDLE, BM_TRUNCATED, BM_ASYNC_RETURN, BM_CORRUPTED, RPC_NET_ERROR
10940 : */
10941 0 : static INT bm_push_event(const char *buffer_name)
10942 : {
10943 0 : std::vector<BUFFER*> mybuffers;
10944 :
10945 0 : gBuffersMutex.lock();
10946 0 : mybuffers = gBuffers;
10947 0 : gBuffersMutex.unlock();
10948 :
10949 0 : for (size_t i = 0; i < mybuffers.size(); i++) {
10950 0 : BUFFER *pbuf = mybuffers[i];
10951 0 : if (!pbuf || !pbuf->attached)
10952 0 : continue;
10953 : // FIXME: unlocked read access to pbuf->buffer_name!
10954 0 : if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
10955 0 : return bm_push_buffer(pbuf, i + 1);
10956 : }
10957 : }
10958 :
10959 0 : return BM_INVALID_HANDLE;
10960 0 : }
10961 :
10962 : #else
10963 :
10964 : static INT bm_push_event(const char *buffer_name)
10965 : {
10966 : return BM_SUCCESS;
10967 : }
10968 :
10969 : #endif /* LOCAL_ROUTINES */
10970 :
10971 : /********************************************************************/
10972 : /**
10973 : Check if any requested event is waiting in a buffer
10974 : @return TRUE More events are waiting<br>
10975 : FALSE No more events are waiting
10976 : */
10977 0 : INT bm_check_buffers() {
10978 : #ifdef LOCAL_ROUTINES
10979 : {
10980 0 : INT status = 0;
10981 : BOOL bMore;
10982 : DWORD start_time;
10983 : //static DWORD last_time = 0;
10984 :
10985 : /* if running as a server, buffer checking is done by client
10986 : via ASYNC bm_receive_event */
10987 0 : if (rpc_is_mserver()) {
10988 0 : return FALSE;
10989 : }
10990 :
10991 0 : bMore = FALSE;
10992 0 : start_time = ss_millitime();
10993 :
10994 0 : std::vector<BUFFER*> mybuffers;
10995 :
10996 0 : gBuffersMutex.lock();
10997 0 : mybuffers = gBuffers;
10998 0 : gBuffersMutex.unlock();
10999 :
11000 : /* go through all buffers */
11001 0 : for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11002 0 : BUFFER* pbuf = mybuffers[idx];
11003 :
11004 0 : if (!pbuf || !pbuf->attached)
11005 0 : continue;
11006 :
11007 : //int count_loops = 0;
11008 : while (1) {
11009 0 : if (pbuf->attached) {
11010 : /* one bm_push_event could cause a run stop and a buffer close, which
11011 : * would crash the next call to bm_push_event(). So check for valid
11012 : * buffer on each call */
11013 :
11014 : /* this is what happens:
11015 : * bm_push_buffer() may call a user callback function
11016 : * user callback function may indirectly call bm_close() of this buffer,
11017 : * i.e. if it stops the run,
11018 : * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11019 : * here we will see pbuf->attched is false and quit this loop
11020 : */
11021 :
11022 0 : status = bm_push_buffer(pbuf, idx + 1);
11023 :
11024 0 : if (status == BM_CORRUPTED) {
11025 0 : return status;
11026 : }
11027 :
11028 : //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11029 :
11030 0 : if (status != BM_MORE_EVENTS) {
11031 : //DWORD t = ss_millitime() - start_time;
11032 : //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11033 0 : break;
11034 : }
11035 :
11036 : // count_loops++;
11037 : }
11038 :
11039 : // NB: this code has a logic error: if 2 buffers always have data,
11040 : // this timeout will cause us to exit reading the 1st buffer
11041 : // after 1000 msec, then we read the 2nd buffer exactly once,
11042 : // and exit the loop because the timeout is still active -
11043 : // we did not reset "start_time" when we started reading
11044 : // from the 2nd buffer. Result is that we always read all
11045 : // the data in a loop from the 1st buffer, but read just
11046 : // one event from the 2nd buffer, resulting in severe unfairness.
11047 :
11048 : /* stop after one second */
11049 0 : DWORD t = ss_millitime() - start_time;
11050 0 : if (t > 1000) {
11051 : //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11052 0 : bMore = TRUE;
11053 0 : break;
11054 : }
11055 0 : }
11056 : }
11057 :
11058 : //last_time = start_time;
11059 :
11060 0 : return bMore;
11061 :
11062 0 : }
11063 : #else /* LOCAL_ROUTINES */
11064 :
11065 : return FALSE;
11066 :
11067 : #endif
11068 : }
11069 :
11070 : /********************************************************************/
11071 0 : static INT bm_notify_client(const char *buffer_name, int client_socket)
11072 : /********************************************************************\
11073 :
11074 : Routine: bm_notify_client
11075 :
11076 : Purpose: Called by cm_dispatch_ipc. Send an event notification to
11077 : the connected client. Used by mserver to relay the BM_MSG
11078 : buffer message from local UDP socket to the remote connected client.
11079 :
11080 : Input:
11081 : char *buffer_name Name of buffer
11082 : int client_socket Network socket to client
11083 :
11084 : Output:
11085 : none
11086 :
11087 : Function value:
11088 : BM_SUCCESS Successful completion
11089 :
11090 : \********************************************************************/
11091 : {
11092 : static DWORD last_time = 0;
11093 0 : DWORD now = ss_millitime();
11094 :
11095 : //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11096 :
11097 0 : BUFFER* fbuf = NULL;
11098 :
11099 0 : gBuffersMutex.lock();
11100 :
11101 0 : for (size_t i = 0; i < gBuffers.size(); i++) {
11102 0 : BUFFER* pbuf = gBuffers[i];
11103 0 : if (!pbuf || !pbuf->attached)
11104 0 : continue;
11105 0 : if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11106 0 : fbuf = pbuf;
11107 0 : break;
11108 : }
11109 : }
11110 :
11111 0 : gBuffersMutex.unlock();
11112 :
11113 0 : if (!fbuf)
11114 0 : return BM_INVALID_HANDLE;
11115 :
11116 : /* don't send notification if client has no callback defined
11117 : to receive events -> client calls bm_receive_event manually */
11118 0 : if (!fbuf->callback)
11119 0 : return DB_SUCCESS;
11120 :
11121 0 : int convert_flags = rpc_get_convert_flags();
11122 :
11123 : /* only send notification once each 500ms */
11124 0 : if (now - last_time < 500)
11125 0 : return DB_SUCCESS;
11126 :
11127 0 : last_time = now;
11128 :
11129 : char buffer[32];
11130 0 : NET_COMMAND *nc = (NET_COMMAND *) buffer;
11131 :
11132 0 : nc->header.routine_id = MSG_BM;
11133 0 : nc->header.param_size = 0;
11134 :
11135 0 : if (convert_flags) {
11136 0 : rpc_convert_single(&nc->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
11137 0 : rpc_convert_single(&nc->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
11138 : }
11139 :
11140 : //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11141 :
11142 : /* send the update notification to the client */
11143 0 : send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11144 :
11145 0 : return BM_SUCCESS;
11146 : }
11147 :
11148 : /********************************************************************/
11149 0 : INT bm_poll_event()
11150 : /********************************************************************\
11151 :
11152 : Routine: bm_poll_event
11153 :
11154 : Purpose: Poll an event from a remote server. Gets called by
11155 : rpc_client_dispatch() and by cm_yield()
11156 :
11157 : Function value:
11158 : BM_SUCCESS At least one event was received and dispatched
11159 : BM_ASYNC_RETURN No events received
11160 : SS_ABORT Network connection broken
11161 :
11162 : \********************************************************************/
11163 : {
11164 0 : BOOL dispatched_something = FALSE;
11165 :
11166 : //printf("bm_poll_event!\n");
11167 :
11168 0 : DWORD start_time = ss_millitime();
11169 :
11170 0 : std::vector<char> vec;
11171 :
11172 : /* loop over all requests */
11173 0 : _request_list_mutex.lock();
11174 0 : bool locked = true;
11175 0 : size_t n = _request_list.size();
11176 0 : for (size_t i = 0; i < n; i++) {
11177 0 : if (!locked) {
11178 0 : _request_list_mutex.lock();
11179 0 : locked = true;
11180 : }
11181 : /* continue if no dispatcher set (manual bm_receive_event) */
11182 0 : if (_request_list[i].dispatcher == NULL)
11183 0 : continue;
11184 :
11185 0 : int buffer_handle = _request_list[i].buffer_handle;
11186 :
11187 : /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11188 0 : _request_list_mutex.unlock();
11189 0 : locked = false;
11190 :
11191 : do {
11192 : /* receive event */
11193 0 : int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11194 :
11195 : //printf("bm_poll_event: request_id %d, buffer_handle %d, bm_receive_event(BM_NO_WAIT) status %d, vec size %d, capacity %d\n", request_id, buffer_handle, status, (int)vec.size(), (int)vec.capacity());
11196 :
11197 : /* call user function if successful */
11198 0 : if (status == BM_SUCCESS) {
11199 0 : bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11200 0 : dispatched_something = TRUE;
11201 : }
11202 :
11203 : /* break if no more events */
11204 0 : if (status == BM_ASYNC_RETURN)
11205 0 : break;
11206 :
11207 : /* break if corrupted event buffer */
11208 0 : if (status == BM_TRUNCATED) {
11209 0 : cm_msg(MERROR, "bm_poll_event", "received event was truncated, buffer size %d is too small, see messages and increase /Experiment/MAX_EVENT_SIZE in ODB", (int)vec.size());
11210 : }
11211 :
11212 : /* break if corrupted event buffer */
11213 0 : if (status == BM_CORRUPTED)
11214 0 : return SS_ABORT;
11215 :
11216 : /* break if server died */
11217 0 : if (status == RPC_NET_ERROR) {
11218 0 : return SS_ABORT;
11219 : }
11220 :
11221 : /* stop after one second */
11222 0 : if (ss_millitime() - start_time > 1000) {
11223 0 : break;
11224 : }
11225 :
11226 0 : } while (TRUE);
11227 : }
11228 :
11229 0 : if (locked)
11230 0 : _request_list_mutex.unlock();
11231 :
11232 0 : if (dispatched_something)
11233 0 : return BM_SUCCESS;
11234 : else
11235 0 : return BM_ASYNC_RETURN;
11236 0 : }
11237 :
11238 : /********************************************************************/
11239 : /**
11240 : Clears event buffer and cache.
11241 : If an event buffer is large and a consumer is slow in analyzing
11242 : events, events are usually received some time after they are produced.
11243 : This effect is even more experienced if a read cache is used
11244 : (via bm_set_cache_size()).
11245 : When changes to the hardware are made in the experience, the consumer will then
11246 : still analyze old events before any new event which reflects the hardware change.
11247 : Users can be fooled by looking at histograms which reflect the hardware change
11248 : many seconds after they have been made.
11249 :
11250 : To overcome this potential problem, the analyzer can call
11251 : bm_empty_buffers() just after the hardware change has been made which
11252 : skips all old events contained in event buffers and read caches.
11253 : Technically this is done by forwarding the read pointer of the client.
11254 : No events are really deleted, they are still visible to other clients like
11255 : the logger.
11256 :
11257 : Note that the front-end also contains write buffers which can delay the
11258 : delivery of events.
11259 : The standard front-end framework mfe.c reduces this effect by flushing
11260 : all buffers once every second.
11261 : @return BM_SUCCESS
11262 : */
11263 0 : INT bm_empty_buffers() {
11264 0 : if (rpc_is_remote())
11265 0 : return rpc_call(RPC_BM_EMPTY_BUFFERS);
11266 :
11267 : #ifdef LOCAL_ROUTINES
11268 : {
11269 0 : std::vector<BUFFER*> mybuffers;
11270 :
11271 0 : gBuffersMutex.lock();
11272 0 : mybuffers = gBuffers;
11273 0 : gBuffersMutex.unlock();
11274 :
11275 : /* go through all buffers */
11276 0 : for (BUFFER* pbuf : mybuffers) {
11277 0 : if (!pbuf)
11278 0 : continue;
11279 0 : if (!pbuf->attached)
11280 0 : continue;
11281 :
11282 0 : int status = bm_skip_event(pbuf);
11283 0 : if (status != BM_SUCCESS)
11284 0 : return status;
11285 : }
11286 0 : }
11287 : #endif /* LOCAL_ROUTINES */
11288 :
11289 0 : return BM_SUCCESS;
11290 : }
11291 :
11292 : /**dox***************************************************************/
11293 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
11294 :
11295 : #define MAX_DEFRAG_EVENTS 10
11296 :
11297 : typedef struct {
11298 : WORD event_id;
11299 : DWORD data_size;
11300 : DWORD received;
11301 : EVENT_HEADER *pevent;
11302 : } EVENT_DEFRAG_BUFFER;
11303 :
11304 : static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS];
11305 :
11306 : /********************************************************************/
11307 0 : static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11308 : EVENT_HEADER *pevent, void *pdata,
11309 : EVENT_HANDLER *dispatcher)
11310 : /********************************************************************\
11311 :
11312 : Routine: bm_defragment_event
11313 :
11314 : Purpose: Called internally from the event receiving routines
11315 : bm_push_event and bm_poll_event to recombine event
11316 : fragments and call the user callback routine upon
11317 : completion.
11318 :
11319 : Input:
11320 : HNDLE buffer_handle Handle for the buffer containing event
11321 : HNDLE request_id Handle for event request
11322 : EVENT_HEADER *pevent Pointer to event header
11323 : void *pata Pointer to event data
11324 : dispatcher() User callback routine
11325 :
11326 : Output:
11327 : <calls dispatcher() after successfull recombination of event>
11328 :
11329 : Function value:
11330 : void
11331 :
11332 : \********************************************************************/
11333 : {
11334 : INT i;
11335 :
11336 0 : if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11337 : /*---- start new event ----*/
11338 :
11339 : //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11340 :
11341 : /* check if fragments already stored */
11342 0 : for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11343 0 : if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11344 0 : break;
11345 :
11346 0 : if (i < MAX_DEFRAG_EVENTS) {
11347 0 : free(defrag_buffer[i].pevent);
11348 0 : defrag_buffer[i].pevent = NULL;
11349 0 : memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11350 0 : cm_msg(MERROR, "bm_defragement_event",
11351 : "Received new event with ID %d while old fragments were not completed",
11352 0 : (pevent->event_id & 0x0FFF));
11353 : }
11354 :
11355 : /* search new slot */
11356 0 : for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11357 0 : if (defrag_buffer[i].event_id == 0)
11358 0 : break;
11359 :
11360 0 : if (i == MAX_DEFRAG_EVENTS) {
11361 0 : cm_msg(MERROR, "bm_defragment_event",
11362 : "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11363 0 : return;
11364 : }
11365 :
11366 : /* check event size */
11367 0 : if (pevent->data_size != sizeof(DWORD)) {
11368 0 : cm_msg(MERROR, "bm_defragment_event",
11369 : "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11370 : pevent->data_size, (int) sizeof(DWORD));
11371 0 : return;
11372 : }
11373 :
11374 : /* setup defragment buffer */
11375 0 : defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11376 0 : defrag_buffer[i].data_size = *(DWORD *) pdata;
11377 0 : defrag_buffer[i].received = 0;
11378 0 : defrag_buffer[i].pevent = (EVENT_HEADER *) malloc(sizeof(EVENT_HEADER) + defrag_buffer[i].data_size);
11379 :
11380 0 : if (defrag_buffer[i].pevent == NULL) {
11381 0 : memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11382 0 : cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11383 0 : return;
11384 : }
11385 :
11386 0 : memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11387 0 : defrag_buffer[i].pevent->event_id = defrag_buffer[i].event_id;
11388 0 : defrag_buffer[i].pevent->data_size = defrag_buffer[i].data_size;
11389 :
11390 : // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11391 : // pevent->serial_number, defrag_buffer[i].data_size);
11392 :
11393 0 : return;
11394 : }
11395 :
11396 : /* search buffer for that event */
11397 0 : for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11398 0 : if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11399 0 : break;
11400 :
11401 0 : if (i == MAX_DEFRAG_EVENTS) {
11402 : /* no buffer available -> no first fragment received */
11403 0 : cm_msg(MERROR, "bm_defragement_event",
11404 : "Received fragment without first fragment (ID %d) Ser#:%d",
11405 0 : pevent->event_id & 0x0FFF, pevent->serial_number);
11406 0 : return;
11407 : }
11408 :
11409 : /* add fragment to buffer */
11410 0 : if (pevent->data_size + defrag_buffer[i].received > defrag_buffer[i].data_size) {
11411 0 : free(defrag_buffer[i].pevent);
11412 0 : defrag_buffer[i].pevent = NULL;
11413 0 : memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11414 0 : cm_msg(MERROR, "bm_defragement_event",
11415 : "Received fragments with more data (%d) than event size (%d)",
11416 0 : pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11417 0 : return;
11418 : }
11419 :
11420 0 : memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11421 0 : defrag_buffer[i].received, pdata, pevent->data_size);
11422 :
11423 0 : defrag_buffer[i].received += pevent->data_size;
11424 :
11425 : //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11426 : // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11427 :
11428 0 : if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11429 : /* event complete */
11430 0 : dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11431 0 : free(defrag_buffer[i].pevent);
11432 0 : defrag_buffer[i].pevent = NULL;
11433 0 : memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11434 : }
11435 : }
11436 :
11437 : /**dox***************************************************************/
11438 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
11439 :
11440 : /**dox***************************************************************/
11441 : /** @} *//* end of bmfunctionc */
11442 :
11443 : /**dox***************************************************************/
11444 : /** @addtogroup rpc_xxx
11445 : *
11446 : * @{ */
11447 :
11448 : /********************************************************************\
11449 : * *
11450 : * RPC functions *
11451 : * *
11452 : \********************************************************************/
11453 :
11454 : class RPC_CLIENT_CONNECTION
11455 : {
11456 : public:
11457 : std::atomic_bool connected{false}; /* socket is connected */
11458 : std::string host_name; /* server name */
11459 : int port = 0; /* server port */
11460 : std::mutex mutex; /* connection lock */
11461 : int index = 0; /* index in the connection array */
11462 : std::string client_name; /* name of remote client */
11463 : int send_sock = 0; /* tcp socket */
11464 : int remote_hw_type = 0; /* remote hardware type */
11465 : int rpc_timeout = 0; /* timeout in milliseconds */
11466 :
11467 0 : void print() {
11468 0 : printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11469 : index,
11470 : client_name.c_str(),
11471 : host_name.c_str(),
11472 : port,
11473 : send_sock,
11474 0 : int(connected),
11475 : rpc_timeout);
11476 0 : }
11477 :
11478 0 : void close_locked() {
11479 0 : if (send_sock > 0) {
11480 0 : ss_socket_close(&send_sock);
11481 : }
11482 0 : connected = false;
11483 0 : }
11484 : };
11485 :
11486 : /* globals */
11487 :
11488 : //
11489 : // locking rules for client connections:
11490 : //
11491 : // lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11492 : // lock _client_connections_mutex, look at _client_connections vector and c->connected, lock individual connection, recheck c->connected, work on the connection, unlock the connection, unlock _client_connections_mutex
11493 : // lock individual connection, check c->connected, work on the connection, unlock connection
11494 : //
11495 : // ok to access without locking client connection:
11496 : //
11497 : // - c->connected (std::atomic, but must recheck it after taking the lock)
11498 : // - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11499 : //
11500 : // this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11501 : // this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11502 : //
11503 : // lifetime of client connections:
11504 : //
11505 : // - client connection slots are allocated by rpc_client_connect()
11506 : // - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11507 : // - client slots marked NULL are free and will be reused by rpc_client_connect()
11508 : // - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11509 : // - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11510 : // - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11511 : // - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11512 : // client call will see an empty slot and return an error
11513 : // - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11514 : // client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11515 : // only when one is sure no other threads are running concurrent rpc client calls.
11516 : //
11517 :
11518 : static std::mutex _client_connections_mutex;
11519 : static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11520 :
11521 : static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11522 : static bool _rpc_is_remote = false;
11523 :
11524 : //static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11525 : static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11526 : static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11527 :
11528 0 : static RPC_SERVER_ACCEPTION* rpc_get_server_acception(int idx)
11529 : {
11530 0 : assert(idx >= 0);
11531 0 : assert(idx < (int)_server_acceptions.size());
11532 0 : assert(_server_acceptions[idx] != NULL);
11533 0 : return _server_acceptions[idx];
11534 : }
11535 :
11536 0 : RPC_SERVER_ACCEPTION* rpc_get_mserver_acception()
11537 : {
11538 0 : return _mserver_acception;
11539 : }
11540 :
11541 0 : static RPC_SERVER_ACCEPTION* rpc_new_server_acception()
11542 : {
11543 0 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11544 0 : if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11545 : //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11546 0 : return _server_acceptions[idx];
11547 : }
11548 : }
11549 :
11550 0 : RPC_SERVER_ACCEPTION* sa = new RPC_SERVER_ACCEPTION;
11551 :
11552 0 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11553 0 : if (_server_acceptions[idx] == NULL) {
11554 : //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11555 0 : _server_acceptions[idx] = sa;
11556 0 : return _server_acceptions[idx];
11557 : }
11558 : }
11559 :
11560 : //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11561 0 : _server_acceptions.push_back(sa);
11562 :
11563 0 : return sa;
11564 : }
11565 :
11566 0 : void RPC_SERVER_ACCEPTION::close()
11567 : {
11568 : //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11569 :
11570 0 : if (is_mserver) {
11571 0 : assert(_mserver_acception == this);
11572 0 : _mserver_acception = NULL;
11573 0 : is_mserver = false;
11574 : }
11575 :
11576 : /* close server connection */
11577 0 : if (recv_sock)
11578 0 : ss_socket_close(&recv_sock);
11579 0 : if (send_sock)
11580 0 : ss_socket_close(&send_sock);
11581 0 : if (event_sock)
11582 0 : ss_socket_close(&event_sock);
11583 :
11584 : /* free TCP cache */
11585 0 : if (net_buffer) {
11586 : //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11587 0 : free(net_buffer);
11588 0 : net_buffer = NULL;
11589 0 : net_buffer_size = 0;
11590 : }
11591 :
11592 : /* mark this entry as invalid */
11593 0 : clear();
11594 0 : }
11595 :
11596 : static std::vector<RPC_LIST> rpc_list;
11597 : static std::mutex rpc_list_mutex;
11598 :
11599 : static int _opt_tcp_size = OPT_TCP_SIZE;
11600 :
11601 :
11602 : /********************************************************************\
11603 : * conversion functions *
11604 : \********************************************************************/
11605 :
11606 0 : void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11607 0 : *convert_flags = 0;
11608 :
11609 : /* big/little endian conversion */
11610 0 : if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11611 0 : (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11612 0 : && (hw_type & DRI_BIG_ENDIAN)))
11613 0 : *convert_flags |= CF_ENDIAN;
11614 :
11615 : /* float conversion between IEEE and VAX G */
11616 0 : if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11617 0 : *convert_flags |= CF_VAX2IEEE;
11618 :
11619 : /* float conversion between VAX G and IEEE */
11620 0 : if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11621 0 : *convert_flags |= CF_IEEE2VAX;
11622 :
11623 : ///* ASCII format */
11624 : //if (remote_hw_type & DR_ASCII)
11625 : // *convert_flags |= CF_ASCII;
11626 0 : }
11627 :
11628 : /********************************************************************/
11629 0 : void rpc_get_convert_flags(INT *convert_flags) {
11630 0 : rpc_calc_convert_flags(rpc_get_hw_type(), _server_connection.remote_hw_type, convert_flags);
11631 0 : }
11632 :
11633 : /********************************************************************/
11634 0 : void rpc_ieee2vax_float(float *var) {
11635 : unsigned short int lo, hi;
11636 :
11637 : /* swap hi and lo word */
11638 0 : lo = *((short int *) (var) + 1);
11639 0 : hi = *((short int *) (var));
11640 :
11641 : /* correct exponent */
11642 0 : if (lo != 0)
11643 0 : lo += 0x100;
11644 :
11645 0 : *((short int *) (var) + 1) = hi;
11646 0 : *((short int *) (var)) = lo;
11647 0 : }
11648 :
11649 0 : void rpc_vax2ieee_float(float *var) {
11650 : unsigned short int lo, hi;
11651 :
11652 : /* swap hi and lo word */
11653 0 : lo = *((short int *) (var) + 1);
11654 0 : hi = *((short int *) (var));
11655 :
11656 : /* correct exponent */
11657 0 : if (hi != 0)
11658 0 : hi -= 0x100;
11659 :
11660 0 : *((short int *) (var) + 1) = hi;
11661 0 : *((short int *) (var)) = lo;
11662 :
11663 0 : }
11664 :
11665 0 : void rpc_vax2ieee_double(double *var) {
11666 : unsigned short int i1, i2, i3, i4;
11667 :
11668 : /* swap words */
11669 0 : i1 = *((short int *) (var) + 3);
11670 0 : i2 = *((short int *) (var) + 2);
11671 0 : i3 = *((short int *) (var) + 1);
11672 0 : i4 = *((short int *) (var));
11673 :
11674 : /* correct exponent */
11675 0 : if (i4 != 0)
11676 0 : i4 -= 0x20;
11677 :
11678 0 : *((short int *) (var) + 3) = i4;
11679 0 : *((short int *) (var) + 2) = i3;
11680 0 : *((short int *) (var) + 1) = i2;
11681 0 : *((short int *) (var)) = i1;
11682 0 : }
11683 :
11684 0 : void rpc_ieee2vax_double(double *var) {
11685 : unsigned short int i1, i2, i3, i4;
11686 :
11687 : /* swap words */
11688 0 : i1 = *((short int *) (var) + 3);
11689 0 : i2 = *((short int *) (var) + 2);
11690 0 : i3 = *((short int *) (var) + 1);
11691 0 : i4 = *((short int *) (var));
11692 :
11693 : /* correct exponent */
11694 0 : if (i1 != 0)
11695 0 : i1 += 0x20;
11696 :
11697 0 : *((short int *) (var) + 3) = i4;
11698 0 : *((short int *) (var) + 2) = i3;
11699 0 : *((short int *) (var) + 1) = i2;
11700 0 : *((short int *) (var)) = i1;
11701 0 : }
11702 :
11703 : /********************************************************************/
11704 0 : void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11705 :
11706 0 : if (convert_flags & CF_ENDIAN) {
11707 0 : if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11708 0 : if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11709 0 : if (tid == TID_DOUBLE) QWORD_SWAP(data);
11710 : }
11711 :
11712 0 : if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11713 0 : ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11714 0 : if (tid == TID_FLOAT)
11715 0 : rpc_ieee2vax_float((float *) data);
11716 0 : if (tid == TID_DOUBLE)
11717 0 : rpc_ieee2vax_double((double *) data);
11718 : }
11719 :
11720 0 : if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11721 0 : ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11722 0 : if (tid == TID_FLOAT)
11723 0 : rpc_vax2ieee_float((float *) data);
11724 0 : if (tid == TID_DOUBLE)
11725 0 : rpc_vax2ieee_double((double *) data);
11726 : }
11727 0 : }
11728 :
11729 0 : void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11730 : /********************************************************************\
11731 :
11732 : Routine: rpc_convert_data
11733 :
11734 : Purpose: Convert data format between differenct computers
11735 :
11736 : Input:
11737 : void *data Pointer to data
11738 : INT tid Type ID of data, one of TID_xxx
11739 : INT flags Combination of following flags:
11740 : RPC_IN: data is input parameter
11741 : RPC_OUT: data is output variable
11742 : RPC_FIXARRAY, RPC_VARARRAY: data is array
11743 : of "size" bytes (see next param.)
11744 : RPC_OUTGOING: data is outgoing
11745 : INT total_size Size of bytes of data. Used for variable
11746 : length arrays.
11747 : INT convert_flags Flags for data conversion
11748 :
11749 : Output:
11750 : void *data Is converted according to _convert_flag
11751 : value
11752 :
11753 : Function value:
11754 : RPC_SUCCESS Successful completion
11755 :
11756 : \********************************************************************/
11757 : {
11758 : /* convert array */
11759 0 : if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11760 0 : int single_size = rpc_tid_size(tid);
11761 : /* don't convert TID_ARRAY & TID_STRUCT */
11762 0 : if (single_size == 0)
11763 0 : return;
11764 :
11765 0 : int n = total_size / single_size;
11766 :
11767 0 : for (int i = 0; i < n; i++) {
11768 0 : char* p = (char *) data + (i * single_size);
11769 0 : rpc_convert_single(p, tid, flags, convert_flags);
11770 : }
11771 : } else {
11772 0 : rpc_convert_single(data, tid, flags, convert_flags);
11773 : }
11774 : }
11775 :
11776 : /********************************************************************\
11777 : * type ID functions *
11778 : \********************************************************************/
11779 :
11780 515 : INT rpc_tid_size(INT id) {
11781 515 : if (id >= 0 && id < TID_LAST)
11782 515 : return tid_size[id];
11783 :
11784 0 : return 0;
11785 : }
11786 :
11787 442 : const char *rpc_tid_name(INT id) {
11788 442 : if (id >= 0 && id < TID_LAST)
11789 442 : return tid_name[id];
11790 : else
11791 0 : return "<unknown>";
11792 : }
11793 :
11794 44 : const char *rpc_tid_name_old(INT id) {
11795 44 : if (id >= 0 && id < TID_LAST)
11796 44 : return tid_name_old[id];
11797 : else
11798 0 : return "<unknown>";
11799 : }
11800 :
11801 0 : int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11802 : {
11803 0 : for (int i=0; i<TID_LAST; i++) {
11804 0 : if (strcmp(name, tid_name[i]) == 0)
11805 0 : return i;
11806 : }
11807 :
11808 0 : for (int i=0; i<TID_LAST; i++) {
11809 0 : if (strcmp(name, tid_name_old[i]) == 0)
11810 0 : return i;
11811 : }
11812 :
11813 0 : return 0;
11814 : }
11815 :
11816 : /********************************************************************\
11817 : * client functions *
11818 : \********************************************************************/
11819 :
11820 : /********************************************************************/
11821 : /**
11822 : Register RPC client for standalone mode (without standard
11823 : midas server)
11824 : @param list Array of RPC_LIST structures containing
11825 : function IDs and parameter definitions.
11826 : The end of the list must be indicated by
11827 : a function ID of zero.
11828 : @param name Name of this client
11829 : @return RPC_SUCCESS
11830 : */
11831 0 : INT rpc_register_client(const char *name, RPC_LIST *list) {
11832 0 : rpc_set_name(name);
11833 0 : rpc_register_functions(rpc_get_internal_list(0), NULL);
11834 0 : rpc_register_functions(list, NULL);
11835 :
11836 0 : return RPC_SUCCESS;
11837 : }
11838 :
11839 : /********************************************************************/
11840 : /**
11841 : Register a set of RPC functions (both as clients or servers)
11842 : @param new_list Array of RPC_LIST structures containing
11843 : function IDs and parameter definitions.
11844 : The end of the list must be indicated by
11845 : a function ID of zero.
11846 : @param func Default dispatch function
11847 :
11848 : @return RPC_SUCCESS, RPC_NO_MEMORY, RPC_DOUBLE_DEFINED
11849 : */
11850 4 : INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
11851 : {
11852 184 : for (int i = 0; new_list[i].id != 0; i++) {
11853 : /* check valid ID for user functions */
11854 180 : if (new_list != rpc_get_internal_list(0) &&
11855 180 : new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
11856 0 : || new_list[i].id > RPC_MAX_ID)) {
11857 0 : cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11858 : }
11859 : }
11860 :
11861 4 : std::lock_guard<std::mutex> guard(rpc_list_mutex);
11862 :
11863 : /* check double defined functions */
11864 184 : for (int i = 0; new_list[i].id != 0; i++) {
11865 702 : for (size_t j = 0; j < rpc_list.size(); j++) {
11866 522 : if (rpc_list[j].id == new_list[i].id) {
11867 0 : return RPC_DOUBLE_DEFINED;
11868 : }
11869 : }
11870 : }
11871 :
11872 : /* append new functions */
11873 184 : for (int i = 0; new_list[i].id != 0; i++) {
11874 180 : RPC_LIST e = new_list[i];
11875 :
11876 : /* set default dispatcher */
11877 180 : if (e.dispatch == NULL) {
11878 180 : e.dispatch = func;
11879 : }
11880 :
11881 180 : rpc_list.push_back(e);
11882 : }
11883 :
11884 4 : return RPC_SUCCESS;
11885 4 : }
11886 :
11887 :
11888 :
11889 : /**dox***************************************************************/
11890 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
11891 :
11892 : /********************************************************************/
11893 2 : INT rpc_deregister_functions()
11894 : /********************************************************************\
11895 :
11896 : Routine: rpc_deregister_functions
11897 :
11898 : Purpose: Free memory of previously registered functions
11899 :
11900 : Input:
11901 : none
11902 :
11903 : Output:
11904 : none
11905 :
11906 : Function value:
11907 : RPC_SUCCESS Successful completion
11908 :
11909 : \********************************************************************/
11910 : {
11911 2 : rpc_list_mutex.lock();
11912 2 : rpc_list.clear();
11913 2 : rpc_list_mutex.unlock();
11914 :
11915 2 : return RPC_SUCCESS;
11916 : }
11917 :
11918 :
11919 : /********************************************************************/
11920 7 : INT rpc_register_function(INT id, INT(*func)(INT, void **))
11921 : /********************************************************************\
11922 :
11923 : Routine: rpc_register_function
11924 :
11925 : Purpose: Replace a dispatch function for a specific rpc routine
11926 :
11927 : Input:
11928 : INT id RPC ID
11929 : INT *func New dispatch function
11930 :
11931 : Output:
11932 : <implicit: func gets copied to rpc_list>
11933 :
11934 : Function value:
11935 : RPC_SUCCESS Successful completion
11936 : RPC_INVALID_ID RPC ID not found
11937 :
11938 : \********************************************************************/
11939 : {
11940 7 : std::lock_guard<std::mutex> guard(rpc_list_mutex);
11941 :
11942 584 : for (size_t i = 0; i < rpc_list.size(); i++) {
11943 584 : if (rpc_list[i].id == id) {
11944 7 : rpc_list[i].dispatch = func;
11945 7 : return RPC_SUCCESS;
11946 : }
11947 : }
11948 :
11949 0 : return RPC_INVALID_ID;
11950 7 : }
11951 :
11952 : /********************************************************************/
11953 :
11954 0 : static int handle_msg_odb(int n, const NET_COMMAND *nc) {
11955 : //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
11956 0 : if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
11957 : /* update a changed record */
11958 0 : HNDLE hDB = *((INT *) nc->param);
11959 0 : HNDLE hKeyRoot = *((INT *) nc->param + 1);
11960 0 : HNDLE hKey = *((INT *) nc->param + 2);
11961 0 : int index = *((INT *) nc->param + 3);
11962 0 : return db_update_record_local(hDB, hKeyRoot, hKey, index);
11963 : }
11964 0 : return CM_VERSION_MISMATCH;
11965 : }
11966 :
11967 : /********************************************************************/
11968 0 : INT rpc_client_dispatch(int sock)
11969 : /********************************************************************\
11970 :
11971 : Routine: rpc_client_dispatch
11972 :
11973 : Purpose: Receive data from the mserver: watchdog and buffer notification messages
11974 :
11975 : \********************************************************************/
11976 : {
11977 0 : INT status = 0;
11978 : char net_buffer[256];
11979 :
11980 0 : int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11981 0 : if (n <= 0)
11982 0 : return SS_ABORT;
11983 :
11984 0 : NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
11985 :
11986 0 : if (nc->header.routine_id == MSG_ODB) {
11987 0 : status = handle_msg_odb(n, nc);
11988 0 : } else if (nc->header.routine_id == MSG_WATCHDOG) {
11989 0 : nc->header.routine_id = 1;
11990 0 : nc->header.param_size = 0;
11991 0 : send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11992 0 : status = RPC_SUCCESS;
11993 0 : } else if (nc->header.routine_id == MSG_BM) {
11994 : fd_set readfds;
11995 : struct timeval timeout;
11996 :
11997 : //printf("rpc_client_dispatch: received MSG_BM!\n");
11998 :
11999 : /* receive further messages to empty TCP queue */
12000 : do {
12001 0 : FD_ZERO(&readfds);
12002 0 : FD_SET(sock, &readfds);
12003 :
12004 0 : timeout.tv_sec = 0;
12005 0 : timeout.tv_usec = 0;
12006 :
12007 0 : select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12008 :
12009 0 : if (FD_ISSET(sock, &readfds)) {
12010 0 : n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12011 0 : if (n <= 0)
12012 0 : return SS_ABORT;
12013 :
12014 0 : if (nc->header.routine_id == MSG_ODB) {
12015 0 : status = handle_msg_odb(n, nc);
12016 0 : } else if (nc->header.routine_id == MSG_WATCHDOG) {
12017 0 : nc->header.routine_id = 1;
12018 0 : nc->header.param_size = 0;
12019 0 : send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12020 0 : status = RPC_SUCCESS;
12021 : }
12022 : }
12023 :
12024 0 : } while (FD_ISSET(sock, &readfds));
12025 :
12026 : /* poll event from server */
12027 0 : status = bm_poll_event();
12028 : }
12029 :
12030 0 : return status;
12031 : }
12032 :
12033 :
12034 : /********************************************************************/
12035 0 : INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12036 : /********************************************************************\
12037 :
12038 : Routine: rpc_client_connect
12039 :
12040 : Purpose: Establish a network connection to a remote client
12041 :
12042 : Input:
12043 : char *host_name IP address of host to connect to.
12044 : INT port TPC port to connect to.
12045 : char *clinet_name Client program name
12046 :
12047 : Output:
12048 : HNDLE *hConnection Handle for new connection which can be used
12049 : in future rpc_call(hConnection....) calls
12050 :
12051 : Function value:
12052 : RPC_SUCCESS Successful completion
12053 : RPC_NET_ERROR Error in socket call
12054 : RPC_NO_CONNECTION Maximum number of connections reached
12055 : RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12056 :
12057 : \********************************************************************/
12058 : {
12059 : INT i, status;
12060 0 : bool debug = false;
12061 :
12062 : /* check if cm_connect_experiment was called */
12063 0 : if (_client_name.length() == 0) {
12064 0 : cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12065 0 : return RPC_NOT_REGISTERED;
12066 : }
12067 :
12068 : /* refuse connection to port 0 */
12069 0 : if (port == 0) {
12070 0 : cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12071 0 : return RPC_NET_ERROR;
12072 : }
12073 :
12074 0 : RPC_CLIENT_CONNECTION* c = NULL;
12075 :
12076 : static std::mutex gHostnameMutex;
12077 :
12078 : {
12079 0 : std::lock_guard<std::mutex> guard(_client_connections_mutex);
12080 :
12081 0 : if (debug) {
12082 0 : printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12083 0 : for (size_t i = 0; i < _client_connections.size(); i++) {
12084 0 : if (_client_connections[i]) {
12085 0 : printf("client connection %d: ", (int)i);
12086 0 : _client_connections[i]->print();
12087 0 : printf("\n");
12088 : }
12089 : }
12090 : }
12091 :
12092 : // slot with index 0 is not used, fill it with a NULL
12093 :
12094 0 : if (_client_connections.empty()) {
12095 0 : _client_connections.push_back(NULL);
12096 : }
12097 :
12098 0 : bool hostname_locked = false;
12099 :
12100 : /* check if connection already exists */
12101 0 : for (size_t i = 1; i < _client_connections.size(); i++) {
12102 0 : RPC_CLIENT_CONNECTION* c = _client_connections[i];
12103 0 : if (c && c->connected) {
12104 :
12105 0 : if (!hostname_locked) {
12106 0 : gHostnameMutex.lock();
12107 0 : hostname_locked = true;
12108 : }
12109 :
12110 0 : if ((c->host_name == host_name) && (c->port == port)) {
12111 : // NB: we must release the hostname lock before taking
12112 : // c->mutex to avoid a locking order inversion deadlock:
12113 : // later on we lock the hostname mutex while holding the c->mutex
12114 0 : gHostnameMutex.unlock();
12115 0 : hostname_locked = false;
12116 0 : std::lock_guard<std::mutex> cguard(c->mutex);
12117 : // check if socket is still connected
12118 0 : if (c->connected) {
12119 : // found connection slot with matching hostname and port number
12120 0 : status = ss_socket_wait(c->send_sock, 0);
12121 0 : if (status == SS_TIMEOUT) { // yes, still connected and empty
12122 : // so reuse it connection
12123 0 : *hConnection = c->index;
12124 0 : if (debug) {
12125 0 : printf("already connected: ");
12126 0 : c->print();
12127 0 : printf("\n");
12128 : }
12129 : // implicit unlock of c->mutex
12130 : // gHostnameLock is not locked here
12131 0 : return RPC_SUCCESS;
12132 : }
12133 : //cm_msg(MINFO, "rpc_client_connect", "Stale connection to \"%s\" on host %s is closed", _client_connection[i].client_name, _client_connection[i].host_name);
12134 0 : c->close_locked();
12135 : }
12136 : // implicit unlock of c->mutex
12137 0 : }
12138 : }
12139 : }
12140 :
12141 0 : if (hostname_locked) {
12142 0 : gHostnameMutex.unlock();
12143 0 : hostname_locked = false;
12144 : }
12145 :
12146 : // only start reusing connections once we have
12147 : // a good number of slots allocated.
12148 0 : if (_client_connections.size() > 10) {
12149 : static int last_reused = 0;
12150 :
12151 0 : int size = _client_connections.size();
12152 0 : for (int j = 1; j < size; j++) {
12153 0 : int i = (last_reused + j) % size;
12154 0 : if (_client_connections[i] && !_client_connections[i]->connected) {
12155 0 : c = _client_connections[i];
12156 0 : if (debug) {
12157 0 : printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12158 0 : c->print();
12159 0 : printf("\n");
12160 : }
12161 0 : last_reused = i;
12162 0 : break;
12163 : }
12164 : }
12165 : }
12166 :
12167 : // no slots to reuse, allocate a new slot.
12168 0 : if (!c) {
12169 0 : c = new RPC_CLIENT_CONNECTION;
12170 :
12171 : // if empty slot not found, add to end of array
12172 0 : c->index = _client_connections.size();
12173 0 : _client_connections.push_back(c);
12174 :
12175 0 : if (debug) {
12176 0 : printf("new connection appended to array: ");
12177 0 : c->print();
12178 0 : printf("\n");
12179 : }
12180 : }
12181 :
12182 0 : c->mutex.lock();
12183 0 : c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12184 :
12185 : // done with the array of connections
12186 : // implicit unlock of _client_connections_mutex
12187 0 : }
12188 :
12189 : // locked connection slot for new connection
12190 0 : assert(c != NULL);
12191 :
12192 0 : std::string errmsg;
12193 :
12194 : /* create a new socket for connecting to remote server */
12195 0 : status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12196 0 : if (status != SS_SUCCESS) {
12197 0 : cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12198 0 : c->mutex.unlock();
12199 0 : return RPC_NET_ERROR;
12200 : }
12201 :
12202 0 : gHostnameMutex.lock();
12203 :
12204 0 : c->host_name = host_name;
12205 0 : c->port = port;
12206 :
12207 0 : gHostnameMutex.unlock();
12208 :
12209 0 : c->client_name = client_name;
12210 0 : c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12211 :
12212 : /* set TCP_NODELAY option for better performance */
12213 0 : i = 1;
12214 0 : setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12215 :
12216 : /* send local computer info */
12217 0 : std::string local_prog_name = rpc_get_name();
12218 0 : std::string local_host_name = ss_gethostname();
12219 :
12220 0 : int hw_type = rpc_get_hw_type();
12221 :
12222 0 : std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12223 :
12224 0 : int size = cstr.length() + 1;
12225 0 : i = send(c->send_sock, cstr.c_str(), size, 0);
12226 0 : if (i < 0 || i != size) {
12227 0 : cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12228 0 : c->mutex.unlock();
12229 0 : return RPC_NET_ERROR;
12230 : }
12231 :
12232 0 : bool restore_watchdog_timeout = false;
12233 : BOOL watchdog_call;
12234 : DWORD watchdog_timeout;
12235 0 : cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12236 :
12237 : //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12238 :
12239 0 : if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12240 0 : restore_watchdog_timeout = true;
12241 0 : cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
12242 : }
12243 :
12244 : char str[256];
12245 :
12246 : /* receive remote computer info */
12247 0 : i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12248 :
12249 0 : if (restore_watchdog_timeout) {
12250 0 : cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12251 : }
12252 :
12253 0 : if (i <= 0) {
12254 0 : cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12255 0 : c->close_locked();
12256 0 : c->mutex.unlock();
12257 0 : return RPC_NET_ERROR;
12258 : }
12259 :
12260 0 : int remote_hw_type = 0;
12261 : char remote_version[32];
12262 0 : remote_version[0] = 0;
12263 0 : sscanf(str, "%d %s", &remote_hw_type, remote_version);
12264 :
12265 0 : c->remote_hw_type = remote_hw_type;
12266 :
12267 : /* print warning if version patch level doesn't agree */
12268 : char v1[32];
12269 0 : mstrlcpy(v1, remote_version, sizeof(v1));
12270 0 : if (strchr(v1, '.'))
12271 0 : if (strchr(strchr(v1, '.') + 1, '.'))
12272 0 : *strchr(strchr(v1, '.') + 1, '.') = 0;
12273 :
12274 0 : mstrlcpy(str, cm_get_version(), sizeof(str));
12275 0 : if (strchr(str, '.'))
12276 0 : if (strchr(strchr(str, '.') + 1, '.'))
12277 0 : *strchr(strchr(str, '.') + 1, '.') = 0;
12278 :
12279 0 : if (strcmp(v1, str) != 0) {
12280 0 : cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12281 : }
12282 :
12283 0 : c->connected = true;
12284 :
12285 0 : *hConnection = c->index;
12286 :
12287 0 : c->mutex.unlock();
12288 :
12289 0 : return RPC_SUCCESS;
12290 0 : }
12291 :
12292 : /********************************************************************/
12293 0 : void rpc_client_check()
12294 : /********************************************************************\
12295 :
12296 : Routine: rpc_client_check
12297 :
12298 : Purpose: Check all client connections if remote client closed link
12299 :
12300 : \********************************************************************/
12301 : {
12302 : #if 0
12303 : for (i = 0; i < MAX_RPC_CONNECTION; i++)
12304 : if (_client_connection[i].send_sock != 0)
12305 : printf("slot %d, checking client %s socket %d, connected %d\n", i, _client_connection[i].client_name, _client_connection[i].send_sock, _client_connection[i].connected);
12306 : #endif
12307 :
12308 0 : std::lock_guard<std::mutex> guard(_client_connections_mutex);
12309 :
12310 : /* check for broken connections */
12311 0 : for (unsigned i = 0; i < _client_connections.size(); i++) {
12312 0 : RPC_CLIENT_CONNECTION* c = _client_connections[i];
12313 0 : if (c && c->connected) {
12314 0 : std::lock_guard<std::mutex> cguard(c->mutex);
12315 :
12316 0 : if (!c->connected) {
12317 : // implicit unlock
12318 0 : continue;
12319 : }
12320 :
12321 : //printf("rpc_client_check: connection %d: ", i);
12322 : //c->print();
12323 : //printf("\n");
12324 :
12325 0 : int ok = 0;
12326 :
12327 : fd_set readfds;
12328 0 : FD_ZERO(&readfds);
12329 0 : FD_SET(c->send_sock, &readfds);
12330 :
12331 : struct timeval timeout;
12332 0 : timeout.tv_sec = 0;
12333 0 : timeout.tv_usec = 0;
12334 :
12335 : int status;
12336 :
12337 : #ifdef OS_WINNT
12338 : status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12339 : #else
12340 : do {
12341 0 : status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12342 0 : } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12343 : #endif
12344 :
12345 0 : if (!FD_ISSET(c->send_sock, &readfds)) {
12346 : // implicit unlock
12347 0 : continue;
12348 : }
12349 :
12350 : char buffer[64];
12351 :
12352 0 : status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12353 : //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12354 :
12355 0 : if (status < 0) {
12356 : #ifndef OS_WINNT
12357 0 : if (errno == EAGAIN) { // still connected
12358 0 : ok = 1;
12359 : } else
12360 : #endif
12361 : {
12362 : // connection error
12363 0 : cm_msg(MERROR, "rpc_client_check",
12364 : "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12365 : c->client_name.c_str(),
12366 : c->host_name.c_str(),
12367 0 : errno, strerror(errno));
12368 0 : ok = 0;
12369 : }
12370 0 : } else if (status == 0) {
12371 : // connection closed by remote end without sending an EXIT message
12372 : // this can happen if the remote end has crashed, so this message
12373 : // is still necessary as a useful diagnostic for unexpected crashes
12374 : // of midas programs. K.O.
12375 0 : cm_msg(MINFO, "rpc_client_check", "RPC client connection to \"%s\" on host \"%s\" unexpectedly closed", c->client_name.c_str(), c->host_name.c_str());
12376 0 : ok = 0;
12377 : } else {
12378 : // read some data
12379 0 : ok = 1;
12380 0 : if (equal_ustring(buffer, "EXIT")) {
12381 : /* normal exit */
12382 0 : ok = 0;
12383 : }
12384 : }
12385 :
12386 0 : if (!ok) {
12387 : //printf("rpc_client_check: closing connection %d: ", i);
12388 : //c->print();
12389 : //printf("\n");
12390 :
12391 : // connection lost, close the socket
12392 0 : c->close_locked();
12393 : }
12394 :
12395 : // implicit unlock
12396 0 : }
12397 : }
12398 :
12399 : // implicit unlock of _client_connections_mutex
12400 0 : }
12401 :
12402 :
12403 : /********************************************************************/
12404 0 : INT rpc_server_connect(const char *host_name, const char *exp_name)
12405 : /********************************************************************\
12406 :
12407 : Routine: rpc_server_connect
12408 :
12409 : Purpose: Extablish a network connection to a remote MIDAS
12410 : server using a callback scheme.
12411 :
12412 : Input:
12413 : char *host_name IP address of host to connect to.
12414 :
12415 : INT port TPC port to connect to.
12416 :
12417 : char *exp_name Name of experiment to connect to. By using
12418 : this name, several experiments (e.g. online
12419 : DAQ and offline analysis) can run simultan-
12420 : eously on the same host.
12421 :
12422 : Output:
12423 : none
12424 :
12425 : Function value:
12426 : RPC_SUCCESS Successful completion
12427 : RPC_NET_ERROR Error in socket call
12428 : RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12429 : CM_UNDEF_EXP Undefined experiment on server
12430 :
12431 : \********************************************************************/
12432 : {
12433 : INT i, status;
12434 : INT remote_hw_type, hw_type;
12435 : char str[200], version[32], v1[32];
12436 : fd_set readfds;
12437 : struct timeval timeout;
12438 0 : int port = MIDAS_TCP_PORT;
12439 : char *s;
12440 :
12441 : #ifdef OS_WINNT
12442 : {
12443 : WSADATA WSAData;
12444 :
12445 : /* Start windows sockets */
12446 : if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12447 : return RPC_NET_ERROR;
12448 : }
12449 : #endif
12450 :
12451 : /* check if local connection */
12452 0 : if (host_name[0] == 0)
12453 0 : return RPC_SUCCESS;
12454 :
12455 : /* register system functions */
12456 0 : rpc_register_functions(rpc_get_internal_list(0), NULL);
12457 :
12458 : /* check if cm_connect_experiment was called */
12459 0 : if (_client_name.length() == 0) {
12460 0 : cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12461 0 : return RPC_NOT_REGISTERED;
12462 : }
12463 :
12464 : /* check if connection already exists */
12465 0 : if (_server_connection.send_sock != 0)
12466 0 : return RPC_SUCCESS;
12467 :
12468 0 : _server_connection.host_name = host_name;
12469 0 : _server_connection.exp_name = exp_name;
12470 0 : _server_connection.rpc_timeout = DEFAULT_RPC_TIMEOUT;
12471 :
12472 0 : bool listen_localhost = false;
12473 :
12474 0 : if (strcmp(host_name, "localhost") == 0)
12475 0 : listen_localhost = true;
12476 :
12477 : int lsock1, lport1;
12478 : int lsock2, lport2;
12479 : int lsock3, lport3;
12480 :
12481 0 : std::string errmsg;
12482 :
12483 0 : status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
12484 :
12485 0 : if (status != SS_SUCCESS) {
12486 0 : cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12487 0 : return RPC_NET_ERROR;
12488 : }
12489 :
12490 0 : status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
12491 :
12492 0 : if (status != SS_SUCCESS) {
12493 0 : cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12494 0 : return RPC_NET_ERROR;
12495 : }
12496 :
12497 0 : status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
12498 :
12499 0 : if (status != SS_SUCCESS) {
12500 0 : cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12501 0 : return RPC_NET_ERROR;
12502 : }
12503 :
12504 : /* extract port number from host_name */
12505 0 : mstrlcpy(str, host_name, sizeof(str));
12506 0 : s = strchr(str, ':');
12507 0 : if (s) {
12508 0 : *s = 0;
12509 0 : port = strtoul(s + 1, NULL, 0);
12510 : }
12511 :
12512 : int sock;
12513 :
12514 0 : status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12515 :
12516 0 : if (status != SS_SUCCESS) {
12517 0 : cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12518 0 : return RPC_NET_ERROR;
12519 : }
12520 :
12521 : /* connect to experiment */
12522 0 : if (exp_name[0] == 0)
12523 0 : sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12524 : else
12525 0 : sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12526 :
12527 0 : send(sock, str, strlen(str) + 1, 0);
12528 0 : i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12529 0 : ss_socket_close(&sock);
12530 0 : if (i <= 0) {
12531 0 : cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12532 0 : return RPC_NET_ERROR;
12533 : }
12534 :
12535 0 : status = version[0] = 0;
12536 0 : sscanf(str, "%d %s", &status, version);
12537 :
12538 0 : if (status == 2) {
12539 : /* message "undefined experiment" should be displayed by application */
12540 0 : return CM_UNDEF_EXP;
12541 : }
12542 :
12543 : /* print warning if version patch level doesn't agree */
12544 0 : strcpy(v1, version);
12545 0 : if (strchr(v1, '.'))
12546 0 : if (strchr(strchr(v1, '.') + 1, '.'))
12547 0 : *strchr(strchr(v1, '.') + 1, '.') = 0;
12548 :
12549 0 : strcpy(str, cm_get_version());
12550 0 : if (strchr(str, '.'))
12551 0 : if (strchr(strchr(str, '.') + 1, '.'))
12552 0 : *strchr(strchr(str, '.') + 1, '.') = 0;
12553 :
12554 0 : if (strcmp(v1, str) != 0) {
12555 0 : cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12556 : cm_get_version());
12557 : }
12558 :
12559 : /* wait for callback on send and recv socket with timeout */
12560 0 : FD_ZERO(&readfds);
12561 0 : FD_SET(lsock1, &readfds);
12562 0 : FD_SET(lsock2, &readfds);
12563 0 : FD_SET(lsock3, &readfds);
12564 :
12565 0 : timeout.tv_sec = _rpc_connect_timeout / 1000;
12566 0 : timeout.tv_usec = 0;
12567 :
12568 : do {
12569 0 : status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12570 :
12571 : /* if an alarm signal was cought, restart select with reduced timeout */
12572 0 : if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12573 0 : timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12574 :
12575 0 : } while (status == -1); /* dont return if an alarm signal was cought */
12576 :
12577 0 : if (!FD_ISSET(lsock1, &readfds)) {
12578 0 : cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12579 0 : ss_socket_close(&lsock1);
12580 0 : ss_socket_close(&lsock2);
12581 0 : ss_socket_close(&lsock3);
12582 0 : return RPC_NET_ERROR;
12583 : }
12584 :
12585 0 : _server_connection.send_sock = accept(lsock1, NULL, NULL);
12586 0 : _server_connection.recv_sock = accept(lsock2, NULL, NULL);
12587 0 : _server_connection.event_sock = accept(lsock3, NULL, NULL);
12588 :
12589 0 : if (_server_connection.send_sock == -1 || _server_connection.recv_sock == -1 || _server_connection.event_sock == -1) {
12590 0 : cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12591 0 : return RPC_NET_ERROR;
12592 : }
12593 :
12594 0 : ss_socket_close(&lsock1);
12595 0 : ss_socket_close(&lsock2);
12596 0 : ss_socket_close(&lsock3);
12597 :
12598 : /* set TCP_NODELAY option for better performance */
12599 0 : int flag = 1;
12600 0 : setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12601 0 : setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12602 :
12603 : /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12604 0 : flag = 2 * 1024 * 1024;
12605 0 : status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
12606 0 : if (status != 0)
12607 0 : cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12608 :
12609 : /* send local computer info */
12610 0 : std::string local_prog_name = rpc_get_name();
12611 0 : hw_type = rpc_get_hw_type();
12612 0 : sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12613 :
12614 0 : send(_server_connection.send_sock, str, strlen(str) + 1, 0);
12615 :
12616 : /* receive remote computer info */
12617 0 : i = recv_string(_server_connection.send_sock, str, sizeof(str), _rpc_connect_timeout);
12618 0 : if (i <= 0) {
12619 0 : cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12620 0 : return RPC_NET_ERROR;
12621 : }
12622 :
12623 0 : sscanf(str, "%d", &remote_hw_type);
12624 0 : _server_connection.remote_hw_type = remote_hw_type;
12625 :
12626 0 : ss_suspend_set_client_connection(&_server_connection);
12627 :
12628 0 : _rpc_is_remote = true;
12629 :
12630 0 : return RPC_SUCCESS;
12631 0 : }
12632 :
12633 : /********************************************************************/
12634 :
12635 0 : static RPC_CLIENT_CONNECTION* rpc_get_locked_client_connection(HNDLE hConn)
12636 : {
12637 0 : _client_connections_mutex.lock();
12638 0 : if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12639 0 : RPC_CLIENT_CONNECTION* c = _client_connections[hConn];
12640 0 : if (c && c->connected) {
12641 0 : _client_connections_mutex.unlock();
12642 0 : c->mutex.lock();
12643 0 : if (!c->connected) {
12644 : // disconnected while we were waiting for the lock
12645 0 : c->mutex.unlock();
12646 0 : return NULL;
12647 : }
12648 0 : return c;
12649 : }
12650 : }
12651 0 : _client_connections_mutex.unlock();
12652 0 : return NULL;
12653 : }
12654 :
12655 2 : static void rpc_client_shutdown()
12656 : {
12657 : /* close all open connections */
12658 :
12659 2 : _client_connections_mutex.lock();
12660 :
12661 2 : for (unsigned i = 0; i < _client_connections.size(); i++) {
12662 0 : RPC_CLIENT_CONNECTION* c = _client_connections[i];
12663 0 : if (c && c->connected) {
12664 0 : int index = c->index;
12665 : // must unlock the array, otherwise we hang -
12666 : // rpc_client_disconnect() will do rpc_call_client()
12667 : // which needs to lock the array to convert handle
12668 : // to connection pointer. Ouch! K.O. Dec 2020.
12669 0 : _client_connections_mutex.unlock();
12670 0 : rpc_client_disconnect(index, FALSE);
12671 0 : _client_connections_mutex.lock();
12672 : }
12673 : }
12674 :
12675 2 : for (unsigned i = 0; i < _client_connections.size(); i++) {
12676 0 : RPC_CLIENT_CONNECTION* c = _client_connections[i];
12677 : //printf("client connection %d %p\n", i, c);
12678 0 : if (c) {
12679 : //printf("client connection %d %p connected %d\n", i, c, c->connected);
12680 0 : if (!c->connected) {
12681 0 : delete c;
12682 0 : _client_connections[i] = NULL;
12683 : }
12684 : }
12685 : }
12686 :
12687 2 : _client_connections_mutex.unlock();
12688 :
12689 : /* close server connection from other clients */
12690 2 : for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12691 0 : if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12692 0 : send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12693 0 : _server_acceptions[i]->close();
12694 : }
12695 : }
12696 2 : }
12697 :
12698 : /********************************************************************/
12699 0 : INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
12700 : /********************************************************************\
12701 :
12702 : Routine: rpc_client_disconnect
12703 :
12704 : Purpose: Close a rpc connection to a MIDAS client
12705 :
12706 : Input:
12707 : HNDLE hConn Handle of connection
12708 : BOOL bShutdown Shut down remote server if TRUE
12709 :
12710 : Output:
12711 : none
12712 :
12713 : Function value:
12714 : RPC_SUCCESS Successful completion
12715 :
12716 : \********************************************************************/
12717 : {
12718 : /* notify server about exit */
12719 :
12720 : /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12721 :
12722 0 : rpc_client_call(hConn, bShutdown ? (RPC_ID_SHUTDOWN | RPC_NO_REPLY) : (RPC_ID_EXIT | RPC_NO_REPLY));
12723 :
12724 0 : return RPC_SUCCESS;
12725 : }
12726 :
12727 : /********************************************************************/
12728 0 : INT rpc_server_disconnect()
12729 : /********************************************************************\
12730 :
12731 : Routine: rpc_server_disconnect
12732 :
12733 : Purpose: Close a rpc connection to a MIDAS server and close all
12734 : server connections from other clients
12735 :
12736 : Input:
12737 : none
12738 :
12739 : Output:
12740 : none
12741 :
12742 : Function value:
12743 : RPC_SUCCESS Successful completion
12744 : RPC_NET_ERROR Error in socket call
12745 : RPC_NO_CONNECTION Maximum number of connections reached
12746 :
12747 : \********************************************************************/
12748 : {
12749 : static int rpc_server_disconnect_recursion_level = 0;
12750 :
12751 0 : if (rpc_server_disconnect_recursion_level)
12752 0 : return RPC_SUCCESS;
12753 :
12754 0 : rpc_server_disconnect_recursion_level = 1;
12755 :
12756 : /* flush remaining events */
12757 0 : rpc_flush_event();
12758 :
12759 : /* notify server about exit */
12760 0 : if (rpc_is_connected()) {
12761 0 : rpc_call(RPC_ID_EXIT);
12762 : }
12763 :
12764 : /* close sockets */
12765 0 : if (_server_connection.send_sock)
12766 0 : ss_socket_close(&_server_connection.send_sock);
12767 0 : if (_server_connection.recv_sock)
12768 0 : ss_socket_close(&_server_connection.recv_sock);
12769 0 : if (_server_connection.event_sock)
12770 0 : ss_socket_close(&_server_connection.event_sock);
12771 :
12772 0 : _server_connection.clear();
12773 :
12774 : /* remove semaphore */
12775 0 : if (_mutex_rpc)
12776 0 : ss_mutex_delete(_mutex_rpc);
12777 0 : _mutex_rpc = NULL;
12778 :
12779 0 : rpc_server_disconnect_recursion_level = 0;
12780 0 : return RPC_SUCCESS;
12781 : }
12782 :
12783 : /********************************************************************/
12784 1273 : bool rpc_is_remote(void)
12785 : /********************************************************************\
12786 :
12787 : Routine: rpc_is_remote
12788 :
12789 : Purpose: Return true if program is connected to a remote server
12790 :
12791 : Input:
12792 : none
12793 :
12794 : Output:
12795 : none
12796 :
12797 : Function value:
12798 : INT true is remote client connected to mserver, false if local connection
12799 :
12800 : \********************************************************************/
12801 : {
12802 1273 : return _rpc_is_remote;
12803 : }
12804 :
12805 : /********************************************************************/
12806 0 : bool rpc_is_connected(void)
12807 : /********************************************************************\
12808 :
12809 : Routine: rpc_is_connected
12810 :
12811 : Purpose: Return true if connection to mserver is still open
12812 :
12813 : Input:
12814 : none
12815 :
12816 : Output:
12817 : none
12818 :
12819 : Function value:
12820 : INT true if connection to mserver is still open, false if connection to mserver is already closed
12821 :
12822 : \********************************************************************/
12823 : {
12824 0 : return _server_connection.send_sock != 0;
12825 : }
12826 :
12827 : /********************************************************************/
12828 0 : std::string rpc_get_mserver_hostname(void)
12829 : /********************************************************************\
12830 :
12831 : Routine: rpc_get_mserver_hostname
12832 :
12833 : Purpose: Return the hostname of the mserver connection (host:port format)
12834 :
12835 : \********************************************************************/
12836 : {
12837 0 : return _server_connection.host_name;
12838 : }
12839 :
12840 : /********************************************************************/
12841 6 : bool rpc_is_mserver(void)
12842 : /********************************************************************\
12843 :
12844 : Routine: rpc_is_mserver
12845 :
12846 : Purpose: Return true if we are the mserver
12847 :
12848 : Function value:
12849 : INT "true" if we are the mserver
12850 :
12851 : \********************************************************************/
12852 : {
12853 6 : return _mserver_acception != NULL;
12854 : }
12855 :
12856 : /********************************************************************/
12857 2 : INT rpc_get_hw_type()
12858 : /********************************************************************\
12859 :
12860 : Routine: rpc_get_hw_type
12861 :
12862 : Purpose: get hardware information
12863 :
12864 : Function value:
12865 : INT combination of DRI_xxx bits
12866 :
12867 : \********************************************************************/
12868 : {
12869 : {
12870 : {
12871 : INT tmp_type, size;
12872 : DWORD dummy;
12873 : unsigned char *p;
12874 : float f;
12875 : double d;
12876 :
12877 2 : tmp_type = 0;
12878 :
12879 : /* test pointer size */
12880 2 : size = sizeof(p);
12881 2 : if (size == 2)
12882 0 : tmp_type |= DRI_16;
12883 2 : if (size == 4)
12884 0 : tmp_type |= DRI_32;
12885 2 : if (size == 8)
12886 2 : tmp_type |= DRI_64;
12887 :
12888 : /* test if little or big endian machine */
12889 2 : dummy = 0x12345678;
12890 2 : p = (unsigned char *) &dummy;
12891 2 : if (*p == 0x78)
12892 2 : tmp_type |= DRI_LITTLE_ENDIAN;
12893 0 : else if (*p == 0x12)
12894 0 : tmp_type |= DRI_BIG_ENDIAN;
12895 : else
12896 0 : cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
12897 :
12898 : /* floating point format */
12899 2 : f = (float) 1.2345;
12900 2 : dummy = 0;
12901 2 : memcpy(&dummy, &f, sizeof(f));
12902 2 : if ((dummy & 0xFF) == 0x19 &&
12903 2 : ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
12904 2 : && ((dummy >> 24) & 0xFF) == 0x3F)
12905 2 : tmp_type |= DRF_IEEE;
12906 0 : else if ((dummy & 0xFF) == 0x9E &&
12907 0 : ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
12908 0 : && ((dummy >> 24) & 0xFF) == 0x04)
12909 0 : tmp_type |= DRF_G_FLOAT;
12910 : else
12911 0 : cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12912 :
12913 2 : d = (double) 1.2345;
12914 2 : dummy = 0;
12915 2 : memcpy(&dummy, &d, sizeof(f));
12916 2 : if ((dummy & 0xFF) == 0x8D && /* little endian */
12917 2 : ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
12918 2 : && ((dummy >> 24) & 0xFF) == 0x12)
12919 2 : tmp_type |= DRF_IEEE;
12920 0 : else if ((dummy & 0xFF) == 0x83 && /* big endian */
12921 0 : ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
12922 0 : && ((dummy >> 24) & 0xFF) == 0x3F)
12923 0 : tmp_type |= DRF_IEEE;
12924 0 : else if ((dummy & 0xFF) == 0x13 &&
12925 0 : ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
12926 0 : && ((dummy >> 24) & 0xFF) == 0xC0)
12927 0 : tmp_type |= DRF_G_FLOAT;
12928 0 : else if ((dummy & 0xFF) == 0x9E &&
12929 0 : ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
12930 0 : && ((dummy >> 24) & 0xFF) == 0x04)
12931 0 : cm_msg(MERROR, "rpc_get_option",
12932 : "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
12933 : else
12934 0 : cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12935 :
12936 2 : return tmp_type;
12937 : }
12938 : }
12939 : }
12940 :
12941 : /**dox***************************************************************/
12942 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
12943 :
12944 : /********************************************************************/
12945 : /**
12946 : Set RPC option
12947 : @param hConn RPC connection handle, -1 for server connection, -2 for rpc connect timeout
12948 : @param item One of RPC_Oxxx
12949 : @param value Value to set
12950 : @return RPC_SUCCESS
12951 : */
12952 : #if 0
12953 : INT rpc_set_option(HNDLE hConn, INT item, INT value) {
12954 : switch (item) {
12955 : case RPC_OTIMEOUT:
12956 : if (hConn == -1)
12957 : _server_connection.rpc_timeout = value;
12958 : else if (hConn == -2)
12959 : _rpc_connect_timeout = value;
12960 : else {
12961 : RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
12962 : if (c) {
12963 : c->rpc_timeout = value;
12964 : c->mutex.unlock();
12965 : }
12966 : }
12967 : break;
12968 :
12969 : case RPC_NODELAY:
12970 : if (hConn == -1) {
12971 : setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12972 : } else {
12973 : RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
12974 : if (c) {
12975 : setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12976 : c->mutex.unlock();
12977 : }
12978 : }
12979 : break;
12980 :
12981 : default:
12982 : cm_msg(MERROR, "rpc_set_option", "invalid argument");
12983 : break;
12984 : }
12985 :
12986 : return 0;
12987 : }
12988 : #endif
12989 :
12990 : /********************************************************************/
12991 : /**
12992 : Get RPC timeout
12993 : @param hConn RPC connection handle, RPC_HNDLE_MSERVER for mserver connection, RPC_HNDLE_CONNECT for rpc connect timeout
12994 : @return timeout value
12995 : */
12996 0 : INT rpc_get_timeout(HNDLE hConn)
12997 : {
12998 0 : if (hConn == RPC_HNDLE_MSERVER) {
12999 0 : return _server_connection.rpc_timeout;
13000 0 : } else if (hConn == RPC_HNDLE_CONNECT) {
13001 0 : return _rpc_connect_timeout;
13002 : } else {
13003 0 : RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
13004 0 : if (c) {
13005 0 : int timeout = c->rpc_timeout;
13006 0 : c->mutex.unlock();
13007 0 : return timeout;
13008 : }
13009 : }
13010 0 : return 0;
13011 : }
13012 :
13013 : /********************************************************************/
13014 : /**
13015 : Set RPC timeout
13016 : @param hConn RPC connection handle, RPC_HNDLE_MSERVER for mserver connection, RPC_HNDLE_CONNECT for rpc connect timeout
13017 : @param timeout_msec RPC timeout in milliseconds
13018 : @param old_timeout_msec returns old value of RPC timeout in milliseconds
13019 : @return RPC_SUCCESS
13020 : */
13021 0 : INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
13022 : {
13023 : //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13024 :
13025 0 : if (hConn == RPC_HNDLE_MSERVER) {
13026 0 : if (old_timeout_msec)
13027 0 : *old_timeout_msec = _server_connection.rpc_timeout;
13028 0 : _server_connection.rpc_timeout = timeout_msec;
13029 0 : } else if (hConn == RPC_HNDLE_CONNECT) {
13030 0 : if (old_timeout_msec)
13031 0 : *old_timeout_msec = _rpc_connect_timeout;
13032 0 : _rpc_connect_timeout = timeout_msec;
13033 : } else {
13034 0 : RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
13035 0 : if (c) {
13036 0 : if (old_timeout_msec)
13037 0 : *old_timeout_msec = c->rpc_timeout;
13038 0 : c->rpc_timeout = timeout_msec;
13039 0 : c->mutex.unlock();
13040 : } else {
13041 0 : if (old_timeout_msec)
13042 0 : *old_timeout_msec = 0;
13043 : }
13044 : }
13045 0 : return RPC_SUCCESS;
13046 : }
13047 :
13048 :
13049 : /**dox***************************************************************/
13050 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
13051 :
13052 : /********************************************************************/
13053 0 : INT rpc_get_convert_flags(void)
13054 : /********************************************************************\
13055 :
13056 : Routine: rpc_get_convert_flags
13057 :
13058 : Purpose: Get RPC convert_flags for the mserver connection
13059 :
13060 : Function value:
13061 : INT Actual option
13062 :
13063 : \********************************************************************/
13064 : {
13065 0 : if (_mserver_acception)
13066 0 : return _mserver_acception->convert_flags;
13067 : else
13068 0 : return 0;
13069 : }
13070 :
13071 : static std::string _mserver_path;
13072 :
13073 : /********************************************************************/
13074 0 : const char *rpc_get_mserver_path()
13075 : /********************************************************************\
13076 :
13077 : Routine: rpc_get_mserver_path()
13078 :
13079 : Purpose: Get path of the mserver executable
13080 :
13081 : \********************************************************************/
13082 : {
13083 0 : return _mserver_path.c_str();
13084 : }
13085 :
13086 : /********************************************************************/
13087 0 : INT rpc_set_mserver_path(const char *path)
13088 : /********************************************************************\
13089 :
13090 : Routine: rpc_set_mserver_path
13091 :
13092 : Purpose: Remember the path of the mserver executable
13093 :
13094 : Input:
13095 : char *path Full path of the mserver executable
13096 :
13097 : Function value:
13098 : RPC_SUCCESS Successful completion
13099 :
13100 : \********************************************************************/
13101 : {
13102 0 : _mserver_path = path;
13103 0 : return RPC_SUCCESS;
13104 : }
13105 :
13106 : /********************************************************************/
13107 19 : std::string rpc_get_name()
13108 : /********************************************************************\
13109 :
13110 : Routine: rpc_get_name
13111 :
13112 : Purpose: Get name set by rpc_set_name
13113 :
13114 : Input:
13115 : none
13116 :
13117 : Output:
13118 : char* name The location pointed by *name receives a
13119 : copy of the _prog_name
13120 :
13121 : Function value:
13122 : RPC_SUCCESS Successful completion
13123 :
13124 : \********************************************************************/
13125 : {
13126 19 : return _client_name;
13127 : }
13128 :
13129 :
13130 : /********************************************************************/
13131 4 : INT rpc_set_name(const char *name)
13132 : /********************************************************************\
13133 :
13134 : Routine: rpc_set_name
13135 :
13136 : Purpose: Set name of actual program for further rpc connections
13137 :
13138 : Input:
13139 : char *name Program name, up to NAME_LENGTH chars,
13140 : no blanks
13141 :
13142 : Output:
13143 : none
13144 :
13145 : Function value:
13146 : RPC_SUCCESS Successful completion
13147 :
13148 : \********************************************************************/
13149 : {
13150 4 : _client_name = name;
13151 :
13152 4 : return RPC_SUCCESS;
13153 : }
13154 :
13155 :
13156 : /********************************************************************/
13157 0 : INT rpc_set_debug(void (*func)(const char *), INT mode)
13158 : /********************************************************************\
13159 :
13160 : Routine: rpc_set_debug
13161 :
13162 : Purpose: Set a function which is called on every RPC call to
13163 : display the function name and parameters of the RPC
13164 : call.
13165 :
13166 : Input:
13167 : void *func(char*) Pointer to function.
13168 : INT mode Debug mode
13169 :
13170 : Output:
13171 : none
13172 :
13173 : Function value:
13174 : RPC_SUCCESS Successful completion
13175 :
13176 : \********************************************************************/
13177 : {
13178 0 : _debug_print = func;
13179 0 : _debug_mode = mode;
13180 0 : return RPC_SUCCESS;
13181 : }
13182 :
13183 : /********************************************************************/
13184 0 : void rpc_debug_printf(const char *format, ...)
13185 : /********************************************************************\
13186 :
13187 : Routine: rpc_debug_print
13188 :
13189 : Purpose: Calls function set via rpc_set_debug to output a string.
13190 :
13191 : Input:
13192 : char *str Debug string
13193 :
13194 : Output:
13195 : none
13196 :
13197 : \********************************************************************/
13198 : {
13199 : va_list argptr;
13200 : char str[1000];
13201 :
13202 0 : if (_debug_mode) {
13203 0 : va_start(argptr, format);
13204 0 : vsprintf(str, (char *) format, argptr);
13205 0 : va_end(argptr);
13206 :
13207 0 : if (_debug_print) {
13208 0 : strcat(str, "\n");
13209 0 : _debug_print(str);
13210 : } else
13211 0 : puts(str);
13212 : }
13213 0 : }
13214 :
13215 : /********************************************************************/
13216 0 : void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
13217 0 : switch (arg_type) {
13218 : /* On the stack, the minimum parameter size is sizeof(int).
13219 : To avoid problems on little endian systems, treat all
13220 : smaller parameters as int's */
13221 0 : case TID_UINT8:
13222 : case TID_INT8:
13223 : case TID_CHAR:
13224 : case TID_UINT16:
13225 : case TID_INT16:
13226 0 : *((int *) arg) = va_arg(*arg_ptr, int);
13227 0 : break;
13228 :
13229 0 : case TID_INT32:
13230 : case TID_BOOL:
13231 0 : *((INT *) arg) = va_arg(*arg_ptr, INT);
13232 0 : break;
13233 :
13234 0 : case TID_UINT32:
13235 0 : *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13236 0 : break;
13237 :
13238 : /* float variables are passed as double by the compiler */
13239 0 : case TID_FLOAT:
13240 0 : *((float *) arg) = (float) va_arg(*arg_ptr, double);
13241 0 : break;
13242 :
13243 0 : case TID_DOUBLE:
13244 0 : *((double *) arg) = va_arg(*arg_ptr, double);
13245 0 : break;
13246 :
13247 0 : case TID_ARRAY:
13248 0 : *((char **) arg) = va_arg(*arg_ptr, char *);
13249 0 : break;
13250 : }
13251 0 : }
13252 :
13253 : /********************************************************************/
13254 0 : static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13255 : {
13256 0 : bool debug = false;
13257 :
13258 0 : if (debug) {
13259 0 : printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13260 0 : for (int i=0; rl.param[i].tid != 0; i++) {
13261 0 : int tid = rl.param[i].tid;
13262 0 : int flags = rl.param[i].flags;
13263 0 : int n = rl.param[i].n;
13264 0 : printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13265 : }
13266 : }
13267 :
13268 : char args[MAX_RPC_PARAMS][8];
13269 :
13270 0 : for (int i=0; rl.param[i].tid != 0; i++) {
13271 0 : int tid = rl.param[i].tid;
13272 0 : int flags = rl.param[i].flags;
13273 0 : int arg_type = 0;
13274 :
13275 0 : bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13276 0 : (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13277 0 : tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13278 :
13279 0 : if (bpointer)
13280 0 : arg_type = TID_ARRAY;
13281 : else
13282 0 : arg_type = tid;
13283 :
13284 : /* floats are passed as doubles, at least under NT */
13285 0 : if (tid == TID_FLOAT && !bpointer)
13286 0 : arg_type = TID_DOUBLE;
13287 :
13288 : //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13289 :
13290 0 : rpc_va_arg(&ap, arg_type, args[i]);
13291 : }
13292 :
13293 0 : size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13294 0 : char* buf = (char *)malloc(buf_size);
13295 0 : assert(buf);
13296 :
13297 0 : (*nc) = (NET_COMMAND*) buf;
13298 :
13299 : /* find out if we are on a big endian system */
13300 0 : bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13301 :
13302 0 : char* param_ptr = (*nc)->param;
13303 :
13304 0 : for (int i=0; rl.param[i].tid != 0; i++) {
13305 0 : int tid = rl.param[i].tid;
13306 0 : int flags = rl.param[i].flags;
13307 0 : int arg_type = 0;
13308 :
13309 0 : bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13310 0 : (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13311 0 : tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13312 :
13313 0 : if (bpointer)
13314 0 : arg_type = TID_ARRAY;
13315 : else
13316 0 : arg_type = tid;
13317 :
13318 : /* floats are passed as doubles, at least under NT */
13319 0 : if (tid == TID_FLOAT && !bpointer)
13320 0 : arg_type = TID_DOUBLE;
13321 :
13322 : /* get pointer to argument */
13323 : //char arg[8];
13324 : //rpc_va_arg(&ap, arg_type, arg);
13325 :
13326 0 : char* arg = args[i];
13327 :
13328 : /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13329 0 : if (bbig) {
13330 0 : if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13331 0 : arg[0] = arg[3];
13332 : }
13333 0 : if (tid == TID_UINT16 || tid == TID_INT16) {
13334 0 : arg[0] = arg[2];
13335 0 : arg[1] = arg[3];
13336 : }
13337 : }
13338 :
13339 0 : if (flags & RPC_IN) {
13340 0 : int arg_size = 0;
13341 :
13342 0 : if (bpointer)
13343 0 : arg_size = rpc_tid_size(tid);
13344 : else
13345 0 : arg_size = rpc_tid_size(arg_type);
13346 :
13347 : /* for strings, the argument size depends on the string length */
13348 0 : if (tid == TID_STRING || tid == TID_LINK) {
13349 0 : arg_size = 1 + strlen((char *) *((char **) arg));
13350 : }
13351 :
13352 : /* for varibale length arrays, the size is given by
13353 : the next parameter on the stack */
13354 0 : if (flags & RPC_VARARRAY) {
13355 : //va_list aptmp;
13356 : ////memcpy(&aptmp, &ap, sizeof(ap));
13357 : //va_copy(aptmp, ap);
13358 :
13359 : //char arg_tmp[8];
13360 : //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13361 :
13362 0 : const char* arg_tmp = args[i+1];
13363 :
13364 : /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13365 0 : if (flags & RPC_OUT)
13366 0 : arg_size = *((INT *) *((void **) arg_tmp));
13367 : else
13368 0 : arg_size = *((INT *) arg_tmp);
13369 :
13370 0 : *((INT *) param_ptr) = ALIGN8(arg_size);
13371 0 : param_ptr += ALIGN8(sizeof(INT));
13372 :
13373 : //va_end(aptmp);
13374 : }
13375 :
13376 0 : if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13377 0 : arg_size = rl.param[i].n;
13378 :
13379 : /* always align parameter size */
13380 0 : int param_size = ALIGN8(arg_size);
13381 :
13382 : {
13383 0 : size_t param_offset = (char *) param_ptr - (char *)(*nc);
13384 :
13385 0 : if (param_offset + param_size + 16 > buf_size) {
13386 0 : size_t new_size = param_offset + param_size + 1024;
13387 : //printf("resize nc %zu to %zu\n", buf_size, new_size);
13388 0 : buf = (char *) realloc(buf, new_size);
13389 0 : assert(buf);
13390 0 : buf_size = new_size;
13391 0 : (*nc) = (NET_COMMAND*) buf;
13392 0 : param_ptr = buf + param_offset;
13393 : }
13394 : }
13395 :
13396 0 : if (bpointer) {
13397 0 : if (debug) {
13398 0 : printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy pointer %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
13399 : }
13400 0 : memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13401 0 : } else if (tid == TID_FLOAT) {
13402 0 : if (debug) {
13403 0 : printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, double->float\n", i, flags, tid, arg_type, arg_size, param_size);
13404 : }
13405 : /* floats are passed as doubles on most systems */
13406 0 : *((float *) param_ptr) = (float) *((double *) arg);
13407 : } else {
13408 0 : if (debug) {
13409 0 : printf("encode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
13410 : }
13411 0 : memcpy(param_ptr, arg, arg_size);
13412 : }
13413 :
13414 0 : param_ptr += param_size;
13415 : }
13416 : }
13417 :
13418 0 : (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13419 :
13420 0 : if (debug)
13421 0 : printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13422 0 : }
13423 :
13424 : /********************************************************************/
13425 0 : static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13426 : {
13427 0 : bool debug = false;
13428 :
13429 0 : if (debug)
13430 0 : printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13431 :
13432 : /* extract result variables and place it to argument list */
13433 :
13434 0 : const char* param_ptr = buf;
13435 :
13436 0 : for (int i = 0; rl.param[i].tid != 0; i++) {
13437 0 : int tid = rl.param[i].tid;
13438 0 : int flags = rl.param[i].flags;
13439 0 : int arg_type = 0;
13440 :
13441 0 : bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13442 0 : (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13443 0 : tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13444 :
13445 0 : if (bpointer)
13446 0 : arg_type = TID_ARRAY;
13447 : else
13448 0 : arg_type = rl.param[i].tid;
13449 :
13450 0 : if (tid == TID_FLOAT && !bpointer)
13451 0 : arg_type = TID_DOUBLE;
13452 :
13453 : char arg[8];
13454 0 : rpc_va_arg(&ap, arg_type, arg);
13455 :
13456 0 : if (rl.param[i].flags & RPC_OUT) {
13457 :
13458 0 : if (param_ptr == NULL) {
13459 0 : cm_msg(MERROR, "rpc_call_decode", "routine \"%s\": no data in RPC reply, needed to decode an RPC_OUT parameter. param_ptr is NULL", rl.name);
13460 0 : return RPC_NET_ERROR;
13461 : }
13462 :
13463 0 : tid = rl.param[i].tid;
13464 0 : int arg_size = rpc_tid_size(tid);
13465 :
13466 0 : if (tid == TID_STRING || tid == TID_LINK)
13467 0 : arg_size = strlen((char *) (param_ptr)) + 1;
13468 :
13469 0 : if (flags & RPC_VARARRAY) {
13470 0 : arg_size = *((INT *) param_ptr);
13471 0 : param_ptr += ALIGN8(sizeof(INT));
13472 : }
13473 :
13474 0 : if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13475 0 : arg_size = rl.param[i].n;
13476 :
13477 : /* parameter size is always aligned */
13478 0 : int param_size = ALIGN8(arg_size);
13479 :
13480 : /* return parameters are always pointers */
13481 0 : if (*((char **) arg)) {
13482 0 : if (debug)
13483 0 : printf("decode param %d, flags 0x%x, tid %d, arg_type %d, arg_size %d, param_size %d, memcpy %d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
13484 0 : memcpy((void *) *((char **) arg), param_ptr, arg_size);
13485 : }
13486 :
13487 0 : param_ptr += param_size;
13488 : }
13489 : }
13490 :
13491 0 : return RPC_SUCCESS;
13492 : }
13493 :
13494 : /********************************************************************/
13495 0 : INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
13496 : /********************************************************************\
13497 :
13498 : Routine: rpc_client_call
13499 :
13500 : Purpose: Call a function on a MIDAS client
13501 :
13502 : Input:
13503 : INT hConn Client connection
13504 : INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13505 :
13506 : ... variable argument list
13507 :
13508 : Output:
13509 : (depends on argument list)
13510 :
13511 : Function value:
13512 : RPC_SUCCESS Successful completion
13513 : RPC_NET_ERROR Error in socket call
13514 : RPC_NO_CONNECTION No active connection
13515 : RPC_TIMEOUT Timeout in RPC call
13516 : RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13517 : RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13518 :
13519 : \********************************************************************/
13520 : {
13521 0 : RPC_CLIENT_CONNECTION* c = rpc_get_locked_client_connection(hConn);
13522 :
13523 0 : if (!c) {
13524 0 : cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13525 0 : return RPC_NO_CONNECTION;
13526 : }
13527 :
13528 : //printf("rpc_client_call: handle %d, connection: ", hConn);
13529 : //c->print();
13530 : //printf("\n");
13531 :
13532 : INT i, status;
13533 :
13534 0 : BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13535 0 : routine_id &= ~RPC_NO_REPLY;
13536 :
13537 : //if (rpc_no_reply)
13538 : // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13539 :
13540 : // make local copy of the client name just in case _client_connection is erased by another thread
13541 :
13542 : /* find rpc_index */
13543 :
13544 0 : int rpc_index = -1;
13545 0 : const char *rpc_name = NULL;
13546 : RPC_LIST rpc_entry;
13547 :
13548 0 : rpc_list_mutex.lock();
13549 0 : for (size_t i = 0; i < rpc_list.size(); i++) {
13550 0 : if (rpc_list[i].id == (int) routine_id) {
13551 0 : rpc_index = i;
13552 0 : rpc_name = rpc_list[rpc_index].name;
13553 0 : rpc_entry = rpc_list[rpc_index];
13554 0 : break;
13555 : }
13556 : }
13557 0 : rpc_list_mutex.unlock();
13558 :
13559 0 : if (rpc_index < 0) {
13560 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" with invalid RPC ID %d", c->client_name.c_str(), c->host_name.c_str(), routine_id);
13561 0 : c->mutex.unlock();
13562 0 : return RPC_INVALID_ID;
13563 : }
13564 :
13565 0 : NET_COMMAND *nc = NULL;
13566 :
13567 : /* examine variable argument list and convert it to parameter array */
13568 : va_list ap;
13569 0 : va_start(ap, routine_id);
13570 :
13571 0 : rpc_call_encode(ap, rpc_entry, &nc);
13572 :
13573 0 : va_end(ap);
13574 :
13575 0 : nc->header.routine_id = routine_id;
13576 :
13577 0 : if (rpc_no_reply)
13578 0 : nc->header.routine_id |= RPC_NO_REPLY;
13579 :
13580 0 : int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13581 :
13582 : /* in FAST TCP mode, only send call and return immediately */
13583 0 : if (rpc_no_reply) {
13584 0 : i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13585 :
13586 0 : if (i != send_size) {
13587 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": send_tcp() failed", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
13588 0 : free(nc);
13589 0 : c->mutex.unlock();
13590 0 : return RPC_NET_ERROR;
13591 : }
13592 :
13593 0 : free(nc);
13594 :
13595 0 : if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
13596 : //printf("rpc_client_call: routine_id %d is RPC_ID_EXIT %d or RPC_ID_SHUTDOWN %d, closing connection: ", routine_id, RPC_ID_EXIT, RPC_ID_SHUTDOWN);
13597 : //c->print();
13598 : //printf("\n");
13599 0 : c->close_locked();
13600 : }
13601 :
13602 0 : c->mutex.unlock();
13603 0 : return RPC_SUCCESS;
13604 : }
13605 :
13606 : /* in TCP mode, send and wait for reply on send socket */
13607 0 : i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13608 0 : if (i != send_size) {
13609 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": send_tcp() failed", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
13610 0 : c->mutex.unlock();
13611 0 : return RPC_NET_ERROR;
13612 : }
13613 :
13614 0 : free(nc);
13615 0 : nc = NULL;
13616 :
13617 0 : bool restore_watchdog_timeout = false;
13618 : BOOL watchdog_call;
13619 : DWORD watchdog_timeout;
13620 0 : cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13621 :
13622 : //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
13623 :
13624 0 : if (c->rpc_timeout >= (int) watchdog_timeout) {
13625 0 : restore_watchdog_timeout = true;
13626 0 : cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
13627 : }
13628 :
13629 0 : DWORD rpc_status = 0;
13630 0 : DWORD buf_size = 0;
13631 0 : char* buf = NULL;
13632 :
13633 : /* receive result on send socket */
13634 0 : status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
13635 :
13636 0 : if (restore_watchdog_timeout) {
13637 0 : cm_set_watchdog_params(watchdog_call, watchdog_timeout);
13638 : }
13639 :
13640 0 : if (status == SS_TIMEOUT) {
13641 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": timeout waiting for reply", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
13642 0 : if (buf)
13643 0 : free(buf);
13644 0 : c->mutex.unlock();
13645 0 : return RPC_TIMEOUT;
13646 : }
13647 :
13648 0 : if (status != SS_SUCCESS) {
13649 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": error, ss_recv_net_command() status %d", c->client_name.c_str(), c->host_name.c_str(), rpc_name, status);
13650 0 : if (buf)
13651 0 : free(buf);
13652 0 : c->mutex.unlock();
13653 0 : return RPC_NET_ERROR;
13654 : }
13655 :
13656 0 : c->mutex.unlock();
13657 :
13658 0 : if (rpc_status == RPC_INVALID_ID) {
13659 0 : cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": error, unknown RPC, status %d", c->client_name.c_str(), c->host_name.c_str(), rpc_name, rpc_status);
13660 0 : if (buf)
13661 0 : free(buf);
13662 0 : return rpc_status;
13663 : }
13664 :
13665 : /* extract result variables and place it to argument list */
13666 :
13667 0 : va_start(ap, routine_id);
13668 :
13669 0 : status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13670 :
13671 0 : if (status != RPC_SUCCESS) {
13672 0 : rpc_status = status;
13673 : }
13674 :
13675 0 : va_end(ap);
13676 :
13677 0 : if (buf)
13678 0 : free(buf);
13679 0 : buf = NULL;
13680 0 : buf_size = 0;
13681 :
13682 0 : return rpc_status;
13683 : }
13684 :
13685 : /********************************************************************/
13686 0 : INT rpc_call(DWORD routine_id, ...)
13687 : /********************************************************************\
13688 :
13689 : Routine: rpc_call
13690 :
13691 : Purpose: Call a function on a MIDAS server
13692 :
13693 : Input:
13694 : INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13695 :
13696 : ... variable argument list
13697 :
13698 : Output:
13699 : (depends on argument list)
13700 :
13701 : Function value:
13702 : RPC_SUCCESS Successful completion
13703 : RPC_NET_ERROR Error in socket call
13704 : RPC_NO_CONNECTION No active connection
13705 : RPC_TIMEOUT Timeout in RPC call
13706 : RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13707 : RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13708 :
13709 : \********************************************************************/
13710 : {
13711 : va_list ap;
13712 : INT i, status;
13713 :
13714 0 : BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13715 0 : routine_id &= ~RPC_NO_REPLY;
13716 :
13717 : //if (rpc_no_reply)
13718 : // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13719 :
13720 0 : int send_sock = _server_connection.send_sock;
13721 0 : int rpc_timeout = _server_connection.rpc_timeout;
13722 :
13723 0 : if (!send_sock) {
13724 0 : fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
13725 0 : return RPC_NET_ERROR;
13726 : }
13727 :
13728 0 : if (!_mutex_rpc) {
13729 : /* create a local mutex for multi-threaded applications */
13730 0 : ss_mutex_create(&_mutex_rpc, FALSE);
13731 : }
13732 :
13733 0 : status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
13734 0 : if (status != SS_SUCCESS) {
13735 0 : cm_msg(MERROR, "rpc_call", "Mutex timeout");
13736 0 : return RPC_MUTEX_TIMEOUT;
13737 : }
13738 :
13739 : /* find rpc definition */
13740 :
13741 0 : int idx = -1;
13742 0 : const char* rpc_name = NULL;
13743 : RPC_LIST rpc_entry;
13744 :
13745 0 : rpc_list_mutex.lock();
13746 :
13747 0 : for (size_t i = 0; i < rpc_list.size(); i++) {
13748 0 : if (rpc_list[i].id == (int) routine_id) {
13749 0 : idx = i;
13750 0 : rpc_name = rpc_list[idx].name;
13751 0 : rpc_entry = rpc_list[idx];
13752 0 : break;
13753 : }
13754 : }
13755 :
13756 0 : rpc_list_mutex.unlock();
13757 :
13758 0 : if (idx < 0) {
13759 0 : ss_mutex_release(_mutex_rpc);
13760 0 : cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
13761 0 : return RPC_INVALID_ID;
13762 : }
13763 :
13764 : /* prepare output buffer */
13765 :
13766 0 : NET_COMMAND* nc = NULL;
13767 :
13768 : /* examine variable argument list and convert it to parameter array */
13769 0 : va_start(ap, routine_id);
13770 :
13771 0 : rpc_call_encode(ap, rpc_entry, &nc);
13772 :
13773 0 : va_end(ap);
13774 :
13775 0 : nc->header.routine_id = routine_id;
13776 :
13777 0 : if (rpc_no_reply)
13778 0 : nc->header.routine_id |= RPC_NO_REPLY;
13779 :
13780 0 : int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13781 :
13782 : /* do not wait for reply if requested RPC_NO_REPLY */
13783 0 : if (rpc_no_reply) {
13784 0 : i = send_tcp(send_sock, (char *) nc, send_size, 0);
13785 :
13786 0 : if (i != send_size) {
13787 0 : ss_mutex_release(_mutex_rpc);
13788 0 : cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13789 0 : free(nc);
13790 0 : return RPC_NET_ERROR;
13791 : }
13792 :
13793 0 : ss_mutex_release(_mutex_rpc);
13794 0 : free(nc);
13795 0 : return RPC_SUCCESS;
13796 : }
13797 :
13798 : /* in TCP mode, send and wait for reply on send socket */
13799 0 : i = send_tcp(send_sock, (char *) nc, send_size, 0);
13800 0 : if (i != send_size) {
13801 0 : ss_mutex_release(_mutex_rpc);
13802 0 : cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13803 0 : free(nc);
13804 0 : return RPC_NET_ERROR;
13805 : }
13806 :
13807 0 : free(nc);
13808 0 : nc = NULL;
13809 :
13810 0 : bool restore_watchdog_timeout = false;
13811 : BOOL watchdog_call;
13812 : DWORD watchdog_timeout;
13813 0 : cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13814 :
13815 : //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
13816 :
13817 0 : if (!rpc_is_remote()) {
13818 : // if RPC is remote, we are connected to an mserver,
13819 : // the mserver takes care of watchdog timeouts.
13820 : // otherwise we should make sure the watchdog timeout
13821 : // is longer than the RPC timeout. K.O.
13822 0 : if (rpc_timeout >= (int) watchdog_timeout) {
13823 0 : restore_watchdog_timeout = true;
13824 0 : cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
13825 : }
13826 : }
13827 :
13828 0 : DWORD rpc_status = 0;
13829 0 : DWORD buf_size = 0;
13830 0 : char* buf = NULL;
13831 :
13832 0 : status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
13833 :
13834 0 : if (restore_watchdog_timeout) {
13835 0 : cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
13836 : }
13837 :
13838 : /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
13839 :
13840 0 : ss_mutex_release(_mutex_rpc);
13841 :
13842 : /* check for reply errors */
13843 :
13844 0 : if (status == SS_TIMEOUT) {
13845 0 : cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
13846 0 : if (buf)
13847 0 : free(buf);
13848 0 : abort(); // cannot continue - our mserver is not talking to us!
13849 : return RPC_TIMEOUT;
13850 : }
13851 :
13852 0 : if (status != SS_SUCCESS) {
13853 0 : cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
13854 0 : if (buf)
13855 0 : free(buf);
13856 0 : abort(); // cannot continue - something is wrong with our mserver connection
13857 : return RPC_NET_ERROR;
13858 : }
13859 :
13860 0 : if (rpc_status == RPC_INVALID_ID) {
13861 0 : cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
13862 0 : if (buf)
13863 0 : free(buf);
13864 0 : return rpc_status;
13865 : }
13866 :
13867 : /* extract result variables and place it to argument list */
13868 :
13869 0 : va_start(ap, routine_id);
13870 :
13871 0 : status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13872 :
13873 0 : if (status != RPC_SUCCESS) {
13874 0 : rpc_status = status;
13875 : }
13876 :
13877 0 : va_end(ap);
13878 :
13879 0 : if (buf)
13880 0 : free(buf);
13881 :
13882 0 : return rpc_status;
13883 : }
13884 :
13885 :
13886 : /********************************************************************/
13887 0 : INT rpc_set_opt_tcp_size(INT tcp_size) {
13888 : INT old;
13889 :
13890 0 : old = _opt_tcp_size;
13891 0 : _opt_tcp_size = tcp_size;
13892 0 : return old;
13893 : }
13894 :
13895 0 : INT rpc_get_opt_tcp_size() {
13896 0 : return _opt_tcp_size;
13897 : }
13898 :
13899 : /**dox***************************************************************/
13900 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
13901 :
13902 : /********************************************************************/
13903 : /**
13904 : Fast send_event routine which bypasses the RPC layer and
13905 : sends the event directly at the TCP level.
13906 : @param buffer_handle Handle of the buffer to send the event to.
13907 : Must be obtained via bm_open_buffer.
13908 : @param source Address of the event to send. It must have
13909 : a proper event header.
13910 : @param buf_size Size of event in bytes with header.
13911 : @param async_flag BM_WAIT / BM_NO_WAIT flag. In BM_NO_WAIT mode, the
13912 : function returns immediately if it cannot
13913 : send the event over the network. In BM_WAIT
13914 : mode, it waits until the packet is sent
13915 : (blocking).
13916 : @param mode Determines in which mode the event is sent.
13917 : If zero, use RPC socket, if one, use special
13918 : event socket to bypass RPC layer on the
13919 : server side.
13920 :
13921 : @return BM_INVALID_PARAM, BM_ASYNC_RETURN, RPC_SUCCESS, RPC_NET_ERROR,
13922 : RPC_NO_CONNECTION, RPC_EXCEED_BUFFER
13923 : */
13924 0 : INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
13925 : {
13926 0 : if (rpc_is_remote()) {
13927 0 : return rpc_send_event1(buffer_handle, pevent);
13928 : } else {
13929 0 : return bm_send_event(buffer_handle, pevent, unused, async_flag);
13930 : }
13931 : }
13932 :
13933 : /********************************************************************/
13934 : /**
13935 : Send event to mserver using the event socket connection, bypassing the RPC layer
13936 : @param buffer_handle Handle of the buffer to send the event to.
13937 : Must be obtained via bm_open_buffer.
13938 : @param event Pointer to event header
13939 :
13940 : @return RPC_SUCCESS, RPC_NET_ERROR, RPC_NO_CONNECTION
13941 : */
13942 0 : INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
13943 : {
13944 0 : const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
13945 0 : return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
13946 : }
13947 :
13948 0 : INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
13949 : {
13950 0 : if (sg_n < 1) {
13951 0 : cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
13952 0 : return BM_INVALID_SIZE;
13953 : }
13954 :
13955 0 : if (sg_ptr[0] == NULL) {
13956 0 : cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
13957 0 : return BM_INVALID_SIZE;
13958 : }
13959 :
13960 0 : if (sg_len[0] < sizeof(EVENT_HEADER)) {
13961 0 : cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_len[0] value %d is smaller than event header size %d", (int)sg_len[0], (int)sizeof(EVENT_HEADER));
13962 0 : return BM_INVALID_SIZE;
13963 : }
13964 :
13965 0 : const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
13966 :
13967 0 : const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
13968 0 : const DWORD data_size = pevent->data_size; // 32-bit unsigned value
13969 :
13970 0 : if (data_size == 0) {
13971 0 : cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
13972 0 : return BM_INVALID_SIZE;
13973 : }
13974 :
13975 0 : if (data_size > MAX_DATA_SIZE) {
13976 0 : cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size %d (0x%x) maximum is %d (0x%x)", data_size, data_size, MAX_DATA_SIZE, MAX_DATA_SIZE);
13977 0 : return BM_INVALID_SIZE;
13978 : }
13979 :
13980 0 : const size_t event_size = sizeof(EVENT_HEADER) + data_size;
13981 0 : const size_t total_size = ALIGN8(event_size);
13982 :
13983 0 : size_t count = 0;
13984 0 : for (int i=0; i<sg_n; i++) {
13985 0 : count += sg_len[i];
13986 : }
13987 :
13988 0 : if (count != event_size) {
13989 0 : cm_msg(MERROR, "rpc_send_event_sg", "data size mismatch: event data_size %d, event_size %d not same as sum of sg_len %d", (int)data_size, (int)event_size, (int)count);
13990 0 : return BM_INVALID_SIZE;
13991 : }
13992 :
13993 : // protect non-atomic access to _server_connection.event_sock. K.O.
13994 :
13995 0 : std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
13996 :
13997 : //printf("rpc_send_event_sg: pevent %p, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", pevent, pevent->event_id, pevent->serial_number, (int)data_size, (int)event_size, (int)total_size);
13998 :
13999 0 : if (_server_connection.event_sock == 0) {
14000 0 : return RPC_NO_CONNECTION;
14001 : }
14002 :
14003 : //
14004 : // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14005 : //
14006 : // 4 bytes of buffer handle
14007 : // 16 bytes of event header, includes data_size
14008 : // ALIGN8(data_size) bytes of event data
14009 : //
14010 :
14011 : int status;
14012 :
14013 : /* send buffer handle */
14014 :
14015 : assert(sizeof(DWORD) == 4);
14016 0 : DWORD bh_buf = buffer_handle;
14017 :
14018 0 : status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14019 0 : if (status != SS_SUCCESS) {
14020 0 : ss_socket_close(&_server_connection.event_sock);
14021 0 : cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14022 0 : return RPC_NET_ERROR;
14023 : }
14024 :
14025 : /* send data */
14026 :
14027 0 : for (int i=0; i<sg_n; i++) {
14028 0 : status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
14029 0 : if (status != SS_SUCCESS) {
14030 0 : ss_socket_close(&_server_connection.event_sock);
14031 0 : cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14032 0 : return RPC_NET_ERROR;
14033 : }
14034 : }
14035 :
14036 : /* send padding */
14037 :
14038 0 : if (count < total_size) {
14039 0 : char padding[8] = { 0,0,0,0,0,0,0,0 };
14040 0 : size_t padlen = total_size - count;
14041 0 : assert(padlen < 8);
14042 0 : status = ss_write_tcp(_server_connection.event_sock, padding, padlen);
14043 0 : if (status != SS_SUCCESS) {
14044 0 : ss_socket_close(&_server_connection.event_sock);
14045 0 : cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14046 0 : return RPC_NET_ERROR;
14047 : }
14048 : }
14049 :
14050 0 : return RPC_SUCCESS;
14051 0 : }
14052 :
14053 : /********************************************************************/
14054 : /**
14055 : Send event residing in the TCP cache buffer filled by
14056 : rpc_send_event. This routine should be called when a
14057 : run is stopped.
14058 :
14059 : @return RPC_SUCCESS, RPC_NET_ERROR
14060 : */
14061 0 : INT rpc_flush_event() {
14062 0 : return RPC_SUCCESS;
14063 : }
14064 :
14065 : /********************************************************************/
14066 :
14067 : struct TR_FIFO {
14068 : int transition = 0;
14069 : int run_number = 0;
14070 : time_t trans_time = 0;
14071 : int sequence_number = 0;
14072 : };
14073 :
14074 : static std::mutex _tr_fifo_mutex;
14075 : static TR_FIFO _tr_fifo[10];
14076 : static int _tr_fifo_wp = 0;
14077 : static int _tr_fifo_rp = 0;
14078 :
14079 0 : static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
14080 : /********************************************************************\
14081 :
14082 : Routine: rpc_transition_dispatch
14083 :
14084 : Purpose: Gets called when a transition function was registered and
14085 : a transition occured. Internal use only.
14086 :
14087 : Input:
14088 : INT idx RPC function ID
14089 : void *prpc_param RPC parameters
14090 :
14091 : Output:
14092 : none
14093 :
14094 : Function value:
14095 : INT return value from called user routine
14096 :
14097 : \********************************************************************/
14098 : {
14099 : /* erase error string */
14100 0 : *(CSTRING(2)) = 0;
14101 :
14102 0 : if (idx == RPC_RC_TRANSITION) {
14103 : // find registered handler
14104 : // NB: this code should match same code in cm_transition_call_direct()
14105 : // NB: only use the first handler, this is how MIDAS always worked
14106 : // NB: we could run all handlers, but we can return the status and error string of only one of them.
14107 0 : _trans_table_mutex.lock();
14108 0 : size_t n = _trans_table.size();
14109 0 : _trans_table_mutex.unlock();
14110 :
14111 0 : for (size_t i = 0; i < n; i++) {
14112 0 : _trans_table_mutex.lock();
14113 0 : TRANS_TABLE tt = _trans_table[i];
14114 0 : _trans_table_mutex.unlock();
14115 :
14116 0 : if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14117 0 : if (tt.func) {
14118 : /* execute callback if defined */
14119 0 : return tt.func(CINT(1), CSTRING(2));
14120 : } else {
14121 0 : std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14122 : /* store transition in FIFO */
14123 0 : _tr_fifo[_tr_fifo_wp].transition = CINT(0);
14124 0 : _tr_fifo[_tr_fifo_wp].run_number = CINT(1);
14125 0 : _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
14126 0 : _tr_fifo[_tr_fifo_wp].sequence_number = CINT(4);
14127 0 : _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14128 : // implicit unlock
14129 0 : return RPC_SUCCESS;
14130 0 : }
14131 : }
14132 : }
14133 : // no handler for this transition
14134 0 : cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14135 0 : return CM_SUCCESS;
14136 : } else {
14137 0 : cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14138 0 : return RPC_INVALID_ID;
14139 : }
14140 : }
14141 :
14142 : /********************************************************************/
14143 0 : int cm_query_transition(int *transition, int *run_number, int *trans_time)
14144 : /********************************************************************\
14145 :
14146 : Routine: cm_query_transition
14147 :
14148 : Purpose: Query system if transition has occured. Normally, one
14149 : registers callbacks for transitions via
14150 : cm_register_transition. In some environments however,
14151 : callbacks are not possible. In that case one spciefies
14152 : a NULL pointer as the callback routine and can query
14153 : transitions "manually" by calling this functions. A small
14154 : FIFO takes care that no transition is lost if this functions
14155 : did not get called between some transitions.
14156 :
14157 : Output:
14158 : INT *transition Type of transition, one of TR_xxx
14159 : INT *run_nuber Run number for transition
14160 : time_t *trans_time Time (in UNIX time) of transition
14161 :
14162 : Function value:
14163 : FALSE No transition occured since last call
14164 : TRUE Transition occured
14165 :
14166 : \********************************************************************/
14167 : {
14168 0 : std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14169 :
14170 0 : if (_tr_fifo_wp == _tr_fifo_rp)
14171 0 : return FALSE;
14172 :
14173 0 : if (transition)
14174 0 : *transition = _tr_fifo[_tr_fifo_rp].transition;
14175 :
14176 0 : if (run_number)
14177 0 : *run_number = _tr_fifo[_tr_fifo_rp].run_number;
14178 :
14179 0 : if (trans_time)
14180 0 : *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14181 :
14182 0 : _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14183 :
14184 : // implicit unlock
14185 0 : return TRUE;
14186 0 : }
14187 :
14188 : /********************************************************************\
14189 : * server functions *
14190 : \********************************************************************/
14191 :
14192 : #if 0
14193 : void debug_dump(unsigned char *p, int size)
14194 : {
14195 : int i, j;
14196 : unsigned char c;
14197 :
14198 : for (i = 0; i < (size - 1) / 16 + 1; i++) {
14199 : printf("%p ", p + i * 16);
14200 : for (j = 0; j < 16; j++)
14201 : if (i * 16 + j < size)
14202 : printf("%02X ", p[i * 16 + j]);
14203 : else
14204 : printf(" ");
14205 : printf(" ");
14206 :
14207 : for (j = 0; j < 16; j++) {
14208 : c = p[i * 16 + j];
14209 : if (i * 16 + j < size)
14210 : printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14211 : }
14212 : printf("\n");
14213 : }
14214 :
14215 : printf("\n");
14216 : }
14217 : #endif
14218 :
14219 : /********************************************************************/
14220 0 : static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
14221 : /********************************************************************\
14222 :
14223 : Routine: recv_net_command
14224 :
14225 : Purpose: TCP receive routine with local cache. To speed up network
14226 : performance, a 64k buffer is read in at once and split into
14227 : several RPC command on successive calls to recv_net_command.
14228 : Therefore, the number of recv() calls is minimized.
14229 :
14230 : This routine is ment to be called by the server process.
14231 : Clients should call recv_tcp instead.
14232 :
14233 : Input:
14234 : INT idx Index of server connection
14235 : DWORD buffer_size Size of the buffer in bytes.
14236 : INT flags Flags passed to recv()
14237 : INT convert_flags Convert flags needed for big/little
14238 : endian conversion
14239 :
14240 : Output:
14241 : char *buffer Network receive buffer.
14242 : INT *remaining Remaining data in cache
14243 :
14244 : Function value:
14245 : INT Same as recv()
14246 :
14247 : \********************************************************************/
14248 : {
14249 0 : char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14250 :
14251 0 : RPC_SERVER_ACCEPTION* sa = rpc_get_server_acception(idx);
14252 :
14253 0 : int sock = sa->recv_sock;
14254 :
14255 0 : if (!sa->net_buffer) {
14256 0 : if (sa->is_mserver)
14257 0 : sa->net_buffer_size = NET_TCP_SIZE;
14258 : else
14259 0 : sa->net_buffer_size = NET_BUFFER_SIZE;
14260 :
14261 0 : sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14262 : //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14263 0 : sa->write_ptr = 0;
14264 0 : sa->read_ptr = 0;
14265 0 : sa->misalign = 0;
14266 : }
14267 0 : if (!sa->net_buffer) {
14268 0 : cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14269 0 : return -1;
14270 : }
14271 :
14272 0 : int copied = 0;
14273 0 : int param_size = -1;
14274 :
14275 0 : int write_ptr = sa->write_ptr;
14276 0 : int read_ptr = sa->read_ptr;
14277 0 : int misalign = sa->misalign;
14278 0 : char *net_buffer = sa->net_buffer;
14279 :
14280 : do {
14281 0 : if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14282 0 : if (param_size == -1) {
14283 0 : if (copied > 0) {
14284 : /* assemble split header */
14285 0 : memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14286 0 : NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14287 0 : param_size = (INT) nc->header.param_size;
14288 : } else {
14289 0 : NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14290 0 : param_size = (INT) nc->header.param_size;
14291 : }
14292 :
14293 0 : if (sa->convert_flags)
14294 0 : rpc_convert_single(¶m_size, TID_UINT32, 0, sa->convert_flags);
14295 : }
14296 :
14297 : //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14298 :
14299 : /* check if parameters fit in buffer */
14300 0 : if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14301 0 : int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14302 0 : char *p = (char *) realloc(*pbuf, new_size);
14303 : //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14304 0 : if (p == NULL) {
14305 0 : cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14306 0 : sa->read_ptr = 0;
14307 0 : sa->write_ptr = 0;
14308 0 : return -1;
14309 : }
14310 0 : *pbuf = p;
14311 0 : *pbufsize = new_size;
14312 : }
14313 :
14314 0 : buffer = *pbuf;
14315 :
14316 : /* check if we have all parameters in buffer */
14317 0 : if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14318 0 : break;
14319 : }
14320 :
14321 : /* not enough data, so copy partially and get new */
14322 0 : int size = write_ptr - read_ptr;
14323 :
14324 0 : if (size > 0) {
14325 0 : memcpy(buffer + copied, net_buffer + read_ptr, size);
14326 0 : copied += size;
14327 0 : read_ptr = write_ptr;
14328 : }
14329 : #ifdef OS_UNIX
14330 : do {
14331 0 : write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14332 :
14333 : /* don't return if an alarm signal was cought */
14334 0 : } while (write_ptr == -1 && errno == EINTR);
14335 : #else
14336 : write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14337 : #endif
14338 :
14339 : /* abort if connection broken */
14340 0 : if (write_ptr <= 0) {
14341 0 : if (write_ptr == 0)
14342 0 : cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14343 : else
14344 0 : cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14345 :
14346 0 : if (remaining)
14347 0 : *remaining = 0;
14348 :
14349 0 : return write_ptr;
14350 : }
14351 :
14352 0 : read_ptr = misalign;
14353 0 : write_ptr += misalign;
14354 :
14355 0 : misalign = write_ptr % 8;
14356 0 : } while (TRUE);
14357 :
14358 : /* copy rest of parameters */
14359 0 : int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14360 0 : memcpy(buffer + copied, net_buffer + read_ptr, size);
14361 0 : read_ptr += size;
14362 :
14363 0 : if (remaining) {
14364 : /* don't keep rpc_server_receive in an infinite loop */
14365 0 : if (write_ptr - read_ptr < param_size)
14366 0 : *remaining = 0;
14367 : else
14368 0 : *remaining = write_ptr - read_ptr;
14369 : }
14370 :
14371 0 : sa->write_ptr = write_ptr;
14372 0 : sa->read_ptr = read_ptr;
14373 0 : sa->misalign = misalign;
14374 :
14375 0 : return size + copied;
14376 : }
14377 :
14378 :
14379 : /********************************************************************/
14380 0 : INT recv_tcp_check(int sock)
14381 : /********************************************************************\
14382 :
14383 : Routine: recv_tcp_check
14384 :
14385 : Purpose: Check if in TCP receive buffer associated with sock is
14386 : some data. Called by ss_suspend.
14387 :
14388 : Input:
14389 : INT sock TCP receive socket
14390 :
14391 : Output:
14392 : none
14393 :
14394 : Function value:
14395 : INT count Number of bytes remaining in TCP buffer
14396 :
14397 : \********************************************************************/
14398 : {
14399 : /* figure out to which connection socket belongs */
14400 0 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14401 0 : if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14402 0 : return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14403 : }
14404 :
14405 0 : return 0;
14406 : }
14407 :
14408 :
14409 : /********************************************************************/
14410 0 : static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
14411 : /********************************************************************\
14412 :
14413 : Routine: recv_event_server_realloc
14414 :
14415 : Purpose: receive events sent by rpc_send_event()
14416 :
14417 : Input:
14418 : INT idx Index of server connection
14419 : DWORD buffer_size Size of the buffer in bytes.
14420 : INT flags Flags passed to recv()
14421 : INT convert_flags Convert flags needed for big/little
14422 : endian conversion
14423 :
14424 : Output:
14425 : char *buffer Network receive buffer.
14426 : INT *remaining Remaining data in cache
14427 :
14428 : Function value:
14429 : INT Same as recv()
14430 :
14431 : \********************************************************************/
14432 : {
14433 0 : int sock = psa->event_sock;
14434 :
14435 : //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14436 :
14437 0 : const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14438 :
14439 : char header_buf[header_size];
14440 :
14441 : // First read the header.
14442 : //
14443 : // Data format is:
14444 : // INT buffer handle (4 bytes)
14445 : // EVENT_HEADER (16 bytes)
14446 : // event data
14447 : // ALIGN8() padding
14448 : // ...next event
14449 :
14450 0 : int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14451 :
14452 0 : if (hrd == 0) {
14453 : // timeout waiting for data
14454 0 : return 0;
14455 : }
14456 :
14457 : /* abort if connection broken */
14458 0 : if (hrd < 0) {
14459 0 : cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14460 0 : return -1;
14461 : }
14462 :
14463 0 : if (hrd < (int) header_size) {
14464 0 : int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14465 :
14466 : /* abort if connection broken */
14467 0 : if (hrd1 <= 0) {
14468 0 : cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14469 0 : return -1;
14470 : }
14471 :
14472 0 : hrd += hrd1;
14473 : }
14474 :
14475 : /* abort if connection broken */
14476 0 : if (hrd != (int) header_size) {
14477 0 : cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14478 0 : return -1;
14479 : }
14480 :
14481 0 : INT *pbh = (INT *) header_buf;
14482 0 : EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14483 :
14484 : /* convert header little endian/big endian */
14485 0 : if (psa->convert_flags) {
14486 0 : rpc_convert_single(&pbh, TID_INT32, 0, psa->convert_flags);
14487 0 : rpc_convert_single(&pevent->event_id, TID_INT16, 0, psa->convert_flags);
14488 0 : rpc_convert_single(&pevent->trigger_mask, TID_INT16, 0, psa->convert_flags);
14489 0 : rpc_convert_single(&pevent->serial_number, TID_UINT32, 0, psa->convert_flags);
14490 0 : rpc_convert_single(&pevent->time_stamp, TID_UINT32, 0, psa->convert_flags);
14491 0 : rpc_convert_single(&pevent->data_size, TID_UINT32, 0, psa->convert_flags);
14492 : }
14493 :
14494 0 : int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14495 0 : int total_size = ALIGN8(event_size);
14496 :
14497 : //printf("recv_event_server: buffer_handle %d, event_id 0x%04x, serial 0x%08x, data_size %d, event_size %d, total_size %d\n", *pbh, pevent->event_id, pevent->serial_number, pevent->data_size, event_size, total_size);
14498 :
14499 0 : if (pevent->data_size == 0) {
14500 0 : for (int i=0; i<5; i++) {
14501 0 : printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14502 : }
14503 0 : abort();
14504 : }
14505 :
14506 : /* check for sane event size */
14507 0 : if (event_size <= 0 || total_size <= 0) {
14508 0 : cm_msg(MERROR, "recv_event_server",
14509 : "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14510 : event_size, total_size);
14511 0 : return -1;
14512 : }
14513 :
14514 : //printf("recv_event_server: idx %d, bh %d, event header: id %d, mask %d, serial %d, data_size %d, event_size %d, total_size %d\n", idx, *pbh, pevent->event_id, pevent->trigger_mask, pevent->serial_number, pevent->data_size, event_size, total_size);
14515 :
14516 :
14517 0 : int bufsize = sizeof(INT) + total_size;
14518 :
14519 : // Second, check that output buffer is big enough
14520 :
14521 : /* check if data part fits in buffer */
14522 0 : if (*pbuffer_size < bufsize) {
14523 0 : int newsize = 1024 + ALIGN8(bufsize);
14524 :
14525 : //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14526 :
14527 0 : char *newbuf = (char *) realloc(*pbuffer, newsize);
14528 0 : if (newbuf == NULL) {
14529 0 : cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14530 : newsize);
14531 0 : return -1;
14532 : }
14533 0 : *pbuffer = newbuf;
14534 0 : *pbuffer_size = newsize;
14535 : }
14536 :
14537 : // Third, copy header into output buffer
14538 :
14539 0 : memcpy(*pbuffer, header_buf, header_size);
14540 :
14541 : // Forth, read the event data
14542 :
14543 0 : int to_read = sizeof(INT) + total_size - header_size;
14544 0 : int rptr = header_size;
14545 :
14546 0 : if (to_read > 0) {
14547 0 : int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14548 :
14549 : /* abort if connection broken */
14550 0 : if (drd <= 0) {
14551 0 : cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14552 0 : return -1;
14553 : }
14554 : }
14555 :
14556 0 : return bufsize;
14557 : }
14558 :
14559 :
14560 : /********************************************************************/
14561 2 : INT rpc_register_server(int port, int *plsock, int *pport)
14562 : /********************************************************************\
14563 :
14564 : Routine: rpc_register_listener
14565 :
14566 : Purpose: Register the calling process as a MIDAS RPC server. Note
14567 : that cm_connnect_experiment must be called prior to any call of
14568 : rpc_register_server.
14569 :
14570 : Input:
14571 : INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14572 :
14573 : Output:
14574 : int *plsock Listener socket, can be NULL
14575 : int *pport Port under which server is listening, can be NULL
14576 :
14577 : Function value:
14578 : RPC_SUCCESS Successful completion
14579 : RPC_NET_ERROR Error in socket call
14580 : RPC_NOT_REGISTERED cm_connect_experiment was not called
14581 :
14582 : \********************************************************************/
14583 : {
14584 : int status;
14585 : int lsock;
14586 :
14587 2 : status = rpc_register_listener(port, NULL, &lsock, pport);
14588 2 : if (status != RPC_SUCCESS)
14589 0 : return status;
14590 :
14591 2 : status = ss_suspend_set_client_listener(lsock);
14592 2 : if (status != SS_SUCCESS)
14593 0 : return status;
14594 :
14595 2 : if (plsock)
14596 2 : *plsock = lsock;
14597 :
14598 2 : return RPC_SUCCESS;
14599 : }
14600 :
14601 : /********************************************************************/
14602 2 : INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
14603 : /********************************************************************\
14604 :
14605 : Routine: rpc_register_listener
14606 :
14607 : Purpose: Register the calling process as a MIDAS RPC server. Note
14608 : that cm_connnect_experiment must be called prior to any call of
14609 : rpc_register_listener.
14610 :
14611 : Input:
14612 : INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14613 : INT *func Default dispatch function
14614 :
14615 : Output:
14616 : int *plsock Listener socket, should not be NULL
14617 : int *pport Port under which server is listening, can be NULL
14618 :
14619 : Function value:
14620 : RPC_SUCCESS Successful completion
14621 : RPC_NET_ERROR Error in socket call
14622 : RPC_NOT_REGISTERED cm_connect_experiment was not called
14623 :
14624 : \********************************************************************/
14625 : {
14626 : /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
14627 2 : rpc_register_functions(rpc_get_internal_list(0), func);
14628 :
14629 : /* create a socket for listening */
14630 2 : int lsock = 0;
14631 2 : int lport = 0;
14632 2 : std::string errmsg;
14633 :
14634 2 : int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
14635 :
14636 2 : if (status != SS_SUCCESS) {
14637 0 : cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
14638 0 : return RPC_NET_ERROR;
14639 : }
14640 :
14641 : /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
14642 : #if defined(F_SETFD) && defined(FD_CLOEXEC)
14643 2 : status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
14644 2 : if (status < 0) {
14645 0 : cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
14646 0 : return RPC_NET_ERROR;
14647 : }
14648 : #endif
14649 :
14650 : /* return port wich OS has choosen */
14651 2 : if (pport) {
14652 2 : *pport = lport;
14653 : }
14654 :
14655 2 : if (plsock)
14656 2 : *plsock = lsock;
14657 :
14658 : //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
14659 :
14660 2 : return RPC_SUCCESS;
14661 2 : }
14662 :
14663 : typedef struct {
14664 : midas_thread_t thread_id;
14665 : int buffer_size;
14666 : char *buffer;
14667 : } TLS_POINTER;
14668 :
14669 : static TLS_POINTER *tls_buffer = NULL;
14670 : static int tls_size = 0;
14671 :
14672 : /********************************************************************/
14673 0 : INT rpc_execute(INT sock, char *buffer, INT convert_flags)
14674 : /********************************************************************\
14675 :
14676 : Routine: rpc_execute
14677 :
14678 : Purpose: Execute a RPC command received over the network
14679 :
14680 : Input:
14681 : INT sock TCP socket to which the result should be
14682 : send back
14683 :
14684 : char *buffer Command buffer
14685 : INT convert_flags Flags for data conversion
14686 :
14687 : Output:
14688 : none
14689 :
14690 : Function value:
14691 : RPC_SUCCESS Successful completion
14692 : RPC_INVALID_ID Invalid routine_id received
14693 : RPC_NET_ERROR Error in socket call
14694 : RPC_EXCEED_BUFFER Not enough memory for network buffer
14695 : RPC_SHUTDOWN Shutdown requested
14696 : SS_ABORT TCP connection broken
14697 : SS_EXIT TCP connection closed
14698 :
14699 : \********************************************************************/
14700 : {
14701 : INT i, routine_id, status;
14702 : char *in_param_ptr, *out_param_ptr, *last_param_ptr;
14703 : INT tid, flags;
14704 : NET_COMMAND *nc_in, *nc_out;
14705 : INT param_size, max_size;
14706 : void *prpc_param[20];
14707 : char debug_line[1024], *return_buffer;
14708 : int return_buffer_size;
14709 : int return_buffer_tls;
14710 : #ifdef FIXED_BUFFER
14711 : int initial_buffer_size = NET_BUFFER_SIZE;
14712 : #else
14713 0 : int initial_buffer_size = 1024;
14714 : #endif
14715 :
14716 : /* return buffer must must use thread local storage multi-thread servers */
14717 0 : if (!tls_size) {
14718 0 : tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
14719 0 : tls_buffer[tls_size].thread_id = ss_gettid();
14720 0 : tls_buffer[tls_size].buffer_size = initial_buffer_size;
14721 0 : tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14722 0 : tls_size = 1;
14723 : }
14724 0 : for (i = 0; i < tls_size; i++)
14725 0 : if (tls_buffer[i].thread_id == ss_gettid())
14726 0 : break;
14727 0 : if (i == tls_size) {
14728 : /* new thread -> allocate new buffer */
14729 0 : tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
14730 0 : tls_buffer[tls_size].thread_id = ss_gettid();
14731 0 : tls_buffer[tls_size].buffer_size = initial_buffer_size;
14732 0 : tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14733 0 : tls_size++;
14734 : }
14735 :
14736 0 : return_buffer_tls = i;
14737 0 : return_buffer_size = tls_buffer[i].buffer_size;
14738 0 : return_buffer = tls_buffer[i].buffer;
14739 0 : assert(return_buffer);
14740 :
14741 : // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
14742 : // and valgrind complains that we transmit uninitialized data
14743 : //memset(return_buffer, 0, return_buffer_size);
14744 :
14745 : /* extract pointer array to parameters */
14746 0 : nc_in = (NET_COMMAND *) buffer;
14747 :
14748 : /* convert header format (byte swapping) */
14749 0 : if (convert_flags) {
14750 0 : rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
14751 0 : rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
14752 : }
14753 :
14754 : //if (nc_in->header.routine_id & RPC_NO_REPLY) {
14755 : // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
14756 : //}
14757 :
14758 : /* no result return as requested */
14759 0 : if (nc_in->header.routine_id & RPC_NO_REPLY)
14760 0 : sock = 0;
14761 :
14762 : /* find entry in rpc_list */
14763 0 : routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
14764 :
14765 0 : int idx = -1;
14766 : RPC_LIST rl;
14767 :
14768 0 : rpc_list_mutex.lock();
14769 :
14770 0 : for (size_t i = 0; i < rpc_list.size(); i++) {
14771 0 : if (rpc_list[i].id == routine_id) {
14772 0 : idx = i;
14773 0 : rl = rpc_list[idx];
14774 0 : break;
14775 : }
14776 : }
14777 :
14778 0 : rpc_list_mutex.unlock();
14779 :
14780 0 : if (idx < 0) {
14781 0 : cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
14782 0 : return RPC_INVALID_ID;
14783 : }
14784 :
14785 0 : again:
14786 :
14787 0 : in_param_ptr = nc_in->param;
14788 :
14789 0 : nc_out = (NET_COMMAND *) return_buffer;
14790 0 : out_param_ptr = nc_out->param;
14791 :
14792 0 : sprintf(debug_line, "%s(", rl.name);
14793 :
14794 0 : for (i = 0; rl.param[i].tid != 0; i++) {
14795 0 : tid = rl.param[i].tid;
14796 0 : flags = rl.param[i].flags;
14797 :
14798 0 : if (flags & RPC_IN) {
14799 0 : param_size = ALIGN8(rpc_tid_size(tid));
14800 :
14801 0 : if (tid == TID_STRING || tid == TID_LINK)
14802 0 : param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
14803 :
14804 0 : if (flags & RPC_VARARRAY) {
14805 : /* for arrays, the size is stored as a INT in front of the array */
14806 0 : param_size = *((INT *) in_param_ptr);
14807 0 : if (convert_flags)
14808 0 : rpc_convert_single(¶m_size, TID_INT32, 0, convert_flags);
14809 0 : param_size = ALIGN8(param_size);
14810 :
14811 0 : in_param_ptr += ALIGN8(sizeof(INT));
14812 : }
14813 :
14814 0 : if (tid == TID_STRUCT)
14815 0 : param_size = ALIGN8(rl.param[i].n);
14816 :
14817 0 : prpc_param[i] = in_param_ptr;
14818 :
14819 : /* convert data format */
14820 0 : if (convert_flags) {
14821 0 : if (flags & RPC_VARARRAY)
14822 0 : rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
14823 : else
14824 0 : rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
14825 : convert_flags);
14826 : }
14827 :
14828 0 : std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
14829 0 : if (rl.param[i].tid == TID_STRING) {
14830 : /* check for long strings (db_create_record...) */
14831 0 : if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
14832 0 : strcat(debug_line, "\"");
14833 0 : strcat(debug_line, str.c_str());
14834 0 : strcat(debug_line, "\"");
14835 : } else
14836 0 : strcat(debug_line, "...");
14837 : } else
14838 0 : strcat(debug_line, str.c_str());
14839 :
14840 0 : in_param_ptr += param_size;
14841 0 : }
14842 :
14843 0 : if (flags & RPC_OUT) {
14844 0 : param_size = ALIGN8(rpc_tid_size(tid));
14845 :
14846 0 : if (flags & RPC_VARARRAY || tid == TID_STRING) {
14847 :
14848 : /* save maximum array length from the value of the next argument.
14849 : * this means RPC_OUT arrays and strings should always be passed like this:
14850 : * rpc_call(..., array_ptr, array_max_size, ...); */
14851 :
14852 0 : max_size = *((INT *) in_param_ptr);
14853 :
14854 0 : if (convert_flags)
14855 0 : rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
14856 0 : max_size = ALIGN8(max_size);
14857 :
14858 0 : *((INT *) out_param_ptr) = max_size;
14859 :
14860 : /* save space for return array length */
14861 0 : out_param_ptr += ALIGN8(sizeof(INT));
14862 :
14863 : /* use maximum array length from input */
14864 0 : param_size = max_size;
14865 : }
14866 :
14867 0 : if (rl.param[i].tid == TID_STRUCT)
14868 0 : param_size = ALIGN8(rl.param[i].n);
14869 :
14870 0 : if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
14871 : #ifdef FIXED_BUFFER
14872 : cm_msg(MERROR, "rpc_execute",
14873 : "return parameters (%d) too large for network buffer (%d)",
14874 : (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
14875 :
14876 : return RPC_EXCEED_BUFFER;
14877 : #else
14878 : int itls;
14879 0 : int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
14880 :
14881 : #if 0
14882 : cm_msg(MINFO, "rpc_execute",
14883 : "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
14884 : (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
14885 : #endif
14886 :
14887 0 : itls = return_buffer_tls;
14888 :
14889 0 : tls_buffer[itls].buffer_size = new_size;
14890 0 : tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
14891 :
14892 0 : if (!tls_buffer[itls].buffer) {
14893 0 : cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
14894 0 : return RPC_EXCEED_BUFFER;
14895 : }
14896 :
14897 0 : return_buffer_size = tls_buffer[itls].buffer_size;
14898 0 : return_buffer = tls_buffer[itls].buffer;
14899 0 : assert(return_buffer);
14900 :
14901 0 : goto again;
14902 : #endif
14903 : }
14904 :
14905 : /* if parameter goes both directions, copy input to output */
14906 0 : if (rl.param[i].flags & RPC_IN)
14907 0 : memcpy(out_param_ptr, prpc_param[i], param_size);
14908 :
14909 0 : if (_debug_print && !(flags & RPC_IN))
14910 0 : strcat(debug_line, "-");
14911 :
14912 0 : prpc_param[i] = out_param_ptr;
14913 0 : out_param_ptr += param_size;
14914 : }
14915 :
14916 0 : if (rl.param[i + 1].tid)
14917 0 : strcat(debug_line, ", ");
14918 : }
14919 :
14920 : //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
14921 :
14922 0 : strcat(debug_line, ")");
14923 0 : rpc_debug_printf(debug_line);
14924 :
14925 0 : last_param_ptr = out_param_ptr;
14926 :
14927 : /*********************************\
14928 : * call dispatch function *
14929 : \*********************************/
14930 0 : if (rl.dispatch)
14931 0 : status = rl.dispatch(routine_id, prpc_param);
14932 : else
14933 0 : status = RPC_INVALID_ID;
14934 :
14935 0 : if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
14936 0 : status = RPC_SUCCESS;
14937 :
14938 : /* return immediately for closed down client connections */
14939 0 : if (!sock && routine_id == RPC_ID_EXIT)
14940 0 : return SS_EXIT;
14941 :
14942 0 : if (!sock && routine_id == RPC_ID_SHUTDOWN)
14943 0 : return RPC_SHUTDOWN;
14944 :
14945 : /* Return if TCP connection broken */
14946 0 : if (status == SS_ABORT)
14947 0 : return SS_ABORT;
14948 :
14949 : /* if sock == 0, we are in FTCP mode and may not sent results */
14950 0 : if (!sock)
14951 0 : return RPC_SUCCESS;
14952 :
14953 : /* compress variable length arrays */
14954 0 : out_param_ptr = nc_out->param;
14955 0 : for (i = 0; rl.param[i].tid != 0; i++)
14956 0 : if (rl.param[i].flags & RPC_OUT) {
14957 0 : tid = rl.param[i].tid;
14958 0 : flags = rl.param[i].flags;
14959 0 : param_size = ALIGN8(rpc_tid_size(tid));
14960 :
14961 0 : if (tid == TID_STRING) {
14962 0 : max_size = *((INT *) out_param_ptr);
14963 : // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14964 : // and prpc_param() is now pointing to the wrong place. here we know our string data
14965 : // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
14966 : //const char* param_ptr = (char *) prpc_param[i];
14967 0 : const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
14968 : //printf("string param [%s] max_size %d\n", param_ptr, max_size);
14969 0 : param_size = strlen(param_ptr) + 1;
14970 0 : param_size = ALIGN8(param_size);
14971 :
14972 : /* move string ALIGN8(sizeof(INT)) left */
14973 0 : memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
14974 :
14975 : /* move remaining parameters to end of string */
14976 0 : memmove(out_param_ptr + param_size,
14977 0 : out_param_ptr + max_size + ALIGN8(sizeof(INT)),
14978 0 : (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
14979 : }
14980 :
14981 0 : if (flags & RPC_VARARRAY) {
14982 : /* store array length at current out_param_ptr */
14983 0 : max_size = *((INT *) out_param_ptr);
14984 : // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14985 : // and prpc_param() is now pointing to the wrong place. instead, compute location
14986 : // of next parameter using max_size. K.O.
14987 : // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
14988 0 : if (rl.param[i+1].flags & RPC_OUT)
14989 0 : param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
14990 : else
14991 0 : param_size = *((INT *) prpc_param[i + 1]);
14992 0 : *((INT *) out_param_ptr) = param_size; // store new array size
14993 0 : if (convert_flags)
14994 0 : rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
14995 :
14996 0 : out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
14997 :
14998 0 : param_size = ALIGN8(param_size);
14999 :
15000 : /* move remaining parameters to end of array */
15001 0 : memmove(out_param_ptr + param_size,
15002 0 : out_param_ptr + max_size,
15003 0 : (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
15004 : }
15005 :
15006 0 : if (tid == TID_STRUCT)
15007 0 : param_size = ALIGN8(rl.param[i].n);
15008 :
15009 : /* convert data format */
15010 0 : if (convert_flags) {
15011 0 : if (flags & RPC_VARARRAY)
15012 0 : rpc_convert_data(out_param_ptr, tid,
15013 0 : rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15014 : else
15015 0 : rpc_convert_data(out_param_ptr, tid,
15016 0 : rl.param[i].flags | RPC_OUTGOING,
15017 0 : rl.param[i].n * rpc_tid_size(tid), convert_flags);
15018 : }
15019 :
15020 0 : out_param_ptr += param_size;
15021 : }
15022 :
15023 : /* send return parameters */
15024 0 : param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15025 0 : nc_out->header.routine_id = status;
15026 0 : nc_out->header.param_size = param_size;
15027 :
15028 : //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15029 :
15030 : /* convert header format (byte swapping) if necessary */
15031 0 : if (convert_flags) {
15032 0 : rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15033 0 : rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15034 : }
15035 :
15036 : // valgrind complains about sending uninitialized data, if you care about this, uncomment
15037 : // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15038 :
15039 0 : status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15040 :
15041 0 : if (status < 0) {
15042 0 : cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15043 0 : return RPC_NET_ERROR;
15044 : }
15045 :
15046 : /* print return buffer */
15047 : /*
15048 : printf("Return buffer, ID %d:\n", routine_id);
15049 : for (i=0; i<param_size ; i++)
15050 : {
15051 : status = (char) nc_out->param[i];
15052 : printf("%02X ", status);
15053 : if (i%8 == 7)
15054 : printf("\n");
15055 : }
15056 : */
15057 : /* return SS_EXIT if RPC_EXIT is called */
15058 0 : if (routine_id == RPC_ID_EXIT)
15059 0 : return SS_EXIT;
15060 :
15061 : /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15062 0 : if (routine_id == RPC_ID_SHUTDOWN)
15063 0 : return RPC_SHUTDOWN;
15064 :
15065 0 : return RPC_SUCCESS;
15066 : }
15067 :
15068 : /********************************************************************/
15069 0 : int rpc_test_rpc()
15070 : /********************************************************************\
15071 : Routine: rpc_test_rpc
15072 :
15073 : Purpose: Test RPC parameters encoding and decoding
15074 :
15075 : Input:
15076 : none
15077 :
15078 : Output:
15079 : none
15080 :
15081 : Function value:
15082 : RPC_SUCCESS Successful completion
15083 :
15084 : \********************************************************************/
15085 : {
15086 0 : int status = RPC_SUCCESS;
15087 :
15088 0 : printf("rpc_test_rpc!\n");
15089 :
15090 0 : int int_out = 0;
15091 0 : int int_inout = 456;
15092 :
15093 : char string_out[32];
15094 : char string2_out[48];
15095 :
15096 : char string_inout[25];
15097 0 : strcpy(string_inout, "string_inout");
15098 :
15099 : KEY struct_in;
15100 :
15101 0 : struct_in.type = 111;
15102 0 : struct_in.num_values = 222;
15103 0 : strcpy(struct_in.name, "name");
15104 0 : struct_in.last_written = 333;
15105 :
15106 : KEY struct_out;
15107 : KEY struct_inout;
15108 :
15109 0 : struct_inout.type = 111111;
15110 0 : struct_inout.num_values = 222222;
15111 0 : strcpy(struct_inout.name, "name_name");
15112 0 : struct_inout.last_written = 333333;
15113 :
15114 : uint32_t dwordarray_inout[10];
15115 0 : size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15116 :
15117 0 : for (int i=0; i<10; i++) {
15118 0 : dwordarray_inout[i] = i*10;
15119 : }
15120 :
15121 : char array_in[10];
15122 :
15123 0 : for (size_t i=0; i<sizeof(array_in); i++) {
15124 0 : array_in[i] = 'a' + i;
15125 : }
15126 :
15127 : char array_out[16];
15128 0 : size_t array_out_size = sizeof(array_out);
15129 :
15130 0 : status = rpc_call(RPC_TEST2,
15131 : 123,
15132 : &int_out,
15133 : &int_inout,
15134 : "test string",
15135 : string_out, sizeof(string_out),
15136 : string2_out, sizeof(string2_out),
15137 : string_inout, sizeof(string_inout),
15138 : &struct_in,
15139 : &struct_out,
15140 : &struct_inout,
15141 : dwordarray_inout, &dwordarray_inout_size,
15142 : array_in, sizeof(array_in),
15143 : array_out, &array_out_size
15144 : );
15145 :
15146 0 : printf("rpc_call(RPC_TEST2) status %d\n", status);
15147 :
15148 0 : if (int_out != 789) {
15149 0 : printf("int_out mismatch!\n");
15150 0 : status = 0;
15151 : }
15152 :
15153 0 : if (int_inout != 456*2) {
15154 0 : printf("int_inout mismatch!\n");
15155 0 : status = 0;
15156 : }
15157 :
15158 0 : if (strcmp(string_out, "string_out") != 0) {
15159 0 : printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
15160 0 : status = 0;
15161 : }
15162 :
15163 0 : if (strcmp(string2_out, "second string_out") != 0) {
15164 0 : printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
15165 0 : status = 0;
15166 : }
15167 :
15168 0 : if (strcmp(string_inout, "return string_inout") != 0) {
15169 0 : printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
15170 0 : status = 0;
15171 : }
15172 :
15173 : KEY* pkey;
15174 :
15175 0 : pkey = &struct_in;
15176 :
15177 : //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15178 :
15179 0 : pkey = &struct_out;
15180 :
15181 0 : if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
15182 0 : printf("struct_out mismatch: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15183 0 : status = 0;
15184 : }
15185 :
15186 0 : pkey = &struct_inout;
15187 :
15188 0 : if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
15189 0 : printf("struct_inout mismatch: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15190 0 : status = 0;
15191 : }
15192 :
15193 : #if 0
15194 : if (dwordarray_inout_size != 4*5) {
15195 : printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
15196 : status = 0;
15197 : }
15198 :
15199 : for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
15200 : printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
15201 : }
15202 : #endif
15203 :
15204 : #if 0
15205 : {RPC_TEST_CXX, "test_cxx",
15206 : {{TID_INT32, RPC_IN},
15207 : {TID_INT32, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
15208 : {TID_STRING, RPC_IN},
15209 : {TID_STRING, RPC_IN | RPC_CXX},
15210 : {TID_STRING, RPC_OUT | RPC_CXX},
15211 : {TID_STRING, RPC_IN | RPC_OUT | RPC_CXX},
15212 : {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
15213 : {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
15214 : {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
15215 : {TID_ARRAY, RPC_IN | RPC_VARARRAY | RPC_CXX},
15216 : {TID_ARRAY, RPC_OUT | RPC_VARARRAY | RPC_CXX},
15217 : {TID_ARRAY, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
15218 : {0}}},
15219 : #endif
15220 :
15221 : #if 0
15222 : status = rpc_call(RPC_TEST_CXX, ...);
15223 : #endif
15224 :
15225 0 : return status;
15226 : }
15227 :
15228 : static std::atomic_bool gAllowedHostsEnabled(false);
15229 : static std::vector<std::string> gAllowedHosts;
15230 : static std::mutex gAllowedHostsMutex;
15231 :
15232 : /********************************************************************/
15233 2 : INT rpc_clear_allowed_hosts()
15234 : /********************************************************************\
15235 : Routine: rpc_clear_allowed_hosts
15236 :
15237 : Purpose: Clear list of allowed hosts and permit connections from anybody
15238 :
15239 : Input:
15240 : none
15241 :
15242 : Output:
15243 : none
15244 :
15245 : Function value:
15246 : RPC_SUCCESS Successful completion
15247 :
15248 : \********************************************************************/
15249 : {
15250 2 : gAllowedHostsMutex.lock();
15251 2 : gAllowedHosts.clear();
15252 2 : gAllowedHostsEnabled = false;
15253 2 : gAllowedHostsMutex.unlock();
15254 2 : return RPC_SUCCESS;
15255 : }
15256 :
15257 : /********************************************************************/
15258 2 : INT rpc_add_allowed_host(const char *hostname)
15259 : /********************************************************************\
15260 : Routine: rpc_add_allowed_host
15261 :
15262 : Purpose: Permit connections from listed hosts only
15263 :
15264 : Input:
15265 : none
15266 :
15267 : Output:
15268 : none
15269 :
15270 : Function value:
15271 : RPC_SUCCESS Successful completion
15272 : RPC_NO_MEMORY Too many allowed hosts
15273 :
15274 : \********************************************************************/
15275 : {
15276 : //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
15277 :
15278 2 : gAllowedHostsMutex.lock();
15279 2 : gAllowedHosts.push_back(hostname);
15280 2 : gAllowedHostsEnabled = true;
15281 2 : gAllowedHostsMutex.unlock();
15282 :
15283 2 : return RPC_SUCCESS;
15284 : }
15285 :
15286 : /********************************************************************/
15287 0 : INT rpc_check_allowed_host(const char *hostname)
15288 : /********************************************************************\
15289 : Routine: rpc_check_allowed_host
15290 :
15291 : Purpose: Check if hostname is permitted to connect
15292 :
15293 : Function value:
15294 : RPC_SUCCESS hostname is permitted to connect
15295 : RPC_NOT_REGISTERED hostname not permitted to connect
15296 :
15297 : \********************************************************************/
15298 : {
15299 : //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
15300 :
15301 0 : if (!gAllowedHostsEnabled)
15302 0 : return RPC_SUCCESS;
15303 :
15304 0 : if (strcmp(hostname, "localhost") == 0)
15305 0 : return RPC_SUCCESS;
15306 :
15307 0 : if (strcmp(hostname, "localhost.localdomain") == 0)
15308 0 : return RPC_SUCCESS;
15309 :
15310 0 : if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
15311 0 : return RPC_SUCCESS;
15312 :
15313 0 : if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
15314 0 : return RPC_SUCCESS;
15315 :
15316 0 : int status = RPC_NOT_REGISTERED;
15317 :
15318 0 : gAllowedHostsMutex.lock();
15319 :
15320 0 : for (const auto& h: gAllowedHosts) {
15321 0 : if (h == hostname) {
15322 0 : status = RPC_SUCCESS;
15323 0 : break;
15324 : }
15325 : }
15326 :
15327 0 : gAllowedHostsMutex.unlock();
15328 :
15329 : //if (status != RPC_SUCCESS)
15330 : // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
15331 :
15332 0 : return status;
15333 : }
15334 :
15335 : /*------------------------------------------------------------------*/
15336 0 : static INT rpc_socket_check_allowed_host(int sock)
15337 : {
15338 0 : std::string hostname;
15339 :
15340 0 : int status = ss_socket_get_peer_name(sock, &hostname, NULL);
15341 :
15342 0 : if (status != SS_SUCCESS)
15343 0 : return status;
15344 :
15345 0 : status = rpc_check_allowed_host(hostname.c_str());
15346 :
15347 0 : if (status == RPC_SUCCESS)
15348 0 : return RPC_SUCCESS;
15349 :
15350 : static std::atomic_int max_report(10);
15351 0 : if (max_report > 0) {
15352 0 : max_report--;
15353 0 : if (max_report == 0) {
15354 0 : cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
15355 : } else {
15356 0 : cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\'. Add this host to \"/Experiment/Security/RPC hosts/Allowed hosts\"", hostname.c_str());
15357 : }
15358 : }
15359 :
15360 0 : return RPC_NET_ERROR;
15361 0 : }
15362 :
15363 : /********************************************************************/
15364 0 : INT rpc_server_accept(int lsock)
15365 : /********************************************************************\
15366 :
15367 : Routine: rpc_server_accept
15368 :
15369 : Purpose: Accept new incoming connections
15370 :
15371 : Input:
15372 : INT lscok Listen socket
15373 :
15374 : Output:
15375 : none
15376 :
15377 : Function value:
15378 : RPC_SUCCESS Successful completion
15379 : RPC_NET_ERROR Error in socket call
15380 : RPC_CONNCLOSED Connection was closed
15381 : RPC_SHUTDOWN Listener shutdown
15382 : RPC_EXCEED_BUFFER Not enough memory for network buffer
15383 :
15384 : \********************************************************************/
15385 : {
15386 : INT i;
15387 : INT sock;
15388 : char version[NAME_LENGTH], v1[32];
15389 : char experiment[NAME_LENGTH];
15390 : INT port1, port2, port3;
15391 : char *ptr;
15392 : char net_buffer[256];
15393 : struct linger ling;
15394 :
15395 0 : static struct callback_addr callback;
15396 :
15397 0 : if (lsock > 0) {
15398 0 : sock = accept(lsock, NULL, NULL);
15399 :
15400 0 : if (sock == -1)
15401 0 : return RPC_NET_ERROR;
15402 : } else {
15403 : /* lsock is stdin -> already connected from inetd */
15404 :
15405 0 : sock = lsock;
15406 : }
15407 :
15408 : /* check access control list */
15409 0 : if (gAllowedHostsEnabled) {
15410 0 : int status = rpc_socket_check_allowed_host(sock);
15411 :
15412 0 : if (status != RPC_SUCCESS) {
15413 0 : ss_socket_close(&sock);
15414 0 : return RPC_NET_ERROR;
15415 : }
15416 : }
15417 :
15418 : /* receive string with timeout */
15419 0 : i = recv_string(sock, net_buffer, 256, 10000);
15420 0 : rpc_debug_printf("Received command: %s", net_buffer);
15421 :
15422 0 : if (i > 0) {
15423 0 : char command = (char) toupper(net_buffer[0]);
15424 :
15425 : //printf("rpc_server_accept: command [%c]\n", command);
15426 :
15427 0 : switch (command) {
15428 0 : case 'S': {
15429 :
15430 : /*----------- shutdown listener ----------------------*/
15431 0 : ss_socket_close(&sock);
15432 0 : return RPC_SHUTDOWN;
15433 : }
15434 0 : case 'I': {
15435 :
15436 : /*----------- return available experiments -----------*/
15437 : #ifdef LOCAL_ROUTINES
15438 0 : exptab_struct exptab;
15439 0 : cm_read_exptab(&exptab); // thread safe!
15440 0 : for (unsigned i=0; i<exptab.exptab.size(); i++) {
15441 0 : rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
15442 0 : const char* str = exptab.exptab[i].name.c_str();
15443 0 : send(sock, str, strlen(str) + 1, 0);
15444 : }
15445 0 : send(sock, "", 1, 0);
15446 : #endif
15447 0 : ss_socket_close(&sock);
15448 0 : break;
15449 0 : }
15450 0 : case 'C': {
15451 :
15452 : /*----------- connect to experiment -----------*/
15453 :
15454 : /* get callback information */
15455 0 : callback.experiment[0] = 0;
15456 0 : port1 = port2 = version[0] = 0;
15457 :
15458 : //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
15459 :
15460 : /* parse string in format "C port1 port2 port3 version expt" */
15461 : /* example: C 51046 45838 56832 2.0.0 alpha */
15462 :
15463 0 : port1 = strtoul(net_buffer + 2, &ptr, 0);
15464 0 : port2 = strtoul(ptr, &ptr, 0);
15465 0 : port3 = strtoul(ptr, &ptr, 0);
15466 :
15467 0 : while (*ptr == ' ')
15468 0 : ptr++;
15469 :
15470 0 : i = 0;
15471 0 : for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
15472 0 : version[i++] = *ptr++;
15473 :
15474 : // ensure that we do not overwrite buffer "version"
15475 0 : assert(i < (int) sizeof(version));
15476 0 : version[i] = 0;
15477 :
15478 : // skip wjatever is left from the "version" string
15479 0 : for (; *ptr != 0 && *ptr != ' ';)
15480 0 : ptr++;
15481 :
15482 0 : while (*ptr == ' ')
15483 0 : ptr++;
15484 :
15485 0 : i = 0;
15486 0 : for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
15487 0 : experiment[i++] = *ptr++;
15488 :
15489 : // ensure that we do not overwrite buffer "experiment"
15490 0 : assert(i < (int) sizeof(experiment));
15491 0 : experiment[i] = 0;
15492 :
15493 0 : callback.experiment = experiment;
15494 :
15495 : /* print warning if version patch level doesn't agree */
15496 0 : mstrlcpy(v1, version, sizeof(v1));
15497 0 : if (strchr(v1, '.'))
15498 0 : if (strchr(strchr(v1, '.') + 1, '.'))
15499 0 : *strchr(strchr(v1, '.') + 1, '.') = 0;
15500 :
15501 : char str[100];
15502 0 : mstrlcpy(str, cm_get_version(), sizeof(str));
15503 0 : if (strchr(str, '.'))
15504 0 : if (strchr(strchr(str, '.') + 1, '.'))
15505 0 : *strchr(strchr(str, '.') + 1, '.') = 0;
15506 :
15507 0 : if (strcmp(v1, str) != 0) {
15508 0 : cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
15509 0 : cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
15510 : }
15511 :
15512 0 : callback.host_port1 = (short) port1;
15513 0 : callback.host_port2 = (short) port2;
15514 0 : callback.host_port3 = (short) port3;
15515 0 : callback.debug = _debug_mode;
15516 :
15517 0 : int status = ss_socket_get_peer_name(sock, &callback.host_name, NULL);
15518 :
15519 0 : if (status != SS_SUCCESS) {
15520 0 : ss_socket_close(&sock);
15521 0 : break;
15522 : }
15523 :
15524 : #ifdef LOCAL_ROUTINES
15525 : /* update experiment definition */
15526 0 : exptab_struct exptab;
15527 0 : cm_read_exptab(&exptab); // thread safe!
15528 :
15529 0 : unsigned idx = 0;
15530 0 : bool found = false;
15531 : /* lookup experiment */
15532 0 : if (equal_ustring(callback.experiment.c_str(), "Default")) {
15533 0 : found = true;
15534 0 : idx = 0;
15535 : } else {
15536 0 : for (idx = 0; idx < exptab.exptab.size(); idx++) {
15537 0 : if (exptab.exptab[idx].name == callback.experiment) {
15538 0 : if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
15539 0 : found = true;
15540 0 : break;
15541 : }
15542 : }
15543 : }
15544 : }
15545 :
15546 0 : if (!found) {
15547 0 : cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
15548 :
15549 0 : send(sock, "2", 2, 0); /* 2 means exp. not found */
15550 0 : ss_socket_close(&sock);
15551 0 : break;
15552 : }
15553 :
15554 0 : callback.directory = exptab.exptab[idx].directory;
15555 0 : callback.user = exptab.exptab[idx].user;
15556 :
15557 : /* create a new process */
15558 : char host_port1_str[30], host_port2_str[30], host_port3_str[30];
15559 : char debug_str[30];
15560 :
15561 0 : sprintf(host_port1_str, "%d", callback.host_port1);
15562 0 : sprintf(host_port2_str, "%d", callback.host_port2);
15563 0 : sprintf(host_port3_str, "%d", callback.host_port3);
15564 0 : sprintf(debug_str, "%d", callback.debug);
15565 :
15566 0 : const char *mserver_path = rpc_get_mserver_path();
15567 :
15568 : const char *argv[10];
15569 0 : argv[0] = mserver_path;
15570 0 : argv[1] = callback.host_name.c_str();
15571 0 : argv[2] = host_port1_str;
15572 0 : argv[3] = host_port2_str;
15573 0 : argv[4] = host_port3_str;
15574 0 : argv[5] = debug_str;
15575 0 : argv[6] = callback.experiment.c_str();
15576 0 : argv[7] = callback.directory.c_str();
15577 0 : argv[8] = callback.user.c_str();
15578 0 : argv[9] = NULL;
15579 :
15580 0 : rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
15581 : argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
15582 : argv[9]);
15583 :
15584 0 : status = ss_spawnv(P_NOWAIT, mserver_path, argv);
15585 :
15586 0 : if (status != SS_SUCCESS) {
15587 0 : rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
15588 :
15589 0 : sprintf(str, "3"); /* 3 means cannot spawn subprocess */
15590 0 : send(sock, str, strlen(str) + 1, 0);
15591 0 : ss_socket_close(&sock);
15592 0 : break;
15593 : }
15594 :
15595 0 : sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
15596 0 : send(sock, str, strlen(str) + 1, 0);
15597 : #endif // LOCAL_ROUTINES
15598 0 : ss_socket_close(&sock);
15599 :
15600 0 : break;
15601 0 : }
15602 0 : default: {
15603 0 : cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
15604 0 : ss_socket_close(&sock);
15605 0 : break;
15606 : }
15607 : }
15608 : } else { /* if i>0 */
15609 :
15610 : /* lingering needed for PCTCP */
15611 0 : ling.l_onoff = 1;
15612 0 : ling.l_linger = 0;
15613 0 : setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
15614 0 : ss_socket_close(&sock);
15615 : }
15616 :
15617 0 : return RPC_SUCCESS;
15618 : }
15619 :
15620 : /********************************************************************/
15621 0 : INT rpc_client_accept(int lsock)
15622 : /********************************************************************\
15623 :
15624 : Routine: rpc_client_accept
15625 :
15626 : Purpose: midas program accept new RPC connection (run transitions, etc)
15627 :
15628 : Input:
15629 : INT lsock Listen socket
15630 :
15631 : Output:
15632 : none
15633 :
15634 : Function value:
15635 : RPC_SUCCESS Successful completion
15636 : RPC_NET_ERROR Error in socket call
15637 : RPC_CONNCLOSED Connection was closed
15638 : RPC_SHUTDOWN Listener shutdown
15639 : RPC_EXCEED_BUFFER Not enough memory for network buffer
15640 :
15641 : \********************************************************************/
15642 : {
15643 : INT i, status;
15644 0 : INT client_hw_type = 0, hw_type;
15645 0 : std::string client_program;
15646 0 : std::string host_name;
15647 : INT convert_flags;
15648 : char net_buffer[256], *p;
15649 :
15650 0 : int sock = accept(lsock, NULL, NULL);
15651 :
15652 0 : if (sock == -1)
15653 0 : return RPC_NET_ERROR;
15654 :
15655 : /* check access control list */
15656 0 : if (gAllowedHostsEnabled) {
15657 0 : int status = rpc_socket_check_allowed_host(sock);
15658 :
15659 0 : if (status != RPC_SUCCESS) {
15660 0 : ss_socket_close(&sock);
15661 0 : return RPC_NET_ERROR;
15662 : }
15663 : }
15664 :
15665 0 : host_name = "(unknown)";
15666 0 : client_program = "(unknown)";
15667 :
15668 : /* receive string with timeout */
15669 0 : i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
15670 0 : if (i <= 0) {
15671 0 : ss_socket_close(&sock);
15672 0 : return RPC_NET_ERROR;
15673 : }
15674 :
15675 : /* get remote computer info */
15676 0 : p = strtok(net_buffer, " ");
15677 0 : if (p != NULL) {
15678 0 : client_hw_type = atoi(p);
15679 0 : p = strtok(NULL, " ");
15680 : }
15681 0 : if (p != NULL) {
15682 : //version = atoi(p);
15683 0 : p = strtok(NULL, " ");
15684 : }
15685 0 : if (p != NULL) {
15686 0 : client_program = p;
15687 0 : p = strtok(NULL, " ");
15688 : }
15689 0 : if (p != NULL) {
15690 0 : host_name = p;
15691 0 : p = strtok(NULL, " ");
15692 : }
15693 :
15694 : //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
15695 :
15696 0 : RPC_SERVER_ACCEPTION* sa = rpc_new_server_acception();
15697 :
15698 : /* save information in _server_acception structure */
15699 0 : sa->recv_sock = sock;
15700 0 : sa->send_sock = 0;
15701 0 : sa->event_sock = 0;
15702 0 : sa->remote_hw_type = client_hw_type;
15703 0 : sa->host_name = host_name;
15704 0 : sa->prog_name = client_program;
15705 0 : sa->last_activity = ss_millitime();
15706 0 : sa->watchdog_timeout = 0;
15707 0 : sa->is_mserver = FALSE;
15708 :
15709 : /* send my own computer id */
15710 0 : hw_type = rpc_get_hw_type();
15711 0 : std::string str = msprintf("%d %s", hw_type, cm_get_version());
15712 0 : status = send(sock, str.c_str(), str.length() + 1, 0);
15713 0 : if (status != (INT) str.length() + 1)
15714 0 : return RPC_NET_ERROR;
15715 :
15716 0 : rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
15717 0 : sa->convert_flags = convert_flags;
15718 :
15719 0 : ss_suspend_set_server_acceptions(&_server_acceptions);
15720 :
15721 0 : return RPC_SUCCESS;
15722 0 : }
15723 :
15724 : /********************************************************************/
15725 0 : INT rpc_server_callback(struct callback_addr *pcallback)
15726 : /********************************************************************\
15727 :
15728 : Routine: rpc_server_callback
15729 :
15730 : Purpose: Callback a remote client. Setup _server_acception entry
15731 : with optional conversion flags and establish two-way
15732 : TCP connection.
15733 :
15734 : Input:
15735 : callback_addr pcallback Pointer to a callback structure
15736 :
15737 : Output:
15738 : none
15739 :
15740 : Function value:
15741 : RPC_SUCCESS Successful completion
15742 :
15743 : \********************************************************************/
15744 : {
15745 : INT status;
15746 : int recv_sock, send_sock, event_sock;
15747 : char str[100];
15748 0 : std::string client_program;
15749 : INT client_hw_type, hw_type;
15750 : INT convert_flags;
15751 : char net_buffer[256];
15752 : char *p;
15753 : int flag;
15754 :
15755 : /* copy callback information */
15756 0 : struct callback_addr callback = *pcallback;
15757 : //idx = callback.index;
15758 :
15759 0 : std::string errmsg;
15760 :
15761 : /* create new sockets for TCP */
15762 0 : status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
15763 :
15764 0 : if (status != SS_SUCCESS) {
15765 0 : cm_msg(MERROR, "rpc_server_callback", "cannot connect receive socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port1, errmsg.c_str());
15766 0 : ss_socket_close(&recv_sock);
15767 : //ss_socket_close(&send_sock);
15768 : //ss_socket_close(&event_sock);
15769 0 : return RPC_NET_ERROR;
15770 : }
15771 :
15772 0 : status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
15773 :
15774 0 : if (status != SS_SUCCESS) {
15775 0 : cm_msg(MERROR, "rpc_server_callback", "cannot connect send socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port2, errmsg.c_str());
15776 0 : ss_socket_close(&recv_sock);
15777 0 : ss_socket_close(&send_sock);
15778 : //ss_socket_close(&event_sock);
15779 0 : return RPC_NET_ERROR;
15780 : }
15781 :
15782 0 : status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
15783 :
15784 0 : if (status != SS_SUCCESS) {
15785 0 : cm_msg(MERROR, "rpc_server_callback", "cannot connect event socket, host \"%s\", port %d: %s", callback.host_name.c_str(), callback.host_port2, errmsg.c_str());
15786 0 : ss_socket_close(&recv_sock);
15787 0 : ss_socket_close(&send_sock);
15788 0 : ss_socket_close(&event_sock);
15789 0 : return RPC_NET_ERROR;
15790 : }
15791 : #ifndef OS_ULTRIX /* crashes ULTRIX... */
15792 : /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
15793 0 : flag = 2 * 1024 * 1024;
15794 0 : status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
15795 0 : if (status != 0)
15796 0 : cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
15797 0 : strerror(errno));
15798 : #endif
15799 :
15800 0 : if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
15801 0 : cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
15802 0 : ss_socket_close(&recv_sock);
15803 0 : ss_socket_close(&send_sock);
15804 0 : ss_socket_close(&event_sock);
15805 0 : return RPC_NET_ERROR;
15806 : }
15807 : //printf("rpc_server_callback: \'%s\'\n", net_buffer);
15808 :
15809 : /* get remote computer info */
15810 0 : client_hw_type = strtoul(net_buffer, &p, 0);
15811 :
15812 0 : while (*p == ' ')
15813 0 : p++;
15814 :
15815 0 : client_program = p;
15816 :
15817 : //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
15818 :
15819 0 : std::string host_name;
15820 :
15821 0 : status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
15822 :
15823 0 : if (status != SS_SUCCESS)
15824 0 : host_name = "unknown";
15825 :
15826 : //printf("rpc_server_callback: mserver acception\n");
15827 :
15828 0 : RPC_SERVER_ACCEPTION* sa = rpc_new_server_acception();
15829 :
15830 : /* save information in _server_acception structure */
15831 0 : sa->recv_sock = recv_sock;
15832 0 : sa->send_sock = send_sock;
15833 0 : sa->event_sock = event_sock;
15834 0 : sa->remote_hw_type = client_hw_type;
15835 0 : sa->host_name = host_name;
15836 0 : sa->prog_name = client_program;
15837 0 : sa->last_activity = ss_millitime();
15838 0 : sa->watchdog_timeout = 0;
15839 0 : sa->is_mserver = TRUE;
15840 :
15841 0 : assert(_mserver_acception == NULL);
15842 :
15843 0 : _mserver_acception = sa;
15844 :
15845 : //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
15846 :
15847 : /* send my own computer id */
15848 0 : hw_type = rpc_get_hw_type();
15849 0 : sprintf(str, "%d", hw_type);
15850 0 : send(recv_sock, str, strlen(str) + 1, 0);
15851 :
15852 0 : rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
15853 0 : sa->convert_flags = convert_flags;
15854 :
15855 0 : ss_suspend_set_server_acceptions(&_server_acceptions);
15856 :
15857 0 : if (rpc_is_mserver()) {
15858 0 : rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
15859 : }
15860 :
15861 0 : return RPC_SUCCESS;
15862 0 : }
15863 :
15864 :
15865 : /********************************************************************/
15866 0 : INT rpc_server_loop(void)
15867 : /********************************************************************\
15868 :
15869 : Routine: rpc_server_loop
15870 :
15871 : Purpose: mserver main event loop
15872 :
15873 : \********************************************************************/
15874 : {
15875 : while (1) {
15876 0 : int status = ss_suspend(1000, 0);
15877 :
15878 0 : if (status == SS_ABORT || status == SS_EXIT)
15879 : break;
15880 :
15881 0 : if (rpc_check_channels() == RPC_NET_ERROR)
15882 0 : break;
15883 :
15884 : /* check alarms, etc */
15885 0 : status = cm_periodic_tasks();
15886 :
15887 0 : cm_msg_flush_buffer();
15888 0 : }
15889 :
15890 0 : return RPC_SUCCESS;
15891 : }
15892 :
15893 : /********************************************************************/
15894 0 : INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION* sa)
15895 : /********************************************************************\
15896 :
15897 : Routine: rpc_server_receive_rpc
15898 :
15899 : Purpose: Receive rpc commands and execute them. Close the connection
15900 : if client has broken TCP pipe.
15901 :
15902 : Function value:
15903 : RPC_SUCCESS Successful completion
15904 : RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15905 : SS_EXIT Server connection was closed
15906 : SS_ABORT Server connection was broken
15907 :
15908 : \********************************************************************/
15909 : {
15910 0 : int status = 0;
15911 0 : int remaining = 0;
15912 :
15913 0 : char *buf = NULL;
15914 0 : int bufsize = 0;
15915 :
15916 : do {
15917 0 : int n_received = recv_net_command_realloc(idx, &buf, &bufsize, &remaining);
15918 :
15919 0 : if (n_received <= 0) {
15920 0 : status = SS_ABORT;
15921 0 : cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
15922 0 : goto error;
15923 : }
15924 :
15925 0 : status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
15926 :
15927 0 : if (status == SS_ABORT) {
15928 0 : cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
15929 0 : goto error;
15930 : }
15931 :
15932 0 : if (status == SS_EXIT || status == RPC_SHUTDOWN) {
15933 0 : if (rpc_is_mserver())
15934 0 : rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
15935 0 : goto exit;
15936 : }
15937 :
15938 0 : } while (remaining);
15939 :
15940 0 : if (buf) {
15941 0 : free(buf);
15942 0 : buf = NULL;
15943 0 : bufsize = 0;
15944 : }
15945 :
15946 0 : return RPC_SUCCESS;
15947 :
15948 0 : error:
15949 :
15950 : {
15951 : char str[80];
15952 0 : mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
15953 0 : if (strchr(str, '.'))
15954 0 : *strchr(str, '.') = 0;
15955 0 : cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
15956 : }
15957 :
15958 0 : exit:
15959 :
15960 0 : cm_msg_flush_buffer();
15961 :
15962 0 : if (buf) {
15963 0 : free(buf);
15964 0 : buf = NULL;
15965 0 : bufsize = 0;
15966 : }
15967 :
15968 : /* disconnect from experiment as MIDAS server */
15969 0 : if (rpc_is_mserver()) {
15970 : HNDLE hDB, hKey;
15971 :
15972 0 : cm_get_experiment_database(&hDB, &hKey);
15973 :
15974 : /* only disconnect from experiment if previously connected.
15975 : Necessary for pure RPC servers (RPC_SRVR) */
15976 0 : if (hDB) {
15977 0 : bm_close_all_buffers();
15978 0 : cm_delete_client_info(hDB, 0);
15979 0 : db_close_all_databases();
15980 :
15981 0 : rpc_deregister_functions();
15982 :
15983 0 : cm_set_experiment_database(0, 0);
15984 : }
15985 : }
15986 :
15987 0 : bool is_mserver = sa->is_mserver;
15988 :
15989 0 : sa->close();
15990 :
15991 : /* signal caller a shutdonw */
15992 0 : if (status == RPC_SHUTDOWN)
15993 0 : return status;
15994 :
15995 : /* only the mserver should stop on server connection closure */
15996 0 : if (!is_mserver) {
15997 0 : return SS_SUCCESS;
15998 : }
15999 :
16000 0 : return status;
16001 : }
16002 :
16003 : /********************************************************************/
16004 0 : INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION* sa, int timeout_msec)
16005 : /********************************************************************\
16006 :
16007 : Routine: rpc_server_receive_event
16008 :
16009 : Purpose: Receive event and dispatch it
16010 :
16011 : Function value:
16012 : RPC_SUCCESS Successful completion
16013 : RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
16014 : SS_EXIT Server connection was closed
16015 : SS_ABORT Server connection was broken
16016 :
16017 : \********************************************************************/
16018 : {
16019 0 : int status = 0;
16020 :
16021 0 : DWORD start_time = ss_millitime();
16022 :
16023 : //
16024 : // THIS IS NOT THREAD SAFE!!!
16025 : //
16026 : // IT IS ONLY USED BY THE MSERVER
16027 : // MSERVER IS SINGLE-THREADED!!!
16028 : //
16029 :
16030 : static char *xbuf = NULL;
16031 : static int xbufsize = 0;
16032 : static bool xbufempty = true;
16033 :
16034 : // short cut
16035 0 : if (sa == NULL && xbufempty)
16036 0 : return RPC_SUCCESS;
16037 :
16038 : static bool recurse = false;
16039 :
16040 0 : if (recurse) {
16041 0 : cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
16042 : // do not do anything if we are called recursively
16043 : // via recursive ss_suspend() or otherwise. K.O.
16044 0 : if (xbufempty)
16045 0 : return RPC_SUCCESS;
16046 : else
16047 0 : return BM_ASYNC_RETURN;
16048 : }
16049 :
16050 0 : recurse = true;
16051 :
16052 : do {
16053 0 : if (xbufempty && sa) {
16054 0 : int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
16055 :
16056 0 : if (n_received < 0) {
16057 0 : status = SS_ABORT;
16058 0 : cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
16059 0 : goto error;
16060 : }
16061 :
16062 0 : if (n_received == 0) {
16063 : // no more data in the tcp socket
16064 0 : recurse = false;
16065 0 : return RPC_SUCCESS;
16066 : }
16067 :
16068 0 : xbufempty = false;
16069 : }
16070 :
16071 0 : if (xbufempty) {
16072 : // no event in xbuf buffer
16073 0 : recurse = false;
16074 0 : return RPC_SUCCESS;
16075 : }
16076 :
16077 : /* send event to buffer */
16078 0 : INT *pbh = (INT *) xbuf;
16079 0 : EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
16080 :
16081 0 : status = bm_send_event(*pbh, pevent, 0, timeout_msec);
16082 :
16083 : //printf("rpc_server_receiv: buffer_handle %d, event_id 0x%04x, serial 0x%08x, data_size %d, status %d\n", *pbh, pevent->event_id, pevent->serial_number, pevent->data_size, status);
16084 :
16085 0 : if (status == SS_ABORT) {
16086 0 : cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
16087 0 : goto error;
16088 : }
16089 :
16090 0 : if (status == BM_ASYNC_RETURN) {
16091 : //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
16092 0 : recurse = false;
16093 0 : return status;
16094 : }
16095 :
16096 0 : if (status != BM_SUCCESS) {
16097 0 : cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
16098 : }
16099 :
16100 0 : xbufempty = true;
16101 :
16102 : /* repeat for maximum 0.5 sec */
16103 0 : } while (ss_millitime() - start_time < 500);
16104 :
16105 0 : recurse = false;
16106 0 : return RPC_SUCCESS;
16107 :
16108 0 : error:
16109 :
16110 : {
16111 : char str[80];
16112 0 : mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
16113 0 : if (strchr(str, '.'))
16114 0 : *strchr(str, '.') = 0;
16115 0 : cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
16116 : }
16117 :
16118 : //exit:
16119 :
16120 0 : cm_msg_flush_buffer();
16121 :
16122 : /* disconnect from experiment as MIDAS server */
16123 0 : if (rpc_is_mserver()) {
16124 : HNDLE hDB, hKey;
16125 :
16126 0 : cm_get_experiment_database(&hDB, &hKey);
16127 :
16128 : /* only disconnect from experiment if previously connected.
16129 : Necessary for pure RPC servers (RPC_SRVR) */
16130 0 : if (hDB) {
16131 0 : bm_close_all_buffers();
16132 0 : cm_delete_client_info(hDB, 0);
16133 0 : db_close_all_databases();
16134 :
16135 0 : rpc_deregister_functions();
16136 :
16137 0 : cm_set_experiment_database(0, 0);
16138 : }
16139 : }
16140 :
16141 0 : bool is_mserver = sa->is_mserver;
16142 :
16143 0 : sa->close();
16144 :
16145 : /* signal caller a shutdonw */
16146 0 : if (status == RPC_SHUTDOWN)
16147 0 : return status;
16148 :
16149 : /* only the mserver should stop on server connection closure */
16150 0 : if (!is_mserver) {
16151 0 : return SS_SUCCESS;
16152 : }
16153 :
16154 0 : return status;
16155 : }
16156 :
16157 :
16158 : /********************************************************************/
16159 0 : int rpc_flush_event_socket(int timeout_msec)
16160 : /********************************************************************\
16161 :
16162 : Routine: rpc_flush_event_socket
16163 :
16164 : Purpose: Receive and en-buffer events from the mserver event socket
16165 :
16166 : Function value:
16167 : BM_SUCCESS Event socket is empty, all data was read an en-buffered
16168 : BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
16169 : SS_EXIT Server connection was closed
16170 : SS_ABORT Server connection was broken
16171 :
16172 : \********************************************************************/
16173 : {
16174 0 : bool has_data = ss_event_socket_has_data();
16175 :
16176 : //printf("ss_event_socket_has_data() returned %d\n", has_data);
16177 :
16178 0 : if (has_data) {
16179 0 : if (timeout_msec == BM_NO_WAIT) {
16180 0 : return BM_ASYNC_RETURN;
16181 0 : } else if (timeout_msec == BM_WAIT) {
16182 0 : return BM_ASYNC_RETURN;
16183 : } else {
16184 0 : int status = ss_suspend(timeout_msec, MSG_BM);
16185 0 : if (status == SS_ABORT || status == SS_EXIT)
16186 0 : return status;
16187 0 : return BM_ASYNC_RETURN;
16188 : }
16189 : }
16190 :
16191 0 : int status = rpc_server_receive_event(0, NULL, timeout_msec);
16192 :
16193 : //printf("rpc_server_receive_event() status %d\n", status);
16194 :
16195 0 : if (status == BM_ASYNC_RETURN) {
16196 0 : return BM_ASYNC_RETURN;
16197 : }
16198 :
16199 0 : if (status == SS_ABORT || status == SS_EXIT)
16200 0 : return status;
16201 :
16202 0 : return BM_SUCCESS;
16203 : }
16204 :
16205 : /********************************************************************/
16206 2 : INT rpc_server_shutdown(void)
16207 : /********************************************************************\
16208 :
16209 : Routine: rpc_server_shutdown
16210 :
16211 : Purpose: Shutdown RPC server, abort all connections
16212 :
16213 : Input:
16214 : none
16215 :
16216 : Output:
16217 : none
16218 :
16219 : Function value:
16220 : RPC_SUCCESS Successful completion
16221 :
16222 : \********************************************************************/
16223 : {
16224 : //printf("rpc_server_shutdown!\n");
16225 :
16226 : struct linger ling;
16227 :
16228 : /* close all open connections */
16229 2 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16230 0 : if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
16231 0 : RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
16232 : /* lingering needed for PCTCP */
16233 0 : ling.l_onoff = 1;
16234 0 : ling.l_linger = 0;
16235 0 : setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16236 0 : ss_socket_close(&sa->recv_sock);
16237 :
16238 0 : if (sa->send_sock) {
16239 0 : setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16240 0 : ss_socket_close(&sa->send_sock);
16241 : }
16242 :
16243 0 : if (sa->event_sock) {
16244 0 : setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16245 0 : ss_socket_close(&sa->event_sock);
16246 : }
16247 : }
16248 : }
16249 :
16250 : /* avoid memory leak */
16251 2 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16252 0 : RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
16253 0 : if (sa) {
16254 : //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
16255 0 : if (sa == _mserver_acception) {
16256 : // do not leave behind a stale pointer!
16257 0 : _mserver_acception = NULL;
16258 : }
16259 0 : delete sa;
16260 0 : _server_acceptions[idx] = NULL;
16261 : }
16262 : }
16263 :
16264 2 : if (_rpc_registered) {
16265 2 : ss_socket_close(&_rpc_listen_socket);
16266 2 : _rpc_registered = FALSE;
16267 : }
16268 :
16269 : /* free suspend structures */
16270 2 : ss_suspend_exit();
16271 :
16272 2 : return RPC_SUCCESS;
16273 : }
16274 :
16275 :
16276 : /********************************************************************/
16277 0 : INT rpc_check_channels(void)
16278 : /********************************************************************\
16279 :
16280 : Routine: rpc_check_channels
16281 :
16282 : Purpose: Check open rpc channels by sending watchdog messages
16283 :
16284 : Input:
16285 : none
16286 :
16287 : Output:
16288 : none
16289 :
16290 : Function value:
16291 : RPC_SUCCESS Channel is still alive
16292 : RPC_NET_ERROR Connection is broken
16293 :
16294 : \********************************************************************/
16295 : {
16296 : INT status;
16297 : NET_COMMAND nc;
16298 : fd_set readfds;
16299 : struct timeval timeout;
16300 :
16301 0 : for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16302 0 : if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
16303 0 : RPC_SERVER_ACCEPTION* sa = _server_acceptions[idx];
16304 0 : if (sa == NULL)
16305 0 : continue;
16306 :
16307 0 : if (sa->watchdog_timeout == 0) {
16308 0 : continue;
16309 : }
16310 :
16311 0 : DWORD elapsed = ss_millitime() - sa->last_activity;
16312 : //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
16313 :
16314 0 : if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
16315 :
16316 : //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
16317 :
16318 : /* send a watchdog message */
16319 0 : nc.header.routine_id = MSG_WATCHDOG;
16320 0 : nc.header.param_size = 0;
16321 :
16322 0 : int convert_flags = sa->convert_flags;
16323 0 : if (convert_flags) {
16324 0 : rpc_convert_single(&nc.header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
16325 0 : rpc_convert_single(&nc.header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
16326 : }
16327 :
16328 : /* send the header to the client */
16329 0 : int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
16330 :
16331 0 : if (i < 0) {
16332 0 : cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
16333 : sa->prog_name.c_str(),
16334 : sa->host_name.c_str(),
16335 0 : sa->watchdog_timeout / 1000,
16336 : i);
16337 :
16338 : /* disconnect from experiment */
16339 0 : if (rpc_is_mserver()) {
16340 0 : cm_disconnect_experiment();
16341 0 : return RPC_NET_ERROR;
16342 : }
16343 :
16344 0 : sa->close();
16345 0 : return RPC_NET_ERROR;
16346 : }
16347 :
16348 : /* make some timeout checking */
16349 0 : FD_ZERO(&readfds);
16350 0 : FD_SET(sa->send_sock, &readfds);
16351 0 : FD_SET(sa->recv_sock, &readfds);
16352 :
16353 0 : timeout.tv_sec = sa->watchdog_timeout / 1000;
16354 0 : timeout.tv_usec = (sa->watchdog_timeout % 1000) * 1000;
16355 :
16356 : do {
16357 0 : status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
16358 :
16359 : /* if an alarm signal was cought, restart select with reduced timeout */
16360 0 : if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
16361 0 : timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
16362 :
16363 0 : } while (status == -1); /* dont return if an alarm signal was cought */
16364 :
16365 0 : if (!FD_ISSET(sa->send_sock, &readfds) &&
16366 0 : !FD_ISSET(sa->recv_sock, &readfds)) {
16367 :
16368 0 : cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
16369 : sa->prog_name.c_str(),
16370 : sa->host_name.c_str(),
16371 0 : sa->watchdog_timeout / 1000);
16372 :
16373 : /* disconnect from experiment */
16374 0 : if (rpc_is_mserver()) {
16375 0 : cm_disconnect_experiment();
16376 0 : return RPC_NET_ERROR;
16377 : }
16378 :
16379 0 : sa->close();
16380 0 : return RPC_NET_ERROR;
16381 : }
16382 :
16383 : /* receive result on send socket */
16384 0 : if (FD_ISSET(sa->send_sock, &readfds)) {
16385 0 : i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
16386 0 : if (i <= 0) {
16387 0 : cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
16388 : sa->prog_name.c_str(),
16389 : sa->host_name.c_str(),
16390 0 : sa->watchdog_timeout / 1000,
16391 : i);
16392 :
16393 : /* disconnect from experiment */
16394 0 : if (rpc_is_mserver()) {
16395 0 : cm_disconnect_experiment();
16396 0 : return RPC_NET_ERROR;
16397 : }
16398 :
16399 0 : sa->close();
16400 0 : return RPC_NET_ERROR;
16401 : }
16402 : }
16403 : }
16404 : }
16405 : }
16406 :
16407 0 : return RPC_SUCCESS;
16408 : }
16409 :
16410 : /** @} */
16411 :
16412 : /**dox***************************************************************/
16413 : /** @addtogroup bkfunctionc
16414 : *
16415 : * @{ */
16416 :
16417 : /********************************************************************\
16418 : * *
16419 : * Bank functions *
16420 : * *
16421 : \********************************************************************/
16422 :
16423 : /********************************************************************/
16424 : /**
16425 : Initializes an event for Midas banks structure.
16426 : Before banks can be created in an event, bk_init() has to be called first.
16427 : @param event pointer to the area of event
16428 : */
16429 0 : void bk_init(void *event) {
16430 0 : ((BANK_HEADER *) event)->data_size = 0;
16431 0 : ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
16432 0 : }
16433 :
16434 : /**dox***************************************************************/
16435 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
16436 :
16437 : /********************************************************************/
16438 0 : BOOL bk_is32(const void *event)
16439 : /********************************************************************\
16440 :
16441 : Routine: bk_is32
16442 :
16443 : Purpose: Return true if banks inside event are 32-bit banks
16444 :
16445 : Input:
16446 : void *event pointer to the event
16447 :
16448 : Output:
16449 : none
16450 :
16451 : Function value:
16452 : none
16453 :
16454 : \********************************************************************/
16455 : {
16456 0 : return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
16457 : }
16458 :
16459 : /********************************************************************/
16460 0 : BOOL bk_is32a(const void *event)
16461 : /********************************************************************\
16462 :
16463 : Routine: bk_is32a
16464 :
16465 : Purpose: Return true if banks inside event are 32-bit banks
16466 : and banks are 64-bit aligned
16467 :
16468 : Input:
16469 : void *event pointer to the event
16470 :
16471 : Output:
16472 : none
16473 :
16474 : Function value:
16475 : none
16476 :
16477 : \********************************************************************/
16478 : {
16479 0 : return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
16480 : }
16481 :
16482 : /**dox***************************************************************/
16483 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
16484 :
16485 : /********************************************************************/
16486 : /**
16487 : Initializes an event for Midas banks structure for large bank size (> 32KBytes)
16488 : Before banks can be created in an event, bk_init32() has to be called first.
16489 : @param event pointer to the area of event
16490 : @return void
16491 : */
16492 0 : void bk_init32(void *event) {
16493 0 : ((BANK_HEADER *) event)->data_size = 0;
16494 0 : ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
16495 0 : }
16496 :
16497 : /********************************************************************/
16498 : /**
16499 : Initializes an event for Midas banks structure for large bank size (> 32KBytes)
16500 : which are aligned on 64-bit boundaries.
16501 : Before banks can be created in an event, bk_init32a() has to be called first.
16502 : @param event pointer to the area of event
16503 : @return void
16504 : */
16505 0 : void bk_init32a(void *event) {
16506 0 : ((BANK_HEADER *) event)->data_size = 0;
16507 0 : ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT | BANK_FORMAT_64BIT_ALIGNED;
16508 0 : }
16509 :
16510 : /********************************************************************/
16511 : /**
16512 : Returns the size of an event containing banks.
16513 : The total size of an event is the value returned by bk_size() plus the size
16514 : of the event header (sizeof(EVENT_HEADER)).
16515 : @param event pointer to the area of event
16516 : @return number of bytes contained in data area of event
16517 : */
16518 0 : INT bk_size(const void *event) {
16519 0 : return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
16520 : }
16521 :
16522 0 : static void copy_bk_name(char* dst, const char* src)
16523 : {
16524 : // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
16525 :
16526 0 : if (src[0] == 0) {
16527 : // invalid empty name
16528 0 : dst[0] = 0;
16529 0 : dst[1] = 0;
16530 0 : dst[2] = 0;
16531 0 : dst[3] = 0;
16532 0 : return;
16533 : }
16534 :
16535 0 : dst[0] = src[0];
16536 :
16537 0 : if (src[1] == 0) {
16538 0 : dst[1] = 0;
16539 0 : dst[2] = 0;
16540 0 : dst[3] = 0;
16541 0 : return;
16542 : }
16543 :
16544 0 : dst[1] = src[1];
16545 :
16546 0 : if (src[2] == 0) {
16547 0 : dst[2] = 0;
16548 0 : dst[3] = 0;
16549 0 : return;
16550 : }
16551 :
16552 0 : dst[2] = src[2];
16553 :
16554 0 : if (src[3] == 0) {
16555 0 : dst[3] = 0;
16556 0 : return;
16557 : }
16558 :
16559 0 : dst[3] = src[3];
16560 : }
16561 :
16562 : /********************************************************************/
16563 : /**
16564 : Create a Midas bank.
16565 : The data pointer pdata must be used as an address to fill a
16566 : bank. It is incremented with every value written to the bank and finally points
16567 : to a location just after the last byte of the bank. It is then passed to
16568 : the function bk_close() to finish the bank creation.
16569 : \code
16570 : INT *pdata;
16571 : bk_init(pevent);
16572 : bk_create(pevent, "ADC0", TID_INT32, &pdata);
16573 : *pdata++ = 123
16574 : *pdata++ = 456
16575 : bk_close(pevent, pdata);
16576 : \endcode
16577 : @param event pointer to the data area
16578 : @param name of the bank, must be exactly 4 charaters
16579 : @param type type of bank, one of the @ref Midas_Data_Types values defined in
16580 : midas.h
16581 : @param pdata pointer to the data area of the newly created bank
16582 : @return void
16583 : */
16584 0 : void bk_create(void *event, const char *name, WORD type, void **pdata) {
16585 0 : if (bk_is32a((BANK_HEADER *) event)) {
16586 0 : if (((PTYPE) event & 0x07) != 0) {
16587 0 : cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
16588 0 : return;
16589 : }
16590 : BANK32A *pbk32a;
16591 :
16592 0 : pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16593 0 : copy_bk_name(pbk32a->name, name);
16594 0 : pbk32a->type = type;
16595 0 : pbk32a->data_size = 0;
16596 0 : *pdata = pbk32a + 1;
16597 0 : } else if (bk_is32((BANK_HEADER *) event)) {
16598 : BANK32 *pbk32;
16599 :
16600 0 : pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16601 0 : copy_bk_name(pbk32->name, name);
16602 0 : pbk32->type = type;
16603 0 : pbk32->data_size = 0;
16604 0 : *pdata = pbk32 + 1;
16605 : } else {
16606 : BANK *pbk;
16607 :
16608 0 : pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16609 0 : copy_bk_name(pbk->name, name);
16610 0 : pbk->type = type;
16611 0 : pbk->data_size = 0;
16612 0 : *pdata = pbk + 1;
16613 : }
16614 : }
16615 :
16616 : /**dox***************************************************************/
16617 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
16618 :
16619 : /********************************************************************/
16620 : /**
16621 : Copy a bank given by name if found from a buffer source to a destination buffer.
16622 : @param * pevent pointing after the EVENT_HEADER (as in FE)
16623 : @param * psrce pointing to EVENT_HEADER in this case (for ebch[i].pfragment)
16624 : @param bkname Bank to be found and copied from psrce to pevent
16625 : @return EB_SUCCESS if bank found, 0 if not found (pdest untouched)
16626 : */
16627 0 : INT bk_copy(char *pevent, char *psrce, const char *bkname) {
16628 :
16629 : INT status;
16630 : DWORD bklen, bktype, bksze;
16631 : BANK_HEADER *psBkh;
16632 : BANK *psbkh;
16633 : char *pdest;
16634 : void *psdata;
16635 :
16636 : // source pointing on the BANKxx
16637 0 : psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
16638 : // Find requested bank
16639 0 : status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
16640 : // Return 0 if not found
16641 0 : if (status != SUCCESS) return 0;
16642 :
16643 : // Check bank type...
16644 : // You cannot mix BANK and BANK32 so make sure all the FE use either
16645 : // bk_init(pevent) or bk_init32(pevent).
16646 0 : if (bk_is32a(psBkh)) {
16647 :
16648 : // pointer to the source bank header
16649 0 : BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
16650 : // Data size in the bank
16651 0 : bksze = psbkh32a->data_size;
16652 :
16653 : // Get to the end of the event
16654 0 : pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16655 : // Copy from BANK32 to end of Data
16656 0 : memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
16657 : // Bring pointer to the next free location
16658 0 : pdest += ALIGN8(bksze) + sizeof(BANK32A);
16659 :
16660 0 : } else if (bk_is32(psBkh)) {
16661 :
16662 : // pointer to the source bank header
16663 0 : BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
16664 : // Data size in the bank
16665 0 : bksze = psbkh32->data_size;
16666 :
16667 : // Get to the end of the event
16668 0 : pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16669 : // Copy from BANK32 to end of Data
16670 0 : memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
16671 : // Bring pointer to the next free location
16672 0 : pdest += ALIGN8(bksze) + sizeof(BANK32);
16673 :
16674 : } else {
16675 :
16676 : // pointer to the source bank header
16677 0 : psbkh = ((BANK *) psdata - 1);
16678 : // Data size in the bank
16679 0 : bksze = psbkh->data_size;
16680 :
16681 : // Get to the end of the event
16682 0 : pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16683 : // Copy from BANK to end of Data
16684 0 : memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
16685 : // Bring pointer to the next free location
16686 0 : pdest += ALIGN8(bksze) + sizeof(BANK);
16687 : }
16688 :
16689 : // Close bank (adjust BANK_HEADER size)
16690 0 : bk_close(pevent, pdest);
16691 : // Adjust EVENT_HEADER size
16692 0 : ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
16693 0 : return SUCCESS;
16694 : }
16695 :
16696 : /********************************************************************/
16697 0 : int bk_delete(void *event, const char *name)
16698 : /********************************************************************\
16699 :
16700 : Routine: bk_delete
16701 :
16702 : Purpose: Delete a MIDAS bank inside an event
16703 :
16704 : Input:
16705 : void *event pointer to the event
16706 : char *name Name of bank (exactly four letters)
16707 :
16708 : Function value:
16709 : CM_SUCCESS Bank has been deleted
16710 : 0 Bank has not been found
16711 :
16712 : \********************************************************************/
16713 : {
16714 : BANK *pbk;
16715 : DWORD dname;
16716 : int remaining;
16717 :
16718 0 : if (bk_is32a((BANK_HEADER *) event)) {
16719 : /* locate bank */
16720 0 : BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16721 0 : copy_bk_name((char *) &dname, name);
16722 : do {
16723 0 : if (*((DWORD *) pbk32a->name) == dname) {
16724 : /* bank found, delete it */
16725 0 : remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16726 0 : sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16727 :
16728 : /* reduce total event size */
16729 0 : ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
16730 :
16731 : /* copy remaining bytes */
16732 0 : if (remaining > 0)
16733 0 : memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
16734 0 : return CM_SUCCESS;
16735 : }
16736 :
16737 0 : pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16738 0 : } while ((DWORD) ((char *) pbk32a - (char *) event) <
16739 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16740 0 : } else if (bk_is32((BANK_HEADER *) event)) {
16741 : /* locate bank */
16742 0 : BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16743 0 : copy_bk_name((char *) &dname, name);
16744 : do {
16745 0 : if (*((DWORD *) pbk32->name) == dname) {
16746 : /* bank found, delete it */
16747 0 : remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16748 0 : sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16749 :
16750 : /* reduce total event size */
16751 0 : ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
16752 :
16753 : /* copy remaining bytes */
16754 0 : if (remaining > 0)
16755 0 : memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
16756 0 : return CM_SUCCESS;
16757 : }
16758 :
16759 0 : pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16760 0 : } while ((DWORD) ((char *) pbk32 - (char *) event) <
16761 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16762 : } else {
16763 : /* locate bank */
16764 0 : pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16765 0 : copy_bk_name((char *) &dname, name);
16766 : do {
16767 0 : if (*((DWORD *) pbk->name) == dname) {
16768 : /* bank found, delete it */
16769 0 : remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16770 0 : sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16771 :
16772 : /* reduce total event size */
16773 0 : ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
16774 :
16775 : /* copy remaining bytes */
16776 0 : if (remaining > 0)
16777 0 : memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
16778 0 : return CM_SUCCESS;
16779 : }
16780 :
16781 0 : pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16782 0 : } while ((DWORD) ((char *) pbk - (char *) event) <
16783 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16784 : }
16785 :
16786 0 : return 0;
16787 : }
16788 :
16789 : /**dox***************************************************************/
16790 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
16791 :
16792 : /********************************************************************/
16793 : /**
16794 : Close the Midas bank priviously created by bk_create().
16795 : The data pointer pdata must be obtained by bk_create() and
16796 : used as an address to fill a bank. It is incremented with every value written
16797 : to the bank and finally points to a location just after the last byte of the
16798 : bank. It is then passed to bk_close() to finish the bank creation
16799 : @param event pointer to current composed event
16800 : @param pdata pointer to the data
16801 : @return number of bytes contained in bank
16802 : */
16803 0 : INT bk_close(void *event, void *pdata) {
16804 0 : if (bk_is32a((BANK_HEADER *) event)) {
16805 0 : BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16806 0 : pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
16807 0 : if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
16808 0 : printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk32a->name[0], pbk32a->name[1], pbk32a->name[2], pbk32a->name[3]);
16809 0 : ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
16810 0 : return pbk32a->data_size;
16811 0 : } else if (bk_is32((BANK_HEADER *) event)) {
16812 0 : BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16813 0 : pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
16814 0 : if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
16815 0 : printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk32->name[0], pbk32->name[1], pbk32->name[2], pbk32->name[3]);
16816 0 : ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
16817 0 : return pbk32->data_size;
16818 : } else {
16819 0 : BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16820 0 : uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
16821 0 : if (size > 0xFFFF) {
16822 0 : printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16823 0 : size = 0;
16824 : }
16825 0 : pbk->data_size = (WORD) (size);
16826 0 : if (pbk->type == TID_STRUCT && pbk->data_size == 0)
16827 0 : printf("Warning: TID_STRUCT bank %c%c%c%c has zero size\n", pbk->name[0], pbk->name[1], pbk->name[2], pbk->name[3]);
16828 0 : size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
16829 0 : if (size > 0xFFFF) {
16830 0 : printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16831 0 : size = 0;
16832 : }
16833 0 : ((BANK_HEADER *) event)->data_size = size;
16834 0 : return pbk->data_size;
16835 : }
16836 : }
16837 :
16838 : /********************************************************************/
16839 : /**
16840 : Extract the MIDAS bank name listing of an event.
16841 : The bklist should be dimensioned with STRING_BANKLIST_MAX
16842 : which corresponds to a max of BANKLIST_MAX banks (midas.h: 32 banks max).
16843 : \code
16844 : INT adc_calib(EVENT_HEADER *pheader, void *pevent)
16845 : {
16846 : INT n_adc, nbanks;
16847 : WORD *pdata;
16848 : char banklist[STRING_BANKLIST_MAX];
16849 :
16850 : // Display # of banks and list of banks in the event
16851 : nbanks = bk_list(pevent, banklist);
16852 : printf("#banks:%d List:%s\n", nbanks, banklist);
16853 :
16854 : // look for ADC0 bank, return if not present
16855 : n_adc = bk_locate(pevent, "ADC0", &pdata);
16856 : ...
16857 : }
16858 : \endcode
16859 : @param event pointer to current composed event
16860 : @param bklist returned ASCII string, has to be booked with STRING_BANKLIST_MAX.
16861 : @return number of bank found in this event.
16862 : */
16863 0 : INT bk_list(const void *event, char *bklist) { /* Full event */
16864 : INT nbk;
16865 0 : BANK *pmbk = NULL;
16866 0 : BANK32 *pmbk32 = NULL;
16867 0 : BANK32A *pmbk32a = NULL;
16868 : char *pdata;
16869 :
16870 : /* compose bank list */
16871 0 : bklist[0] = 0;
16872 0 : nbk = 0;
16873 : do {
16874 : /* scan all banks for bank name only */
16875 0 : if (bk_is32a(event)) {
16876 0 : bk_iterate32a(event, &pmbk32a, &pdata);
16877 0 : if (pmbk32a == NULL)
16878 0 : break;
16879 0 : } else if (bk_is32(event)) {
16880 0 : bk_iterate32(event, &pmbk32, &pdata);
16881 0 : if (pmbk32 == NULL)
16882 0 : break;
16883 : } else {
16884 0 : bk_iterate(event, &pmbk, &pdata);
16885 0 : if (pmbk == NULL)
16886 0 : break;
16887 : }
16888 0 : nbk++;
16889 :
16890 0 : if (nbk > BANKLIST_MAX) {
16891 0 : cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
16892 0 : return (nbk - 1);
16893 : }
16894 0 : if (bk_is32a(event))
16895 0 : strncat(bklist, (char *) pmbk32a->name, 4);
16896 0 : else if (bk_is32(event))
16897 0 : strncat(bklist, (char *) pmbk32->name, 4);
16898 : else
16899 0 : strncat(bklist, (char *) pmbk->name, 4);
16900 : } while (1);
16901 0 : return (nbk);
16902 : }
16903 :
16904 : /********************************************************************/
16905 : /**
16906 : Locates a MIDAS bank of given name inside an event.
16907 : @param event pointer to current composed event
16908 : @param name bank name to look for
16909 : @param pdata pointer to data area of bank, NULL if bank not found
16910 : @return number of values inside the bank
16911 : */
16912 0 : INT bk_locate(const void *event, const char *name, void *pdata) {
16913 : BANK *pbk;
16914 : BANK32 *pbk32;
16915 : BANK32A *pbk32a;
16916 : DWORD dname;
16917 :
16918 0 : if (bk_is32a(event)) {
16919 0 : pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16920 0 : copy_bk_name((char *) &dname, name);
16921 0 : while ((DWORD) ((char *) pbk32a - (char *) event) <
16922 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16923 0 : if (*((DWORD *) pbk32a->name) == dname) {
16924 0 : *((void **) pdata) = pbk32a + 1;
16925 0 : if (tid_size[pbk32a->type & 0xFF] == 0)
16926 0 : return pbk32a->data_size;
16927 0 : return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16928 : }
16929 0 : pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16930 : }
16931 0 : } else if (bk_is32(event)) {
16932 0 : pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16933 0 : copy_bk_name((char *) &dname, name);
16934 0 : while ((DWORD) ((char *) pbk32 - (char *) event) <
16935 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16936 0 : if (*((DWORD *) pbk32->name) == dname) {
16937 0 : *((void **) pdata) = pbk32 + 1;
16938 0 : if (tid_size[pbk32->type & 0xFF] == 0)
16939 0 : return pbk32->data_size;
16940 0 : return pbk32->data_size / tid_size[pbk32->type & 0xFF];
16941 : }
16942 0 : pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16943 : }
16944 : } else {
16945 0 : pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16946 0 : copy_bk_name((char *) &dname, name);
16947 0 : while ((DWORD) ((char *) pbk - (char *) event) <
16948 0 : ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16949 0 : if (*((DWORD *) pbk->name) == dname) {
16950 0 : *((void **) pdata) = pbk + 1;
16951 0 : if (tid_size[pbk->type & 0xFF] == 0)
16952 0 : return pbk->data_size;
16953 0 : return pbk->data_size / tid_size[pbk->type & 0xFF];
16954 : }
16955 0 : pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16956 : }
16957 :
16958 : }
16959 :
16960 : /* bank not found */
16961 0 : *((void **) pdata) = NULL;
16962 0 : return 0;
16963 : }
16964 :
16965 : /********************************************************************/
16966 : /**
16967 : Finds a MIDAS bank of given name inside an event.
16968 : @param pbkh pointer to current composed event
16969 : @param name bank name to look for
16970 : @param bklen number of elemtents in bank
16971 : @param bktype bank type, one of TID_xxx
16972 : @param pdata pointer to data area of bank, NULL if bank not found
16973 : @return 1 if bank found, 0 otherwise
16974 : */
16975 0 : INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
16976 : DWORD dname;
16977 :
16978 0 : if (bk_is32a(pbkh)) {
16979 0 : BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
16980 0 : copy_bk_name((char *) &dname, name);
16981 : do {
16982 0 : if (*((DWORD *) pbk32a->name) == dname) {
16983 0 : *((void **) pdata) = pbk32a + 1;
16984 0 : if (tid_size[pbk32a->type & 0xFF] == 0)
16985 0 : *bklen = pbk32a->data_size;
16986 : else
16987 0 : *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16988 :
16989 0 : *bktype = pbk32a->type;
16990 0 : return 1;
16991 : }
16992 0 : pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16993 0 : } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16994 0 : } else if (bk_is32(pbkh)) {
16995 0 : BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
16996 0 : copy_bk_name((char *) &dname, name);
16997 : do {
16998 0 : if (*((DWORD *) pbk32->name) == dname) {
16999 0 : *((void **) pdata) = pbk32 + 1;
17000 0 : if (tid_size[pbk32->type & 0xFF] == 0)
17001 0 : *bklen = pbk32->data_size;
17002 : else
17003 0 : *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
17004 :
17005 0 : *bktype = pbk32->type;
17006 0 : return 1;
17007 : }
17008 0 : pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17009 0 : } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17010 : } else {
17011 0 : BANK *pbk = (BANK *) (pbkh + 1);
17012 0 : copy_bk_name((char *) &dname, name);
17013 : do {
17014 0 : if (*((DWORD *) pbk->name) == dname) {
17015 0 : *((void **) pdata) = pbk + 1;
17016 0 : if (tid_size[pbk->type & 0xFF] == 0)
17017 0 : *bklen = pbk->data_size;
17018 : else
17019 0 : *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
17020 :
17021 0 : *bktype = pbk->type;
17022 0 : return 1;
17023 : }
17024 0 : pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17025 0 : } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17026 : }
17027 :
17028 : /* bank not found */
17029 0 : *((void **) pdata) = NULL;
17030 0 : return 0;
17031 : }
17032 :
17033 : /********************************************************************/
17034 : /**
17035 : Iterates through banks inside an event.
17036 : The function can be used to enumerate all banks of an event.
17037 : The returned pointer to the bank header has following structure:
17038 : \code
17039 : typedef struct {
17040 : char name[4];
17041 : WORD type;
17042 : WORD data_size;
17043 : } BANK;
17044 : \endcode
17045 : where type is a TID_xxx value and data_size the size of the bank in bytes.
17046 : \code
17047 : BANK *pbk;
17048 : INT size;
17049 : void *pdata;
17050 : char name[5];
17051 : pbk = NULL;
17052 : do
17053 : {
17054 : size = bk_iterate(event, &pbk, &pdata);
17055 : if (pbk == NULL)
17056 : break;
17057 : *((DWORD *)name) = *((DWORD *)(pbk)->name);
17058 : name[4] = 0;
17059 : printf("bank %s found\n", name);
17060 : } while(TRUE);
17061 : \endcode
17062 : @param event Pointer to data area of event.
17063 : @param pbk pointer to the bank header, must be NULL for the first call to
17064 : this function.
17065 : @param pdata Pointer to the bank header, must be NULL for the first
17066 : call to this function
17067 : @return Size of bank in bytes
17068 : */
17069 0 : INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
17070 0 : if (*pbk == NULL)
17071 0 : *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
17072 : else
17073 0 : *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17074 :
17075 0 : *((void **) pdata) = (*pbk) + 1;
17076 :
17077 0 : if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17078 0 : *pbk = *((BANK **) pdata) = NULL;
17079 0 : return 0;
17080 : }
17081 :
17082 0 : return (*pbk)->data_size;
17083 : }
17084 :
17085 :
17086 : /**dox***************************************************************/
17087 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
17088 :
17089 : /********************************************************************/
17090 0 : INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
17091 : /********************************************************************\
17092 :
17093 : Routine: bk_iterate32
17094 :
17095 : Purpose: Iterate through 32 bit MIDAS banks inside an event
17096 :
17097 : Input:
17098 : void *event pointer to the event
17099 : BANK32 **pbk32 must be NULL for the first call to bk_iterate
17100 :
17101 : Output:
17102 : BANK32 **pbk32 pointer to the bank header
17103 : void *pdata pointer to data area of the bank
17104 :
17105 : Function value:
17106 : INT size of the bank in bytes
17107 :
17108 : \********************************************************************/
17109 : {
17110 0 : if (*pbk == NULL)
17111 0 : *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
17112 : else
17113 0 : *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17114 :
17115 0 : *((void **) pdata) = (*pbk) + 1;
17116 :
17117 0 : if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17118 0 : *pbk = NULL;
17119 0 : pdata = NULL;
17120 0 : return 0;
17121 : }
17122 :
17123 0 : return (*pbk)->data_size;
17124 : }
17125 :
17126 0 : INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
17127 : /********************************************************************\
17128 :
17129 : Routine: bk_iterate32a
17130 :
17131 : Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
17132 :
17133 : Input:
17134 : void *event pointer to the event
17135 : BANK32A **pbk32a must be NULL for the first call to bk_iterate
17136 :
17137 : Output:
17138 : BANK32A **pbk32 pointer to the bank header
17139 : void *pdata pointer to data area of the bank
17140 :
17141 : Function value:
17142 : INT size of the bank in bytes
17143 :
17144 : \********************************************************************/
17145 : {
17146 0 : if (*pbk32a == NULL)
17147 0 : *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
17148 : else
17149 0 : *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
17150 :
17151 0 : *((void **) pdata) = (*pbk32a) + 1;
17152 :
17153 0 : if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17154 0 : *pbk32a = NULL;
17155 0 : pdata = NULL;
17156 0 : return 0;
17157 : }
17158 :
17159 0 : return (*pbk32a)->data_size;
17160 : }
17161 :
17162 : /**dox***************************************************************/
17163 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
17164 :
17165 : /********************************************************************/
17166 : /**
17167 : Swaps bytes from little endian to big endian or vice versa for a whole event.
17168 :
17169 : An event contains a flag which is set by bk_init() to identify
17170 : the endian format of an event. If force is FALSE, this flag is evaluated and
17171 : the event is only swapped if it is in the "wrong" format for this system.
17172 : An event can be swapped to the "wrong" format on purpose for example by a
17173 : front-end which wants to produce events in a "right" format for a back-end
17174 : analyzer which has different byte ordering.
17175 : @param event pointer to data area of event
17176 : @param force If TRUE, the event is always swapped, if FALSE, the event
17177 : is only swapped if it is in the wrong format.
17178 : @return 1==event has been swap, 0==event has not been swapped.
17179 : */
17180 0 : INT bk_swap(void *event, BOOL force) {
17181 : BANK_HEADER *pbh;
17182 : BANK *pbk;
17183 : BANK32 *pbk32;
17184 : BANK32A *pbk32a;
17185 : void *pdata;
17186 : WORD type;
17187 :
17188 0 : pbh = (BANK_HEADER *) event;
17189 :
17190 : /* only swap if flags in high 16-bit */
17191 0 : if (pbh->flags < 0x10000 && !force)
17192 0 : return 0;
17193 :
17194 : /* swap bank header */
17195 0 : DWORD_SWAP(&pbh->data_size);
17196 0 : DWORD_SWAP(&pbh->flags);
17197 :
17198 0 : pbk = (BANK *) (pbh + 1);
17199 0 : pbk32 = (BANK32 *) pbk;
17200 0 : pbk32a = (BANK32A *) pbk;
17201 :
17202 : /* scan event */
17203 0 : while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
17204 : /* swap bank header */
17205 0 : if (bk_is32a(event)) {
17206 0 : DWORD_SWAP(&pbk32a->type);
17207 0 : DWORD_SWAP(&pbk32a->data_size);
17208 0 : pdata = pbk32a + 1;
17209 0 : type = (WORD) pbk32a->type;
17210 0 : } else if (bk_is32(event)) {
17211 0 : DWORD_SWAP(&pbk32->type);
17212 0 : DWORD_SWAP(&pbk32->data_size);
17213 0 : pdata = pbk32 + 1;
17214 0 : type = (WORD) pbk32->type;
17215 : } else {
17216 0 : WORD_SWAP(&pbk->type);
17217 0 : WORD_SWAP(&pbk->data_size);
17218 0 : pdata = pbk + 1;
17219 0 : type = pbk->type;
17220 : }
17221 :
17222 : /* pbk points to next bank */
17223 0 : if (bk_is32a(event)) {
17224 0 : pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17225 0 : pbk = (BANK *) pbk32a;
17226 0 : } else if (bk_is32(event)) {
17227 0 : pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17228 0 : pbk = (BANK *) pbk32;
17229 : } else {
17230 0 : pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17231 0 : pbk32 = (BANK32 *) pbk;
17232 : }
17233 :
17234 0 : switch (type) {
17235 0 : case TID_UINT16:
17236 : case TID_INT16:
17237 0 : while ((char *) pdata < (char *) pbk) {
17238 0 : WORD_SWAP(pdata);
17239 0 : pdata = (void *) (((WORD *) pdata) + 1);
17240 : }
17241 0 : break;
17242 :
17243 0 : case TID_UINT32:
17244 : case TID_INT32:
17245 : case TID_BOOL:
17246 : case TID_FLOAT:
17247 0 : while ((char *) pdata < (char *) pbk) {
17248 0 : DWORD_SWAP(pdata);
17249 0 : pdata = (void *) (((DWORD *) pdata) + 1);
17250 : }
17251 0 : break;
17252 :
17253 0 : case TID_DOUBLE:
17254 : case TID_INT64:
17255 : case TID_UINT64:
17256 0 : while ((char *) pdata < (char *) pbk) {
17257 0 : QWORD_SWAP(pdata);
17258 0 : pdata = (void *) (((double *) pdata) + 1);
17259 : }
17260 0 : break;
17261 : }
17262 : }
17263 :
17264 0 : return CM_SUCCESS;
17265 : }
17266 :
17267 : /**dox***************************************************************/
17268 :
17269 : /** @} *//* end of bkfunctionc */
17270 :
17271 :
17272 : /**dox***************************************************************/
17273 : /** @addtogroup rbfunctionc
17274 : *
17275 : * @{ */
17276 :
17277 : /**dox***************************************************************/
17278 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
17279 : /********************************************************************/
17280 :
17281 : /********************************************************************\
17282 : * *
17283 : * Ring buffer functions *
17284 : * *
17285 : * Provide an inter-thread buffer scheme for handling front-end *
17286 : * events. This code allows concurrent data acquisition, calibration *
17287 : * and network transfer on a multi-CPU machine. One thread reads *
17288 : * out the data, passes it vis the ring buffer functions *
17289 : * to another thread running on the other CPU, which can then *
17290 : * calibrate and/or send the data over the network. *
17291 : * *
17292 : \********************************************************************/
17293 :
17294 : typedef struct {
17295 : unsigned char *buffer;
17296 : unsigned int size;
17297 : unsigned int max_event_size;
17298 : unsigned char *rp;
17299 : unsigned char *wp;
17300 : unsigned char *ep;
17301 : } RING_BUFFER;
17302 :
17303 : #define MAX_RING_BUFFER 100
17304 :
17305 : static RING_BUFFER rb[MAX_RING_BUFFER];
17306 :
17307 : static volatile int _rb_nonblocking = 0;
17308 :
17309 : /**dox***************************************************************/
17310 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
17311 :
17312 : /********************************************************************/
17313 : /**
17314 : Set all rb_get_xx to nonblocking. Needed in multi-thread
17315 : environments for stopping all theads without deadlock
17316 : @return DB_SUCCESS
17317 : */
17318 0 : int rb_set_nonblocking()
17319 : /********************************************************************\
17320 :
17321 : Routine: rb_set_nonblocking
17322 :
17323 : Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17324 : environments for stopping all theads without deadlock
17325 :
17326 : Input:
17327 : NONE
17328 :
17329 : Output:
17330 : NONE
17331 :
17332 : Function value:
17333 : DB_SUCCESS Successful completion
17334 :
17335 : \********************************************************************/
17336 : {
17337 0 : _rb_nonblocking = 1;
17338 :
17339 0 : return DB_SUCCESS;
17340 : }
17341 :
17342 : /********************************************************************/
17343 : /**
17344 : Create a ring buffer with a given size
17345 :
17346 : Provide an inter-thread buffer scheme for handling front-end
17347 : events. This code allows concurrent data acquisition, calibration
17348 : and network transfer on a multi-CPU machine. One thread reads
17349 : out the data, passes it via the ring buffer functions
17350 : to another thread running on the other CPU, which can then
17351 : calibrate and/or send the data over the network.
17352 :
17353 : @param size Size of ring buffer, must be larger than
17354 : 2*max_event_size
17355 : @param max_event_size Maximum event size to be placed into
17356 : @param *handle Handle to ring buffer
17357 : @return DB_SUCCESS, DB_NO_MEMORY, DB_INVALID_PARAM
17358 : */
17359 0 : int rb_create(int size, int max_event_size, int *handle)
17360 : /********************************************************************\
17361 :
17362 : Routine: rb_create
17363 :
17364 : Purpose: Create a ring buffer with a given size
17365 :
17366 : Input:
17367 : int size Size of ring buffer, must be larger than
17368 : 2*max_event_size
17369 : int max_event_size Maximum event size to be placed into
17370 : ring buffer
17371 : Output:
17372 : int *handle Handle to ring buffer
17373 :
17374 : Function value:
17375 : DB_SUCCESS Successful completion
17376 : DB_NO_MEMORY Maximum number of ring buffers exceeded
17377 : DB_INVALID_PARAM Invalid event size specified
17378 :
17379 : \********************************************************************/
17380 : {
17381 : int i;
17382 :
17383 0 : for (i = 0; i < MAX_RING_BUFFER; i++)
17384 0 : if (rb[i].buffer == NULL)
17385 0 : break;
17386 :
17387 0 : if (i == MAX_RING_BUFFER)
17388 0 : return DB_NO_MEMORY;
17389 :
17390 0 : if (size < max_event_size * 2)
17391 0 : return DB_INVALID_PARAM;
17392 :
17393 0 : memset(&rb[i], 0, sizeof(RING_BUFFER));
17394 0 : rb[i].buffer = (unsigned char *) M_MALLOC(size);
17395 0 : assert(rb[i].buffer);
17396 0 : rb[i].size = size;
17397 0 : rb[i].max_event_size = max_event_size;
17398 0 : rb[i].rp = rb[i].buffer;
17399 0 : rb[i].wp = rb[i].buffer;
17400 0 : rb[i].ep = rb[i].buffer;
17401 :
17402 0 : *handle = i + 1;
17403 :
17404 0 : return DB_SUCCESS;
17405 : }
17406 :
17407 : /********************************************************************/
17408 : /**
17409 : Delete a ring buffer
17410 : @param handle Handle of the ring buffer
17411 : @return DB_SUCCESS
17412 : */
17413 0 : int rb_delete(int handle)
17414 : /********************************************************************\
17415 :
17416 : Routine: rb_delete
17417 :
17418 : Purpose: Delete a ring buffer
17419 :
17420 : Input:
17421 : none
17422 : Output:
17423 : int handle Handle to ring buffer
17424 :
17425 : Function value:
17426 : DB_SUCCESS Successful completion
17427 :
17428 : \********************************************************************/
17429 : {
17430 0 : if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17431 0 : return DB_INVALID_HANDLE;
17432 :
17433 0 : M_FREE(rb[handle - 1].buffer);
17434 0 : rb[handle - 1].buffer = NULL;
17435 0 : memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
17436 :
17437 0 : return DB_SUCCESS;
17438 : }
17439 :
17440 : /********************************************************************/
17441 : /**
17442 : Retrieve write pointer where new data can be written
17443 : @param handle Ring buffer handle
17444 : @param millisec Optional timeout in milliseconds if
17445 : buffer is full. Zero to not wait at
17446 : all (non-blocking)
17447 : @param **p Write pointer
17448 : @return DB_SUCCESS, DB_TIMEOUT, DB_INVALID_HANDLE
17449 : */
17450 0 : int rb_get_wp(int handle, void **p, int millisec)
17451 : /********************************************************************\
17452 :
17453 : Routine: rb_get_wp
17454 :
17455 : Purpose: Retrieve write pointer where new data can be written
17456 :
17457 : Input:
17458 : int handle Ring buffer handle
17459 : int millisec Optional timeout in milliseconds if
17460 : buffer is full. Zero to not wait at
17461 : all (non-blocking)
17462 :
17463 : Output:
17464 : char **p Write pointer
17465 :
17466 : Function value:
17467 : DB_SUCCESS Successful completion
17468 :
17469 : \********************************************************************/
17470 : {
17471 : int h, i;
17472 : unsigned char *rp;
17473 :
17474 0 : if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17475 0 : return DB_INVALID_HANDLE;
17476 :
17477 0 : h = handle - 1;
17478 :
17479 0 : for (i = 0; i <= millisec / 10; i++) {
17480 :
17481 0 : rp = rb[h].rp; // keep local copy for convenience
17482 :
17483 : /* check if enough size for wp >= rp without wrap-around */
17484 0 : if (rb[h].wp >= rp
17485 0 : && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17486 0 : *p = rb[h].wp;
17487 0 : return DB_SUCCESS;
17488 : }
17489 :
17490 : /* check if enough size for wp >= rp with wrap-around */
17491 0 : if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17492 0 : rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
17493 0 : *p = rb[h].wp;
17494 0 : return DB_SUCCESS;
17495 : }
17496 :
17497 : /* check if enough size for wp < rp */
17498 0 : if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17499 0 : *p = rb[h].wp;
17500 0 : return DB_SUCCESS;
17501 : }
17502 :
17503 0 : if (millisec == 0)
17504 0 : return DB_TIMEOUT;
17505 :
17506 0 : if (_rb_nonblocking)
17507 0 : return DB_TIMEOUT;
17508 :
17509 : /* wait one time slice */
17510 0 : ss_sleep(10);
17511 : }
17512 :
17513 0 : return DB_TIMEOUT;
17514 : }
17515 :
17516 : /********************************************************************/
17517 : /** rb_increment_wp
17518 :
17519 : Increment current write pointer, making the data at
17520 : the write pointer available to the receiving thread
17521 : @param handle Ring buffer handle
17522 : @param size Number of bytes placed at the WP
17523 : @return DB_SUCCESS, DB_INVALID_PARAM, DB_INVALID_HANDLE
17524 : */
17525 0 : int rb_increment_wp(int handle, int size)
17526 : /********************************************************************\
17527 :
17528 : Routine: rb_increment_wp
17529 :
17530 : Purpose: Increment current write pointer, making the data at
17531 : the write pointer available to the receiving thread
17532 :
17533 : Input:
17534 : int handle Ring buffer handle
17535 : int size Number of bytes placed at the WP
17536 :
17537 : Output:
17538 : NONE
17539 :
17540 : Function value:
17541 : DB_SUCCESS Successful completion
17542 : DB_INVALID_PARAM Event size too large or invalid handle
17543 : \********************************************************************/
17544 : {
17545 : int h;
17546 : unsigned char *new_wp;
17547 :
17548 0 : if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17549 0 : return DB_INVALID_HANDLE;
17550 :
17551 0 : h = handle - 1;
17552 :
17553 0 : if ((DWORD) size > rb[h].max_event_size) {
17554 0 : cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
17555 0 : size/1024/1024, rb[h].max_event_size/1024/1024);
17556 0 : abort();
17557 : }
17558 :
17559 0 : new_wp = rb[h].wp + size;
17560 :
17561 : /* wrap around wp if not enough space */
17562 0 : if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17563 0 : rb[h].ep = new_wp;
17564 0 : new_wp = rb[h].buffer;
17565 0 : assert(rb[h].rp != rb[h].buffer);
17566 : } else
17567 0 : if (new_wp > rb[h].ep)
17568 0 : rb[h].ep = new_wp;
17569 :
17570 0 : rb[h].wp = new_wp;
17571 :
17572 0 : return DB_SUCCESS;
17573 : }
17574 :
17575 : /********************************************************************/
17576 : /**
17577 : Obtain the current read pointer at which new data is
17578 : available with optional timeout
17579 :
17580 : @param handle Ring buffer handle
17581 : @param millisec Optional timeout in milliseconds if
17582 : buffer is full. Zero to not wait at
17583 : all (non-blocking)
17584 :
17585 : @param **p Address of pointer pointing to newly
17586 : available data. If p == NULL, only
17587 : return status.
17588 : @return DB_SUCCESS, DB_TIEMOUT, DB_INVALID_HANDLE
17589 :
17590 : */
17591 0 : int rb_get_rp(int handle, void **p, int millisec)
17592 : /********************************************************************\
17593 :
17594 : Routine: rb_get_rp
17595 :
17596 : Purpose: Obtain the current read pointer at which new data is
17597 : available with optional timeout
17598 :
17599 : Input:
17600 : int handle Ring buffer handle
17601 : int millisec Optional timeout in milliseconds if
17602 : buffer is full. Zero to not wait at
17603 : all (non-blocking)
17604 :
17605 : Output:
17606 : char **p Address of pointer pointing to newly
17607 : available data. If p == NULL, only
17608 : return status.
17609 :
17610 : Function value:
17611 : DB_SUCCESS Successful completion
17612 :
17613 : \********************************************************************/
17614 : {
17615 : int i, h;
17616 :
17617 0 : if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17618 0 : return DB_INVALID_HANDLE;
17619 :
17620 0 : h = handle - 1;
17621 :
17622 0 : for (i = 0; i <= millisec / 10; i++) {
17623 :
17624 0 : if (rb[h].wp != rb[h].rp) {
17625 0 : if (p != NULL)
17626 0 : *p = rb[handle - 1].rp;
17627 0 : return DB_SUCCESS;
17628 : }
17629 :
17630 0 : if (millisec == 0)
17631 0 : return DB_TIMEOUT;
17632 :
17633 0 : if (_rb_nonblocking)
17634 0 : return DB_TIMEOUT;
17635 :
17636 : /* wait one time slice */
17637 0 : ss_sleep(10);
17638 : }
17639 :
17640 0 : return DB_TIMEOUT;
17641 : }
17642 :
17643 : /********************************************************************/
17644 : /**
17645 : Increment current read pointer, freeing up space for the writing thread.
17646 :
17647 : @param handle Ring buffer handle
17648 : @param size Number of bytes to free up at current
17649 : read pointer
17650 : @return DB_SUCCESS, DB_INVALID_PARAM
17651 :
17652 : */
17653 0 : int rb_increment_rp(int handle, int size)
17654 : /********************************************************************\
17655 :
17656 : Routine: rb_increment_rp
17657 :
17658 : Purpose: Increment current read pointer, freeing up space for
17659 : the writing thread.
17660 :
17661 : Input:
17662 : int handle Ring buffer handle
17663 : int size Number of bytes to free up at current
17664 : read pointer
17665 :
17666 : Output:
17667 : NONE
17668 :
17669 : Function value:
17670 : DB_SUCCESS Successful completion
17671 : DB_INVALID_PARAM Event size too large or invalid handle
17672 :
17673 : \********************************************************************/
17674 : {
17675 : int h;
17676 :
17677 : unsigned char *new_rp;
17678 : unsigned char *ep;
17679 :
17680 0 : if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17681 0 : return DB_INVALID_HANDLE;
17682 :
17683 0 : h = handle - 1;
17684 :
17685 0 : if ((DWORD) size > rb[h].max_event_size)
17686 0 : return DB_INVALID_PARAM;
17687 :
17688 0 : new_rp = rb[h].rp + size;
17689 0 : ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
17690 :
17691 : /* wrap around if end pointer reached */
17692 0 : if (new_rp >= ep && rb[h].wp < ep)
17693 0 : new_rp = rb[h].buffer;
17694 :
17695 0 : rb[handle - 1].rp = new_rp;
17696 :
17697 0 : return DB_SUCCESS;
17698 : }
17699 :
17700 : /********************************************************************/
17701 : /**
17702 : Return number of bytes in a ring buffer
17703 :
17704 : @param handle Handle of the buffer to get the info
17705 : @param *n_bytes Number of bytes in buffer
17706 : @return DB_SUCCESS, DB_INVALID_HANDLE
17707 : */
17708 0 : int rb_get_buffer_level(int handle, int *n_bytes)
17709 : /********************************************************************\
17710 :
17711 : Routine: rb_get_buffer_level
17712 :
17713 : Purpose: Return number of bytes in a ring buffer
17714 :
17715 : Input:
17716 : int handle Handle of the buffer to get the info
17717 :
17718 : Output:
17719 : int *n_bytes Number of bytes in buffer
17720 :
17721 : Function value:
17722 : DB_SUCCESS Successful completion
17723 : DB_INVALID_HANDLE Buffer handle is invalid
17724 :
17725 : \********************************************************************/
17726 : {
17727 : int h;
17728 :
17729 0 : if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17730 0 : return DB_INVALID_HANDLE;
17731 :
17732 0 : h = handle - 1;
17733 :
17734 0 : if (rb[h].wp >= rb[h].rp)
17735 0 : *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
17736 : else
17737 0 : *n_bytes =
17738 0 : (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
17739 :
17740 0 : return DB_SUCCESS;
17741 : }
17742 :
17743 : /** @} *//* end of rbfunctionc */
17744 :
17745 :
17746 0 : int cm_write_event_to_odb(HNDLE hDB, HNDLE hKey, const EVENT_HEADER* pevent, INT format)
17747 : {
17748 0 : if (format == FORMAT_FIXED) {
17749 : int status;
17750 0 : status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
17751 0 : if (status != DB_SUCCESS) {
17752 0 : cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
17753 0 : return status;
17754 : }
17755 0 : return SUCCESS;
17756 0 : } else if (format == FORMAT_MIDAS) {
17757 : INT size, i, status, n_data;
17758 : int n;
17759 : char *pdata, *pdata0;
17760 :
17761 : char name[5];
17762 : BANK_HEADER *pbh;
17763 : BANK *pbk;
17764 : BANK32 *pbk32;
17765 : BANK32A *pbk32a;
17766 : DWORD bkname;
17767 : WORD bktype;
17768 : HNDLE hKeyRoot, hKeyl, *hKeys;
17769 : KEY key;
17770 :
17771 0 : pbh = (BANK_HEADER *) (pevent + 1);
17772 0 : pbk = NULL;
17773 0 : pbk32 = NULL;
17774 0 : pbk32a = NULL;
17775 :
17776 : /* count number of banks */
17777 0 : for (n=0 ; ; n++) {
17778 0 : if (bk_is32a(pbh)) {
17779 0 : bk_iterate32a(pbh, &pbk32a, &pdata);
17780 0 : if (pbk32a == NULL)
17781 0 : break;
17782 0 : } else if (bk_is32(pbh)) {
17783 0 : bk_iterate32(pbh, &pbk32, &pdata);
17784 0 : if (pbk32 == NULL)
17785 0 : break;
17786 : } else {
17787 0 : bk_iterate(pbh, &pbk, &pdata);
17788 0 : if (pbk == NULL)
17789 0 : break;
17790 : }
17791 : }
17792 :
17793 : /* build array of keys */
17794 0 : hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
17795 :
17796 0 : pbk = NULL;
17797 0 : pbk32 = NULL;
17798 0 : n = 0;
17799 : do {
17800 : /* scan all banks */
17801 0 : if (bk_is32a(pbh)) {
17802 0 : size = bk_iterate32a(pbh, &pbk32a, &pdata);
17803 0 : if (pbk32a == NULL)
17804 0 : break;
17805 0 : bkname = *((DWORD *) pbk32a->name);
17806 0 : bktype = (WORD) pbk32a->type;
17807 0 : } else if (bk_is32(pbh)) {
17808 0 : size = bk_iterate32(pbh, &pbk32, &pdata);
17809 0 : if (pbk32 == NULL)
17810 0 : break;
17811 0 : bkname = *((DWORD *) pbk32->name);
17812 0 : bktype = (WORD) pbk32->type;
17813 : } else {
17814 0 : size = bk_iterate(pbh, &pbk, &pdata);
17815 0 : if (pbk == NULL)
17816 0 : break;
17817 0 : bkname = *((DWORD *) pbk->name);
17818 0 : bktype = (WORD) pbk->type;
17819 : }
17820 :
17821 0 : n_data = size;
17822 0 : if (rpc_tid_size(bktype & 0xFF))
17823 0 : n_data /= rpc_tid_size(bktype & 0xFF);
17824 :
17825 : /* get bank key */
17826 0 : *((DWORD *) name) = bkname;
17827 0 : name[4] = 0;
17828 : /* record the start of the data in case it is struct */
17829 0 : pdata0 = pdata;
17830 0 : if (bktype == TID_STRUCT) {
17831 0 : status = db_find_key(hDB, hKey, name, &hKeyRoot);
17832 0 : if (status != DB_SUCCESS) {
17833 0 : cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
17834 0 : continue;
17835 : }
17836 :
17837 : /* write structured bank */
17838 0 : for (i = 0;; i++) {
17839 0 : status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
17840 0 : if (status == DB_NO_MORE_SUBKEYS)
17841 0 : break;
17842 :
17843 0 : db_get_key(hDB, hKeyl, &key);
17844 :
17845 : /* adjust for alignment */
17846 0 : if (key.type != TID_STRING && key.type != TID_LINK)
17847 0 : pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
17848 :
17849 0 : status = db_set_data1(hDB, hKeyl, pdata, key.item_size * key.num_values, key.num_values, key.type);
17850 0 : if (status != DB_SUCCESS) {
17851 0 : cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17852 0 : continue;
17853 : }
17854 0 : hKeys[n++] = hKeyl;
17855 :
17856 : /* shift data pointer to next item */
17857 0 : pdata += key.item_size * key.num_values;
17858 : }
17859 : } else {
17860 : /* write variable length bank */
17861 0 : status = db_find_key(hDB, hKey, name, &hKeyRoot);
17862 0 : if (status != DB_SUCCESS) {
17863 0 : status = db_create_key(hDB, hKey, name, bktype);
17864 0 : if (status != DB_SUCCESS) {
17865 0 : cm_msg(MERROR, "cm_write_event_to_odb", "cannot create key for bank \"%s\" with tid %d in ODB, db_create_key() status %d", name, bktype, status);
17866 0 : continue;
17867 : }
17868 0 : status = db_find_key(hDB, hKey, name, &hKeyRoot);
17869 0 : if (status != DB_SUCCESS) {
17870 0 : cm_msg(MERROR, "cm_write_event_to_odb", "cannot find key for bank \"%s\" in ODB, after db_create_key(), db_find_key() status %d", name, status);
17871 0 : continue;
17872 : }
17873 : }
17874 0 : if (n_data > 0) {
17875 0 : status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
17876 0 : if (status != DB_SUCCESS) {
17877 0 : cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17878 : }
17879 0 : hKeys[n++] = hKeyRoot;
17880 : }
17881 : }
17882 : } while (1);
17883 :
17884 : /* notify all hot-lined clients in one go */
17885 0 : db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
17886 :
17887 0 : free(hKeys);
17888 :
17889 0 : return SUCCESS;
17890 : } else {
17891 0 : cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
17892 0 : return CM_DB_ERROR;
17893 : }
17894 : }
17895 :
17896 : /* emacs
17897 : * Local Variables:
17898 : * tab-width: 8
17899 : * c-basic-offset: 3
17900 : * indent-tabs-mode: nil
17901 : * End:
17902 : */
|