MIDAS
Loading...
Searching...
No Matches
midas.cxx
Go to the documentation of this file.
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
62#ifndef DOXYGEN_SHOULD_SKIP_THIS
63
64/********************************************************************/
65/* data type sizes */
66static 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 */
89static 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
111static 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
134{
135 if (transition == TR_START) return "START";
136 if (transition == TR_STOP) return "STOP";
137 if (transition == TR_PAUSE) return "PAUSE";
138 if (transition == TR_RESUME) return "RESUME";
139 if (transition == TR_STARTABORT) return "STARTABORT";
140 if (transition == TR_DEFERRED) return "DEFERRED";
141 return msprintf("UNKNOWN TRANSITION %d", transition);
142}
143
144const 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
161extern unsigned _stklen = 60000U;
162#endif
163
164extern DATABASE *_database;
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
195static std::mutex gBuffersMutex; // protects gBuffers vector itself, but not it's contents!
196static std::vector<BUFFER*> gBuffers;
197
198static INT _msg_buffer = 0;
200
201/* Event request descriptor */
202
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 void clear()
211 {
212 buffer_handle = 0;
213 event_id = 0;
214 trigger_mask = 0;
216 }
217};
218
219static std::mutex _request_list_mutex;
220static 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
227static MUTEX_T *_mutex_rpc = NULL; // mutex to protect RPC calls
228
229static void (*_debug_print)(const char *) = NULL;
230
231static INT _debug_mode = 0;
232
233static 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.
238
239/* table for transition functions */
240
246
247static std::mutex _trans_table_mutex;
248static std::vector<TRANS_TABLE> _trans_table;
249
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
259static int _rpc_listen_socket = 0;
260
262
263void cm_ctrlc_handler(int sig);
264
265typedef struct {
267 const char *string;
269
270static const ERROR_TABLE _error_table[] = {
271 {CM_WRONG_PASSWORD, "Wrong password"},
272 {CM_UNDEF_EXP, "Experiment not defined"},
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
279typedef struct {
280 void *adr;
281 int size;
282 char file[80];
283 int line;
285
287static INT _n_mem = 0;
288
290{
293 char *errstr;
297 std::atomic_int status{0};
298 std::atomic_bool finished{false};
299 std::atomic<std::thread*> thread{NULL};
300};
301
303
304/*------------------------------------------------------------------*/
305
306void *dbg_malloc(unsigned int size, char *file, int line) {
307 FILE *f;
308 void *adr;
309 int i;
310
311 adr = malloc(size);
312
313 /* search for deleted entry */
314 for (i = 0; i < _n_mem; i++)
315 if (_mem_loc[i].adr == NULL)
316 break;
317
318 if (i == _n_mem) {
319 _n_mem++;
320 if (!_mem_loc)
321 _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
322 else
324 }
325
326 _mem_loc[i].adr = adr;
327 _mem_loc[i].size = size;
328 strcpy(_mem_loc[i].file, file);
329 _mem_loc[i].line = line;
330
331 f = fopen("mem.txt", "w");
332 for (i = 0; i < _n_mem; i++)
333 if (_mem_loc[i].adr)
334 fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size,
335 _mem_loc[i].adr);
336 fclose(f);
337
338 return adr;
339}
340
341void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line) {
342 void *adr;
343
344 adr = dbg_malloc(size * count, file, line);
345 if (adr)
346 memset(adr, 0, size * count);
347
348 return adr;
349}
350
351void dbg_free(void *adr, char *file, int line) {
352 FILE *f;
353 int i;
354
355 free(adr);
356
357 for (i = 0; i < _n_mem; i++)
358 if (_mem_loc[i].adr == adr)
359 break;
360
361 if (i < _n_mem)
362 _mem_loc[i].adr = NULL;
363
364 f = fopen("mem.txt", "w");
365 for (i = 0; i < _n_mem; i++)
366 if (_mem_loc[i].adr)
367 fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
368 file, line, _mem_loc[i].size, _mem_loc[i].adr);
369 fclose(f);
370}
371
372static std::vector<std::string> split(const char* sep, const std::string& s)
373{
374 unsigned sep_len = strlen(sep);
375 std::vector<std::string> v;
376 std::string::size_type pos = 0;
377 while (1) {
378 std::string::size_type next = s.find(sep, pos);
379 if (next == std::string::npos) {
380 v.push_back(s.substr(pos));
381 break;
382 }
383 v.push_back(s.substr(pos, next-pos));
384 pos = next+sep_len;
385 }
386 return v;
387}
388
389static std::string join(const char* sep, const std::vector<std::string>& v)
390{
391 std::string s;
392
393 for (unsigned i=0; i<v.size(); i++) {
394 if (i>0) {
395 s += sep;
396 }
397 s += v[i];
398 }
399
400 return s;
401}
402
403bool ends_with_char(const std::string& s, char c)
404{
405 if (s.length() < 1)
406 return false;
407 return s[s.length()-1] == c;
408}
409
410std::string msprintf(const char *format, ...) {
411 va_list ap, ap1;
412 va_start(ap, format);
413 va_copy(ap1, ap);
414 size_t size = vsnprintf(nullptr, 0, format, ap1) + 1;
415 char *buffer = (char *)malloc(size);
416 if (!buffer)
417 return "";
418 vsnprintf(buffer, size, format, ap);
419 va_end(ap);
420 std::string s(buffer);
421 free(buffer);
422 return s;
423}
424
425/********************************************************************\
426* *
427* Common message functions *
428* *
429\********************************************************************/
430
431typedef int (*MessagePrintCallback)(const char *);
432
433static std::atomic<MessagePrintCallback> _message_print{puts};
434
435static std::atomic_int _message_mask_system{MT_ALL};
436static std::atomic_int _message_mask_user{MT_ALL};
437
438
440#endif /* DOXYGEN_SHOULD_SKIP_THIS */
441
447/********************************************************************/
455std::string cm_get_error(INT code)
456{
457 for (int i = 0; _error_table[i].code; i++) {
458 if (_error_table[i].code == code) {
459 return _error_table[i].string;
460 }
461 }
462
463 return msprintf("unlisted status code %d", code);
464}
465
466/********************************************************************/
468
469 return CM_SUCCESS;
470}
471
472/********************************************************************/
473
475 //printf("cm_msg_open_buffer!\n");
476 if (_msg_buffer == 0) {
478 if (status != BM_SUCCESS && status != BM_CREATED) {
479 return status;
480 }
481 }
482 return CM_SUCCESS;
483}
484
485/********************************************************************/
486
488 //printf("cm_msg_close_buffer!\n");
489 if (_msg_buffer) {
491 _msg_buffer = 0;
492 }
493 return CM_SUCCESS;
494}
495
496/********************************************************************/
497
505 std::string path;
506
507 cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
508
509 /* extract directory name from full path name of midas.log */
510 size_t pos = path.rfind(DIR_SEPARATOR);
511 if (pos != std::string::npos) {
512 path.resize(pos);
513 } else {
514 path = "";
515 }
516
517 //printf("cm_msg_facilities: path [%s]\n", path.c_str());
518
520
521 ss_file_find(path.c_str(), "*.log", &flist);
522
523 for (size_t i = 0; i < flist.size(); i++) {
524 const char *p = flist[i].c_str();
525 if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
526 size_t pos = flist[i].rfind('.');
527 if (pos != std::string::npos) {
528 flist[i].resize(pos);
529 }
530 list->push_back(flist[i]);
531 }
532 }
533
534 return SUCCESS;
535}
536
537/********************************************************************/
538
539void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
540 HNDLE hDB;
541 int status;
542
544
545 // check for call to cm_msg() before MIDAS is fully initialized
546 // or after MIDAS is partially shutdown.
547 if (status != CM_SUCCESS) {
548 if (filename)
549 *filename = std::string(fac) + ".log";
550 if (linkname)
551 *linkname = "";
552 if (linktarget)
553 *linktarget = "";
554 return;
555 }
556
557 if (filename)
558 *filename = "";
559 if (linkname)
560 *linkname = "";
561 if (linktarget)
562 *linktarget = "";
563
564 std::string facility;
565 if (fac && fac[0])
566 facility = fac;
567 else
568 facility = "midas";
569
570 std::string message_format;
571 db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
572 if (message_format.find('%') != std::string::npos) {
573 /* replace stings such as %y%m%d with current date */
574 struct tm tms;
575
576 ss_tzset();
577 if (t == 0)
578 time(&t);
579 localtime_r(&t, &tms);
580
581 char de[256];
582 de[0] = '_';
583 strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
585 }
586
587 std::string message_dir;
588 db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
589 if (message_dir.empty()) {
590 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
591 if (message_dir.empty()) {
593 if (message_dir.empty()) {
595 }
596 }
597 }
598
599 // prepend experiment directory
600 if (message_dir[0] != DIR_SEPARATOR)
602
603 if (message_dir.back() != DIR_SEPARATOR)
604 message_dir.push_back(DIR_SEPARATOR);
605
606 if (filename)
607 *filename = message_dir + facility + message_format + ".log";
608 if (!message_format.empty()) {
609 if (linkname)
610 *linkname = message_dir + facility + ".log";
611 if (linktarget)
612 *linktarget = facility + message_format + ".log";
613 }
614}
615
616/********************************************************************/
647INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
650 _message_print = func;
651
652 return BM_SUCCESS;
653}
654
655/********************************************************************/
664INT cm_msg_log(INT message_type, const char *facility, const char *message) {
665 INT status;
666
667 if (rpc_is_remote()) {
668 if (rpc_is_connected()) {
669 status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
670 if (status != RPC_SUCCESS) {
671 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);
672 }
673 return status;
674 } else {
675 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
676 return RPC_NET_ERROR;
677 }
678 }
679
680 if (message_type != MT_DEBUG) {
681 std::string filename, linkname, linktarget;
682
684
685#ifdef OS_LINUX
686 if (!linkname.empty()) {
687 //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
688 // If filename does not exist, user just switched from non-date format to date format.
689 // In that case we must copy linkname to filename, otherwise messages might get lost.
690 if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
691 ss_file_copy(linkname.c_str(), filename.c_str(), true);
692 }
693
694 unlink(linkname.c_str());
695 status = symlink(linktarget.c_str(), linkname.c_str());
696 if (status != 0) {
698 "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
699 linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
700 }
701 }
702#endif
703
704 int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
705 if (fh < 0) {
707 "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
708 message, filename.c_str(), errno, strerror(errno));
709 } else {
710
711 struct timeval tv;
712 struct tm tms;
713
714 ss_tzset();
716 localtime_r(&tv.tv_sec, &tms);
717
718 char str[256];
719 strftime(str, sizeof(str), "%H:%M:%S", &tms);
720 sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
721 strftime(str + strlen(str), sizeof(str), "%G/%m/%d", &tms);
722
723 std::string msg;
724 msg += str;
725 msg += " ";
726 msg += message;
727 msg += "\n";
728
729 /* avoid c++ complaint about comparison between
730 unsigned size_t returned by msg.length() and
731 signed ssize_t returned by write() */
732 ssize_t len = msg.length();
733
734 /* atomic write, no need to take a semaphore */
735 ssize_t wr = write(fh, msg.c_str(), len);
736
737 if (wr < 0) {
738 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
739 } else if (wr != len) {
740 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);
741 }
742
743 close(fh);
744 }
745 }
746
747 return CM_SUCCESS;
748}
749
750
751static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
752{
753 /* strip path */
754 const char* pc = filename + strlen(filename);
755 while (*pc != '\\' && *pc != '/' && pc != filename)
756 pc--;
757 if (pc != filename)
758 pc++;
759
760 /* convert type to string */
761 std::string type_str;
762 if (message_type & MT_ERROR)
764 if (message_type & MT_INFO)
766 if (message_type & MT_DEBUG)
768 if (message_type & MT_USER)
770 if (message_type & MT_LOG)
772 if (message_type & MT_TALK)
774
775 std::string message;
776
777 /* print client name into string */
778 if (message_type == MT_USER)
779 message = msprintf("[%s] ", routine);
780 else {
781 std::string name = rpc_get_name();
782 if (name.length() > 0)
783 message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
784 else
785 message = "";
786 }
787
788 /* preceed error messages with file and line info */
789 if (message_type == MT_ERROR) {
790 message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
791 } else if (message_type == MT_USER) {
792 message = msprintf("[%s,%s] ", routine, type_str.c_str());
793 }
794
795 int bufsize = 1024;
796 char* buf = (char*)malloc(bufsize);
797 assert(buf);
798
799 for (int i=0; i<10; i++) {
800 va_list ap;
801 va_copy(ap, *argptr);
802
803 /* print argument list into message */
804 int n = vsnprintf(buf, bufsize-1, format, ap);
805
806 //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
807
808 if (n < bufsize) {
809 break;
810 }
811
812 bufsize += 100;
813 bufsize *= 2;
814 buf = (char*)realloc(buf, bufsize);
815 assert(buf);
816 }
817
818 message += buf;
819 free(buf);
820
821 return message;
822}
823
824static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
825 //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
826
827 /* send event if not of type MLOG */
828 if (message_type != MT_LOG) {
829 if (_msg_buffer) {
830 /* copy message to event */
831 size_t len = strlen(send_message);
832 int event_length = sizeof(EVENT_HEADER) + len + 1;
833 char event[event_length];
834 EVENT_HEADER *pevent = (EVENT_HEADER *) event;
835
836 memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
837
838 /* setup the event header and send the message */
839 bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
840 if (ts)
841 pevent->time_stamp = ts;
842 //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)));
843 bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
844 }
845 }
846
847 return CM_SUCCESS;
848}
849
853 std::string message;
854};
855
856static std::deque<msg_buffer_entry> gMsgBuf;
857static std::mutex gMsgBufMutex;
858
859/********************************************************************/
866 int i;
867
868 //printf("cm_msg_flush_buffer!\n");
869
870 for (i = 0; i < 100; i++) {
872 {
873 std::lock_guard<std::mutex> lock(gMsgBufMutex);
874 if (gMsgBuf.empty())
875 break;
876 e = gMsgBuf.front();
877 gMsgBuf.pop_front();
878 // implicit unlock
879 }
880
881 /* log message */
882 cm_msg_log(e.message_type, "midas", e.message.c_str());
883
884 /* send message to SYSMSG */
885 int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
886 if (status != CM_SUCCESS)
887 return status;
888 }
889
890 return CM_SUCCESS;
891}
892
893/********************************************************************/
915INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
916{
917 DWORD ts = ss_time();
918
919 /* print argument list into message */
920 std::string message;
922 va_start(argptr, format);
923 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
924 va_end(argptr);
925
926 //printf("message [%s]\n", message.c_str());
927
928 /* call user function if set via cm_set_msg_print */
930 if (f != NULL && (message_type & _message_mask_user) != 0) {
931 if (message_type != MT_LOG) { // do not print MLOG messages
932 (*f)(message.c_str());
933 }
934 }
935
936 /* return if system mask is not set */
937 if ((message_type & _message_mask_system) == 0) {
938 return CM_SUCCESS;
939 }
940
941 gMsgBufMutex.lock();
942 gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
943 gMsgBufMutex.unlock();
944
945 return CM_SUCCESS;
946}
947
948/********************************************************************/
973INT cm_msg1(INT message_type, const char *filename, INT line,
974 const char *facility, const char *routine, const char *format, ...) {
976 std::string message;
977 static BOOL in_routine = FALSE;
978
979 /* avoid recursive calles */
980 if (in_routine)
981 return 0;
982
984
985 /* print argument list into message */
986 va_start(argptr, format);
987 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
988 va_end(argptr);
989
990 /* call user function if set via cm_set_msg_print */
992 if (f != NULL && (message_type & _message_mask_user) != 0)
993 (*f)(message.c_str());
994
995 /* return if system mask is not set */
996 if ((message_type & _message_mask_system) == 0) {
998 return CM_SUCCESS;
999 }
1000
1001 /* send message to SYSMSG */
1002 cm_msg_send_event(0, message_type, message.c_str());
1003
1004 /* log message */
1005 cm_msg_log(message_type, facility, message.c_str());
1006
1007 in_routine = FALSE;
1008
1009 return CM_SUCCESS;
1010}
1011
1012/********************************************************************/
1052 INT status, id;
1053
1054 // we should only come here after the message buffer
1055 // was opened by cm_connect_experiment()
1056 assert(_msg_buffer);
1057
1058 _msg_dispatch = func;
1059
1061
1062 return status;
1063}
1064
1065static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1067 int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1068 char buf[100];
1069 int buf_length;
1070
1071 //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);
1072
1073 if (*length + new_message_length + 100 > *allocated) {
1074 *messages = (char *) realloc(*messages, new_allocated);
1075 assert(*messages != NULL);
1077 }
1078
1079 if (*length > 0)
1080 if ((*messages)[(*length) - 1] != '\n') {
1081 (*messages)[*length] = '\n'; // separator between messages
1082 (*length) += 1;
1083 }
1084
1085 sprintf(buf, "%ld ", tstamp);
1086 buf_length = strlen(buf);
1087 memcpy(&((*messages)[*length]), buf, buf_length);
1088 (*length) += buf_length;
1089
1091 (*length) += new_message_length;
1092 (*messages)[*length] = 0; // make sure string is NUL terminated
1093}
1094
1095/* Retrieve message from an individual file. Internal use only */
1096static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1097 int *num_messages) {
1098 BOOL stop;
1099 int fh;
1100 char *p, str[1000];
1101 struct stat stat_buf;
1103
1104 ss_tzset(); // required by localtime_r()
1105
1106 *num_messages = 0;
1107
1108 fh = open(filename, O_RDONLY | O_TEXT, 0644);
1109 if (fh < 0) {
1110 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1111 strerror(errno));
1112 return SS_FILE_ERROR;
1113 }
1114
1115 /* read whole file into memory */
1116 fstat(fh, &stat_buf);
1117 ssize_t size = stat_buf.st_size;
1118
1119 /* if file is too big, only read tail of file */
1120 ssize_t maxsize = 10 * 1024 * 1024;
1121 if (size > maxsize) {
1123 //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1124 size = maxsize;
1125 }
1126
1127 char *buffer = (char *) malloc(size + 1);
1128
1129 if (buffer == NULL) {
1130 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1131 filename, errno, strerror(errno));
1132 close(fh);
1133 return SS_FILE_ERROR;
1134 }
1135
1136 ssize_t rd = read(fh, buffer, size);
1137
1138 if (rd != size) {
1139 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1140 (int) size, filename, (int) rd, errno, strerror(errno));
1141 close(fh);
1142 return SS_FILE_ERROR;
1143 }
1144
1145 buffer[size] = 0;
1146 close(fh);
1147
1148 p = buffer + size - 1;
1150 stop = FALSE;
1151
1152 while (*p == '\n' || *p == '\r')
1153 p--;
1154
1155 int n;
1156 for (n = 0; !stop && p > buffer;) {
1157
1158 /* go to beginning of line */
1159 int i;
1160 for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1161 p--;
1162
1163 /* limit line length to sizeof(str) */
1164 if (i >= (int) sizeof(str))
1165 i = sizeof(str) - 1;
1166
1167 if (p == buffer) {
1168 i++;
1169 memcpy(str, p, i);
1170 } else
1171 memcpy(str, p + 1, i);
1172 str[i] = 0;
1173 if (strchr(str, '\n'))
1174 *strchr(str, '\n') = 0;
1175 if (strchr(str, '\r'))
1176 *strchr(str, '\r') = 0;
1177 mstrlcat(str, "\n", sizeof(str));
1178
1179 // extract time tag
1180 time_t now;
1181 time(&now);
1182
1183 struct tm tms;
1184 localtime_r(&now, &tms); // must call tzset() beforehand!
1185
1186 if (str[0] >= '0' && str[0] <= '9') {
1187 // new format
1188 tms.tm_hour = atoi(str);
1189 tms.tm_min = atoi(str + 3);
1190 tms.tm_sec = atoi(str + 6);
1191 tms.tm_year = atoi(str + 13) - 1900;
1192 tms.tm_mon = atoi(str + 18) - 1;
1193 tms.tm_mday = atoi(str + 21);
1194 } else {
1195 // old format
1196 tms.tm_hour = atoi(str + 11);
1197 tms.tm_min = atoi(str + 14);
1198 tms.tm_sec = atoi(str + 17);
1199 tms.tm_year = atoi(str + 20) - 1900;
1200 for (i = 0; i < 12; i++)
1201 if (strncmp(str + 4, mname[i], 3) == 0)
1202 break;
1203 tms.tm_mon = i;
1204 tms.tm_mday = atoi(str + 8);
1205 }
1206 tstamp = ss_mktime(&tms);
1207 if (tstamp != -1)
1209
1210 // for new messages (n=0!), stop when t reached
1211 if (n_messages == 0) {
1212 if (tstamp_valid < t)
1213 break;
1214 }
1215
1216 // for old messages, stop when all messages belonging to tstamp_last are sent
1217 if (n_messages != 0) {
1219 break;
1220 }
1221
1222 if (t == 0 || tstamp == -1 ||
1223 (n_messages > 0 && tstamp <= t) ||
1224 (n_messages == 0 && tstamp >= t)) {
1225
1226 n++;
1227
1229 }
1230
1231 while (*p == '\n' || *p == '\r')
1232 p--;
1233
1234 if (n_messages == 1)
1235 stop = TRUE;
1236 else if (n_messages > 1) {
1237 // continue collecting messages until time stamp differs from current one
1238 if (n == n_messages)
1240
1241 // if all messages without time tags, just return after n
1242 if (n == n_messages && tstamp_valid == 0)
1243 break;
1244 }
1245 }
1246
1247 free(buffer);
1248
1249 *num_messages = n;
1250
1251 return CM_SUCCESS;
1252}
1253
1254/********************************************************************/
1265 std::string filename, linkname;
1266 INT n, i;
1268 int length = 0;
1269 int allocated = 0;
1270
1271 time(&filedate);
1273
1274 //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1275
1276 // see if file exists, use linkname if not
1277 if (!linkname.empty()) {
1278 if (!ss_file_exist(filename.c_str()))
1279 filename = linkname;
1280 }
1281
1282 if (ss_file_exist(filename.c_str())) {
1283 cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1284 } else {
1285 n = 0;
1286 }
1287
1288 /* if there is no symlink, then there is no additional log files to read */
1289 if (linkname.empty()) {
1290 *num_messages = n;
1291 return CM_SUCCESS;
1292 }
1293
1294 //printf("read more messages %d %d!\n", n, n_message);
1295
1296 int missing = 0;
1297 while (n < n_message) {
1298 filedate -= 3600 * 24; // go one day back
1299
1301
1302 //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1303
1304 if (ss_file_exist(filename.c_str())) {
1305 cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1306 n += i;
1307 missing = 0;
1308 } else {
1309 missing++;
1310 }
1311
1312 // stop if ten consecutive files are not found
1313 if (missing > 10)
1314 break;
1315 }
1316
1317 *num_messages = n;
1318
1319 return CM_SUCCESS;
1320}
1321
1322/********************************************************************/
1335 int status;
1336 char *messages = NULL;
1337 int num_messages = 0;
1338
1339 if (rpc_is_remote())
1340 return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1341
1343
1344 if (messages) {
1345 mstrlcpy(message, messages, buf_size);
1346 int len = strlen(messages);
1347 if (len > buf_size)
1349 free(messages);
1350 }
1351
1352 return status;
1353}
1354
/* end of msgfunctionc */
1357
1363/********************************************************************/
1370 INT sec, status;
1371
1372 /* if connected to server, get time from there */
1373 if (rpc_is_remote()) {
1375
1376 /* set local time */
1377 if (status == CM_SUCCESS)
1378 ss_settime(sec);
1379 }
1380
1381 /* return time to caller */
1382 if (seconds != NULL) {
1383 *seconds = ss_time();
1384 }
1385
1386 return CM_SUCCESS;
1387}
1388
1389/********************************************************************/
1396INT cm_asctime(char *str, INT buf_size) {
1397 /* if connected to server, get time from there */
1398 if (rpc_is_remote())
1399 return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1400
1401 /* return local time */
1402 mstrlcpy(str, ss_asctime().c_str(), buf_size);
1403
1404 return CM_SUCCESS;
1405}
1406
1407/********************************************************************/
1412std::string cm_asctime() {
1413 /* if connected to server, get time from there */
1414 if (rpc_is_remote()) {
1415 char buf[256];
1416 int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1417 if (status == CM_SUCCESS) {
1418 return buf;
1419 } else {
1420 return "";
1421 }
1422 }
1423
1424 /* return local time */
1425 return ss_asctime();
1426}
1427
1428/********************************************************************/
1435 /* if connected to server, get time from there */
1436 if (rpc_is_remote())
1437 return rpc_call(RPC_CM_TIME, t);
1438
1439 /* return local time */
1440 *t = ss_time();
1441
1442 return CM_SUCCESS;
1443}
1444
/* end of cmfunctionc */
1447
1448/********************************************************************\
1449* *
1450* cm_xxx - Common Functions to buffer & database *
1451* *
1452\********************************************************************/
1453
1454/* Globals */
1455
1456static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1457static HNDLE _hDB = 0; /* Database handle */
1458static std::string _experiment_name;
1459static std::string _client_name;
1460static std::string _path_name;
1465//INT _semaphore_msg = -1;
1466
1476const char *cm_get_version() {
1477 return MIDAS_VERSION;
1478}
1479
1484const char *cm_get_revision() {
1485 return GIT_REVISION;
1486}
1487
1488/********************************************************************/
1497INT cm_set_path(const char *path) {
1498 assert(path);
1499 assert(path[0] != 0);
1500
1501 _path_name = path;
1502
1503 if (_path_name.back() != DIR_SEPARATOR) {
1505 }
1506
1507 //printf("cm_set_path [%s]\n", _path_name.c_str());
1508
1509 return CM_SUCCESS;
1510}
1511
1512/********************************************************************/
1518INT cm_get_path(char *path, int path_size) {
1519 // check that we were not accidentally called
1520 // with the size of the pointer to a string
1521 // instead of the size of the string buffer
1522 assert(path_size != sizeof(char *));
1523 assert(path);
1524 assert(_path_name.length() > 0);
1525
1526 mstrlcpy(path, _path_name.c_str(), path_size);
1527
1528 return CM_SUCCESS;
1529}
1530
1531/********************************************************************/
1537std::string cm_get_path() {
1538 assert(_path_name.length() > 0);
1539 return _path_name;
1540}
1541
1542/********************************************************************/
1543/* C++ wrapper for cm_get_path */
1544
1545INT EXPRT cm_get_path_string(std::string *path) {
1546 assert(path != NULL);
1547 assert(_path_name.length() > 0);
1548 *path = _path_name;
1549 return CM_SUCCESS;
1550}
1551
1552/********************************************************************/
1560 return CM_SUCCESS;
1561}
1562
1563/********************************************************************/
1574
1575/********************************************************************/
1581 return _experiment_name;
1582}
1583
/* end of cmfunctionc */
1586
1592#ifdef LOCAL_ROUTINES
1593
1595 std::string name;
1596 std::string directory;
1597 std::string user;
1598};
1599
1601 std::string filename;
1602 std::vector<exptab_entry> exptab;
1603};
1604
1605static exptab_struct _exptab; // contents of exptab file
1606
1615{
1616 exptab->exptab.clear();
1617
1618 /* MIDAS_DIR overrides exptab */
1619 if (getenv("MIDAS_DIR")) {
1620 exptab->filename = "MIDAS_DIR";
1621
1623
1624 if (getenv("MIDAS_EXPT_NAME")) {
1625 e.name = getenv("MIDAS_EXPT_NAME");
1626 } else {
1627 e.name = "Default";
1628 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());
1629 }
1630
1631 e.directory = getenv("MIDAS_DIR");
1632 e.user = "";
1633
1634 exptab->exptab.push_back(e);
1635
1636 return CM_SUCCESS;
1637 }
1638
1639 /* default directory for different OSes */
1640#if defined (OS_WINNT)
1641 std::string str;
1642 if (getenv("SystemRoot"))
1643 str = getenv("SystemRoot");
1644 else if (getenv("windir"))
1645 str = getenv("windir");
1646 else
1647 str = "";
1648
1649 std::string alt_str = str;
1650 str += "\\system32\\exptab";
1651 alt_str += "\\system\\exptab";
1652#elif defined (OS_UNIX)
1653 std::string str = "/etc/exptab";
1654 std::string alt_str = "/exptab";
1655#else
1656 std::strint str = "exptab";
1657 std::string alt_str = "exptab";
1658#endif
1659
1660 /* MIDAS_EXPTAB overrides default directory */
1661 if (getenv("MIDAS_EXPTAB")) {
1662 str = getenv("MIDAS_EXPTAB");
1663 alt_str = getenv("MIDAS_EXPTAB");
1664 }
1665
1666 exptab->filename = str;
1667
1668 /* read list of available experiments */
1669 FILE* f = fopen(str.c_str(), "r");
1670 if (f == NULL) {
1671 f = fopen(alt_str.c_str(), "r");
1672 if (f == NULL)
1673 return CM_UNDEF_ENVIRON;
1674 exptab->filename = alt_str;
1675 }
1676
1677 if (f != NULL) {
1678 do {
1679 char buf[256];
1680 memset(buf, 0, sizeof(buf));
1681 char* str = fgets(buf, sizeof(buf)-1, f);
1682 if (str == NULL)
1683 break;
1684 if (str[0] == 0) continue; // empty line
1685 if (str[0] == '#') continue; // comment line
1686
1688
1689 // following code emulates the function of this sprintf():
1690 //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1691
1692 // skip leading spaces
1693 while (*str && isspace(*str))
1694 str++;
1695
1696 char* p1 = str;
1697 char* p2 = str;
1698
1699 while (*p2 && !isspace(*p2))
1700 p2++;
1701
1702 ssize_t len = p2-p1;
1703
1704 if (len<1)
1705 continue;
1706
1707 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1708
1709 e.name = std::string(p1, len);
1710
1711 if (*p2 == 0)
1712 continue;
1713
1714 str = p2;
1715
1716 // skip leading spaces
1717 while (*str && isspace(*str))
1718 str++;
1719
1720 p1 = str;
1721 p2 = str;
1722
1723 while (*p2 && !isspace(*p2))
1724 p2++;
1725
1726 len = p2-p1;
1727
1728 if (len<1)
1729 continue;
1730
1731 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1732
1733 e.directory = std::string(p1, len);
1734
1735 if (*p2 == 0)
1736 continue;
1737
1738 str = p2;
1739
1740 // skip leading spaces
1741 while (*str && isspace(*str))
1742 str++;
1743
1744 p1 = str;
1745 p2 = str;
1746
1747 while (*p2 && !isspace(*p2))
1748 p2++;
1749
1750 len = p2-p1;
1751
1752 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1753
1754 e.user = std::string(p1, len);
1755
1756 /* check for trailing directory separator */
1757 if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1758 e.directory += DIR_SEPARATOR_STR;
1759 }
1760
1761 exptab->exptab.push_back(e);
1762 } while (!feof(f));
1763 fclose(f);
1764 }
1765
1766#if 0
1767 cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1768 for (unsigned j=0; j<exptab->exptab.size(); j++) {
1769 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());
1770 }
1771#endif
1772
1773 return CM_SUCCESS;
1774}
1775
1776/********************************************************************/
1783int cm_get_exptab_filename(char *s, int size) {
1784 mstrlcpy(s, _exptab.filename.c_str(), size);
1785 return CM_SUCCESS;
1786}
1787
1789 return _exptab.filename;
1790}
1791
1792/********************************************************************/
1799int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1800
1801 if (_exptab.exptab.size() == 0) {
1803 if (status != CM_SUCCESS)
1804 return status;
1805 }
1806
1807 for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1808 if (_exptab.exptab[i].name == expname) {
1809 if (dir)
1810 *dir = _exptab.exptab[i].directory;
1811 if (user)
1812 *user = _exptab.exptab[i].user;
1813 return CM_SUCCESS;
1814 }
1815 }
1816 if (dir)
1817 *dir = "";
1818 if (user)
1819 *user = "";
1820 return CM_UNDEF_EXP;
1821}
1822
1823/********************************************************************/
1830int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1831 std::string sdir, suser;
1833 if (status == CM_SUCCESS) {
1834 if (dir)
1835 mstrlcpy(dir, sdir.c_str(), dir_size);
1836 if (user)
1837 mstrlcpy(user, suser.c_str(), user_size);
1838 return CM_SUCCESS;
1839 }
1840 return CM_UNDEF_EXP;
1841}
1842
1843#endif // LOCAL_ROUTINES
1844
1845/********************************************************************/
1853 /* only do it if local */
1854 if (!rpc_is_remote()) {
1856 }
1857 return CM_SUCCESS;
1858}
1859
1860/********************************************************************/
1870 if (rpc_is_remote())
1872
1873#ifdef LOCAL_ROUTINES
1875#endif /*LOCAL_ROUTINES */
1876 return CM_SUCCESS;
1877}
1878
1879/********************************************************************/
1894 char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1895 if (rpc_is_remote())
1897 host_name, client_name, hw_type, password, watchdog_timeout);
1898
1899#ifdef LOCAL_ROUTINES
1900 {
1901 INT status, pid, data, i, idx, size;
1906
1907 /* check security if password is present */
1908 status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1909 if (hKey) {
1910 /* get password */
1911 size = sizeof(pwd);
1912 db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1913
1914 /* first check allowed hosts list */
1915 allow = FALSE;
1916 db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1918 allow = TRUE;
1919
1920 /* check allowed programs list */
1921 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1922 if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
1923 allow = TRUE;
1924
1925 /* now check password */
1926 if (!allow && strcmp(password, pwd) != 0) {
1927 if (password[0])
1928 cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
1929 return CM_WRONG_PASSWORD;
1930 }
1931 }
1932
1933 /* make following operation atomic by locking database */
1935
1936 /* check if entry with this pid exists already */
1937 pid = ss_getpid();
1938
1939 sprintf(str, "System/Clients/%0d", pid);
1940 status = db_find_key(hDB, 0, str, &hKey);
1941 if (status == DB_SUCCESS) {
1944 }
1945
1946 if (strlen(client_name) >= NAME_LENGTH)
1947 client_name[NAME_LENGTH] = 0;
1948
1949 strcpy(name, client_name);
1950 strcpy(orig_name, client_name);
1951
1952 /* check if client name already exists */
1953 status = db_find_key(hDB, 0, "System/Clients", &hKey);
1954
1955 for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
1956 for (i = 0;; i++) {
1959 break;
1960
1961 if (status == DB_SUCCESS) {
1962 size = sizeof(str);
1963 status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
1964 if (status != DB_SUCCESS)
1965 continue;
1966 }
1967
1968 /* check if client is living */
1970 continue;
1971
1972 if (equal_ustring(str, name)) {
1973 sprintf(name, "%s%d", client_name, idx);
1974 break;
1975 }
1976 }
1977 }
1978
1979 /* set name */
1980 sprintf(str, "System/Clients/%0d/Name", pid);
1982 if (status != DB_SUCCESS) {
1984 cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
1985 return status;
1986 }
1987
1988 /* copy new client name */
1989 strcpy(client_name, name);
1990 db_set_client_name(hDB, client_name);
1991
1992 /* set also as rpc name */
1993 rpc_set_name(client_name);
1994
1995 /* use /system/clients/PID as root */
1996 sprintf(str, "System/Clients/%0d", pid);
1997 db_find_key(hDB, 0, str, &hKey);
1998
1999 /* set host name */
2001 if (status != DB_SUCCESS) {
2003 return status;
2004 }
2005
2006 /* set computer id */
2007 status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2008 if (status != DB_SUCCESS) {
2010 return status;
2011 }
2012
2013 /* set server port */
2014 data = 0;
2015 status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2016 if (status != DB_SUCCESS) {
2018 return status;
2019 }
2020
2021 /* lock client entry */
2023
2024 /* get (set) default watchdog timeout */
2025 size = sizeof(watchdog_timeout);
2026 sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
2027 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2028
2029 /* define /programs entry */
2030 sprintf(str, "/Programs/%s", orig_name);
2032
2033 /* save handle for ODB and client */
2035
2036 /* save watchdog timeout */
2038 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2039
2040 /* end of atomic operations */
2042
2043 /* touch notify key to inform others */
2044 data = 0;
2045 db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2046
2047 *hKeyClient = hKey;
2048 }
2049#endif /* LOCAL_ROUTINES */
2050
2051 return CM_SUCCESS;
2052}
2053
2054/********************************************************************/
2060{
2061 INT status;
2062 HNDLE hDB, hKey;
2063
2064 /* get root key of client */
2066 if (!hDB) {
2067 return "unknown";
2068 }
2069
2070 std::string name;
2071
2072 status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2073 if (status != DB_SUCCESS) {
2074 return "unknown";
2075 }
2076
2077 //printf("get client name: [%s]\n", name.c_str());
2078
2079 return name;
2080}
2081
2082/********************************************************************/
2135 if (host_name)
2136 host_name[0] = 0;
2137 if (exp_name)
2138 exp_name[0] = 0;
2139
2140 if (host_name && getenv("MIDAS_SERVER_HOST"))
2141 mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2142
2143 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2144 mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2145
2146 return CM_SUCCESS;
2147}
2148
2149INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2150 if (host_name)
2151 *host_name = "";
2152 if (exp_name)
2153 *exp_name = "";
2154
2155 if (host_name && getenv("MIDAS_SERVER_HOST"))
2156 *host_name = getenv("MIDAS_SERVER_HOST");
2157
2158 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2159 *exp_name = getenv("MIDAS_EXPT_NAME");
2160
2161 return CM_SUCCESS;
2162}
2163
2164#ifdef LOCAL_ROUTINES
2165
2167{
2168 std::string exp_name1;
2169
2170 if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2172 } else {
2174 if (status != CM_SUCCESS)
2175 return status;
2176 }
2177
2178 std::string expdir, expuser;
2179
2180 int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2181
2182 if (status != CM_SUCCESS) {
2183 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());
2184 return CM_UNDEF_EXP;
2185 }
2186
2187 if (!ss_dir_exist(expdir.c_str())) {
2188 cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2189 return CM_UNDEF_EXP;
2190 }
2191
2193 cm_set_path(expdir.c_str());
2194
2195 return CM_SUCCESS;
2196}
2197
2198#endif // LOCAL_ROUTINES
2199
2200/********************************************************************/
2202 if (_hKeyClient) {
2203 cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2205 }
2206}
2207
2208/********************************************************************/
2278INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2279 INT status;
2280
2283 if (status != CM_SUCCESS) {
2284 std::string s = cm_get_error(status);
2285 puts(s.c_str());
2286 }
2287
2288 return status;
2289}
2290
2291/********************************************************************/
2298 const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2299 INT status, size;
2301 char password[NAME_LENGTH], str[256];
2302 HNDLE hDB = 0, hKeyClient = 0;
2304
2305 ss_tzset(); // required for localtime_r()
2306
2307 if (_hKeyClient)
2309
2311
2312 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2313 //cm_msg_flush_buffer();
2314
2315 rpc_set_name(client_name);
2316
2317 /* check for local host */
2318 if (equal_ustring(host_name, "local"))
2319 host_name = NULL;
2320
2321#ifdef OS_WINNT
2322 {
2324
2325 /* Start windows sockets */
2326 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2327 return RPC_NET_ERROR;
2328 }
2329#endif
2330
2331 std::string default_exp_name1;
2332 if (default_exp_name)
2334
2335 /* connect to MIDAS server */
2336 if (host_name && host_name[0]) {
2337 if (default_exp_name1.length() == 0) {
2339 if (status != CM_SUCCESS)
2340 return status;
2341 }
2342
2344
2346 if (status != RPC_SUCCESS)
2347 return status;
2348
2349 /* register MIDAS library functions */
2351 if (status != RPC_SUCCESS)
2352 return status;
2353 } else {
2354 /* lookup path for *SHM files and save it */
2355
2356#ifdef LOCAL_ROUTINES
2358 if (status != CM_SUCCESS)
2359 return status;
2360
2362
2364
2366
2367 /* create alarm and elog semaphores */
2369 if (status != SS_CREATED && status != SS_SUCCESS) {
2370 cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2371 return status;
2372 }
2374 if (status != SS_CREATED && status != SS_SUCCESS) {
2375 cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2376 return status;
2377 }
2379 if (status != SS_CREATED && status != SS_SUCCESS) {
2380 cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2381 return status;
2382 }
2384 if (status != SS_CREATED && status != SS_SUCCESS) {
2385 cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2386 return status;
2387 }
2388
2390#else
2391 return CM_UNDEF_EXP;
2392#endif
2393 }
2394
2395 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2396 //cm_msg_flush_buffer();
2397
2398 /* open ODB */
2399 if (odb_size == 0)
2401
2402 status = db_open_database("ODB", odb_size, &hDB, client_name);
2403 if (status != DB_SUCCESS && status != DB_CREATED) {
2404 cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2405 return status;
2406 }
2407
2408 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2409 //cm_msg_flush_buffer();
2410
2412 size = sizeof(odb_timeout);
2413 status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2414 if (status != DB_SUCCESS) {
2415 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2416 }
2417
2418 if (odb_timeout > 0) {
2420 }
2421
2423 size = sizeof(protect_odb);
2424 status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2425 if (status != DB_SUCCESS) {
2426 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2427 }
2428
2429 if (protect_odb) {
2431 }
2432
2434 size = sizeof(enable_core_dumps);
2435 status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2436 if (status != DB_SUCCESS) {
2437 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2438 }
2439
2440 if (enable_core_dumps) {
2441#ifdef RLIMIT_CORE
2442 struct rlimit limit;
2443 limit.rlim_cur = RLIM_INFINITY;
2444 limit.rlim_max = RLIM_INFINITY;
2446 if (status != 0) {
2447 cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2448 strerror(errno));
2449 }
2450#else
2451#warning setrlimit(RLIMIT_CORE) is not available
2452#endif
2453 }
2454
2455 size = sizeof(disable_bind_rpc_to_localhost);
2456 status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2457 TID_BOOL, TRUE);
2458 if (status != DB_SUCCESS) {
2459 cm_msg(MERROR, "cm_connect_experiment1",
2460 "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2461 }
2462
2463 std::string local_host_name;
2464
2465 /* now setup client info */
2467 local_host_name = "localhost";
2468 else
2470
2471 /* check watchdog timeout */
2472 if (watchdog_timeout == 0)
2473 watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2474
2475 strcpy(client_name1, client_name);
2476 password[0] = 0;
2477 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2478
2479 if (status == CM_WRONG_PASSWORD) {
2480 if (func == NULL)
2481 strcpy(str, ss_getpass("Password: "));
2482 else
2483 func(str);
2484
2485 strcpy(password, ss_crypt(str, "mi"));
2486 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2487 if (status != CM_SUCCESS) {
2488 /* disconnect */
2489 if (rpc_is_remote())
2492
2493 return status;
2494 }
2495 }
2496
2497 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2498 //cm_msg_flush_buffer();
2499
2500 /* tell the rest of MIDAS that ODB is open for business */
2501
2503
2504 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2505 //cm_msg_flush_buffer();
2506
2507 /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2508 * to get event buffer size, etc */
2509
2511 if (status != CM_SUCCESS) {
2512 cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2513 return status;
2514 }
2515
2516 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2517 //cm_msg_flush_buffer();
2518
2519 /* set experiment name in ODB if not present */
2520 std::string current_name;
2521 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
2522 if (current_name.length() == 0 || current_name == "Default") {
2523 db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2524 }
2525
2526 if (!rpc_is_remote()) {
2527 /* experiment path is only set for local connections */
2528 /* set data dir in ODB */
2529 std::string path = cm_get_path();
2530 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2531 }
2532
2533 /* register server to be able to be called by other clients */
2535 if (status != CM_SUCCESS) {
2536 cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2537 if (!equal_ustring(client_name, "odbedit")) {
2538 return status;
2539 }
2540 }
2541
2542 /* set watchdog timeout */
2543 cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2544 size = sizeof(watchdog_timeout);
2545 sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2546 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2547 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2548
2549 /* set name of executable */
2550 std::string executable = ss_get_executable();
2551 std::string path = "/Programs/" + std::string(client_name);
2552 midas::odb prog(path);
2553 if (!midas::odb::exists(path + "/Start command") ||
2554 prog["Start command"] == std::string(""))
2555 prog["Start command"].set_string_size(executable, 256);
2556
2557 /* get final client name */
2558 std::string xclient_name = rpc_get_name();
2559
2560 /* startup message is not displayed */
2561 cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2562
2563 /* enable system and user messages to stdout as default */
2565
2566 /* call cm_check_connect when exiting */
2567 atexit((void (*)(void)) cm_check_connect);
2568
2569 /* register ctrl-c handler */
2571
2572 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2573 //cm_msg_flush_buffer();
2574
2575 return CM_SUCCESS;
2576}
2577
2578#ifdef LOCAL_ROUTINES
2579/********************************************************************/
2587 assert(exp_names != NULL);
2588 exp_names->clear();
2589
2590 if (_exptab.exptab.size() == 0) {
2592 if (status != CM_SUCCESS)
2593 return status;
2594 }
2595
2596 for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2597 exp_names->push_back(_exptab.exptab[i].name);
2598 }
2599
2600 return CM_SUCCESS;
2601}
2602#endif // LOCAL_ROUTINES
2603
2604/********************************************************************/
2613 INT status;
2614 INT sock;
2615 int port = MIDAS_TCP_PORT;
2616 char hname[256];
2617 char *s;
2618
2619 assert(exp_names != NULL);
2620 exp_names->clear();
2621
2622 /* extract port number from host_name */
2623 mstrlcpy(hname, host_name, sizeof(hname));
2624 s = strchr(hname, ':');
2625 if (s) {
2626 *s = 0;
2627 port = strtoul(s + 1, NULL, 0);
2628 }
2629
2630 std::string errmsg;
2631
2632 status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2633
2634 if (status != SS_SUCCESS) {
2635 cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2636 return RPC_NET_ERROR;
2637 }
2638
2639 /* request experiment list */
2640 send(sock, "I", 2, 0);
2641
2642 while (1) {
2643 char str[256];
2644
2645 status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2646
2647 if (status < 0)
2648 return RPC_NET_ERROR;
2649
2650 if (status == 0)
2651 break;
2652
2653 exp_names->push_back(str);
2654 }
2655
2656 ss_socket_close(&sock);
2657
2658 return CM_SUCCESS;
2659}
2660
2661#ifdef LOCAL_ROUTINES
2662/********************************************************************/
2671 INT status;
2673
2674 assert(exp_name != NULL);
2675
2676 /* retrieve list of experiments and make selection */
2678 if (status != CM_SUCCESS)
2679 return status;
2680
2681 if (expts.size() == 1) {
2682 *exp_name = expts[0];
2683 } else if (expts.size() > 1) {
2684 printf("Available experiments on local computer:\n");
2685
2686 for (unsigned i = 0; i < expts.size(); i++) {
2687 printf("%d : %s\n", i, expts[i].c_str());
2688 }
2689
2690 while (1) {
2691 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2692 char str[32];
2693 ss_gets(str, 32);
2694 int isel = atoi(str);
2695 if (isel < 0)
2696 continue;
2697 if (isel >= (int)expts.size())
2698 continue;
2699 *exp_name = expts[isel];
2700 break;
2701 }
2702 } else {
2703 return CM_UNDEF_EXP;
2704 }
2705
2706 return CM_SUCCESS;
2707}
2708#endif // LOCAL_ROUTINES
2709
2710/********************************************************************/
2720 INT status;
2722
2723 assert(exp_name != NULL);
2724
2725 /* retrieve list of experiments and make selection */
2727 if (status != CM_SUCCESS)
2728 return status;
2729
2730 if (expts.size() > 1) {
2731 printf("Available experiments on server %s:\n", host_name);
2732
2733 for (unsigned i = 0; i < expts.size(); i++) {
2734 printf("%d : %s\n", i, expts[i].c_str());
2735 }
2736
2737 while (1) {
2738 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2739 char str[32];
2740 ss_gets(str, 32);
2741 int isel = atoi(str);
2742 if (isel < 0)
2743 continue;
2744 if (isel >= (int)expts.size())
2745 continue;
2746 *exp_name = expts[isel];
2747 break;
2748 }
2749 } else {
2750 *exp_name = expts[0];
2751 }
2752
2753 return CM_SUCCESS;
2754}
2755
2756/********************************************************************/
2766INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2768 INT status, i, length, port;
2770
2771 /* find client entry in ODB */
2773
2774 status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2775 if (status != DB_SUCCESS)
2776 return status;
2777
2778 i = 0;
2779 do {
2780 /* search for client with specific name */
2783 return CM_NO_CLIENT;
2784
2785 status = db_find_key(hDB, hSubkey, "Name", &hKey);
2786 if (status != DB_SUCCESS)
2787 return status;
2788
2791 if (status != DB_SUCCESS)
2792 return status;
2793
2794 if (equal_ustring(name, client_name)) {
2795 status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2796 if (status != DB_SUCCESS)
2797 return status;
2798
2799 length = sizeof(INT);
2800 status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2801 if (status != DB_SUCCESS)
2802 return status;
2803
2804 status = db_find_key(hDB, hSubkey, "Host", &hKey);
2805 if (status != DB_SUCCESS)
2806 return status;
2807
2808 length = sizeof(host_name);
2810 if (status != DB_SUCCESS)
2811 return status;
2812
2813 /* client found -> connect to its server port */
2814 return rpc_client_connect(host_name, port, client_name, hConn);
2815 }
2816
2817
2818 } while (TRUE);
2819}
2820
2821static void rpc_client_shutdown();
2822
2823/********************************************************************/
2836
2837/********************************************************************/
2847 HNDLE hDB, hKey;
2848
2849 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2850 //cm_msg_flush_buffer();
2851
2852 /* wait on any transition thread */
2853 if (_trp.transition && !_trp.finished) {
2854 printf("Waiting for transition to finish...\n");
2855 do {
2856 ss_sleep(10);
2857 } while (!_trp.finished);
2858 }
2859
2860 /* stop the watchdog thread */
2862
2863 /* send shutdown notification */
2864 std::string client_name = rpc_get_name();
2865
2866 std::string local_host_name;
2867
2869 local_host_name = "localhost";
2870 else {
2872 //if (strchr(local_host_name, '.'))
2873 // *strchr(local_host_name, '.') = 0;
2874 }
2875
2876 /* disconnect message not displayed */
2877 cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2879
2880 if (rpc_is_remote()) {
2881 if (rpc_is_connected()) {
2882 /* close open records */
2884
2886 }
2887
2890
2892 } else {
2894
2895 /* delete client info */
2897
2898 if (hDB)
2900
2901 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2902 //cm_msg_flush_buffer();
2903
2907
2909
2910 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2911 //cm_msg_flush_buffer();
2912 }
2913
2914 if (!rpc_is_mserver())
2916
2917 /* free RPC list */
2919
2920 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2921 //cm_msg_flush_buffer();
2922
2923 /* last flush before we delete the message ring buffer */
2925
2926 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2927 //cm_msg_flush_buffer();
2928
2929 return CM_SUCCESS;
2930}
2931
2932/********************************************************************/
2940 //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
2941
2942 _hDB = hDB;
2944
2945 //if (hDB == 0) {
2946 // rpc_set_server_option(RPC_ODB_HANDLE, 0);
2947 //}
2948
2949 return CM_SUCCESS;
2950}
2951
2952
2953
2955#ifndef DOXYGEN_SHOULD_SKIP_THIS
2956
2957/********************************************************************/
2959/********************************************************************\
2960
2961 Routine: cm_set_experiment_semaphore
2962
2963 Purpose: Set the handle to the experiment wide semaphorees
2964
2965 Input:
2966 INT semaphore_alarm Alarm semaphore
2967 INT semaphore_elog Elog semaphore
2968 INT semaphore_history History semaphore
2969 INT semaphore_msg Message semaphore
2970
2971 Output:
2972 none
2973
2974 Function value:
2975 CM_SUCCESS Successful completion
2976
2977\********************************************************************/
2978{
2982 //_semaphore_msg = semaphore_msg;
2983
2984 return CM_SUCCESS;
2985}
2986
2988#endif /* DOXYGEN_SHOULD_SKIP_THIS */
2989
2990/********************************************************************/
3012 if (_hDB) {
3013 //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3014 if (hDB != NULL)
3015 *hDB = _hDB;
3016 if (hKeyClient != NULL)
3018 return CM_SUCCESS;
3019 } else {
3020 //printf("cm_get_experiment_database no init\n");
3021 if (hDB != NULL)
3022 *hDB = 0;
3023 if (hKeyClient != NULL)
3024 *hKeyClient = 0;
3025 return CM_DB_ERROR;
3026 }
3027}
3028
3030#ifndef DOXYGEN_SHOULD_SKIP_THIS
3031
3032/********************************************************************/
3034/********************************************************************\
3035
3036 Routine: cm_get_experiment_semaphore
3037
3038 Purpose: Get the handle to the experiment wide semaphores
3039
3040 Input:
3041 none
3042
3043 Output:
3044 INT semaphore_alarm Alarm semaphore
3045 INT semaphore_elog Elog semaphore
3046 INT semaphore_history History semaphore
3047 INT semaphore_msg Message semaphore
3048
3049 Function value:
3050 CM_SUCCESS Successful completion
3051
3052\********************************************************************/
3053{
3054 if (semaphore_alarm)
3056 if (semaphore_elog)
3060 //if (semaphore_msg)
3061 // *semaphore_msg = _semaphore_msg;
3062 if (semaphore_msg)
3063 *semaphore_msg = -1;
3064
3065 return CM_SUCCESS;
3066}
3067
3069#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3070
3071#ifdef LOCAL_ROUTINES
3072static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3075static int bm_lock_buffer_mutex(BUFFER *pbuf);
3076static int xbm_lock_buffer(BUFFER *pbuf);
3077static void xbm_unlock_buffer(BUFFER *pbuf);
3078
3080{
3081public:
3082 bool fDebug = false;
3083
3084public:
3086 {
3087 assert(pbuf != NULL);
3088 fBuf = pbuf;
3089 if (do_not_lock) {
3090 if (fDebug)
3091 printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3092 return;
3093 }
3094 if (fDebug)
3095 printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3097 if (status != BM_SUCCESS) {
3098 fLocked = false;
3099 fError = true;
3100 fStatus = status;
3101 } else {
3102 fLocked = true;
3103 }
3104 }
3105
3107 {
3108 if (fInvalid) {
3109 if (fDebug)
3110 printf("lock_buffer_guard(invalid) dtor\n");
3111 } else {
3112 assert(fBuf != NULL);
3113 if (fDebug)
3114 printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3115 if (fLocked) {
3117 fLocked = false;
3118 fError = false;
3119 }
3120 fBuf = NULL;
3121 }
3122 }
3123
3124 // make object uncopyable
3127
3128 void unlock()
3129 {
3130 assert(fBuf != NULL);
3131 if (fDebug)
3132 printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3133 assert(fLocked);
3135 fLocked = false;
3136 fError = false;
3137 }
3138
3139 bool relock()
3140 {
3141 assert(fBuf != NULL);
3142 if (fDebug)
3143 printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3144 assert(!fLocked);
3146 if (status != BM_SUCCESS) {
3147 fLocked = false;
3148 fError = true;
3149 fStatus = status;
3150 } else {
3151 fLocked = true;
3152 }
3153 return fLocked;
3154 }
3155
3157 {
3158 assert(fBuf != NULL);
3159 if (fDebug)
3160 printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3161 assert(!fLocked);
3162 fInvalid = true;
3163 fBuf = NULL;
3164 }
3165
3166 bool is_locked() const
3167 {
3168 return fLocked;
3169 }
3170
3171 bool is_error() const
3172 {
3173 return fError;
3174 }
3175
3176 int get_status() const
3177 {
3178 return fStatus;
3179 }
3180
3182 {
3183 assert(!fInvalid); // pbuf was deleted
3184 assert(fBuf); // we do not return NULL
3185 return fBuf;
3186 }
3187
3188private:
3190 bool fLocked = false;
3191 bool fError = false;
3192 bool fInvalid = false;
3193 int fStatus = 0;
3194};
3195
3197
3198#endif
3199
3200static INT bm_notify_client(const char *buffer_name, int s);
3201
3202static INT bm_push_event(const char *buffer_name);
3203
3204static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3205 EVENT_HEADER *pevent, void *pdata,
3206 EVENT_HANDLER *dispatcher);
3207
3208/********************************************************************/
3245{
3246#ifdef LOCAL_ROUTINES
3247 _watchdog_timeout = timeout;
3248
3249 std::vector<BUFFER*> mybuffers;
3250
3251 gBuffersMutex.lock();
3253 gBuffersMutex.unlock();
3254
3255 /* set watchdog timeout of all open buffers */
3256 for (BUFFER* pbuf : mybuffers) {
3257
3258 if (!pbuf || !pbuf->attached)
3259 continue;
3260
3262
3263 if (!pbuf_guard.is_locked())
3264 continue;
3265
3267
3268 /* clear entry from client structure in buffer header */
3269 pclient->watchdog_timeout = timeout;
3270
3271 /* show activity */
3272 pclient->last_activity = ss_millitime();
3273 }
3274
3275 /* set watchdog timeout for ODB */
3276 db_set_watchdog_params(timeout);
3277
3278#endif /* LOCAL_ROUTINES */
3279
3280 return CM_SUCCESS;
3281}
3282
3284{
3285 /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3286 _watchdog_timeout = timeout;
3287
3288 if (rpc_is_remote()) { // we are connected remotely
3289
3291
3292 } else if (rpc_is_mserver()) { // we are the mserver
3293
3295 if (sa)
3296 sa->watchdog_timeout = timeout;
3297
3298 /* write timeout value to client entry in ODB */
3299 HNDLE hDB, hKey;
3301
3302 if (hDB) {
3304 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3306 }
3307
3308 /* set the watchdog for the local mserver program */
3310
3311 } else { // only running locally
3312
3314
3315 }
3316}
3317
3318/********************************************************************/
3326 if (call_watchdog)
3328 if (timeout)
3329 *timeout = _watchdog_timeout;
3330
3331 return CM_SUCCESS;
3332}
3333
3334/********************************************************************/
3344INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3345 if (rpc_is_remote())
3346 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3347
3348#ifdef LOCAL_ROUTINES
3349 return db_get_watchdog_info(hDB, client_name, timeout, last);
3350#else /* LOCAL_ROUTINES */
3351 return CM_SUCCESS;
3352#endif /* LOCAL_ROUTINES */
3353}
3354
3355
3357#ifndef DOXYGEN_SHOULD_SKIP_THIS
3358
3359/********************************************************************/
3360
3361static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3362 int status;
3363 int i, last;
3364 KEY key;
3365 int max_size;
3366 char *str;
3367
3368// if (index != -99)
3369// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3370
3372
3373 if (status != DB_SUCCESS)
3374 return;
3375
3376 //printf("clear rpc hosts!\n");
3378
3380 str = (char *) malloc(max_size);
3381
3382 last = 0;
3383 for (i = 0; i < key.num_values; i++) {
3384 int size = max_size;
3386 if (status != DB_SUCCESS)
3387 break;
3388
3389 if (strlen(str) < 1) // skip emties
3390 continue;
3391
3392 if (str[0] == '#') // skip commented-out entries
3393 continue;
3394
3395 //printf("add rpc hosts %d [%s]\n", i, str);
3397 last = i;
3398 }
3399
3400 if (key.num_values - last < 10) {
3401 int new_size = last + 10;
3403 if (status != DB_SUCCESS) {
3404 cm_msg(MERROR, "load_rpc_hosts",
3405 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3406 }
3407 }
3408
3409 free(str);
3410}
3411
3413 int status;
3414 char buf[256];
3415 int size, i;
3416 HNDLE hKey;
3417
3418 strcpy(buf, "localhost");
3419 size = sizeof(buf);
3420
3421 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3422
3423 if (status != DB_SUCCESS) {
3424 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3425 status);
3426 return;
3427 }
3428
3429 size = sizeof(i);
3430 i = 0;
3431 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3432
3433 if (status != DB_SUCCESS) {
3434 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3435 return;
3436 }
3437
3438 if (i != 0) // RPC hosts check is disabled
3439 return;
3440
3441 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3442
3443 if (status != DB_SUCCESS || hKey == 0) {
3444 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3445 status);
3446 return;
3447 }
3448
3449 load_rpc_hosts(hDB, hKey, -99, NULL);
3450
3452
3453 if (status != DB_SUCCESS) {
3454 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3455 return;
3456 }
3457}
3458
3459/********************************************************************/
3461/********************************************************************\
3462
3463 Routine: cm_register_server
3464
3465 Purpose: Register a server which can be called from other clients
3466 of a specific experiment.
3467
3468 Input:
3469 none
3470
3471 Output:
3472 none
3473
3474 Function value:
3475 CM_SUCCESS Successful completion
3476
3477\********************************************************************/
3478{
3479 if (!_rpc_registered) {
3480 INT status;
3481 int size;
3482 HNDLE hDB, hKey;
3483 char name[NAME_LENGTH];
3484 char str[256];
3485 int port = 0;
3486
3488
3489 size = sizeof(name);
3490 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3491
3492 if (status != DB_SUCCESS) {
3493 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3494 return status;
3495 }
3496
3497 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3498 mstrlcat(str, name, sizeof(str));
3499
3500 size = sizeof(port);
3501 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3502
3503 if (status != DB_SUCCESS) {
3504 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3505 return status;
3506 }
3507
3508 int lport = 0; // actual port number assigned to us by the OS
3509
3511 if (status != RPC_SUCCESS) {
3512 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3513 return status;
3514 }
3515
3517
3518 /* register MIDAS library functions */
3520
3521 /* store port number in ODB */
3522
3523 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3524 if (status != DB_SUCCESS) {
3525 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3526 return status;
3527 }
3528
3529 /* unlock database */
3531
3532 /* set value */
3533 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3534 if (status != DB_SUCCESS) {
3535 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3536 return status;
3537 }
3538
3539 /* lock database */
3541
3543 }
3544
3545 return CM_SUCCESS;
3546}
3547
3549#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3550
3551/********************************************************************/
3601INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3602 INT status;
3604 KEY key;
3605 char str[256];
3606
3607 /* check for valid transition */
3609 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3610 return CM_INVALID_TRANSITION;
3611 }
3612
3614
3616
3617 /* register new transition request */
3618
3619 {
3620 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3621
3622 for (size_t i = 0; i < _trans_table.size(); i++) {
3623 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3624 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3625 return CM_INVALID_TRANSITION;
3626 }
3627 }
3628
3629 bool found = false;
3630 for (size_t i = 0; i < _trans_table.size(); i++) {
3631 if (!_trans_table[i].transition) {
3632 _trans_table[i].transition = transition;
3633 _trans_table[i].sequence_number = sequence_number;
3634 _trans_table[i].func = func;
3635 found = true;
3636 break;
3637 }
3638 }
3639
3640 if (!found) {
3643 tt.sequence_number = sequence_number;
3644 tt.func = func;
3645 _trans_table.push_back(tt);
3646 }
3647
3648 // implicit unlock
3649 }
3650
3651 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3652
3653 /* unlock database */
3655
3656 /* set value */
3658 if (!hKeyTrans) {
3659 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3660 if (status != DB_SUCCESS)
3661 return status;
3662 } else {
3664 if (status != DB_SUCCESS)
3665 return status;
3666 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3667 if (status != DB_SUCCESS)
3668 return status;
3669 }
3670
3671 /* re-lock database */
3673
3674 return CM_SUCCESS;
3675}
3676
3678 INT status;
3680 char str[256];
3681
3682 /* check for valid transition */
3684 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3685 return CM_INVALID_TRANSITION;
3686 }
3687
3689
3690 {
3691 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3692
3693 /* remove existing transition request */
3694 for (size_t i = 0; i < _trans_table.size(); i++) {
3696 _trans_table[i].transition = 0;
3697 _trans_table[i].sequence_number = 0;
3698 _trans_table[i].func = NULL;
3699 }
3700 }
3701
3702 // implicit unlock
3703 }
3704
3705 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3706
3707 /* unlock database */
3709
3710 /* set value */
3712 if (hKeyTrans) {
3714 if (status != DB_SUCCESS)
3715 return status;
3716 }
3717
3718 /* re-lock database */
3720
3721 return CM_SUCCESS;
3722}
3723
3724/********************************************************************/
3732 INT status;
3733 HNDLE hDB, hKey;
3734 char str[256];
3735
3736 /* check for valid transition */
3738 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3739 return CM_INVALID_TRANSITION;
3740 }
3741
3742 {
3743 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3744
3745 int count = 0;
3746 for (size_t i = 0; i < _trans_table.size(); i++) {
3748 _trans_table[i].sequence_number = sequence_number;
3749 count++;
3750 }
3751 }
3752
3753 if (count == 0) {
3754 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3755 return CM_INVALID_TRANSITION;
3756 } else if (count > 1) {
3757 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3758 return CM_INVALID_TRANSITION;
3759 }
3760
3761 /* Change local sequence number for this transition type */
3762
3763 for (size_t i = 0; i < _trans_table.size(); i++) {
3765 _trans_table[i].sequence_number = sequence_number;
3766 }
3767 }
3768
3769 // implicit unlock
3770 }
3771
3773
3774 /* unlock database */
3776
3777 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3778
3779 /* set value */
3780 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3781 if (status != DB_SUCCESS)
3782 return status;
3783
3784 /* re-lock database */
3786
3787 return CM_SUCCESS;
3788
3789}
3790
3792 INT status;
3793 HNDLE hDB, hKey;
3794 KEY key;
3795
3797
3798 /* check that hKey is still valid */
3800
3801 if (status != DB_SUCCESS) {
3802 cm_msg(MERROR, "cm_set_client_run_state",
3803 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3804 hKey);
3805 return status;
3806 }
3807
3808 /* unlock database */
3810
3811 /* set value */
3812 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3813 if (status != DB_SUCCESS)
3814 return status;
3815
3816 /* re-lock database */
3818
3819 return CM_SUCCESS;
3820
3821}
3822
3824#ifndef DOXYGEN_SHOULD_SKIP_THIS
3825
3828
3830#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3831
3832/********************************************************************/
3846 INT status, size;
3847 char tr_key_name[256];
3848 HNDLE hDB, hKey;
3849
3851
3852 for (int i = 0; _deferred_trans_table[i].transition; i++)
3854 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3855
3856 /* set new transition mask */
3858
3859 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3860
3861 /* unlock database */
3863
3864 /* set value */
3865 int i = 0;
3866 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3867 if (status != DB_SUCCESS)
3868 return status;
3869
3870 /* re-lock database */
3872
3873 /* hot link requested transition */
3874 size = sizeof(_requested_transition);
3875 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3876 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3878 if (status != DB_SUCCESS) {
3879 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3880 return status;
3881 }
3882
3883 return CM_SUCCESS;
3884}
3885
3886/********************************************************************/
3898 INT i, status;
3899 char str[256];
3900 static BOOL first;
3901
3902 if (_requested_transition == 0)
3903 first = TRUE;
3904
3906 for (i = 0; _deferred_trans_table[i].transition; i++)
3908 break;
3909
3913 if (status != CM_SUCCESS)
3914 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3915
3916 /* bypass hotlink and set _requested_transition directly to zero */
3918
3919 return status;
3920 }
3921 first = FALSE;
3922 }
3923 }
3924
3925 return SUCCESS;
3926}
3927
3928
3930#ifndef DOXYGEN_SHOULD_SKIP_THIS
3931
3932/********************************************************************/
3933
3935#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3936
3937struct TrClient {
3938 int transition = 0;
3939 int run_number = 0;
3940 int async_flag = 0;
3941 int debug_flag = 0;
3943 std::vector<int> wait_for_index;
3944 std::string host_name;
3945 std::string client_name;
3946 int port = 0;
3947 std::string key_name; /* this client key name in /System/Clients */
3948 std::atomic_int status{0};
3949 std::thread* thread = NULL;
3950 std::string errorstr;
3951 DWORD init_time = 0; // time when tr_client created
3952 std::string waiting_for_client; // name of client we are waiting for
3954 DWORD connect_start_time = 0; // time when client rpc connection is started
3955 DWORD connect_end_time = 0; // time when client rpc connection is finished
3957 DWORD rpc_start_time = 0; // time client rpc call is started
3958 DWORD rpc_end_time = 0; // time client rpc call is finished
3959 DWORD end_time = 0; // time client thread is finished
3960
3961 TrClient() // ctor
3962 {
3963 // empty
3964 }
3965
3966 ~TrClient() // dtor
3967 {
3968 //printf("TrClient::dtor: client \"%s\"\n", client_name);
3969 assert(thread == NULL);
3970 }
3971
3972 void Print() const
3973 {
3974 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3975 if (wait_for_index.size() > 0) {
3976 printf(", wait for:");
3977 for (size_t i=0; i<wait_for_index.size(); i++) {
3978 printf(" %d", wait_for_index[i]);
3979 }
3980 }
3981 }
3982};
3983
3984static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
3985 return arg1->sequence_number < arg2->sequence_number;
3986}
3987
3988/*------------------------------------------------------------------*/
3989
3990struct TrState {
3991 int transition = 0;
3992 int run_number = 0;
3993 int async_flag = 0;
3994 int debug_flag = 0;
3995 int status = 0;
3996 std::string errorstr;
3999 std::vector<std::unique_ptr<TrClient>> clients;
4000};
4001
4002/*------------------------------------------------------------------*/
4003
4004static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4005{
4006 DWORD end_time = ss_millitime();
4007
4008 if (transition != TR_STARTABORT) {
4009 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4010 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4011
4012 if (errorstr) {
4013 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4014 } else if (status == CM_SUCCESS) {
4015 const char *buf = "Success";
4016 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4017 } else {
4018 char buf[256];
4019 sprintf(buf, "status %d", status);
4020 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4021 }
4022 }
4023
4024 tr->status = status;
4025 tr->end_time = end_time;
4026 if (errorstr) {
4027 tr->errorstr = errorstr;
4028 } else {
4029 tr->errorstr = "(null)";
4030 }
4031
4032 return status;
4033}
4034
4035/*------------------------------------------------------------------*/
4036
4038 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4039
4040 int status;
4041 HNDLE hKey;
4042
4043 if (tr_client->transition == TR_STARTABORT) {
4044 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4045 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4046 if (status != DB_SUCCESS)
4047 return;
4048 } else {
4049 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4050 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4051 if (status != DB_SUCCESS)
4052 return;
4053 }
4054
4055 // same client_name can exist with different sequence numbers!
4056 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4057
4059 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4060 if (status != DB_SUCCESS)
4061 return;
4062
4064
4065 //int transition;
4066 //int run_number;
4067 //int async_flag;
4068 //int debug_flag;
4069 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4070 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4071 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4072 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4073 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4074 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);
4075 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4076 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4077 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4078 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4079 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4080 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4081 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4082 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4083 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4084 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4085}
4086
4087/*------------------------------------------------------------------*/
4088
4089/* Perform a detached transition through the external "mtransition" program */
4090static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4091 HNDLE hDB;
4092 int status;
4093 const char *args[100];
4094 std::string path;
4095 char debug_arg[256];
4096 char start_arg[256];
4097 std::string expt_name;
4098 std::string mserver_hostname;
4099
4100 int iarg = 0;
4101
4103
4104 const char *midassys = getenv("MIDASSYS");
4105 if (midassys) {
4106 path += midassys;
4107 path += DIR_SEPARATOR_STR;
4108 path += "bin";
4109 path += DIR_SEPARATOR_STR;
4110 }
4111 path += "mtransition";
4112
4113 args[iarg++] = path.c_str();
4114
4115 if (rpc_is_remote()) {
4116 /* if connected to mserver, pass connection info to mtransition */
4118 args[iarg++] = "-h";
4119 args[iarg++] = mserver_hostname.c_str();
4120 }
4121
4122 /* get experiment name from ODB */
4123 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4124
4125 if (expt_name.length() > 0) {
4126 args[iarg++] = "-e";
4127 args[iarg++] = expt_name.c_str();
4128 }
4129
4130 if (debug_flag) {
4131 args[iarg++] = "-d";
4132
4133 sprintf(debug_arg, "%d", debug_flag);
4134 args[iarg++] = debug_arg;
4135 }
4136
4137 if (transition == TR_STOP)
4138 args[iarg++] = "STOP";
4139 else if (transition == TR_PAUSE)
4140 args[iarg++] = "PAUSE";
4141 else if (transition == TR_RESUME)
4142 args[iarg++] = "RESUME";
4143 else if (transition == TR_START) {
4144 args[iarg++] = "START";
4145
4147 args[iarg++] = start_arg;
4148 }
4149
4150 args[iarg++] = NULL;
4151
4152#if 0
4153 for (iarg = 0; args[iarg] != NULL; iarg++) {
4154 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4155 }
4156#endif
4157
4159
4160 if (status != SS_SUCCESS) {
4161 if (errstr != NULL) {
4162 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4163 }
4164 return CM_SET_ERROR;
4165 }
4166
4167 return CM_SUCCESS;
4168}
4169
4170/*------------------------------------------------------------------*/
4171
4172/* contact a client via RPC and execute the remote transition */
4173static int cm_transition_call(TrState* s, int idx) {
4174 INT old_timeout, status, i, t1, t0, size;
4175 HNDLE hDB;
4176 HNDLE hConn = -1;
4177 int connect_timeout = 10000;
4178 int timeout = 120000;
4179
4181 assert(hDB);
4182
4183 TrClient *tr_client = s->clients[idx].get();
4184
4185 tr_client->errorstr = "";
4186 //tr_client->init_time = ss_millitime();
4187 tr_client->waiting_for_client = "";
4188 tr_client->connect_timeout = 0;
4189 tr_client->connect_start_time = 0;
4190 tr_client->connect_end_time = 0;
4191 tr_client->rpc_timeout = 0;
4192 tr_client->rpc_start_time = 0;
4193 tr_client->rpc_end_time = 0;
4194 tr_client->end_time = 0;
4195
4197
4198 /* wait for predecessor if set */
4199 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4200 while (1) {
4202
4203 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4204 int wait_for_index = tr_client->wait_for_index[i];
4205
4206 assert(wait_for_index >= 0);
4207 assert(wait_for_index < (int)s->clients.size());
4208
4209 TrClient *t = s->clients[wait_for_index].get();
4210
4211 if (!t)
4212 continue;
4213
4214 if (t->status == 0) {
4215 wait_for = t;
4216 break;
4217 }
4218
4219 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4220 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));
4221 tr_client->status = -1;
4222 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4223 tr_client->end_time = ss_millitime();
4225 return CM_SUCCESS;
4226 }
4227 }
4228
4229 if (wait_for == NULL)
4230 break;
4231
4232 tr_client->waiting_for_client = wait_for->client_name;
4234
4235 if (tr_client->debug_flag == 1)
4236 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4237
4238 i = 0;
4239 size = sizeof(i);
4240 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4241
4242 if (status == DB_SUCCESS && i == 0) {
4243 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());
4244 tr_client->status = -1;
4245 tr_client->errorstr = "Canceled";
4246 tr_client->end_time = ss_millitime();
4248 return CM_SUCCESS;
4249 }
4250
4251 ss_sleep(100);
4252 };
4253 }
4254
4255 tr_client->waiting_for_client[0] = 0;
4256
4257 /* contact client if transition mask set */
4258 if (tr_client->debug_flag == 1)
4259 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4260 if (tr_client->debug_flag == 2)
4261 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());
4262
4263 /* get transition timeout for rpc connect */
4264 size = sizeof(timeout);
4265 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4266
4267 if (connect_timeout < 1000)
4268 connect_timeout = 1000;
4269
4270 /* get transition timeout */
4271 size = sizeof(timeout);
4272 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4273
4274 if (timeout < 1000)
4275 timeout = 1000;
4276
4277 /* set our timeout for rpc_client_connect() */
4278 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4279 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4280
4281 tr_client->connect_timeout = connect_timeout;
4282 tr_client->connect_start_time = ss_millitime();
4283
4285
4286 /* client found -> connect to its server port */
4287 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4288
4290
4291 tr_client->connect_end_time = ss_millitime();
4293
4294 if (status != RPC_SUCCESS) {
4295 cm_msg(MERROR, "cm_transition_call",
4296 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4297 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4298 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4299
4300 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4301 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4302 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4303
4304 if (tr_client->transition != TR_STOP) {
4305 /* indicate abort */
4306 i = 1;
4307 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4308 i = 0;
4309 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4310 }
4311
4312 tr_client->status = status;
4313 tr_client->end_time = ss_millitime();
4314
4316 return status;
4317 }
4318
4319 if (tr_client->debug_flag == 1)
4320 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4321 if (tr_client->debug_flag == 2)
4322 cm_msg(MINFO, "cm_transition_call",
4323 "cm_transition: Connection established to client \"%s\" on host %s",
4324 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4325
4326 /* call RC_TRANSITION on remote client with increased timeout */
4327 //old_timeout = rpc_get_timeout(hConn);
4328 rpc_set_timeout(hConn, timeout, &old_timeout);
4329
4330 tr_client->rpc_timeout = timeout;
4331 tr_client->rpc_start_time = ss_millitime();
4333
4334 if (tr_client->debug_flag == 1)
4335 printf("Executing RPC transition client \"%s\" on host %s...\n",
4336 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4337 if (tr_client->debug_flag == 2)
4338 cm_msg(MINFO, "cm_transition_call",
4339 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4340 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4341
4342 t0 = ss_millitime();
4343
4344 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4345 errorstr[0] = 0;
4346
4347 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4348
4349 tr_client->errorstr = errorstr;
4350
4351 t1 = ss_millitime();
4352
4353 tr_client->rpc_end_time = ss_millitime();
4354
4356
4357 /* fix for clients returning 0 as error code */
4358 if (status == 0)
4359 status = FE_ERR_HW;
4360
4361 /* reset timeout */
4363
4364 //DWORD t2 = ss_millitime();
4365
4366 if (tr_client->debug_flag == 1)
4367 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4368 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4369 if (tr_client->debug_flag == 2)
4370 cm_msg(MINFO, "cm_transition_call",
4371 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4372 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4373
4374 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4375 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());
4376 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4377 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4378 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4379 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4380 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());
4381 }
4382
4383 tr_client->status = status;
4384 tr_client->end_time = ss_millitime();
4385
4386 // write updated status and end_time to ODB
4387
4389
4390#if 0
4391 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4392 hConn,
4393 tr_client->client_name.c_str(),
4394 tr_client->init_time - tr_client->init_time,
4395 tr_client->connect_start_time - tr_client->init_time,
4396 tr_client->connect_end_time - tr_client->init_time,
4397 tr_client->rpc_start_time - tr_client->init_time,
4398 tr_client->rpc_end_time - tr_client->init_time,
4399 t2 - tr_client->init_time,
4400 tr_client->end_time - tr_client->init_time);
4401#endif
4402
4403 return CM_SUCCESS;
4404}
4405
4406/*------------------------------------------------------------------*/
4407
4409{
4410 HNDLE hDB;
4411
4413
4415
4416 tr_client->errorstr = "";
4417 //tr_client->init_time = now;
4418 tr_client->waiting_for_client = "";
4419 tr_client->connect_timeout = 0;
4420 tr_client->connect_start_time = now;
4421 tr_client->connect_end_time = now;
4422 tr_client->rpc_timeout = 0;
4423 tr_client->rpc_start_time = 0;
4424 tr_client->rpc_end_time = 0;
4425 tr_client->end_time = 0;
4426
4428
4429 // find registered handler
4430 // NB: this code should match same code in rpc_transition_dispatch()
4431 // NB: only use the first handler, this is how MIDAS always worked
4432 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4433
4434 _trans_table_mutex.lock();
4435 size_t n = _trans_table.size();
4436 _trans_table_mutex.unlock();
4437
4438 for (size_t i = 0; i < n; i++) {
4439 _trans_table_mutex.lock();
4441 _trans_table_mutex.unlock();
4442 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4443 /* call registered function */
4444 if (tt.func) {
4445 if (tr_client->debug_flag == 1)
4446 printf("Calling local transition callback\n");
4447 if (tr_client->debug_flag == 2)
4448 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4449
4450 tr_client->rpc_start_time = ss_millitime();
4451
4453
4454 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4455 errorstr[0] = 0;
4456
4457 tr_client->status = tt.func(tr_client->run_number, errorstr);
4458
4459 tr_client->errorstr = errorstr;
4460
4461 tr_client->rpc_end_time = ss_millitime();
4462
4463 if (tr_client->debug_flag == 1)
4464 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4465 if (tr_client->debug_flag == 2)
4466 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4467
4468 tr_client->end_time = ss_millitime();
4469
4470 // write status and end_time to ODB
4471
4473
4474 return tr_client->status;
4475 }
4476 }
4477 }
4478
4479 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4480
4481 tr_client->status = CM_SUCCESS;
4482 tr_client->end_time = ss_millitime();
4483
4484 // write status and end_time to ODB
4485
4487
4488 return CM_SUCCESS;
4489}
4490
4491/********************************************************************/
4531static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4532{
4533 INT i, status, size, sequence_number, port, state;
4535 DWORD seconds;
4536 char tr_key_name[256];
4537 KEY key;
4538 BOOL deferred;
4540
4541 //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);
4542
4543 /* if needed, use internal error string */
4544 if (!errstr) {
4545 errstr = xerrstr;
4546 errstr_size = sizeof(xerrstr);
4547 }
4548
4549 /* erase error string */
4550 errstr[0] = 0;
4551
4552 /* get key of local client */
4554
4557
4558 /* check for valid transition */
4560 && transition != TR_STARTABORT) {
4561 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4562 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4563 return CM_INVALID_TRANSITION;
4564 }
4565
4566 /* check if transition in progress */
4567 if (!deferred) {
4568 i = 0;
4569 size = sizeof(i);
4570 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4571 if (i == 1) {
4572 if (errstr) {
4573 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4574 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4575 }
4576 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4578 }
4579 }
4580
4581 /* indicate transition in progress */
4582 i = transition;
4583 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4584
4585 /* clear run abort flag */
4586 i = 0;
4587 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4588
4589 /* construct new transition state */
4590
4591 TrState s;
4592
4595 s.async_flag = async_flag;
4596 s.debug_flag = debug_flag;
4597 s.status = 0;
4598 s.errorstr[0] = 0;
4600 s.end_time = 0;
4601
4602 /* construct the ODB tree /System/Transition */
4603
4604 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4605 if (status == DB_SUCCESS) {
4607 }
4608
4609 if (transition != TR_STARTABORT) {
4610 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4611 if (status == DB_SUCCESS) {
4613 }
4614 }
4615
4616 if (transition != TR_STARTABORT) {
4617 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4618 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4619 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4620 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4621 status = 0;
4622 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4623 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4624 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4625 }
4626
4627 /* check for alarms */
4628 i = 0;
4629 size = sizeof(i);
4630 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4631 if (i == TRUE && transition == TR_START) {
4632 al_check();
4633 std::string alarms;
4634 if (al_get_alarms(&alarms) > 0) {
4635 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4636 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4637 mstrlcat(errstr, alarms.c_str(), errstr_size);
4638 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4639 }
4640 }
4641
4642 /* check for required programs */
4643 i = 0;
4644 size = sizeof(i);
4645 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4646 if (i == TRUE && transition == TR_START) {
4647
4649
4650 /* check /programs alarms */
4651 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4652 if (hkeyroot) {
4653 for (i = 0;; i++) {
4657 break;
4658
4659 db_get_key(hDB, hkey, &key);
4660
4661 /* don't check "execute on xxx" */
4662 if (key.type != TID_KEY)
4663 continue;
4664
4665 size = sizeof(program_info_required);
4666 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4667 if (status != DB_SUCCESS) {
4668 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4669 continue;
4670 }
4671
4673 std::string name = rpc_get_name();
4674 std::string str = name;
4675 str.resize(strlen(key.name));
4676 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4677 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4678 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4679 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4680 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4681 }
4682 }
4683 }
4684 }
4685 }
4686
4687 /* do detached transition via mtransition tool */
4688 if (async_flag & TR_DETACH) {
4689 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4690 return tr_finish(hDB, &s, transition, status, errstr);
4691 }
4692
4693 mstrlcpy(errstr, "Unknown error", errstr_size);
4694
4695 if (debug_flag == 0) {
4696 size = sizeof(i);
4697 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4698 }
4699
4700 /* if no run number is given, get it from ODB and increment it */
4701 if (run_number == 0) {
4702 size = sizeof(run_number);
4703 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4704 assert(status == SUCCESS);
4705 if (transition == TR_START) {
4706 run_number++;
4707 }
4709
4710 if (transition != TR_STARTABORT) {
4711 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4712 }
4713 }
4714
4715 if (run_number <= 0) {
4716 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4717 abort();
4718 }
4719
4720 /* Set new run number in ODB */
4721 if (transition == TR_START) {
4722 if (debug_flag == 1)
4723 printf("Setting run number %d in ODB\n", run_number);
4724 if (debug_flag == 2)
4725 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4726
4727 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4728 if (status != DB_SUCCESS) {
4729 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4730 abort();
4731 }
4732 }
4733
4734 if (deferred) {
4735 if (debug_flag == 1)
4736 printf("Clearing /Runinfo/Requested transition\n");
4737 if (debug_flag == 2)
4738 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4739
4740 /* remove transition request */
4741 i = 0;
4742 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4743 } else {
4744 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4745 if (status != DB_SUCCESS) {
4746 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4747 if (errstr)
4748 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4749 return tr_finish(hDB, &s, transition, status, errstr);
4750 }
4751
4752 /* check if deferred transition already in progress */
4753 size = sizeof(i);
4754 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4755 if (i) {
4756 if (errstr) {
4757 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4758 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4759 }
4760 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4761 }
4762
4763 std::string trname = cm_transition_name(transition);
4764
4765 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4766
4767 /* search database for clients with deferred transition request */
4768 for (i = 0, status = 0;; i++) {
4771 break;
4772
4773 if (status == DB_SUCCESS) {
4774 size = sizeof(sequence_number);
4775 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4776
4777 /* if registered for deferred transition, set flag in ODB and return */
4778 if (status == DB_SUCCESS) {
4779 char str[256];
4780 size = NAME_LENGTH;
4781 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4782
4783 if (debug_flag == 1)
4784 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4785 if (debug_flag == 2)
4786 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4787
4788 if (debug_flag == 1)
4789 printf("Setting /Runinfo/Requested transition\n");
4790 if (debug_flag == 2)
4791 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4792
4793 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4794 * will activate the deferred transition code in the frontend.
4795 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4796
4797 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4798
4799 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4800
4801 if (errstr)
4802 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4803
4804 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4805 }
4806 }
4807 }
4808 }
4809
4810 /* execute programs on start */
4811 if (transition == TR_START) {
4812 char str[256];
4813 str[0] = 0;
4814 size = sizeof(str);
4815 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4816 if (str[0])
4817 ss_system(str);
4818
4819 db_find_key(hDB, 0, "/Programs", &hRootKey);
4820 if (hRootKey) {
4821 for (i = 0;; i++) {
4825 break;
4826
4827 db_get_key(hDB, hKey, &key);
4828
4829 /* don't check "execute on xxx" */
4830 if (key.type != TID_KEY)
4831 continue;
4832
4833 size = sizeof(program_info_auto_start);
4834 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4835 if (status != DB_SUCCESS) {
4836 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4837 continue;
4838 }
4839
4841 char start_command[MAX_STRING_LENGTH];
4842 start_command[0] = 0;
4843
4844 size = sizeof(start_command);
4845 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4846 if (status != DB_SUCCESS) {
4847 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4848 continue;
4849 }
4850
4851 if (start_command[0]) {
4852 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4853 start_command);
4854 ss_system(start_command);
4855 }
4856 }
4857 }
4858 }
4859 }
4860
4861 /* execute programs on startabort */
4862 if (transition == TR_STARTABORT) {
4863 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4864 std::string cmd;
4865 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4866
4867 if (!cmd.empty())
4868 ss_system(cmd.c_str());
4869 }
4870
4871 /* set new start time in database */
4872 if (transition == TR_START) {
4873 /* ASCII format */
4874 std::string now = cm_asctime();
4875 now.reserve(32);
4876 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4877
4878 /* reset stop time */
4879 seconds = 0;
4880 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4881
4882 /* Seconds since 1.1.1970 */
4883 cm_time(&seconds);
4884 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4885 }
4886
4887 size = sizeof(state);
4888 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4889
4890 /* set stop time in database */
4891 if (transition == TR_STOP) {
4892 if (status != DB_SUCCESS)
4893 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4894
4895 if (state != STATE_STOPPED) {
4896 /* stop time binary */
4897 cm_time(&seconds);
4898 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4899 if (status != DB_SUCCESS)
4900 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4901
4902 /* stop time ascii */
4903 std::string now = cm_asctime();
4904 now.reserve(32);
4905 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4906 if (status != DB_SUCCESS)
4907 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4908 }
4909 }
4910
4911 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4912 if (status != DB_SUCCESS) {
4913 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4914 if (errstr)
4915 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4916 return tr_finish(hDB, &s, transition, status, errstr);
4917 }
4918
4919 std::string trname = cm_transition_name(transition);
4920
4921 /* check that all transition clients are alive */
4922 for (int i = 0;;) {
4924 if (status != DB_SUCCESS)
4925 break;
4926
4928
4929 if (status == DB_SUCCESS) {
4930 /* this client is alive. Check next one! */
4931 i++;
4932 continue;
4933 }
4934
4935 assert(status == CM_NO_CLIENT);
4936
4937 /* start from scratch: removing odb entries as we iterate over them
4938 * does strange things to db_enum_key() */
4939 i = 0;
4940 }
4941
4942 /* check for broken RPC connections */
4944
4945 if (debug_flag == 1)
4946 printf("---- Transition %s started ----\n", trname.c_str());
4947 if (debug_flag == 2)
4948 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4949
4950 sprintf(tr_key_name, "Transition %s", trname.c_str());
4951
4952 /* search database for clients which registered for transition */
4953
4954 for (int i = 0, status = 0;; i++) {
4955 KEY subkey;
4958 break;
4959
4961 assert(status == DB_SUCCESS);
4962
4963 if (status == DB_SUCCESS) {
4965
4966 if (status == DB_SUCCESS) {
4967
4969
4970 for (int j = 0; j < key.num_values; j++) {
4971 size = sizeof(sequence_number);
4972 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4973 assert(status == DB_SUCCESS);
4974
4975 TrClient *c = new TrClient;
4976
4978 c->transition = transition;
4979 c->run_number = run_number;
4980 c->async_flag = async_flag;
4981 c->debug_flag = debug_flag;
4982 c->sequence_number = sequence_number;
4983 c->status = 0;
4984 c->key_name = subkey.name;
4985
4986 /* get client info */
4987 char client_name[NAME_LENGTH];
4988 size = sizeof(client_name);
4989 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
4990 c->client_name = client_name;
4991
4993 size = sizeof(host_name);
4994 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
4995 c->host_name = host_name;
4996
4997 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
4998
4999 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5000 /* remember own client */
5001 c->port = 0;
5002 } else {
5003 size = sizeof(port);
5004 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5005 c->port = port;
5006 }
5007
5008 /* check for duplicates */
5009
5010 bool found = false;
5011 for (size_t k=0; k<s.clients.size(); k++) {
5012 TrClient* cc = s.clients[k].get();
5013 if (cc->client_name == c->client_name)
5014 if (cc->host_name == c->host_name)
5015 if (cc->port == c->port)
5016 if (cc->sequence_number == c->sequence_number)
5017 found = true;
5018 }
5019
5020 if (!found) {
5021 s.clients.push_back(std::unique_ptr<TrClient>(c));
5022 c = NULL;
5023 } else {
5024 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);
5025 delete c;
5026 c = NULL;
5027 }
5028 }
5029 }
5030 }
5031 }
5032
5033 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5034
5035 /* set predecessor for multi-threaded transitions */
5036 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5037 if (s.clients[idx]->sequence_number == 0) {
5038 // sequence number 0 means "don't care"
5039 } else {
5040 /* find clients with smaller sequence number */
5041 if (idx > 0) {
5042 for (size_t i = idx - 1; ; i--) {
5043 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5044 if (s.clients[i]->sequence_number > 0) {
5045 s.clients[idx]->wait_for_index.push_back(i);
5046 }
5047 }
5048 if (i==0)
5049 break;
5050 }
5051 }
5052 }
5053 }
5054
5055 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5057 }
5058
5059#if 0
5060 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5061 printf("TrClient[%d]: ", int(idx));
5062 s.clients[idx]->Print();
5063 printf("\n");
5064 }
5065#endif
5066
5067 /* contact ordered clients for transition -----------------------*/
5069 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5070 if (debug_flag == 1)
5071 printf("\n==== Found client \"%s\" with sequence number %d\n",
5072 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5073 if (debug_flag == 2)
5074 cm_msg(MINFO, "cm_transition",
5075 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5076 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5077
5078 if (async_flag & TR_MTHREAD) {
5080 assert(s.clients[idx]->thread == NULL);
5081 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5082 } else {
5083 if (s.clients[idx]->port == 0) {
5084 /* if own client call transition callback directly */
5086 } else {
5087 /* if other client call transition via RPC layer */
5089 }
5090
5091 if (status == CM_SUCCESS && transition != TR_STOP)
5092 if (s.clients[idx]->status != SUCCESS) {
5093 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5094 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5095 break;
5096 }
5097 }
5098
5099 if (status != CM_SUCCESS)
5100 break;
5101 }
5102
5103 /* wait until all threads have finished */
5104 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5105 if (s.clients[idx]->thread) {
5106 // join() will wait forever until thread finishes
5107 s.clients[idx]->thread->join();
5108 delete s.clients[idx]->thread;
5109 s.clients[idx]->thread = NULL;
5110 }
5111 }
5112
5113 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5114
5115 i = 0;
5116 size = sizeof(i);
5117 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5118
5119 if (status == DB_SUCCESS && i == 0) {
5120 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5121
5122 if (errstr != NULL)
5123 mstrlcpy(errstr, "Canceled", errstr_size);
5124
5125 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5126 }
5127
5128 /* search for any error */
5129 for (size_t idx = 0; idx < s.clients.size(); idx++)
5130 if (s.clients[idx]->status != CM_SUCCESS) {
5131 status = s.clients[idx]->status;
5132 if (errstr)
5133 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5134 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5135 break;
5136 }
5137
5138 if (transition != TR_STOP && status != CM_SUCCESS) {
5139 /* indicate abort */
5140 i = 1;
5141 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5142 i = 0;
5143 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5144
5145 return tr_finish(hDB, &s, transition, status, errstr);
5146 }
5147
5148 if (debug_flag == 1)
5149 printf("\n---- Transition %s finished ----\n", trname.c_str());
5150 if (debug_flag == 2)
5151 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5152
5153 /* set new run state in database */
5156
5157 if (transition == TR_PAUSE)
5159
5160 if (transition == TR_STOP)
5162
5165
5166 size = sizeof(state);
5167 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5168 if (status != DB_SUCCESS)
5169 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5170
5171 /* send notification message */
5172 if (transition == TR_START)
5173 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5174 if (transition == TR_STOP)
5175 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5176 if (transition == TR_PAUSE)
5177 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5178 if (transition == TR_RESUME)
5179 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5181 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5182
5183 /* lock/unlock ODB values if present */
5184 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5185 if (hKey) {
5186 if (state == STATE_STOPPED)
5188 else
5190 }
5191
5192 /* flush online database */
5193 if (transition == TR_STOP)
5195
5196 /* execute/stop programs on stop */
5197 if (transition == TR_STOP) {
5198 std::string cmd;
5199 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5200 if (!cmd.empty())
5201 ss_system(cmd.c_str());
5202
5203 db_find_key(hDB, 0, "/Programs", &hRootKey);
5204 if (hRootKey) {
5205 for (i = 0;; i++) {
5209 break;
5210
5211 db_get_key(hDB, hKey, &key);
5212
5213 /* don't check "execute on xxx" */
5214 if (key.type != TID_KEY)
5215 continue;
5216
5217 size = sizeof(program_info_auto_stop);
5218 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5219 if (status != DB_SUCCESS) {
5220 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5221 continue;
5222 }
5223
5225 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5227 }
5228 }
5229 }
5230 }
5231
5232
5233 /* indicate success */
5234 i = 0;
5235 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5236
5237 if (errstr != NULL)
5238 mstrlcpy(errstr, "Success", errstr_size);
5239
5240 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5241}
5242
5243/*------------------------------------------------------------------*/
5244
5245/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5246static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5247 int status;
5248
5249 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5250
5251 if (transition == TR_START && status != CM_SUCCESS) {
5252 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5253 errstr);
5254 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5255 }
5256
5257 return status;
5258}
5259
5260/*------------------------------------------------------------------*/
5261
5262static INT tr_main_thread(void *param) {
5263 INT status;
5264 TR_PARAM *trp;
5265
5266 trp = (TR_PARAM *) param;
5267 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5268
5269 trp->status = status;
5270 trp->finished = TRUE;
5271
5272 return 0;
5273}
5274
5276{
5277 if (_trp.thread && !_trp.finished) {
5278 //printf("main transition thread did not finish yet!\n");
5280 }
5281
5282 std::thread* t = _trp.thread.exchange(NULL);
5283
5284 if (t) {
5285 t->join();
5286 delete t;
5287 t = NULL;
5288 }
5289
5290 return CM_SUCCESS;
5291}
5292
5293/* wrapper around cm_transition1() for detached multi-threaded transitions */
5294INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5295 int mflag = async_flag & TR_MTHREAD;
5296 int sflag = async_flag & TR_SYNC;
5297
5299
5300 if (status != CM_SUCCESS) {
5301 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5303 }
5304
5305 /* get key of local client */
5306 HNDLE hDB;
5308
5309 bool deferred = (transition & TR_DEFERRED) > 0;
5311
5312 /* check for valid transition */
5314 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5315 if (errstr) {
5316 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5317 }
5318 return CM_INVALID_TRANSITION;
5319 }
5320
5321 /* check if transition in progress */
5322 if (!deferred) {
5323 int i = 0;
5324 int size = sizeof(i);
5325 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5326 if (i == 1) {
5327 if (errstr) {
5328 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5329 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5330 }
5331 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5333 }
5334 }
5335
5336 if (mflag) {
5339 if (sflag) {
5340 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5341 _trp.errstr = errstr;
5342 _trp.errstr_size = errstr_size;
5343 } else {
5344 /* in normal MTHREAD mode, we return right away and
5345 * if errstr is a local variable in the caller and they return too,
5346 * errstr becomes a stale reference and writing into it will corrupt the stack
5347 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5348 * and we definitely corrupt mlogger memory with out this: */
5349 _trp.errstr = NULL;
5350 _trp.errstr_size = 0;
5351 }
5352 _trp.async_flag = async_flag;
5353 _trp.debug_flag = debug_flag;
5354 _trp.status = 0;
5356
5357 if (errstr)
5358 *errstr = 0; // null error string
5359
5360 //ss_thread_create(tr_main_thread, &_trp);
5361
5362 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5363
5364 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5365
5366 if (sflag) {
5367
5368 /* wait until main thread has finished */
5369 do {
5370 ss_sleep(10);
5371 } while (!_trp.finished);
5372
5373 std::thread* t = _trp.thread.exchange(NULL);
5374
5375 if (t) {
5376 t->join();
5377 delete t;
5378 t = NULL;
5379 }
5380
5381 return _trp.status;
5382 }
5383 } else
5384 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5385
5386 return CM_SUCCESS;
5387}
5388
5390#ifndef DOXYGEN_SHOULD_SKIP_THIS
5391
5392/********************************************************************/
5394/********************************************************************\
5395
5396 Routine: cm_dispatch_ipc
5397
5398 Purpose: Called from ss_suspend if an IPC message arrives
5399
5400 Input:
5401 INT msg IPC message we got, MSG_ODB/MSG_BM
5402 INT p1, p2 Optional parameters
5403 int s Optional server socket
5404
5405 Output:
5406 none
5407
5408 Function value:
5409 CM_SUCCESS Successful completion
5410
5411\********************************************************************/
5412{
5413 if (message[0] == 'O') {
5415 INT index;
5416 index = 0;
5417 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5418 if (client_socket) {
5420 } else {
5422 }
5423 }
5424
5425 /* message == "B" means "resume event sender" */
5426 if (message[0] == 'B' && message[2] != ' ') {
5427 char str[NAME_LENGTH];
5428
5429 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5430
5431 mstrlcpy(str, message + 2, sizeof(str));
5432 if (strchr(str, ' '))
5433 *strchr(str, ' ') = 0;
5434
5435 if (client_socket)
5437 else
5438 return bm_push_event(str);
5439 }
5440
5441 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5442
5443 return CM_SUCCESS;
5444}
5445
5446/********************************************************************/
5448
5449void cm_ctrlc_handler(int sig) {
5450 if (_ctrlc_pressed) {
5451 printf("Received 2nd Ctrl-C, hard abort\n");
5452 exit(0);
5453 }
5454 printf("Received Ctrl-C, aborting...\n");
5456
5458}
5459
5463
5467
5468/********************************************************************/
5470/********************************************************************\
5471
5472 Routine: cm_exec_script
5473
5474 Purpose: Execute script from /Script tree
5475
5476 exec_script is enabled by the tree /Script
5477 The /Script struct is composed of list of keys
5478 from which the name of the key is the button name
5479 and the sub-structure is a record as follow:
5480
5481 /Script/<button_name> = <script command> (TID_STRING)
5482
5483 The "Script command", containing possible arguements,
5484 is directly executed.
5485
5486 /Script/<button_name>/<script command>
5487 <soft link1>|<arg1>
5488 <soft link2>|<arg2>
5489 ...
5490
5491 The arguments for the script are derived from the
5492 subtree below <button_name>, where <button_name> must be
5493 TID_KEY. The subtree may then contain arguments or links
5494 to other values in the ODB, like run number etc.
5495
5496\********************************************************************/
5497{
5498 HNDLE hDB, hkey;
5499 KEY key;
5500 int status;
5501
5503 if (status != DB_SUCCESS)
5504 return status;
5505
5507 if (status != DB_SUCCESS)
5508 return status;
5509
5511 if (status != DB_SUCCESS)
5512 return status;
5513
5514 std::string command;
5515
5516 if (key.type == TID_STRING) {
5517 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5518 if (status != DB_SUCCESS) {
5519 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5521 return status;
5522 }
5523 } else if (key.type == TID_KEY) {
5524 for (int i = 0;; i++) {
5525 HNDLE hsubkey;
5526 KEY subkey;
5528 if (!hsubkey)
5529 break;
5531
5532 if (i > 0)
5533 command += " ";
5534
5535 if (subkey.type == TID_KEY) {
5536 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5537 subkey.name);
5538 return DB_TYPE_MISMATCH;
5539 } else {
5540 int size = subkey.item_size;
5541 char *buf = (char *) malloc(size);
5542 assert(buf != NULL);
5543 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5544 if (status != DB_SUCCESS) {
5545 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5546 odb_path_to_script, subkey.name, subkey.type, status);
5547 free(buf);
5548 return status;
5549 }
5550 if (subkey.type == TID_STRING) {
5551 command += buf;
5552 } else {
5553 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5554 }
5555 free(buf);
5556 }
5557 }
5558 } else {
5559 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5561 return DB_TYPE_MISMATCH;
5562 }
5563
5564 // printf("exec_script: %s\n", command.c_str());
5565
5566 if (command.length() > 0) {
5567 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5568 ss_system(command.c_str());
5569 }
5570
5571 return SUCCESS;
5572}
5573
5575#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5576
5577static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5578
5579/********************************************************************/
5588 static DWORD alarm_last_checked_sec = 0;
5589 DWORD now_sec = ss_time();
5590
5592 static DWORD last_millitime = 0;
5594 const DWORD kPeriod = 1000;
5595 if (last_millitime == 0) {
5597 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5598 }
5599
5600 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5601
5602 //if (now_millitime < last_millitime) {
5603 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5604 //}
5605
5606 /* check alarms once every 10 seconds */
5607 if (now_sec - alarm_last_checked_sec > 10) {
5608 al_check();
5610 }
5611
5612 /* run periodic checks previously done by cm_watchdog */
5613
5614 if (tdiff_millitime >= kPeriod) {
5616 if (tdiff_millitime > 60000)
5618
5619 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5620
5621 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5622 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5623
5625
5627 }
5628
5629 /* reap transition thread */
5630
5632
5633 return CM_SUCCESS;
5634}
5635
5636/********************************************************************/
5651 INT status;
5652 INT bMore;
5653 //static DWORD last_yield = 0;
5654 //static DWORD last_yield_time = 0;
5655 //DWORD start_yield = ss_millitime();
5656
5657 /* check for ctrl-c */
5658 if (_ctrlc_pressed)
5659 return RPC_SHUTDOWN;
5660
5661 /* flush the cm_msg buffer */
5663
5664 if (!rpc_is_remote()) {
5665 /* flush the ODB to its binary file */
5666 /* for remote clients, ODB is flushed by the mserver */
5667 HNDLE hDB;
5670 }
5671
5672 /* check for available events */
5673 if (rpc_is_remote()) {
5674 //printf("cm_yield() calling bm_poll_event()\n");
5676
5677 if (status == SS_ABORT) {
5678 return status;
5679 }
5680
5681 if (status == BM_SUCCESS) {
5682 /* one or more events received by bm_poll_event() */
5683 status = ss_suspend(0, 0);
5684 } else {
5686 }
5687
5688 return status;
5689 }
5690
5692
5693 if (status != CM_SUCCESS)
5694 return status;
5695
5696 //DWORD start_check = ss_millitime();
5697
5699
5700 //DWORD end_check = ss_millitime();
5701 //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);
5702 //fflush(stdout);
5703
5704 if (bMore == BM_CORRUPTED) {
5705 status = SS_ABORT;
5706 } else if (bMore) {
5707 /* if events available, quickly check other IPC channels */
5708 status = ss_suspend(0, 0);
5709 } else {
5711 }
5712
5713 /* flush the cm_msg buffer */
5715
5716 //DWORD end_yield = ss_millitime();
5717 //last_yield_time = end_yield - start_yield;
5718 //last_yield = start_yield;
5719
5720 return status;
5721}
5722
5723/********************************************************************/
5731INT cm_execute(const char *command, char *result, INT bufsize) {
5732 char str[256];
5733 INT n;
5734 int fh;
5735 int status = 0;
5736 static int check_cm_execute = 1;
5737 static int enable_cm_execute = 0;
5738
5739 if (rpc_is_remote())
5740 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5741
5742 if (check_cm_execute) {
5743 int status;
5744 int size;
5745 HNDLE hDB;
5746 check_cm_execute = 0;
5747
5749 assert(status == DB_SUCCESS);
5750
5751 size = sizeof(enable_cm_execute);
5752 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5753 assert(status == DB_SUCCESS);
5754
5755 //printf("enable_cm_execute %d\n", enable_cm_execute);
5756 }
5757
5758 if (!enable_cm_execute) {
5759 char buf[32];
5760 mstrlcpy(buf, command, sizeof(buf));
5761 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5762 return CM_WRONG_PASSWORD;
5763 }
5764
5765 if (bufsize > 0) {
5766 strcpy(str, command);
5767 sprintf(str, "%s > %d.tmp", command, ss_getpid());
5768
5769 status = system(str);
5770
5771 sprintf(str, "%d.tmp", ss_getpid());
5772 fh = open(str, O_RDONLY, 0644);
5773 result[0] = 0;
5774 if (fh) {
5775 n = read(fh, result, bufsize - 1);
5776 result[MAX(0, n)] = 0;
5777 close(fh);
5778 }
5779 remove(str);
5780 } else {
5781 status = system(command);
5782 }
5783
5784 if (status < 0) {
5785 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5786 return CM_SET_ERROR;
5787 }
5788
5789 return CM_SUCCESS;
5790}
5791
5792
5793
5795#ifndef DOXYGEN_SHOULD_SKIP_THIS
5796
5797/********************************************************************/
5798INT cm_register_function(INT id, INT(*func)(INT, void **))
5799/********************************************************************\
5800
5801 Routine: cm_register_function
5802
5803 Purpose: Call rpc_register_function and publish the registered
5804 function under system/clients/<pid>/RPC
5805
5806 Input:
5807 INT id RPC ID
5808 INT *func New dispatch function
5809
5810 Output:
5811 <implicit: func gets copied to rpc_list>
5812
5813 Function value:
5814 CM_SUCCESS Successful completion
5815 RPC_INVALID_ID RPC ID not found
5816
5817\********************************************************************/
5818{
5819 HNDLE hDB, hKey;
5820 INT status;
5821 char str[80];
5822
5823 status = rpc_register_function(id, func);
5824 if (status != RPC_SUCCESS)
5825 return status;
5826
5828
5829 /* create new key for this id */
5830 status = 1;
5831 sprintf(str, "RPC/%d", id);
5832
5834 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5836
5837 if (status != DB_SUCCESS)
5838 return status;
5839
5840 return CM_SUCCESS;
5841}
5842
5843
5845#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5846
5847//
5848// Return "/"-terminated file path for given history channel
5849//
5850
5851std::string cm_get_history_path(const char* history_channel)
5852{
5853 int status;
5854 HNDLE hDB;
5855 std::string path;
5856
5858
5859 if (history_channel && (strlen(history_channel) > 0)) {
5860 std::string p;
5861 p += "/Logger/History/";
5862 p += history_channel;
5863 p += "/History dir";
5864
5865 // NB: be careful to avoid creating odb entries under /logger
5866 // for whatever values of "history_channel" we get called with!
5867 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5868 if (status == DB_SUCCESS && path.length() > 0) {
5869 // if not absolute path, prepend with experiment directory
5870 if (path[0] != DIR_SEPARATOR)
5871 path = cm_get_path() + path;
5872 // append directory separator
5873 if (path.back() != DIR_SEPARATOR)
5874 path += DIR_SEPARATOR_STR;
5875 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5876 return path;
5877 }
5878 }
5879
5880 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5881 if (status == DB_SUCCESS && path.length() > 0) {
5882 // if not absolute path, prepend with experiment directory
5883 if (path[0] != DIR_SEPARATOR)
5884 path = cm_get_path() + path;
5885 // append directory separator
5886 if (path.back() != DIR_SEPARATOR)
5887 path += DIR_SEPARATOR_STR;
5888 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5889 return path;
5890 }
5891
5892 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5893 if (status == DB_SUCCESS && path.length() > 0) {
5894 // if not absolute path, prepend with experiment directory
5895 if (path[0] != DIR_SEPARATOR)
5896 path = cm_get_path() + path;
5897 // append directory separator
5898 if (path.back() != DIR_SEPARATOR)
5899 path += DIR_SEPARATOR_STR;
5900 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5901 return path;
5902 }
5903
5904 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5905 return cm_get_path();
5906}
5907
/* end of cmfunctionc */
5910
5916/********************************************************************\
5917* *
5918* bm_xxx - Buffer Manager Functions *
5919* *
5920\********************************************************************/
5921
5923
5924#ifdef LOCAL_ROUTINES
5925
5926// see locking code in xbm_lock_buffer()
5927static int _bm_lock_timeout = 5 * 60 * 1000;
5928static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5929
5931{
5932 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5933
5934 bool badindex = false;
5935 bool badclient = false;
5936
5937 int idx = pbuf->client_index;
5938
5939 if (idx < 0) {
5940 badindex = true;
5941 } else if (idx > pbuf->buffer_header->max_client_index) {
5942 badindex = true;
5943 } else {
5944 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5945 if (pclient->name[0] == 0)
5946 badclient = true;
5947 else if (pclient->pid != ss_getpid())
5948 badclient = true;
5949
5950 //if (strcmp(pclient->name,"mdump")==0) {
5951 // for (int i=0; i<15; i++) {
5952 // printf("sleep %d\n", i);
5953 // ::sleep(1);
5954 // }
5955 //}
5956 }
5957
5958#if 0
5959 if (badindex) {
5960 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5961 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5962 badindex, ss_getpid());
5963 } else if (badclient) {
5964 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",
5965 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5966 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5967 ss_getpid(), badclient);
5968 } else {
5969 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",
5970 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5971 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5972 ss_getpid());
5973 }
5974#endif
5975
5976 if (badindex || badclient) {
5977 static int prevent_recursion = 1;
5978
5979 if (prevent_recursion) {
5981
5982 if (badindex) {
5983 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());
5984 } else {
5985 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());
5986 }
5987
5988 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
5989 }
5990
5991 if (badindex) {
5992 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());
5993 } else {
5994 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());
5995 }
5996
5997 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
5998
5999 pbuf_guard.unlock();
6000
6001 abort();
6002 }
6003
6004 return idx;
6005}
6006
6011
6012#endif // LOCAL_ROUTINES
6013
6014/********************************************************************/
6023INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6024 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6025 // because of mismatch in sign-extension between signed 16-bit event_id and
6026 // unsigned 16-bit constants. K.O.
6027
6028 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)))
6029 /* fragmented event */
6030 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6032
6033 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6035}
6036
6037#ifdef LOCAL_ROUTINES
6038
6039/********************************************************************/
6044 int k, nc;
6046
6047 /* clear entry from client structure in buffer header */
6048 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6049
6050 /* calculate new max_client_index entry */
6051 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6052 if (pheader->client[k].pid != 0)
6053 break;
6054 pheader->max_client_index = k + 1;
6055
6056 /* count new number of clients */
6057 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6058 if (pheader->client[k].pid != 0)
6059 nc++;
6060 pheader->num_clients = nc;
6061
6062 /* check if anyone is waiting and wake him up */
6063 pbctmp = pheader->client;
6064
6065 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6066 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6067 ss_resume(pbctmp->port, "B ");
6068}
6069
6070/********************************************************************/
6075 BUFFER_HEADER *pheader;
6077 int j;
6078
6079 pheader = pbuf->buffer_header;
6080 pbclient = pheader->client;
6081
6082 /* now check other clients */
6083 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6084 if (pbclient->pid) {
6085 if (!ss_pid_exists(pbclient->pid)) {
6086 cm_msg(MINFO, "bm_cleanup",
6087 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6088 pheader->name, who, pbclient->pid);
6089
6090 bm_remove_client_locked(pheader, j);
6091 continue;
6092 }
6093 }
6094
6095 /* If client process has no activity, clear its buffer entry. */
6096 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6097 DWORD tdiff = actual_time - pbclient->last_activity;
6098#if 0
6099 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6100 pheader->name,
6101 pbclient->name,
6102 pbclient->last_activity,
6104 tdiff,
6105 tdiff,
6106 pbclient->watchdog_timeout);
6107#endif
6108 if (actual_time > pbclient->last_activity &&
6109 tdiff > pbclient->watchdog_timeout) {
6110
6111 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6112 pbclient->name, pheader->name, who,
6113 tdiff / 1000.0,
6114 pbclient->watchdog_timeout / 1000.0);
6115
6116 bm_remove_client_locked(pheader, j);
6117 }
6118 }
6119 }
6120}
6121
6126 int pid = ss_getpid();
6127
6128 std::vector<BUFFER*> mybuffers;
6129
6130 gBuffersMutex.lock();
6132 gBuffersMutex.unlock();
6133
6134 for (BUFFER* pbuf : mybuffers) {
6135 if (!pbuf)
6136 continue;
6137 if (pbuf->attached) {
6138
6140
6141 if (!pbuf_guard.is_locked())
6142 continue;
6143
6144 BUFFER_HEADER *pheader = pbuf->buffer_header;
6145 for (int j = 0; j < pheader->max_client_index; j++) {
6146 BUFFER_CLIENT *pclient = pheader->client + j;
6147 if (pclient->pid == pid) {
6149 }
6150 }
6151 }
6152 }
6153}
6154
6155#endif // LOCAL_ROUTINES
6156
6161{
6162#ifdef LOCAL_ROUTINES
6163
6164 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6165
6166 std::vector<BUFFER*> mybuffers;
6167
6168 gBuffersMutex.lock();
6170 gBuffersMutex.unlock();
6171
6172 /* check buffers */
6173 for (BUFFER* pbuf : mybuffers) {
6174 if (!pbuf)
6175 continue;
6176 if (pbuf->attached) {
6177 /* update the last_activity entry to show that we are alive */
6178
6180
6181 if (!pbuf_guard.is_locked())
6182 continue;
6183
6185 pclient->last_activity = actual_time;
6186
6187 /* don't check other clients if interval is strange */
6188 if (!wrong_interval)
6190 }
6191 }
6192#endif // LOCAL_ROUTINES
6193}
6194
6195#ifdef LOCAL_ROUTINES
6196
6197static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6198 if (rp < 0 || rp > pheader->size) {
6199 cm_msg(MERROR, "bm_validate_rp",
6200 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6201 pheader->name,
6202 rp,
6203 pheader->read_pointer,
6204 pheader->write_pointer,
6205 pheader->size,
6206 who);
6207 return FALSE;
6208 }
6209
6210 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6211 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6212 cm_msg(MERROR, "bm_validate_rp",
6213 "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",
6214 pheader->name,
6215 rp,
6216 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6217 pheader->read_pointer,
6218 pheader->write_pointer,
6219 pheader->size,
6220 who);
6221 return FALSE;
6222 }
6223
6224 return TRUE;
6225}
6226
6227#if 0
6228static FILE* gRpLog = NULL;
6229#endif
6230
6231static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6232{
6233#if 0
6234 if (gRpLog == NULL) {
6235 gRpLog = fopen("rp.log", "a");
6236 }
6237 if (gRpLog && (total_size < 16)) {
6238 const char *pdata = (const char *) (pheader + 1);
6239 const DWORD *pevent = (const DWORD*) (pdata + rp);
6240 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,
6241 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6242 }
6243#endif
6244
6245 // these checks are already done before we come here.
6246 // but we check again as last-ressort protection. K.O.
6247 assert(total_size > 0);
6248 assert(total_size >= (int)sizeof(EVENT_HEADER));
6249
6250 rp += total_size;
6251 if (rp >= pheader->size) {
6252 rp -= pheader->size;
6253 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6254 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6255 // if at the end of the buffer, the remaining free space is exactly
6256 // equal to the size of an event header, the event header
6257 // is written there, the pointer is wrapped and the event data
6258 // is written to the beginning of the buffer.
6259 rp = 0;
6260 }
6261 return rp;
6262}
6263
6264static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6265 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6266 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6267 int total_size = ALIGN8(event_size);
6268
6269 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6270 cm_msg(MERROR, "bm_next_rp",
6271 "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",
6272 pheader->name,
6273 rp,
6274 pevent->data_size,
6275 event_size,
6276 total_size,
6277 pheader->read_pointer,
6278 pheader->write_pointer,
6279 pheader->size,
6280 who);
6281 return -1;
6282 }
6283
6284 int remaining = 0;
6285 if (rp < pheader->write_pointer) {
6286 remaining = pheader->write_pointer - rp;
6287 } else {
6288 remaining = pheader->size - rp;
6289 remaining += pheader->write_pointer;
6290 }
6291
6292 //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);
6293
6294 if (total_size > remaining) {
6295 cm_msg(MERROR, "bm_next_rp",
6296 "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",
6297 pheader->name,
6298 rp,
6299 pevent->data_size,
6300 event_size,
6301 total_size,
6302 pheader->read_pointer,
6303 pheader->write_pointer,
6304 pheader->size,
6305 remaining,
6306 who);
6307 return -1;
6308 }
6309
6310 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6311
6312 return rp;
6313}
6314
6316 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6317 const char *pdata = (const char *) (pheader + 1);
6318
6319 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6320
6321 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6322
6323 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6324
6325 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6326 cm_msg(MERROR, "bm_validate_buffer",
6327 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6328 pheader->read_pointer, pheader->size, pheader->write_pointer);
6329 return BM_CORRUPTED;
6330 }
6331
6332 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6333 cm_msg(MERROR, "bm_validate_buffer",
6334 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6335 pheader->write_pointer, pheader->size, pheader->read_pointer);
6336 return BM_CORRUPTED;
6337 }
6338
6339 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6340 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6341 pheader->read_pointer);
6342 return BM_CORRUPTED;
6343 }
6344
6345 int rp = pheader->read_pointer;
6346 int rp0 = -1;
6347 while (rp != pheader->write_pointer) {
6348 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6349 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6350 pheader->name, rp, rp0);
6351 return BM_CORRUPTED;
6352 }
6353 //bm_print_event(pdata, rp);
6354 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6355 if (rp1 < 0) {
6356 cm_msg(MERROR, "bm_validate_buffer",
6357 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6358 return BM_CORRUPTED;
6359 }
6360 rp0 = rp;
6361 rp = rp1;
6362 }
6363
6364 int i;
6365 for (i = 0; i < MAX_CLIENTS; i++) {
6366 const BUFFER_CLIENT *c = &pheader->client[i];
6367 if (c->pid == 0)
6368 continue;
6369 BOOL get_all = FALSE;
6370 int j;
6371 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6372 const EVENT_REQUEST *r = &c->event_request[j];
6373 if (!r->valid)
6374 continue;
6376 get_all = (get_all || xget_all);
6377 //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);
6378 }
6379
6380 int rp = c->read_pointer;
6381 int rp0 = -1;
6382 while (rp != pheader->write_pointer) {
6383 //bm_print_event(pdata, rp);
6384 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6385 if (rp1 < 0) {
6386 cm_msg(MERROR, "bm_validate_buffer",
6387 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6388 pheader->name, c->name, c->read_pointer, rp, rp0);
6389 return BM_CORRUPTED;
6390 }
6391 rp0 = rp;
6392 rp = rp1;
6393 }
6394 }
6395
6396 return BM_SUCCESS;
6397}
6398
6400 BUFFER_HEADER *pheader = pbuf->buffer_header;
6401
6402 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6403
6404 pheader->read_pointer = 0;
6405 pheader->write_pointer = 0;
6406
6407 int i;
6408 for (i = 0; i < pheader->max_client_index; i++) {
6409 BUFFER_CLIENT *pc = pheader->client + i;
6410 if (pc->pid) {
6411 pc->read_pointer = 0;
6412 }
6413 }
6414}
6415
6417 HNDLE hKey;
6418 int status;
6419
6420 char str[256 + 2 * NAME_LENGTH];
6421 sprintf(str, "/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6422 //printf("delete [%s]\n", str);
6423 status = db_find_key(hDB, 0, str, &hKey);
6424 if (status == DB_SUCCESS) {
6426 }
6427}
6428
6430{
6433 /* buffer statistics */
6434 int count_lock = 0;
6435 int count_sent = 0;
6436 double bytes_sent = 0;
6443 int count_read = 0;
6444 double bytes_read = 0;
6449 {
6450 get_all_flag = pbuf->get_all_flag;
6451
6452 /* buffer statistics */
6453 count_lock = pbuf->count_lock;
6454 count_sent = pbuf->count_sent;
6455 bytes_sent = pbuf->bytes_sent;
6456 count_write_wait = pbuf->count_write_wait;
6457 time_write_wait = pbuf->time_write_wait;
6458 last_count_lock = pbuf->last_count_lock;
6459 wait_start_time = pbuf->wait_start_time;
6460 wait_client_index = pbuf->wait_client_index;
6461 max_requested_space = pbuf->max_requested_space;
6462 count_read = pbuf->count_read;
6463 bytes_read = pbuf->bytes_read;
6464
6465 for (int i=0; i<MAX_CLIENTS; i++) {
6466 client_count_write_wait[i] = pbuf->client_count_write_wait[i];
6467 client_time_write_wait[i] = pbuf->client_time_write_wait[i];
6468 }
6469 };
6470};
6471
6472static 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)
6473{
6474 int status;
6475
6477
6478 HNDLE hKey;
6479 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6480 if (status != DB_SUCCESS) {
6481 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6482 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6483 if (status != DB_SUCCESS)
6484 return;
6485 }
6486
6489 if (status != DB_SUCCESS) {
6492 if (status != DB_SUCCESS)
6493 return;
6494 }
6495
6496 double buf_size = pheader->size;
6497 double buf_rptr = pheader->read_pointer;
6498 double buf_wptr = pheader->write_pointer;
6499
6500 double buf_fill = 0;
6501 double buf_cptr = 0;
6502 double buf_cused = 0;
6503 double buf_cused_pct = 0;
6504
6505 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6506 buf_cptr = pheader->client[client_index].read_pointer;
6507
6508 if (buf_wptr == buf_cptr) {
6509 buf_cused = 0;
6510 } else if (buf_wptr > buf_cptr) {
6512 } else {
6513 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6514 }
6515
6516 buf_cused_pct = buf_cused / buf_size * 100.0;
6517
6518 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6519 // because some other GET_ALL client may have different buf_cused & etc,
6520 // so they must be written into the per-client statistics
6521 // and the web page should look at all the GET_ALL clients and used
6522 // the biggest buf_cused as the whole-buffer "bytes used" value.
6523 }
6524
6525 if (buf_wptr == buf_rptr) {
6526 buf_fill = 0;
6527 } else if (buf_wptr > buf_rptr) {
6529 } else {
6530 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6531 }
6532
6533 double buf_fill_pct = buf_fill / buf_size * 100.0;
6534
6535 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6536 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6537 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6538 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6539 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6540
6541 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6542 if (status != DB_SUCCESS) {
6543 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6544 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6545 if (status != DB_SUCCESS)
6546 return;
6547 }
6548
6550 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6551 if (status != DB_SUCCESS) {
6552 db_create_key(hDB, hKey, client_name, TID_KEY);
6553 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6554 if (status != DB_SUCCESS)
6555 return;
6556 }
6557
6558 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6559 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6560 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6561 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6562 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6563 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6564 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6565 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6566 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6567 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6568 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6569 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6570
6571 for (int i = 0; i < MAX_CLIENTS; i++) {
6572 if (!pbuf->client_count_write_wait[i])
6573 continue;
6574
6575 if (pheader->client[i].pid == 0)
6576 continue;
6577
6578 if (pheader->client[i].name[0] == 0)
6579 continue;
6580
6581 char str[100 + NAME_LENGTH];
6582
6583 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6584 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6585
6586 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6587 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6588 }
6589
6590 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6591 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6592}
6593
6595{
6596 //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);
6597
6599
6600 if (!pbuf_guard.is_locked())
6601 return;
6602
6603 if (!force) {
6604 if (pbuf->count_lock == pbuf->last_count_lock) {
6605 return;
6606 }
6607 }
6608
6609 std::string buffer_name = pbuf->buffer_name;
6610 std::string client_name = pbuf->client_name;
6611
6612 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6613 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6614 pbuf_guard.unlock(); // unlock before cm_msg()
6615 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());
6616 return;
6617 }
6618
6619 pbuf->last_count_lock = pbuf->count_lock;
6620
6622 BUFFER_HEADER xheader = *pbuf->buffer_header;
6623 int client_index = pbuf->client_index;
6624
6625 pbuf_guard.unlock();
6626
6627 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6628}
6629
6630static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6631{
6632 size_t sbuffer_handle = buffer_handle;
6633
6634 size_t nbuf = 0;
6635 BUFFER* pbuf = NULL;
6636
6637 gBuffersMutex.lock();
6638
6639 nbuf = gBuffers.size();
6640 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6641 pbuf = gBuffers[buffer_handle-1];
6642 }
6643
6644 gBuffersMutex.unlock();
6645
6646 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6647 if (who)
6648 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6649 if (pstatus)
6651 return NULL;
6652 }
6653
6654 if (!pbuf) {
6655 if (who)
6656 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6657 if (pstatus)
6659 return NULL;
6660 }
6661
6662 if (!pbuf->attached) {
6663 if (who)
6664 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6665 if (pstatus)
6667 return NULL;
6668 }
6669
6670 if (pstatus)
6672
6673 return pbuf;
6674}
6675
6676#endif // LOCAL_ROUTINES
6677
6678/********************************************************************/
6725INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6726 INT status;
6727
6728 if (rpc_is_remote()) {
6729 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6730
6731 HNDLE hDB;
6733 if (status != SUCCESS || hDB == 0) {
6734 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6735 return BM_NO_SHM;
6736 }
6737
6739
6740 int size = sizeof(INT);
6741 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6742
6743 if (status != DB_SUCCESS) {
6744 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6745 status);
6746 return status;
6747 }
6748
6749 return status;
6750 }
6751#ifdef LOCAL_ROUTINES
6752 {
6753 HNDLE shm_handle;
6754 size_t shm_size;
6755 HNDLE hDB;
6756 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6757
6758 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6759
6760 if (!buffer_name || !buffer_name[0]) {
6761 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6762 return BM_INVALID_PARAM;
6763 }
6764
6765 if (strlen(buffer_name) >= NAME_LENGTH) {
6766 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6767 return BM_INVALID_PARAM;
6768 }
6769
6771
6772 if (status != SUCCESS || hDB == 0) {
6773 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6774 return BM_NO_SHM;
6775 }
6776
6777 /* get buffer size from ODB, user parameter as default if not present in ODB */
6778 std::string odb_path;
6779 odb_path += "/Experiment/Buffer sizes/";
6780 odb_path += buffer_name;
6781
6782 int size = sizeof(INT);
6783 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6784
6786 cm_msg(MERROR, "bm_open_buffer",
6787 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6788 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6789 return BM_INVALID_PARAM;
6790 }
6791
6793
6794 size = sizeof(INT);
6795 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6796
6797 if (status != DB_SUCCESS) {
6798 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6799 status);
6800 return status;
6801 }
6802
6803 /* check if buffer already is open */
6804 gBuffersMutex.lock();
6805 for (size_t i = 0; i < gBuffers.size(); i++) {
6806 BUFFER* pbuf = gBuffers[i];
6807 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6808 *buffer_handle = i + 1;
6809 gBuffersMutex.unlock();
6810 return BM_SUCCESS;
6811 }
6812 }
6813 gBuffersMutex.unlock();
6814
6815 // only one thread at a time should create new buffers
6816
6817 static std::mutex gNewBufferMutex;
6818 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6819
6820 // if we had a race against another thread
6821 // and while we were waiting for gNewBufferMutex
6822 // the other thread created this buffer, we return it.
6823
6824 gBuffersMutex.lock();
6825 for (size_t i = 0; i < gBuffers.size(); i++) {
6826 BUFFER* pbuf = gBuffers[i];
6827 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6828 *buffer_handle = i + 1;
6829 gBuffersMutex.unlock();
6830 return BM_SUCCESS;
6831 }
6832 }
6833 gBuffersMutex.unlock();
6834
6835 /* allocate new BUFFER object */
6836
6837 BUFFER* pbuf = new BUFFER;
6838
6839 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6840
6841 for (int i=0; i<MAX_CLIENTS; i++) {
6843 pbuf->client_time_write_wait[i] = 0;
6844 }
6845
6846 /* create buffer semaphore */
6847
6848 status = ss_semaphore_create(buffer_name, &(pbuf->semaphore));
6849
6850 if (status != SS_CREATED && status != SS_SUCCESS) {
6851 *buffer_handle = 0;
6852 delete pbuf;
6853 return BM_NO_SEMAPHORE;
6854 }
6855
6856 std::string client_name = cm_get_client_name();
6857
6858 /* store client name */
6859 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6860
6861 /* store buffer name */
6862 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6863
6864 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6865
6866 pbuf->attached = true; // required by bm_lock_buffer()
6867
6869
6870 if (!pbuf_guard.is_locked()) {
6871 // cannot happen, no other thread can see this pbuf
6872 abort();
6873 return BM_NO_SEMAPHORE;
6874 }
6875
6876 /* open shared memory */
6877
6878 void *p = NULL;
6879 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6880
6881 if (status != SS_SUCCESS && status != SS_CREATED) {
6882 *buffer_handle = 0;
6883 pbuf_guard.unlock();
6884 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6885 delete pbuf;
6886 return BM_NO_SHM;
6887 }
6888
6889 pbuf->buffer_header = (BUFFER_HEADER *) p;
6890
6891 BUFFER_HEADER *pheader = pbuf->buffer_header;
6892
6893 bool shm_created = (status == SS_CREATED);
6894
6895 if (shm_created) {
6896 /* initialize newly created shared memory */
6897
6898 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6899
6900 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6901 pheader->size = buffer_size;
6902
6903 } else {
6904 /* validate existing shared memory */
6905
6906 if (!equal_ustring(pheader->name, buffer_name)) {
6907 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6908 pbuf_guard.unlock();
6909 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6910 cm_msg(MERROR, "bm_open_buffer",
6911 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6912 pheader->name);
6913 *buffer_handle = 0;
6914 delete pbuf;
6915 return BM_CORRUPTED;
6916 }
6917
6918 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6919 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6920 pbuf_guard.unlock();
6921 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6922 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6924 *buffer_handle = 0;
6925 delete pbuf;
6926 return BM_CORRUPTED;
6927 }
6928
6929 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6930 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6931 pbuf_guard.unlock();
6932 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6933 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6935 *buffer_handle = 0;
6936 delete pbuf;
6937 return BM_CORRUPTED;
6938 }
6939
6940 /* check if buffer size is identical */
6941 if (pheader->size != buffer_size) {
6942 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6943 buffer_name, buffer_size, pheader->size);
6944
6945 buffer_size = pheader->size;
6946
6947 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6948
6949 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6950
6951 if (status != SS_SUCCESS) {
6952 *buffer_handle = 0;
6953 pbuf_guard.unlock();
6954 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6955 delete pbuf;
6956 return BM_NO_SHM;
6957 }
6958
6959 pbuf->buffer_header = (BUFFER_HEADER *) p;
6960 pheader = pbuf->buffer_header;
6961 }
6962 }
6963
6964 /* shared memory is good from here down */
6965
6966 pbuf->attached = true;
6967
6968 pbuf->shm_handle = shm_handle;
6969 pbuf->shm_size = shm_size;
6970 pbuf->callback = FALSE;
6971
6972 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6973
6975 if (status != BM_SUCCESS) {
6976 cm_msg(MERROR, "bm_open_buffer",
6977 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6978 status);
6980 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6981 }
6982
6983 /* add our client BUFFER_HEADER */
6984
6985 int iclient = 0;
6986 for (; iclient < MAX_CLIENTS; iclient++)
6987 if (pheader->client[iclient].pid == 0)
6988 break;
6989
6990 if (iclient == MAX_CLIENTS) {
6991 *buffer_handle = 0;
6992 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6993 pbuf_guard.unlock();
6994 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6995 delete pbuf;
6996 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
6997 return BM_NO_SLOT;
6998 }
6999
7000 /* store slot index in _buffer structure */
7001 pbuf->client_index = iclient;
7002
7003 /*
7004 Save the index of the last client of that buffer so that later only
7005 the clients 0..max_client_index-1 have to be searched through.
7006 */
7007 pheader->num_clients++;
7008 if (iclient + 1 > pheader->max_client_index)
7009 pheader->max_client_index = iclient + 1;
7010
7011 /* setup buffer header and client structure */
7012 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7013
7014 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7015
7016 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7017
7018 pclient->pid = ss_getpid();
7019
7021
7022 pclient->read_pointer = pheader->write_pointer;
7023 pclient->last_activity = ss_millitime();
7024
7025 cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
7026
7027 pbuf_guard.unlock();
7028
7029 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7030
7031 pheader = NULL;
7032
7033 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7034
7037
7038 /* add pbuf to buffer list */
7039
7040 gBuffersMutex.lock();
7041
7042 bool added = false;
7043 for (size_t i=0; i<gBuffers.size(); i++) {
7044 if (gBuffers[i] == NULL) {
7045 gBuffers[i] = pbuf;
7046 added = true;
7047 *buffer_handle = i+1;
7048 break;
7049 }
7050 }
7051 if (!added) {
7052 *buffer_handle = gBuffers.size() + 1;
7053 gBuffers.push_back(pbuf);
7054 }
7055
7056 /* from here down we should not touch pbuf without locking it */
7057
7058 pbuf = NULL;
7059
7060 gBuffersMutex.unlock();
7061
7062 /* new buffer is now ready for use */
7063
7064 /* initialize buffer counters */
7065 bm_init_buffer_counters(*buffer_handle);
7066
7067 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7068
7069 if (shm_created)
7070 return BM_CREATED;
7071 }
7072#endif /* LOCAL_ROUTINES */
7073
7074 return BM_SUCCESS;
7075}
7076
7077/********************************************************************/
7083INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7084{
7085 gBuffersMutex.lock();
7086 for (size_t i = 0; i < gBuffers.size(); i++) {
7087 BUFFER* pbuf = gBuffers[i];
7088 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7089 *buffer_handle = i + 1;
7090 gBuffersMutex.unlock();
7091 return BM_SUCCESS;
7092 }
7093 }
7094 gBuffersMutex.unlock();
7095 return BM_NOT_FOUND;
7096}
7097
7098/********************************************************************/
7104INT bm_close_buffer(INT buffer_handle) {
7105 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7106
7107 if (rpc_is_remote())
7108 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7109
7110#ifdef LOCAL_ROUTINES
7111 {
7112 int status = 0;
7113
7114 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7115
7116 if (!pbuf)
7117 return status;
7118
7119 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7120
7121 int i;
7122
7123 { /* delete all requests for this buffer */
7124 _request_list_mutex.lock();
7125 std::vector<EventRequest> request_list_copy = _request_list;
7126 _request_list_mutex.unlock();
7127 for (size_t i = 0; i < request_list_copy.size(); i++) {
7128 if (request_list_copy[i].buffer_handle == buffer_handle) {
7130 }
7131 }
7132 }
7133
7134 HNDLE hDB;
7136
7137 if (hDB) {
7138 /* write statistics to odb */
7140 }
7141
7142 /* lock buffer in correct order */
7143
7145
7146 if (status != BM_SUCCESS) {
7147 return status;
7148 }
7149
7151
7152 if (status != BM_SUCCESS) {
7153 pbuf->read_cache_mutex.unlock();
7154 return status;
7155 }
7156
7158
7159 if (!pbuf_guard.is_locked()) {
7160 pbuf->write_cache_mutex.unlock();
7161 pbuf->read_cache_mutex.unlock();
7162 return pbuf_guard.get_status();
7163 }
7164
7165 BUFFER_HEADER *pheader = pbuf->buffer_header;
7166
7167 /* mark entry in _buffer as empty */
7168 pbuf->attached = false;
7169
7171
7172 if (pclient) {
7173 /* clear entry from client structure in buffer header */
7174 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7175 }
7176
7177 /* calculate new max_client_index entry */
7178 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7179 if (pheader->client[i].pid != 0)
7180 break;
7181 pheader->max_client_index = i + 1;
7182
7183 /* count new number of clients */
7184 int j = 0;
7185 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7186 if (pheader->client[i].pid != 0)
7187 j++;
7188 pheader->num_clients = j;
7189
7190 int destroy_flag = (pheader->num_clients == 0);
7191
7192 // we hold the locks on the read cache and the write cache.
7193
7194 /* free cache */
7195 if (pbuf->read_cache_size > 0) {
7196 free(pbuf->read_cache);
7197 pbuf->read_cache = NULL;
7198 pbuf->read_cache_size = 0;
7199 pbuf->read_cache_rp = 0;
7200 pbuf->read_cache_wp = 0;
7201 }
7202
7203 if (pbuf->write_cache_size > 0) {
7204 free(pbuf->write_cache);
7205 pbuf->write_cache = NULL;
7206 pbuf->write_cache_size = 0;
7207 pbuf->write_cache_rp = 0;
7208 pbuf->write_cache_wp = 0;
7209 }
7210
7211 /* check if anyone is waiting and wake him up */
7212
7213 for (int i = 0; i < pheader->max_client_index; i++) {
7214 BUFFER_CLIENT *pclient = pheader->client + i;
7215 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7216 ss_resume(pclient->port, "B ");
7217 }
7218
7219 /* unmap shared memory, delete it if we are the last */
7220
7221 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7222
7223 /* after ss_shm_close() these are invalid: */
7224
7225 pheader = NULL;
7226 pbuf->buffer_header = NULL;
7227 pbuf->shm_size = 0;
7228 pbuf->shm_handle = 0;
7229
7230 /* unlock buffer in correct order */
7231
7232 pbuf_guard.unlock();
7233
7234 pbuf->write_cache_mutex.unlock();
7235 pbuf->read_cache_mutex.unlock();
7236
7237 /* delete semaphore */
7238
7240 }
7241#endif /* LOCAL_ROUTINES */
7242
7243 return BM_SUCCESS;
7244}
7245
7246/********************************************************************/
7252 if (rpc_is_remote())
7254
7255#ifdef LOCAL_ROUTINES
7256 {
7258
7259 gBuffersMutex.lock();
7260 size_t nbuf = gBuffers.size();
7261 gBuffersMutex.unlock();
7262
7263 for (size_t i = nbuf; i > 0; i--) {
7265 }
7266
7267 gBuffersMutex.lock();
7268 for (size_t i=0; i< gBuffers.size(); i++) {
7269 BUFFER* pbuf = gBuffers[i];
7270 if (!pbuf)
7271 continue;
7272 delete pbuf;
7273 pbuf = NULL;
7274 gBuffers[i] = NULL;
7275 }
7276 gBuffersMutex.unlock();
7277 }
7278#endif /* LOCAL_ROUTINES */
7279
7280 return BM_SUCCESS;
7281}
7282
7283/********************************************************************/
7289#ifdef LOCAL_ROUTINES
7290 {
7291 int status;
7292 HNDLE hDB;
7293
7295
7296 if (status != CM_SUCCESS) {
7297 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7298 return BM_SUCCESS;
7299 }
7300
7301 std::vector<BUFFER*> mybuffers;
7302
7303 gBuffersMutex.lock();
7305 gBuffersMutex.unlock();
7306
7307 for (BUFFER* pbuf : mybuffers) {
7308 if (!pbuf || !pbuf->attached)
7309 continue;
7311 }
7312 }
7313#endif /* LOCAL_ROUTINES */
7314
7315 return BM_SUCCESS;
7316}
7317
/* end of bmfunctionc */
7320
7326/*-- Watchdog routines ---------------------------------------------*/
7327#ifdef LOCAL_ROUTINES
7328
7329static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7330static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7331static std::atomic<std::thread*> _watchdog_thread{NULL};
7332
7333/********************************************************************/
7339 //printf("cm_watchdog_thread started!\n");
7340 while (_watchdog_thread_run) {
7341 //printf("cm_watchdog_thread runs!\n");
7345 int i;
7346 for (i = 0; i < 20; i++) {
7347 ss_sleep(100);
7349 break;
7350 }
7351 }
7352 //printf("cm_watchdog_thread stopped!\n");
7354 return 0;
7355}
7356
7357static void xcm_watchdog_thread() {
7359}
7360
7361#endif
7362
7364 /* watchdog does not run inside remote clients.
7365 * watchdog timeout timers are maintained by the mserver */
7366 if (rpc_is_remote())
7367 return CM_SUCCESS;
7368#ifdef LOCAL_ROUTINES
7369 /* only start once */
7370 if (_watchdog_thread)
7371 return CM_SUCCESS;
7372 _watchdog_thread_run = true;
7373 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7374#endif
7375 return CM_SUCCESS;
7376}
7377
7379 /* watchdog does not run inside remote clients.
7380 * watchdog timeout timers are maintained by the mserver */
7381 if (rpc_is_remote())
7382 return CM_SUCCESS;
7383#ifdef LOCAL_ROUTINES
7384 _watchdog_thread_run = false;
7386 //printf("waiting for watchdog thread to shut down\n");
7387 ss_sleep(10);
7388 }
7389 if (_watchdog_thread != NULL) {
7390 _watchdog_thread.load()->join();
7391 delete static_cast<std::thread *>(_watchdog_thread);
7393 }
7394#endif
7395 return CM_SUCCESS;
7396}
7397
7398/********************************************************************/
7409 INT status, return_status, i, size;
7411 KEY key;
7412 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7413 INT port;
7414 DWORD start_time;
7415 DWORD timeout;
7416 DWORD last;
7417
7419
7420 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7421 if (status != DB_SUCCESS)
7422 return DB_NO_KEY;
7423
7425
7426 /* loop over all clients */
7427 for (i = 0;; i++) {
7430 break;
7431
7432 /* don't shutdown ourselves */
7433 if (hSubkey == hKeyClient)
7434 continue;
7435
7436 if (status == DB_SUCCESS) {
7438
7439 /* contact client */
7440 size = sizeof(client_name);
7441 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7442 if (status != DB_SUCCESS)
7443 continue;
7444
7445 if (!bUnique)
7446 client_name[strlen(name)] = 0; /* strip number */
7447
7448 /* check if individual client */
7449 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7450 continue;
7451
7452 size = sizeof(port);
7453 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7454
7455 size = sizeof(remote_host);
7456 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7457
7458 cm_get_watchdog_info(hDB, name, &timeout, &last);
7459 if (timeout == 0)
7460 timeout = 5000;
7461
7462 /* client found -> connect to its server port */
7463 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7464 if (status != RPC_SUCCESS) {
7465 int client_pid = atoi(key.name);
7467 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7468 client_name, remote_host, port);
7469#ifdef SIGKILL
7470 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7471 client_pid);
7475 if (status != CM_SUCCESS)
7476 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7478#endif
7479 } else {
7480 /* call disconnect with shutdown=TRUE */
7482
7483 /* wait until client has shut down */
7484 start_time = ss_millitime();
7485 do {
7486 ss_sleep(100);
7488 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7489
7490 if (status == DB_SUCCESS) {
7491 int client_pid = atoi(key.name);
7493 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7494#ifdef SIGKILL
7495 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7496 client_pid);
7499 if (status != CM_SUCCESS)
7500 cm_msg(MERROR, "cm_shutdown",
7501 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7502 status);
7503#endif
7505 } else {
7507 i--;
7508 }
7509 }
7510 }
7511
7512 /* display any message created during each shutdown */
7514 }
7515
7516 return return_status;
7517}
7518
7519/********************************************************************/
7529 INT status, i, size;
7531 char client_name[NAME_LENGTH];
7532
7533 if (rpc_is_remote())
7535
7537
7538 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7539 if (status != DB_SUCCESS)
7540 return DB_NO_KEY;
7541
7543
7544 /* loop over all clients */
7545 for (i = 0;; i++) {
7548 break;
7549
7550 if (hSubkey == hKeyClient)
7551 continue;
7552
7553 if (status == DB_SUCCESS) {
7554 /* get client name */
7555 size = sizeof(client_name);
7556 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7557
7558 if (status != DB_SUCCESS) {
7559 //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);
7560 continue;
7561 }
7562
7563 if (equal_ustring(client_name, name)) {
7565 return CM_SUCCESS;
7566 }
7567
7568 if (!bUnique) {
7569 client_name[strlen(name)] = 0; /* strip number */
7570 if (equal_ustring(client_name, name)) {
7572 return CM_SUCCESS;
7573 }
7574 }
7575 }
7576 }
7577
7579
7580 return CM_NO_CLIENT;
7581}
7582
7583/********************************************************************/
7618INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7619 if (rpc_is_remote())
7620 return rpc_call(RPC_CM_CLEANUP, client_name);
7621
7622#ifdef LOCAL_ROUTINES
7623 {
7626
7627 std::vector<BUFFER*> mybuffers;
7628
7629 gBuffersMutex.lock();
7631 gBuffersMutex.unlock();
7632
7633 /* check buffers */
7634 for (BUFFER* pbuf : mybuffers) {
7635 if (!pbuf)
7636 continue;
7637 if (pbuf->attached) {
7638 std::string msg;
7639
7641
7642 if (!pbuf_guard.is_locked())
7643 continue;
7644
7645 /* update the last_activity entry to show that we are alive */
7646 BUFFER_HEADER *pheader = pbuf->buffer_header;
7648 pclient->last_activity = ss_millitime();
7649
7650 /* now check other clients */
7651 for (int j = 0; j < pheader->max_client_index; j++) {
7652 BUFFER_CLIENT *pbclient = &pheader->client[j];
7653 if (j != pbuf->client_index && pbclient->pid &&
7654 (client_name == NULL || client_name[0] == 0
7655 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7656 if (ignore_timeout)
7658 else
7660
7661 /* If client process has no activity, clear its buffer entry. */
7662 if (interval > 0
7663 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7664
7665 /* now make again the check with the buffer locked */
7666 if (interval > 0
7667 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7668 msg = msprintf(
7669 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7670 pbclient->name, pheader->name,
7671 (ss_millitime() - pbclient->last_activity) / 1000.0,
7672 interval / 1000.0);
7673
7674 bm_remove_client_locked(pheader, j);
7675 }
7676
7677 /* go again through whole list */
7678 j = 0;
7679 }
7680 }
7681 }
7682
7683 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7684 pbuf_guard.unlock();
7685
7686 /* display info message after unlocking buffer */
7687 if (!msg.empty())
7688 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7689 }
7690 }
7691
7692 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7693 }
7694#endif /* LOCAL_ROUTINES */
7695
7696 return CM_SUCCESS;
7697}
7698
7699/********************************************************************/
7718std::string cm_expand_env(const char *str) {
7719 const char *s = str;
7720 std::string r;
7721 for (; *s;) {
7722 if (*s == '$') {
7723 s++;
7724 std::string envname;
7725 for (; *s;) {
7726 if (*s == DIR_SEPARATOR)
7727 break;
7728 envname += *s;
7729 s++;
7730 }
7731 const char *e = getenv(envname.c_str());
7732 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7733 if (!e) {
7734 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7735 r += '$';
7736 r += envname;
7737 } else {
7738 r += e;
7739 //if (r[r.length()-1] != DIR_SEPARATOR)
7740 //r += DIR_SEPARATOR_STR;
7741 }
7742 } else {
7743 r += *s;
7744 s++;
7745 }
7746 }
7747 return r;
7748}
7749
7750static bool test_cm_expand_env1(const char *str, const char *expected) {
7751 std::string s = cm_expand_env(str);
7752 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7753 str,
7754 s.c_str(),
7755 expected);
7756 if (s != expected) {
7757 printf(", MISMATCH!\n");
7758 return false;
7759 }
7760
7761 printf("\n");
7762 return true;
7763}
7764
7766 printf("Test expand_end()\n");
7767 setenv("FOO", "foo", 1);
7768 setenv("BAR", "bar", 1);
7769 setenv("EMPTY", "", 1);
7770 unsetenv("UNDEF");
7771
7772 bool ok = true;
7773
7774 ok &= test_cm_expand_env1("aaa", "aaa");
7775 ok &= test_cm_expand_env1("$FOO", "foo");
7776 ok &= test_cm_expand_env1("/$FOO", "/foo");
7777 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7778 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7779 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7780 ok &= test_cm_expand_env1("1$FOO", "1foo");
7781 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7782 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7783
7784 if (ok) {
7785 printf("test_expand_env: all tests passed!\n");
7786 } else {
7787 printf("test_expand_env: test FAILED!\n");
7788 }
7789}
7790
/* end of cmfunctionc */
7794
7796#ifndef DOXYGEN_SHOULD_SKIP_THIS
7797
7798/********************************************************************/
7799INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7800/********************************************************************\
7801
7802 Routine: bm_buffer_info
7803
7804 Purpose: Copies the current buffer header referenced by buffer_handle
7805 into the *buffer_header structure which must be supplied
7806 by the calling routine.
7807
7808 Input:
7809 INT buffer_handle Handle of the buffer to get the header from
7810
7811 Output:
7812 BUFFER_HEADER *buffer_header Destination address which gets a copy
7813 of the buffer header structure.
7814
7815 Function value:
7816 BM_SUCCESS Successful completion
7817 BM_INVALID_HANDLE Buffer handle is invalid
7818 RPC_NET_ERROR Network error
7819
7820\********************************************************************/
7821{
7822 if (rpc_is_remote())
7823 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7824
7825#ifdef LOCAL_ROUTINES
7826
7827 int status = 0;
7828 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7829
7830 if (!pbuf)
7831 return status;
7832
7834
7835 if (!pbuf_guard.is_locked())
7836 return pbuf_guard.get_status();
7837
7838 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7839
7840#endif /* LOCAL_ROUTINES */
7841
7842 return BM_SUCCESS;
7843}
7844
7845/********************************************************************/
7846INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7847/********************************************************************\
7848
7849 Routine: bm_get_buffer_level
7850
7851 Purpose: Return number of bytes in buffer or in cache
7852
7853 Input:
7854 INT buffer_handle Handle of the buffer to get the info
7855
7856 Output:
7857 INT *n_bytes Number of bytes in buffer
7858
7859 Function value:
7860 BM_SUCCESS Successful completion
7861 BM_INVALID_HANDLE Buffer handle is invalid
7862 RPC_NET_ERROR Network error
7863
7864\********************************************************************/
7865{
7866 if (rpc_is_remote())
7867 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7868
7869#ifdef LOCAL_ROUTINES
7870 {
7871 int status = 0;
7872
7873 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7874
7875 if (!pbuf)
7876 return status;
7877
7879
7880 if (!pbuf_guard.is_locked())
7881 return pbuf_guard.get_status();
7882
7883 BUFFER_HEADER *pheader = pbuf->buffer_header;
7884
7886
7887 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7888 if (*n_bytes < 0)
7889 *n_bytes += pheader->size;
7890
7891 pbuf_guard.unlock();
7892
7893 if (pbuf->read_cache_size) {
7895 if (status == BM_SUCCESS) {
7896 /* add bytes in cache */
7897 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7898 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7899 pbuf->read_cache_mutex.unlock();
7900 }
7901 }
7902 }
7903#endif /* LOCAL_ROUTINES */
7904
7905 return BM_SUCCESS;
7906}
7907
7908
7909#ifdef LOCAL_ROUTINES
7910
7911/********************************************************************/
7913{
7914 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7915
7916 if (!locked) {
7917 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);
7918 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);
7919 abort();
7920 /* DOES NOT RETURN */
7921 }
7922
7923 if (!pbuf->attached) {
7924 pbuf->read_cache_mutex.unlock();
7925 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);
7926 return BM_INVALID_HANDLE;
7927 }
7928
7929 return BM_SUCCESS;
7930}
7931
7932/********************************************************************/
7934{
7935 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7936
7937 if (!locked) {
7938 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);
7939 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);
7940 abort();
7941 /* DOES NOT RETURN */
7942 }
7943
7944 if (!pbuf->attached) {
7945 pbuf->write_cache_mutex.unlock();
7946 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);
7947 return BM_INVALID_HANDLE;
7948 }
7949
7950 return BM_SUCCESS;
7951}
7952
7953/********************************************************************/
7955{
7956 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7957
7958 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7959
7960 if (!locked) {
7961 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7962 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7963 abort();
7964 /* DOES NOT RETURN */
7965 }
7966
7967 if (!pbuf->attached) {
7968 pbuf->buffer_mutex.unlock();
7969 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);
7970 return BM_INVALID_HANDLE;
7971 }
7972
7973 //static int counter = 0;
7974 //counter++;
7975 //printf("locked %d!\n", counter);
7976 //if (counter > 50)
7977 // ::sleep(3);
7978
7979 return BM_SUCCESS;
7980}
7981
7982/********************************************************************/
7984{
7985 int status;
7986
7987 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
7988
7989 //if (pbuf->locked) {
7990 // fprintf(stderr, "double lock, abort!\n");
7991 // abort();
7992 //}
7993
7995
7996 if (status != BM_SUCCESS)
7997 return status;
7998
7999 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8000
8001 if (status != SS_SUCCESS) {
8002 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8003
8004 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8005
8006 if (status != SS_SUCCESS) {
8007 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);
8008
8009 if (pbuf->buffer_header) {
8010 for (int i=0; i<MAX_CLIENTS; i++) {
8011 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);
8012 }
8013 }
8014
8016
8017 if (status != SS_SUCCESS) {
8018 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8019 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8020 abort();
8021 /* DOES NOT RETURN */
8022 }
8023 }
8024 }
8025
8026 // protect against double lock
8027 assert(!pbuf->locked);
8028 pbuf->locked = TRUE;
8029
8030#if 0
8031 int x = MAX_CLIENTS - 1;
8032 if (pbuf->buffer_header->client[x].unused1 != 0) {
8033 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8034 }
8035 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8036 pbuf->buffer_header->client[x].unused1 = getpid();
8037#endif
8038
8039 pbuf->count_lock++;
8040
8041 return BM_SUCCESS;
8042}
8043
8044/********************************************************************/
8046 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8047
8048#if 0
8049 int x = MAX_CLIENTS-1;
8050 if (pbuf->attached) {
8051 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8052 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8053 }
8054 pbuf->buffer_header->client[x].unused1 = 0;
8055 } else {
8056 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8057 }
8058#endif
8059
8060 // protect against double unlock
8061 assert(pbuf->locked);
8062 pbuf->locked = FALSE;
8063
8064 ss_semaphore_release(pbuf->semaphore);
8065 pbuf->buffer_mutex.unlock();
8066}
8067
8068#endif /* LOCAL_ROUTINES */
8069
8070/********************************************************************/
8072/********************************************************************\
8073
8074 Routine: bm_init_event_counters
8075
8076 Purpose: Initialize counters for a specific buffer. This routine
8077 should be called at the beginning of a run.
8078
8079 Input:
8080 INT buffer_handle Handle to the buffer to be
8081 initialized.
8082 Output:
8083 none
8084
8085 Function value:
8086 BM_SUCCESS Successful completion
8087 BM_INVALID_HANDLE Buffer handle is invalid
8088
8089\********************************************************************/
8090{
8091 if (rpc_is_remote())
8092 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8093
8094#ifdef LOCAL_ROUTINES
8095
8096 int status = 0;
8097
8098 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8099
8100 if (!pbuf)
8101 return status;
8102
8104
8105 if (!pbuf_guard.is_locked())
8106 return pbuf_guard.get_status();
8107
8108 pbuf->buffer_header->num_in_events = 0;
8109 pbuf->buffer_header->num_out_events = 0;
8110
8111#endif /* LOCAL_ROUTINES */
8112
8113 return BM_SUCCESS;
8114}
8115
8117#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8118
8124/********************************************************************/
8148INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8149/*------------------------------------------------------------------*/
8150{
8151 if (rpc_is_remote())
8152 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8153
8154#ifdef LOCAL_ROUTINES
8155 {
8156 int status = 0;
8157
8158 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8159
8160 if (!pbuf)
8161 return status;
8162
8163 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8164
8166
8167 if (status != BM_SUCCESS)
8168 return status;
8169
8170 if (write_size < 0)
8171 write_size = 0;
8172
8173 if (write_size > 0) {
8175 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);
8177 }
8178 }
8179
8180 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8181
8182 if (write_size > max_write_size) {
8184 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);
8186 }
8187
8188 pbuf->buffer_mutex.unlock();
8189
8190 /* resize read cache */
8191
8193
8194 if (status != BM_SUCCESS) {
8195 return status;
8196 }
8197
8198 if (pbuf->read_cache_size > 0) {
8199 free(pbuf->read_cache);
8200 pbuf->read_cache = NULL;
8201 }
8202
8203 if (read_size > 0) {
8204 pbuf->read_cache = (char *) malloc(read_size);
8205 if (pbuf->read_cache == NULL) {
8206 pbuf->read_cache_size = 0;
8207 pbuf->read_cache_rp = 0;
8208 pbuf->read_cache_wp = 0;
8209 pbuf->read_cache_mutex.unlock();
8210 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);
8211 return BM_NO_MEMORY;
8212 }
8213 }
8214
8215 pbuf->read_cache_size = read_size;
8216 pbuf->read_cache_rp = 0;
8217 pbuf->read_cache_wp = 0;
8218
8219 pbuf->read_cache_mutex.unlock();
8220
8221 /* resize the write cache */
8222
8224
8225 if (status != BM_SUCCESS)
8226 return status;
8227
8228 // FIXME: should flush the write cache!
8229 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8230 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8231 }
8232
8233 /* manage write cache */
8234 if (pbuf->write_cache_size > 0) {
8235 free(pbuf->write_cache);
8236 pbuf->write_cache = NULL;
8237 }
8238
8239 if (write_size > 0) {
8240 pbuf->write_cache = (char *) M_MALLOC(write_size);
8241 if (pbuf->write_cache == NULL) {
8242 pbuf->write_cache_size = 0;
8243 pbuf->write_cache_rp = 0;
8244 pbuf->write_cache_wp = 0;
8245 pbuf->write_cache_mutex.unlock();
8246 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);
8247 return BM_NO_MEMORY;
8248 }
8249 }
8250
8251 pbuf->write_cache_size = write_size;
8252 pbuf->write_cache_rp = 0;
8253 pbuf->write_cache_wp = 0;
8254
8255 pbuf->write_cache_mutex.unlock();
8256 }
8257#endif /* LOCAL_ROUTINES */
8258
8259 return BM_SUCCESS;
8260}
8261
8262/********************************************************************/
8290{
8291 event_header->event_id = event_id;
8292 event_header->trigger_mask = trigger_mask;
8293 event_header->data_size = data_size;
8294 event_header->time_stamp = ss_time();
8295 event_header->serial_number = serial;
8296
8297 return BM_SUCCESS;
8298}
8299
8301{
8302 static std::mutex mutex;
8303
8304 event_header->event_id = event_id;
8305 event_header->trigger_mask = trigger_mask;
8306 event_header->data_size = data_size;
8307 event_header->time_stamp = ss_time();
8308 {
8309 std::lock_guard<std::mutex> lock(mutex);
8310 event_header->serial_number = *serial;
8311 *serial = *serial + 1;
8312 // implicit unlock
8313 }
8314
8315 return BM_SUCCESS;
8316}
8317
8319#ifndef DOXYGEN_SHOULD_SKIP_THIS
8320
8321/********************************************************************/
8322INT bm_add_event_request(INT buffer_handle, short int event_id,
8323 short int trigger_mask,
8324 INT sampling_type,
8325 EVENT_HANDLER *func,
8326 INT request_id)
8327/********************************************************************\
8328
8329 Routine: bm_add_event_request
8330
8331 Purpose: Place a request for a specific event type in the client
8332 structure of the buffer refereced by buffer_handle.
8333
8334 Input:
8335 INT buffer_handle Handle to the buffer where the re-
8336 quest should be placed in
8337
8338 short int event_id Event ID \
8339 short int trigger_mask Trigger mask / Event specification
8340
8341 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8342
8343
8344 Note: to request all types of events, use
8345 event_id = 0 (all others should be !=0 !)
8346 trigger_mask = TRIGGER_ALL
8347 sampling_typ = GET_ALL
8348
8349
8350 void *func Callback function
8351 INT request_id Request id (unique number assigned
8352 by bm_request_event)
8353
8354 Output:
8355 none
8356
8357 Function value:
8358 BM_SUCCESS Successful completion
8359 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8360 MIDAS.H should be increased.
8361 BM_INVALID_HANDLE Buffer handle is invalid
8362 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8363 RPC_NET_ERROR Network error
8364
8365\********************************************************************/
8366{
8367 if (rpc_is_remote())
8368 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8369 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8370
8371#ifdef LOCAL_ROUTINES
8372 {
8373 int status = 0;
8374
8375 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8376
8377 if (!pbuf)
8378 return status;
8379
8380 /* lock buffer */
8382
8383 if (!pbuf_guard.is_locked())
8384 return pbuf_guard.get_status();
8385
8386 /* avoid callback/non callback requests */
8387 if (func == NULL && pbuf->callback) {
8388 pbuf_guard.unlock(); // unlock before cm_msg()
8389 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8390 return BM_INVALID_MIXING;
8391 }
8392
8393 /* do not allow GET_RECENT with nonzero cache size */
8394 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8395 pbuf_guard.unlock(); // unlock before cm_msg()
8396 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8397 return BM_INVALID_PARAM;
8398 }
8399
8400 /* get a pointer to the proper client structure */
8402
8403 /* look for a empty request entry */
8404 int i;
8405 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8406 if (!pclient->event_request[i].valid)
8407 break;
8408
8409 if (i == MAX_EVENT_REQUESTS) {
8410 // implicit unlock
8411 return BM_NO_MEMORY;
8412 }
8413
8414 /* setup event_request structure */
8415 pclient->event_request[i].id = request_id;
8416 pclient->event_request[i].valid = TRUE;
8417 pclient->event_request[i].event_id = event_id;
8418 pclient->event_request[i].trigger_mask = trigger_mask;
8419 pclient->event_request[i].sampling_type = sampling_type;
8420
8421 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8422
8423 pbuf->get_all_flag = pclient->all_flag;
8424
8425 /* set callback flag in buffer structure */
8426 if (func != NULL)
8427 pbuf->callback = TRUE;
8428
8429 /*
8430 Save the index of the last request in the list so that later only the
8431 requests 0..max_request_index-1 have to be searched through.
8432 */
8433
8434 if (i + 1 > pclient->max_request_index)
8435 pclient->max_request_index = i + 1;
8436 }
8437#endif /* LOCAL_ROUTINES */
8438
8439 return BM_SUCCESS;
8440}
8441
8443#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8444
8445/********************************************************************/
8473INT bm_request_event(HNDLE buffer_handle, short int event_id,
8474 short int trigger_mask,
8475 INT sampling_type, HNDLE *request_id,
8476 EVENT_HANDLER *func)
8477{
8478 assert(request_id != NULL);
8479
8480 EventRequest r;
8481 r.buffer_handle = buffer_handle;
8482 r.event_id = event_id;
8484 r.dispatcher = func;
8485
8486 {
8487 std::lock_guard<std::mutex> guard(_request_list_mutex);
8488
8489 bool found = false;
8490
8491 // find deleted entry
8492 for (size_t i = 0; i < _request_list.size(); i++) {
8493 if (_request_list[i].buffer_handle == 0) {
8494 _request_list[i] = r;
8495 *request_id = i;
8496 found = true;
8497 break;
8498 }
8499 }
8500
8501 if (!found) { // not found
8502 *request_id = _request_list.size();
8503 _request_list.push_back(r);
8504 }
8505
8506 // implicit unlock()
8507 }
8508
8509 /* add request in buffer structure */
8510 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8511 if (status != BM_SUCCESS)
8512 return status;
8513
8514 return BM_SUCCESS;
8515}
8516
8517/********************************************************************/
8526INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8527 if (rpc_is_remote())
8528 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8529
8530#ifdef LOCAL_ROUTINES
8531 {
8532 int status = 0;
8533
8534 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8535
8536 if (!pbuf)
8537 return status;
8538
8539 /* lock buffer */
8541
8542 if (!pbuf_guard.is_locked())
8543 return pbuf_guard.get_status();
8544
8545 INT i, deleted;
8546
8547 /* get a pointer to the proper client structure */
8549
8550 /* check all requests and set to zero if matching */
8551 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8552 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8553 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8554 deleted++;
8555 }
8556
8557 /* calculate new max_request_index entry */
8558 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8559 if (pclient->event_request[i].valid)
8560 break;
8561
8562 pclient->max_request_index = i + 1;
8563
8564 /* calculate new all_flag */
8565 pclient->all_flag = FALSE;
8566
8567 for (i = 0; i < pclient->max_request_index; i++)
8568 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8569 pclient->all_flag = TRUE;
8570 break;
8571 }
8572
8573 pbuf->get_all_flag = pclient->all_flag;
8574
8575 if (!deleted)
8576 return BM_NOT_FOUND;
8577 }
8578#endif /* LOCAL_ROUTINES */
8579
8580 return BM_SUCCESS;
8581}
8582
8583/********************************************************************/
8593{
8594 _request_list_mutex.lock();
8595
8596 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8597 _request_list_mutex.unlock();
8598 return BM_INVALID_HANDLE;
8599 }
8600
8601 int buffer_handle = _request_list[request_id].buffer_handle;
8602
8603 _request_list[request_id].clear();
8604
8605 _request_list_mutex.unlock();
8606
8607 /* remove request entry from buffer */
8608 return bm_remove_event_request(buffer_handle, request_id);
8609}
8610
8611#if 0 // currently not used
8612static void bm_show_pointers(const BUFFER_HEADER * pheader)
8613{
8614 int i;
8615 const BUFFER_CLIENT *pclient;
8616
8617 pclient = pheader->client;
8618
8619 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8620 pheader->write_pointer, pheader->size);
8621 for (i = 0; i < pheader->max_client_index; i++)
8622 if (pclient[i].pid) {
8623 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8624 }
8625
8626 printf("done\n");
8627}
8628#endif
8629
8631 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8632 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8633
8634 if (pheader->read_pointer <= pheader->write_pointer) {
8635
8636 if (pclient->read_pointer < pheader->read_pointer) {
8637 cm_msg(MINFO, "bm_validate_client_pointers",
8638 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8639 pclient->name,
8640 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8641
8642 pclient->read_pointer = pheader->read_pointer;
8643 }
8644
8645 if (pclient->read_pointer > pheader->write_pointer) {
8646 cm_msg(MINFO, "bm_validate_client_pointers",
8647 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8648 pclient->name,
8649 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8650
8651 pclient->read_pointer = pheader->write_pointer;
8652 }
8653
8654 } else {
8655
8656 if (pclient->read_pointer < 0) {
8657 cm_msg(MINFO, "bm_validate_client_pointers",
8658 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8659 pclient->name,
8660 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8661
8662 pclient->read_pointer = pheader->read_pointer;
8663 }
8664
8665 if (pclient->read_pointer >= pheader->size) {
8666 cm_msg(MINFO, "bm_validate_client_pointers",
8667 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8668 pclient->name,
8669 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8670
8671 pclient->read_pointer = pheader->read_pointer;
8672 }
8673
8674 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8675 cm_msg(MINFO, "bm_validate_client_pointers",
8676 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8677 pclient->name,
8678 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8679
8680 pclient->read_pointer = pheader->read_pointer;
8681 }
8682 }
8683}
8684
8685#if 0 // currently not used
8686static void bm_validate_pointers(BUFFER_HEADER * pheader)
8687{
8688 BUFFER_CLIENT *pclient = pheader->client;
8689 int i;
8690
8691 for (i = 0; i < pheader->max_client_index; i++)
8692 if (pclient[i].pid) {
8694 }
8695}
8696#endif
8697
8698//
8699// Buffer pointers
8700//
8701// normal:
8702//
8703// zero -->
8704// ... free space
8705// read_pointer -->
8706// client1 rp -->
8707// client2 rp -->
8708// ... buffered data
8709// write_pointer -->
8710// ... free space
8711// pheader->size -->
8712//
8713// inverted:
8714//
8715// zero -->
8716// client3 rp -->
8717// ... buffered data
8718// client4 rp -->
8719// write_pointer -->
8720// ... free space
8721// read_pointer -->
8722// client1 rp -->
8723// client2 rp -->
8724// ... buffered data
8725// pheader->size -->
8726//
8727
8729 assert(caller_name);
8730
8731 /* calculate global read pointer as "minimum" of client read pointers */
8732 int min_rp = pheader->write_pointer;
8733
8734 int i;
8735 for (i = 0; i < pheader->max_client_index; i++) {
8736 BUFFER_CLIENT *pc = pheader->client + i;
8737 if (pc->pid) {
8739
8740#if 0
8741 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8742 pheader->name,
8743 pheader->read_pointer,
8744 pheader->write_pointer,
8745 pheader->size,
8746 min_rp,
8747 pc->name,
8748 pc->read_pointer);
8749#endif
8750
8751 if (pheader->read_pointer <= pheader->write_pointer) {
8752 // normal pointers
8753 if (pc->read_pointer < min_rp)
8754 min_rp = pc->read_pointer;
8755 } else {
8756 // inverted pointers
8757 if (pc->read_pointer <= pheader->write_pointer) {
8758 // clients 3 and 4
8759 if (pc->read_pointer < min_rp)
8760 min_rp = pc->read_pointer;
8761 } else {
8762 // clients 1 and 2
8763 int xptr = pc->read_pointer - pheader->size;
8764 if (xptr < min_rp)
8765 min_rp = xptr;
8766 }
8767 }
8768 }
8769 }
8770
8771 if (min_rp < 0)
8772 min_rp += pheader->size;
8773
8774 assert(min_rp >= 0);
8775 assert(min_rp < pheader->size);
8776
8777 if (min_rp == pheader->read_pointer) {
8778 return FALSE;
8779 }
8780
8781#if 0
8782 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8783 pheader->name,
8784 pheader->read_pointer,
8785 pheader->write_pointer,
8786 pheader->size,
8787 min_rp);
8788#endif
8789
8790 pheader->read_pointer = min_rp;
8791
8792 return TRUE;
8793}
8794
8795static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8796 int i;
8797 int have_get_all_requests = 0;
8798
8799 for (i = 0; i < pc->max_request_index; i++)
8800 if (pc->event_request[i].valid)
8801 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8802
8803 /* only GET_ALL requests actually free space in the event buffer */
8805 return;
8806
8807 /*
8808 If read pointer has been changed, it may have freed up some space
8809 for waiting producers. So check if free space is now more than 50%
8810 of the buffer size and wake waiting producers.
8811 */
8812
8813 int free_space = pc->read_pointer - pheader->write_pointer;
8814 if (free_space <= 0)
8815 free_space += pheader->size;
8816
8817 if (free_space >= pheader->size * 0.5) {
8818 for (i = 0; i < pheader->max_client_index; i++) {
8819 const BUFFER_CLIENT *pc = pheader->client + i;
8820 if (pc->pid && pc->write_wait) {
8821 BOOL send_wakeup = (pc->write_wait < free_space);
8822 //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);
8823 if (send_wakeup) {
8824 ss_resume(pc->port, "B ");
8825 }
8826 }
8827 }
8828 }
8829}
8830
8831static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8832{
8833 _request_list_mutex.lock();
8834 bool locked = true;
8835 size_t n = _request_list.size();
8836 /* call dispatcher */
8837 for (size_t i = 0; i < n; i++) {
8838 if (!locked) {
8839 _request_list_mutex.lock();
8840 locked = true;
8841 }
8843 if (r.buffer_handle != buffer_handle)
8844 continue;
8845 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8846 continue;
8847 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8848 _request_list_mutex.unlock();
8849 locked = false;
8850 /* if event is fragmented, call defragmenter */
8851 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))) {
8852 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8853 } else {
8854 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8855 }
8856 }
8857 if (locked)
8858 _request_list_mutex.unlock();
8859}
8860
8861#ifdef LOCAL_ROUTINES
8862
8863static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8864 /* increment read cache read pointer */
8865 pbuf->read_cache_rp += total_size;
8866
8867 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8868 pbuf->read_cache_rp = 0;
8869 pbuf->read_cache_wp = 0;
8870 }
8871}
8872
8874{
8875 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8876 return FALSE;
8877
8878 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8879 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8880 int total_size = ALIGN8(event_size);
8881
8882 if (ppevent)
8883 *ppevent = pevent;
8884 if (pevent_size)
8886 if (ptotal_size)
8887 *ptotal_size = total_size;
8888
8889 return TRUE;
8890}
8891
8892//
8893// return values:
8894// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8895// BM_ASYNC_RETURN - buffer is empty
8896// BM_CORRUPTED - buffer is corrupted
8897//
8898
8900{
8901 if (pc->read_pointer == pheader->write_pointer) {
8902 /* no more events buffered for this client */
8903 if (!pc->read_wait) {
8904 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8905 pc->read_wait = TRUE;
8906 }
8907 return BM_ASYNC_RETURN;
8908 }
8909
8910 if (pc->read_wait) {
8911 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8912 pc->read_wait = FALSE;
8913 }
8914
8915 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8916 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);
8917 return BM_CORRUPTED;
8918 }
8919
8920 char *pdata = (char *) (pheader + 1);
8921
8922 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8923 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8924 int total_size = ALIGN8(event_size);
8925
8926 if ((total_size <= 0) || (total_size > pheader->size)) {
8927 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);
8928 return BM_CORRUPTED;
8929 }
8930
8931 assert(total_size > 0);
8932 assert(total_size <= pheader->size);
8933
8934 if (ppevent)
8935 *ppevent = pevent;
8936 if (pevent_size)
8938 if (ptotal_size)
8939 *ptotal_size = total_size;
8940
8941 return BM_SUCCESS;
8942}
8943
8944static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8945{
8946 const char *pdata = (const char *) (pheader + 1);
8947
8948 if (rp + event_size <= pheader->size) {
8949 /* copy event to cache */
8950 memcpy(buf, pdata + rp, event_size);
8951 } else {
8952 /* event is splitted */
8953 int size = pheader->size - rp;
8954 memcpy(buf, pdata + rp, size);
8955 memcpy(buf + size, pdata, event_size - size);
8956 }
8957}
8958
8959static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8960{
8961 const char *pdata = (const char *) (pheader + 1);
8962
8963 if (rp + event_size <= pheader->size) {
8964 /* copy event to cache */
8965 vecptr->assign(pdata + rp, pdata + rp + event_size);
8966 } else {
8967 /* event is splitted */
8968 int size = pheader->size - rp;
8969 vecptr->assign(pdata + rp, pdata + rp + size);
8970 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8971 }
8972}
8973
8974static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8975
8977 int i;
8978 for (i = 0; i < pc->max_request_index; i++) {
8979 const EVENT_REQUEST *prequest = pc->event_request + i;
8980 if (prequest->valid) {
8981 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8982 /* check if this is a recent event */
8983 if (prequest->sampling_type == GET_RECENT) {
8984 if (ss_time() - pevent->time_stamp > 1) {
8985 /* skip that event */
8986 continue;
8987 }
8988 }
8989
8991 break;
8992 }
8993 }
8994 }
8995 return is_requested;
8996}
8997
8999
9001{
9002 BUFFER* pbuf = pbuf_guard.get_pbuf();
9003 BUFFER_HEADER* pheader = pbuf->buffer_header;
9006
9007 //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);
9008
9009 /* loop over all events in the buffer */
9010
9011 while (1) {
9012 EVENT_HEADER *pevent = NULL;
9013 int event_size = 3; // poison value
9014 int total_size = 3; // poison value
9015
9016 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9017 if (status == BM_CORRUPTED) {
9018 return status;
9019 } else if (status != BM_SUCCESS) {
9020 /* event buffer is empty */
9021 if (timeout_msec == BM_NO_WAIT) {
9022 if (need_wakeup)
9024 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9025 // read cache is empty
9026 return BM_ASYNC_RETURN;
9027 }
9028 return BM_SUCCESS;
9029 }
9030
9032
9033 if (status != BM_SUCCESS) {
9034 // we only come here with SS_ABORT & co
9035 return status;
9036 }
9037
9038 // make sure we wait for new event only once
9040 // go back to bm_peek_buffer_locked
9041 continue;
9042 }
9043
9044 /* loop over all requests: if this event matches a request,
9045 * copy it to the read cache */
9046
9048
9049 if (is_requested) {
9050 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9051 /* read cache is full */
9052 if (need_wakeup)
9054 return BM_SUCCESS;
9055 }
9056
9057 bm_read_from_buffer_locked(pheader, pc->read_pointer, pbuf->read_cache + pbuf->read_cache_wp, event_size);
9058
9059 pbuf->read_cache_wp += total_size;
9060
9061 /* update statistics */
9062 pheader->num_out_events++;
9063 pbuf->count_read++;
9064 pbuf->bytes_read += event_size;
9065 }
9066
9067 /* shift read pointer */
9068
9069 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9070 pc->read_pointer = new_read_pointer;
9071
9072 need_wakeup = TRUE;
9073 }
9074 /* NOT REACHED */
9075}
9076
9077static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9078 /* now convert event header */
9079 if (convert_flags) {
9080 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9081 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9082 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9083 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9084 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9085 }
9086}
9087
9089{
9090 // return values:
9091 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9092 // BM_CORRUPTED - shared memory is corrupted
9093 // BM_NO_MEMORY - asked for more than buffer size
9094 // BM_ASYNC_RETURN - timeout waiting for free space
9095 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9096 // SS_ABORT - we are told to shutdown (locks releases)
9097
9098 int status;
9099 BUFFER* pbuf = pbuf_guard.get_pbuf();
9100 BUFFER_HEADER *pheader = pbuf->buffer_header;
9101 char *pdata = (char *) (pheader + 1);
9102
9103 /* make sure the buffer never completely full:
9104 * read pointer and write pointer would coincide
9105 * and the code cannot tell if it means the
9106 * buffer is 100% full or 100% empty. It will explode
9107 * or lose events */
9108 requested_space += 100;
9109
9110 if (requested_space >= pheader->size)
9111 return BM_NO_MEMORY;
9112
9115
9116 //DWORD blocking_time = 0;
9117 //int blocking_loops = 0;
9118 int blocking_client_index = -1;
9120 blocking_client_name[0] = 0;
9121
9122 while (1) {
9123 while (1) {
9124 /* check if enough space in buffer */
9125
9126 int free = pheader->read_pointer - pheader->write_pointer;
9127 if (free <= 0)
9128 free += pheader->size;
9129
9130 //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);
9131
9132 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9133 //if (blocking_loops) {
9134 // DWORD wait_time = ss_millitime() - blocking_time;
9135 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9136 //}
9137
9138 if (pbuf->wait_start_time != 0) {
9140 DWORD wait_time = now - pbuf->wait_start_time;
9141 pbuf->time_write_wait += wait_time;
9142 pbuf->wait_start_time = 0;
9143 int iclient = pbuf->wait_client_index;
9144 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9145 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9146 pbuf->client_count_write_wait[iclient] += 1;
9147 pbuf->client_time_write_wait[iclient] += wait_time;
9148 }
9149 }
9150
9151 //if (blocking_loops > 0) {
9152 // 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);
9153 //}
9154
9155 return BM_SUCCESS;
9156 }
9157
9158 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9159 cm_msg(MERROR, "bm_wait_for_free_space",
9160 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9161 pheader->name,
9162 pheader->read_pointer,
9163 pheader->write_pointer,
9164 pheader->size,
9165 free,
9167 return BM_CORRUPTED;
9168 }
9169
9170 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9171 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9172 int total_size = ALIGN8(event_size);
9173
9174#if 0
9175 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);
9176#endif
9177
9178 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9179 cm_msg(MERROR, "bm_wait_for_free_space",
9180 "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",
9181 pheader->name,
9182 pheader->read_pointer,
9183 pheader->write_pointer,
9184 pheader->size,
9185 free,
9187 pevent->data_size,
9188 event_size,
9189 total_size);
9190 return BM_CORRUPTED;
9191 }
9192
9193 int blocking_client = -1;
9194
9195 int i;
9196 for (i = 0; i < pheader->max_client_index; i++) {
9197 BUFFER_CLIENT *pc = pheader->client + i;
9198 if (pc->pid) {
9199 if (pc->read_pointer == pheader->read_pointer) {
9200 /*
9201 First assume that the client with the "minimum" read pointer
9202 is not really blocking due to a GET_ALL request.
9203 */
9205 //int blocking_request_id = -1;
9206
9207 int j;
9208 for (j = 0; j < pc->max_request_index; j++) {
9209 const EVENT_REQUEST *prequest = pc->event_request + j;
9210 if (prequest->valid
9211 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9212 if (prequest->sampling_type & GET_ALL) {
9213 blocking = TRUE;
9214 //blocking_request_id = prequest->id;
9215 break;
9216 }
9217 }
9218 }
9219
9220 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9221
9222 if (blocking) {
9224 break;
9225 }
9226
9227 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9228 }
9229 }
9230 } /* client loop */
9231
9232 if (blocking_client >= 0) {
9235 //if (!blocking_time) {
9236 // blocking_time = ss_millitime();
9237 //}
9238
9239 //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);
9240
9241 // from this "break" we go into timeout check and sleep/wait.
9242 break;
9243 }
9244
9245 /* no blocking clients. move the read pointer and again check for free space */
9246
9247 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9248
9249 if (!moved) {
9250 cm_msg(MERROR, "bm_wait_for_free_space",
9251 "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",
9252 pheader->name,
9253 pheader->read_pointer,
9254 pheader->write_pointer,
9255 pheader->size,
9256 free,
9258 return BM_CORRUPTED;
9259 }
9260
9261 /* we freed one event, loop back to the check for free space */
9262 }
9263
9264 //blocking_loops++;
9265
9266 /* at least one client is blocking */
9267
9269 pc->write_wait = requested_space;
9270
9271 if (pbuf->wait_start_time == 0) {
9272 pbuf->wait_start_time = ss_millitime();
9273 pbuf->count_write_wait++;
9274 if (requested_space > pbuf->max_requested_space)
9275 pbuf->max_requested_space = requested_space;
9276 pbuf->wait_client_index = blocking_client_index;
9277 }
9278
9280
9281 //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);
9282
9283 int sleep_time_msec = 1000;
9284
9285 if (timeout_msec == BM_WAIT) {
9286 // wait forever
9287 } else if (timeout_msec == BM_NO_WAIT) {
9288 // no wait
9289 return BM_ASYNC_RETURN;
9290 } else {
9291 // check timeout
9292 if (now >= time_end) {
9293 // timeout!
9294 return BM_ASYNC_RETURN;
9295 }
9296
9298
9299 if (sleep_time_msec <= 0) {
9300 sleep_time_msec = 10;
9301 } else if (sleep_time_msec > 1000) {
9302 sleep_time_msec = 1000;
9303 }
9304 }
9305
9307
9308 /* before waiting, unlock everything in the correct order */
9309
9310 pbuf_guard.unlock();
9311
9313 pbuf->write_cache_mutex.unlock();
9314
9315 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9316
9317#ifdef DEBUG_MSG
9318 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9319#endif
9320
9322 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9323 //if (idx >= 0)
9324 // pheader->client[idx].write_wait = requested_space;
9325
9326 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9327
9329
9330 /* we are told to shutdown */
9331 if (status == SS_ABORT) {
9332 // NB: buffer is locked!
9333 return SS_ABORT;
9334 }
9335
9336 /* make sure we do sleep in this loop:
9337 * if we are the mserver receiving data on the event
9338 * socket and the data buffer is full, ss_suspend() will
9339 * never sleep: it will detect data on the event channel,
9340 * call rpc_server_receive() (recursively, we already *are* in
9341 * rpc_server_receive()) and return without sleeping. Result
9342 * is a busy loop waiting for free space in data buffer */
9343
9344 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9345 * the event socket, and should sleep now, so this sleep below
9346 * maybe is not needed now. but for safety, I keep it. K.O. */
9347
9348 if (status != SS_TIMEOUT) {
9349 //printf("ss_suspend: status %d\n", status);
9350 ss_sleep(1);
9351 }
9352
9353 /* we may be stuck in this loop for an arbitrary long time,
9354 * depending on how other buffer clients read the accumulated data
9355 * so we should update all the timeouts & etc. K.O. */
9356
9358
9359 /* lock things again in the correct order */
9360
9361 if (unlock_write_cache) {
9363
9364 if (status != BM_SUCCESS) {
9365 // bail out with all locks released
9366 return status;
9367 }
9368 }
9369
9370 if (!pbuf_guard.relock()) {
9371 if (unlock_write_cache) {
9372 pbuf->write_cache_mutex.unlock();
9373 }
9374
9375 // bail out with all locks released
9376 return pbuf_guard.get_status();
9377 }
9378
9379 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9381
9382 pc->write_wait = 0;
9383
9385 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9386 //if (idx >= 0)
9387 // pheader->client[idx].write_wait = 0;
9388 //else {
9389 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9390 // status = SS_ABORT;
9391 //}
9392
9393#ifdef DEBUG_MSG
9394 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);
9395#endif
9396
9397 }
9398}
9399
9401{
9402 BUFFER* pbuf = pbuf_guard.get_pbuf();
9403 BUFFER_HEADER* pheader = pbuf->buffer_header;
9404
9405 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9406
9407 if (pc->read_pointer != pheader->write_pointer) {
9408 // buffer has data
9409 return BM_SUCCESS;
9410 }
9411
9412 if (timeout_msec == BM_NO_WAIT) {
9413 /* event buffer is empty and we are told to not wait */
9414 if (!pc->read_wait) {
9415 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9416 pc->read_wait = TRUE;
9417 }
9418 return BM_ASYNC_RETURN;
9419 }
9420
9423 DWORD sleep_time = 1000;
9424 if (timeout_msec == BM_NO_WAIT) {
9425 // default sleep time
9426 } else if (timeout_msec == BM_WAIT) {
9427 // default sleep time
9428 } else {
9431 }
9432
9433 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9434
9435 while (pc->read_pointer == pheader->write_pointer) {
9436 /* wait until there is data in the buffer (write pointer moves) */
9437
9438 if (!pc->read_wait) {
9439 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9440 pc->read_wait = TRUE;
9441 }
9442
9443 pc->last_activity = ss_millitime();
9444
9446
9447 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9448
9449 pbuf_guard.unlock();
9450
9452 pbuf->read_cache_mutex.unlock();
9453
9455
9456 if (timeout_msec == BM_NO_WAIT) {
9457 // return immediately
9458 } else if (timeout_msec == BM_WAIT) {
9459 // wait forever
9460 } else {
9462 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9463 if (now >= time_wait) {
9464 timeout_msec = BM_NO_WAIT; // cause immediate return
9465 } else {
9467 if (sleep_time > 1000)
9468 sleep_time = 1000;
9469 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9470 }
9471 }
9472
9473 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9474
9475 if (unlock_read_cache) {
9477 if (status != BM_SUCCESS) {
9478 // bail out with all locks released
9479 return status;
9480 }
9481 }
9482
9483 if (!pbuf_guard.relock()) {
9484 if (unlock_read_cache) {
9485 pbuf->read_cache_mutex.unlock();
9486 }
9487 // bail out with all locks released
9488 return pbuf_guard.get_status();
9489 }
9490
9491 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9492 * because we may have been removed from the buffer by bm_cleanup() & co
9493 * due to a timeout or whatever. */
9495
9496 /* return if TCP connection broken */
9497 if (status == SS_ABORT)
9498 return SS_ABORT;
9499
9500 if (timeout_msec == BM_NO_WAIT)
9501 return BM_ASYNC_RETURN;
9502 }
9503
9504 if (pc->read_wait) {
9505 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9506 pc->read_wait = FALSE;
9507 }
9508
9509 return BM_SUCCESS;
9510}
9511
9512static 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)
9513{
9514 char *pdata = (char *) (pheader + 1);
9515
9516 //int old_write_pointer = pheader->write_pointer;
9517
9518 /* new event fits into the remaining space? */
9519 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9520 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9521 char* wptr = pdata + pheader->write_pointer;
9522 for (int i=0; i<sg_n; i++) {
9523 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9524 memcpy(wptr, sg_ptr[i], sg_len[i]);
9525 wptr += sg_len[i];
9526 }
9527 pheader->write_pointer = pheader->write_pointer + total_size;
9528 assert(pheader->write_pointer <= pheader->size);
9529 /* remaining space is smaller than size of an event header? */
9530 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9531 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9532 // equal to the event header size, we will write the next event header here,
9533 // then wrap the pointer and write the event data at the beginning of the buffer.
9534 //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);
9535 pheader->write_pointer = 0;
9536 }
9537 } else {
9538 /* split event */
9539 size_t size = pheader->size - pheader->write_pointer;
9540
9541 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9542
9543 //memcpy(pdata + pheader->write_pointer, pevent, size);
9544 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9545
9546 char* wptr = pdata + pheader->write_pointer;
9547 size_t count = 0;
9548
9549 // copy first part
9550
9551 int i = 0;
9552 for (; i<sg_n; i++) {
9553 if (count + sg_len[i] > size)
9554 break;
9555 memcpy(wptr, sg_ptr[i], sg_len[i]);
9556 wptr += sg_len[i];
9557 count += sg_len[i];
9558 }
9559
9560 //printf("wptr %d, count %d\n", wptr-pdata, count);
9561
9562 // split segment
9563
9564 size_t first = size - count;
9565 size_t second = sg_len[i] - first;
9566 assert(first + second == sg_len[i]);
9567 assert(count + first == size);
9568
9569 //printf("first %d, second %d\n", first, second);
9570
9571 memcpy(wptr, sg_ptr[i], first);
9572 wptr = pdata + 0;
9573 count += first;
9575 wptr += second;
9576 count += second;
9577 i++;
9578
9579 // copy remaining
9580
9581 for (; i<sg_n; i++) {
9582 memcpy(wptr, sg_ptr[i], sg_len[i]);
9583 wptr += sg_len[i];
9584 count += sg_len[i];
9585 }
9586
9587 //printf("wptr %d, count %d\n", wptr-pdata, count);
9588
9589 //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);
9590
9591 pheader->write_pointer = total_size - size;
9592 }
9593
9594 //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);
9595}
9596
9598 if (pc->pid) {
9599 int j;
9600 for (j = 0; j < pc->max_request_index; j++) {
9601 const EVENT_REQUEST *prequest = pc->event_request + j;
9602 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9603 return prequest->id;
9604 }
9605 }
9606 }
9607
9608 return -1;
9609}
9610
9611static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9612 if (request_id >= 0) {
9613 /* if that client has a request and is suspended, wake it up */
9614 if (pc->read_wait) {
9615 char str[80];
9616 sprintf(str, "B %s %d", pheader->name, request_id);
9617 ss_resume(pc->port, str);
9618 //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);
9619 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9620 pc->read_wait = FALSE;
9621 }
9622 }
9623}
9624
9625#endif // LOCAL_ROUTINES
9626
9627#if 0
9628INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9629{
9630 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9631
9634
9636
9637 while (1) {
9638 if (timeout_msec == BM_WAIT) {
9639 xtimeout_msec = 1000;
9640 } else if (timeout_msec == BM_NO_WAIT) {
9642 } else {
9643 if (xtimeout_msec > 1000) {
9644 xtimeout_msec = 1000;
9645 }
9646 }
9647
9648 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9649
9650 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9651
9652 if (status == BM_ASYNC_RETURN) {
9653 if (timeout_msec == BM_WAIT) {
9654 // BM_WAIT means wait forever
9655 continue;
9656 } else if (timeout_msec == BM_NO_WAIT) {
9657 // BM_NO_WAIT means do not wait
9658 return status;
9659 } else {
9661 if (now >= time_end) {
9662 // timeout, return BM_ASYNC_RETURN
9663 return status;
9664 }
9665
9667
9668 if (remain < xtimeout_msec) {
9670 }
9671
9672 // keep asking for event...
9673 continue;
9674 }
9675 } else if (status == BM_SUCCESS) {
9676 // success, return BM_SUCCESS
9677 return status;
9678 } else {
9679 // error
9680 return status;
9681 }
9682 }
9683}
9684#endif
9685
9686INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9687{
9688 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9689 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9690
9691 if (data_size == 0) {
9692 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9693 return BM_INVALID_SIZE;
9694 }
9695
9696 if (data_size > MAX_DATA_SIZE) {
9697 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);
9698 return BM_INVALID_SIZE;
9699 }
9700
9701 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9702
9703 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9704
9705 if (rpc_is_remote()) {
9706 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9707 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9708 } else {
9709 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9710 }
9711}
9712
9713int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9714{
9715 const char* cptr = event.data();
9716 size_t clen = event.size();
9717 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9718}
9719
9720int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9721{
9722 int sg_n = event.size();
9723 const char* sg_ptr[sg_n];
9724 size_t sg_len[sg_n];
9725 for (int i=0; i<sg_n; i++) {
9726 sg_ptr[i] = event[i].data();
9727 sg_len[i] = event[i].size();
9728 }
9729 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9730}
9731
9732#ifdef LOCAL_ROUTINES
9734#endif
9735
9736/********************************************************************/
9786int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9787{
9788 if (rpc_is_remote())
9789 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9790
9791 if (sg_n < 1) {
9792 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9793 return BM_INVALID_SIZE;
9794 }
9795
9796 if (sg_ptr[0] == NULL) {
9797 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9798 return BM_INVALID_SIZE;
9799 }
9800
9801 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9802 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));
9803 return BM_INVALID_SIZE;
9804 }
9805
9806 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9807
9808 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9809 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9810
9811 if (data_size == 0) {
9812 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9813 return BM_INVALID_SIZE;
9814 }
9815
9816 if (data_size > MAX_DATA_SIZE) {
9817 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);
9818 return BM_INVALID_SIZE;
9819 }
9820
9821 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9822
9823 size_t count = 0;
9824 for (int i=0; i<sg_n; i++) {
9825 count += sg_len[i];
9826 }
9827
9828 if (count != event_size) {
9829 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);
9830 return BM_INVALID_SIZE;
9831 }
9832
9833 //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);
9834
9835#ifdef LOCAL_ROUTINES
9836 {
9837 int status = 0;
9838 const size_t total_size = ALIGN8(event_size);
9839
9840 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9841
9842 if (!pbuf)
9843 return status;
9844
9845 /* round up total_size to next DWORD boundary */
9846 //int total_size = ALIGN8(event_size);
9847
9848 /* check if write cache is enabled */
9849 if (pbuf->write_cache_size) {
9851
9852 if (status != BM_SUCCESS)
9853 return status;
9854
9855 /* check if write cache is enabled */
9856 if (pbuf->write_cache_size) {
9857 size_t max_event_size = pbuf->write_cache_size/MAX_WRITE_CACHE_EVENT_SIZE_DIV;
9859
9860 //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);
9861
9862 /* if this event does not fit into the write cache, flush the write cache */
9863 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9864 //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);
9865
9867
9868 if (!pbuf_guard.is_locked()) {
9869 pbuf->write_cache_mutex.unlock();
9870 return pbuf_guard.get_status();
9871 }
9872
9874
9875 if (pbuf_guard.is_locked()) {
9876 // check if bm_wait_for_free_space() failed to relock the buffer
9877 pbuf_guard.unlock();
9878 }
9879
9880 if (status != BM_SUCCESS) {
9881 pbuf->write_cache_mutex.unlock();
9882 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9883 if (status == BM_NO_MEMORY)
9884 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9885 return status;
9886 }
9887
9888 // write cache must be empty here
9889 assert(pbuf->write_cache_wp == 0);
9890 }
9891
9892 /* write this event into the write cache, if it is not too big and if it fits */
9893 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9894 //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);
9895
9896 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9897
9898 for (int i=0; i<sg_n; i++) {
9899 memcpy(wptr, sg_ptr[i], sg_len[i]);
9900 wptr += sg_len[i];
9901 }
9902
9903 pbuf->write_cache_wp += total_size;
9904
9905 pbuf->write_cache_mutex.unlock();
9906 return BM_SUCCESS;
9907 }
9908 }
9909
9910 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9911 pbuf->write_cache_mutex.unlock();
9912 }
9913
9914 /* we come here only for events that are too big to fit into the cache */
9915
9916 /* lock the buffer */
9918
9919 if (!pbuf_guard.is_locked()) {
9920 return pbuf_guard.get_status();
9921 }
9922
9923 /* calculate some shorthands */
9924 BUFFER_HEADER *pheader = pbuf->buffer_header;
9925
9926#if 0
9928 if (status != BM_SUCCESS) {
9929 printf("bm_send_event: corrupted 111!\n");
9930 abort();
9931 }
9932#endif
9933
9934 /* check if buffer is large enough */
9935 if (total_size >= (size_t)pheader->size) {
9936 pbuf_guard.unlock(); // unlock before cm_msg()
9937 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9938 return BM_NO_MEMORY;
9939 }
9940
9942
9943 if (status != BM_SUCCESS) {
9944 // implicit unlock
9945 return status;
9946 }
9947
9948#if 0
9950 if (status != BM_SUCCESS) {
9951 printf("bm_send_event: corrupted 222!\n");
9952 abort();
9953 }
9954#endif
9955
9956 int old_write_pointer = pheader->write_pointer;
9957
9958 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9959
9960 /* write pointer was incremented, but there should
9961 * always be some free space in the buffer and the
9962 * write pointer should never cacth up to the read pointer:
9963 * the rest of the code gets confused this happens (buffer 100% full)
9964 * as it is write_pointer == read_pointer can be either
9965 * 100% full or 100% empty. My solution: never fill
9966 * the buffer to 100% */
9967 assert(pheader->write_pointer != pheader->read_pointer);
9968
9969 /* send wake up messages to all clients that want this event */
9970 int i;
9971 for (i = 0; i < pheader->max_client_index; i++) {
9972 BUFFER_CLIENT *pc = pheader->client + i;
9973 int request_id = bm_find_first_request_locked(pc, pevent);
9974 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9975 }
9976
9977#if 0
9979 if (status != BM_SUCCESS) {
9980 printf("bm_send_event: corrupted 333!\n");
9981 abort();
9982 }
9983#endif
9984
9985 /* update statistics */
9986 pheader->num_in_events++;
9987 pbuf->count_sent += 1;
9988 pbuf->bytes_sent += total_size;
9989 }
9990#endif /* LOCAL_ROUTINES */
9991
9992 return BM_SUCCESS;
9993}
9994
9995static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
9996{
9997 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
9998
10001
10003
10004 while (1) {
10005 if (timeout_msec == BM_WAIT) {
10006 xtimeout_msec = 1000;
10007 } else if (timeout_msec == BM_NO_WAIT) {
10009 } else {
10010 if (xtimeout_msec > 1000) {
10011 xtimeout_msec = 1000;
10012 }
10013 }
10014
10015 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10016
10017 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10018
10019 if (status == BM_ASYNC_RETURN) {
10020 if (timeout_msec == BM_WAIT) {
10021 // BM_WAIT means wait forever
10022 continue;
10023 } else if (timeout_msec == BM_NO_WAIT) {
10024 // BM_NO_WAIT means do not wait
10025 return status;
10026 } else {
10028 if (now >= time_end) {
10029 // timeout, return BM_ASYNC_RETURN
10030 return status;
10031 }
10032
10034
10035 if (remain < (DWORD)xtimeout_msec) {
10037 }
10038
10039 // keep asking for event...
10040 continue;
10041 }
10042 } else if (status == BM_SUCCESS) {
10043 // success, return BM_SUCCESS
10044 return status;
10045 } else {
10046 // error
10047 return status;
10048 }
10049 }
10050}
10051
10052/********************************************************************/
10070#ifdef LOCAL_ROUTINES
10072{
10073 // NB we come here with write cache locked and buffer locked.
10074
10075 {
10076 INT status = 0;
10077
10078 //printf("bm_flush_cache_locked!\n");
10079
10080 BUFFER* pbuf = pbuf_guard.get_pbuf();
10081 BUFFER_HEADER* pheader = pbuf->buffer_header;
10082
10083 //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);
10084
10085 int old_write_pointer = pheader->write_pointer;
10086
10087 int request_id[MAX_CLIENTS];
10088 for (int i = 0; i < pheader->max_client_index; i++) {
10089 request_id[i] = -1;
10090 }
10091
10092 size_t ask_rp = pbuf->write_cache_rp;
10093 size_t ask_wp = pbuf->write_cache_wp;
10094
10095 if (ask_wp == 0) { // nothing to do
10096 return BM_SUCCESS;
10097 }
10098
10099 if (ask_rp == ask_wp) { // nothing to do
10100 return BM_SUCCESS;
10101 }
10102
10103 assert(ask_rp < ask_wp);
10104
10105 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10106
10107 if (ask_free == 0) { // nothing to do
10108 return BM_SUCCESS;
10109 }
10110
10111#if 0
10113 if (status != BM_SUCCESS) {
10114 printf("bm_flush_cache: corrupted 111!\n");
10115 abort();
10116 }
10117#endif
10118
10120
10121 if (status != BM_SUCCESS) {
10122 return status;
10123 }
10124
10125 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10126 //
10127 // wait_for_free_space() will sleep with all locks released,
10128 // during this time, another thread may call bm_send_event() that will
10129 // add one or more events to the write cache and after wait_for_free_space()
10130 // returns, size of data in cache will be bigger than the amount
10131 // of free space we requested. so we need to keep track of how
10132 // much data we write to the buffer and ask for more data
10133 // if we run short. This is the reason for the big loop
10134 // around wait_for_free_space(). We ask for slightly too little free
10135 // space to make sure all this code is always used and does work. K.O.
10136
10137 if (pbuf->write_cache_wp == 0) {
10138 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10139 return BM_SUCCESS;
10140 }
10141
10142 //size_t written = 0;
10143 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10144 /* loop over all events in cache */
10145
10146 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10147 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10148 size_t total_size = ALIGN8(event_size);
10149
10150#if 0
10151 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",
10152 int(pbuf->write_cache_size),
10153 int(pbuf->write_cache_wp),
10154 int(pbuf->write_cache_rp),
10155 int(pevent->data_size),
10156 int(event_size),
10157 int(total_size),
10158 int(ask_free),
10159 int(written));
10160#endif
10161
10162 // check for crazy event size
10163 assert(total_size >= sizeof(EVENT_HEADER));
10164 assert(total_size <= (size_t)pheader->size);
10165
10166 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10167
10168 /* update statistics */
10169 pheader->num_in_events++;
10170 pbuf->count_sent += 1;
10171 pbuf->bytes_sent += total_size;
10172
10173 /* see comment for the same code in bm_send_event().
10174 * We make sure the buffer is never 100% full */
10175 assert(pheader->write_pointer != pheader->read_pointer);
10176
10177 /* check if anybody has a request for this event */
10178 for (int i = 0; i < pheader->max_client_index; i++) {
10179 BUFFER_CLIENT *pc = pheader->client + i;
10180 int r = bm_find_first_request_locked(pc, pevent);
10181 if (r >= 0) {
10182 request_id[i] = r;
10183 }
10184 }
10185
10186 /* this loop does not loop forever because rp
10187 * is monotonously incremented here. write_cache_wp does
10188 * not change */
10189
10190 pbuf->write_cache_rp += total_size;
10191 //written += total_size;
10192
10193 assert(pbuf->write_cache_rp > 0);
10194 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10195 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10196 }
10197
10198 /* the write cache is now empty */
10199 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10200 pbuf->write_cache_wp = 0;
10201 pbuf->write_cache_rp = 0;
10202
10203 /* check which clients are waiting */
10204 for (int i = 0; i < pheader->max_client_index; i++) {
10205 BUFFER_CLIENT *pc = pheader->client + i;
10206 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10207 }
10208 }
10209
10210 return BM_SUCCESS;
10211}
10212
10213#endif /* LOCAL_ROUTINES */
10214
10215INT bm_flush_cache(int buffer_handle, int timeout_msec)
10216{
10217 if (rpc_is_remote()) {
10218 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10219 }
10220
10221#ifdef LOCAL_ROUTINES
10222 {
10223 INT status = 0;
10224
10225 //printf("bm_flush_cache!\n");
10226
10227 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10228
10229 if (!pbuf)
10230 return status;
10231
10232 if (pbuf->write_cache_size == 0)
10233 return BM_SUCCESS;
10234
10236
10237 if (status != BM_SUCCESS)
10238 return status;
10239
10240 /* check if anything needs to be flushed */
10241 if (pbuf->write_cache_wp == 0) {
10242 pbuf->write_cache_mutex.unlock();
10243 return BM_SUCCESS;
10244 }
10245
10246 /* lock the buffer */
10248
10249 if (!pbuf_guard.is_locked())
10250 return pbuf_guard.get_status();
10251
10253
10254 /* unlock in correct order */
10255
10256 if (pbuf_guard.is_locked()) {
10257 // check if bm_wait_for_free_space() failed to relock the buffer
10258 pbuf_guard.unlock();
10259 }
10260
10261 pbuf->write_cache_mutex.unlock();
10262
10263 return status;
10264 }
10265#endif /* LOCAL_ROUTINES */
10266
10267 return BM_SUCCESS;
10268}
10269
10270#ifdef LOCAL_ROUTINES
10271
10272static 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) {
10274
10275 int max_size = 0;
10276 if (buf_size) {
10277 max_size = *buf_size;
10278 *buf_size = 0;
10279 }
10280
10281 //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);
10282
10283 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10284
10285 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10286
10287 /* look if there is anything in the cache */
10288 if (pbuf->read_cache_size > 0) {
10289
10291
10292 if (status != BM_SUCCESS)
10293 return status;
10294
10295 if (pbuf->read_cache_wp == 0) {
10296
10297 // lock buffer for the first time
10298
10299 if (!pbuf_guard.relock()) {
10300 pbuf->read_cache_mutex.unlock();
10301 return pbuf_guard.get_status();
10302 }
10303
10305 if (status != BM_SUCCESS) {
10306 // unlock in correct order
10307 if (pbuf_guard.is_locked()) {
10308 // check if bm_wait_for_more_events() failed to relock the buffer
10309 pbuf_guard.unlock();
10310 }
10311 pbuf->read_cache_mutex.unlock();
10312 return status;
10313 }
10314
10315 // buffer remains locked here
10316 }
10317 EVENT_HEADER *pevent;
10318 int event_size;
10319 int total_size;
10320 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10321 if (pbuf_guard.is_locked()) {
10322 // do not need to keep the event buffer locked
10323 // when reading from the read cache
10324 pbuf_guard.unlock();
10325 }
10326 //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);
10328 if (buf) {
10329 if (event_size > max_size) {
10330 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);
10333 }
10334
10335 memcpy(buf, pevent, event_size);
10336
10337 if (buf_size) {
10338 *buf_size = event_size;
10339 }
10340 if (convert_flags) {
10341 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10342 }
10343 } else if (bufptr) {
10345 memcpy(*bufptr, pevent, event_size);
10347 } else if (vecptr) {
10348 vecptr->resize(0);
10349 char* cptr = (char*)pevent;
10350 vecptr->assign(cptr, cptr+event_size);
10351 }
10352 bm_incr_read_cache_locked(pbuf, total_size);
10353 pbuf->read_cache_mutex.unlock();
10354 if (dispatch) {
10355 // FIXME need to protect currently dispatched event against
10356 // another thread overwriting it by refilling the read cache
10357 bm_dispatch_event(buffer_handle, pevent);
10358 return BM_MORE_EVENTS;
10359 }
10360 // buffer is unlocked here
10361 return status;
10362 }
10363 pbuf->read_cache_mutex.unlock();
10364 }
10365
10366 /* we come here if the read cache is disabled */
10367 /* we come here if the next event is too big to fit into the read cache */
10368
10369 if (!pbuf_guard.is_locked()) {
10370 if (!pbuf_guard.relock())
10371 return pbuf_guard.get_status();
10372 }
10373
10375
10376 BUFFER_HEADER *pheader = pbuf->buffer_header;
10377
10379
10380 while (1) {
10381 /* loop over events in the event buffer */
10382
10384
10385 if (status != BM_SUCCESS) {
10386 // implicit unlock
10387 return status;
10388 }
10389
10390 /* check if event at current read pointer matches a request */
10391
10392 EVENT_HEADER *pevent;
10393 int event_size;
10394 int total_size;
10395
10396 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10397 if (status == BM_CORRUPTED) {
10398 // implicit unlock
10399 return status;
10400 } else if (status != BM_SUCCESS) {
10401 /* event buffer is empty */
10402 break;
10403 }
10404
10406
10407 if (is_requested) {
10408 //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);
10409
10411
10412 if (buf) {
10413 if (event_size > max_size) {
10414 cm_msg(MERROR, "bm_read_buffer",
10415 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10416 event_size, pheader->name);
10419 }
10420
10421 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10422
10423 if (buf_size) {
10424 *buf_size = event_size;
10425 }
10426
10427 if (convert_flags) {
10428 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10429 }
10430
10431 pbuf->count_read++;
10432 pbuf->bytes_read += event_size;
10433 } else if (dispatch || bufptr) {
10434 assert(event_buffer == NULL); // make sure we only come here once
10437 pbuf->count_read++;
10438 pbuf->bytes_read += event_size;
10439 } else if (vecptr) {
10441 pbuf->count_read++;
10442 pbuf->bytes_read += event_size;
10443 }
10444
10445 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10446 pc->read_pointer = new_read_pointer;
10447
10448 pheader->num_out_events++;
10449 /* exit loop over events */
10450 break;
10451 }
10452
10453 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10454 pc->read_pointer = new_read_pointer;
10455 pheader->num_out_events++;
10456 }
10457
10458 /*
10459 If read pointer has been changed, it may have freed up some space
10460 for waiting producers. So check if free space is now more than 50%
10461 of the buffer size and wake waiting producers.
10462 */
10463
10465
10466 pbuf_guard.unlock();
10467
10468 if (dispatch && event_buffer) {
10469 bm_dispatch_event(buffer_handle, event_buffer);
10470 free(event_buffer);
10472 return BM_MORE_EVENTS;
10473 }
10474
10475 if (bufptr && event_buffer) {
10479 }
10480
10481 if (event_buffer) {
10482 free(event_buffer);
10484 }
10485
10486 return status;
10487}
10488
10489#endif
10490
10491static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10492{
10493 //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);
10494
10495 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10496
10497 void *xbuf = NULL;
10498 int xbuf_size = 0;
10499
10500 if (buf) {
10501 xbuf = buf;
10502 xbuf_size = *buf_size;
10503 } else if (ppevent) {
10506 } else if (pvec) {
10507 pvec->resize(_bm_max_event_size);
10508 xbuf = pvec->data();
10509 xbuf_size = pvec->size();
10510 } else {
10511 assert(!"incorrect call to bm_receivent_event_rpc()");
10512 }
10513
10514 int status;
10517
10519
10520 int zbuf_size = xbuf_size;
10521
10522 while (1) {
10523 if (timeout_msec == BM_WAIT) {
10524 xtimeout_msec = 1000;
10525 } else if (timeout_msec == BM_NO_WAIT) {
10527 } else {
10528 if (xtimeout_msec > 1000) {
10529 xtimeout_msec = 1000;
10530 }
10531 }
10532
10534
10536
10537 //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);
10538
10539 if (status == BM_ASYNC_RETURN) {
10540 if (timeout_msec == BM_WAIT) {
10541 // BM_WAIT means wait forever
10542 continue;
10543 } else if (timeout_msec == BM_NO_WAIT) {
10544 // BM_NO_WAIT means do not wait
10545 break;
10546 } else {
10548 if (now >= time_end) {
10549 // timeout, return BM_ASYNC_RETURN
10550 break;
10551 }
10552
10554
10555 if (remain < (DWORD)xtimeout_msec) {
10557 }
10558
10559 // keep asking for event...
10560 continue;
10561 }
10562 } else if (status == BM_SUCCESS) {
10563 // success, return BM_SUCCESS
10564 break;
10565 }
10566
10567 // RPC error
10568
10569 if (buf) {
10570 *buf_size = 0;
10571 } else if (ppevent) {
10572 free(*ppevent);
10573 *ppevent = NULL;
10574 } else if (pvec) {
10575 pvec->resize(0);
10576 } else {
10577 assert(!"incorrect call to bm_receivent_event_rpc()");
10578 }
10579
10580 return status;
10581 }
10582
10583 // status is BM_SUCCESS or BM_ASYNC_RETURN
10584
10585 if (buf) {
10586 *buf_size = zbuf_size;
10587 } else if (ppevent) {
10588 // nothing to do
10589 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10590 } else if (pvec) {
10591 pvec->resize(zbuf_size);
10592 } else {
10593 assert(!"incorrect call to bm_receivent_event_rpc()");
10594 }
10595
10596 return status;
10597}
10598
10599/********************************************************************/
10658INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10659 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10660 if (rpc_is_remote()) {
10661 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10662 }
10663#ifdef LOCAL_ROUTINES
10664 {
10666
10667 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10668
10669 if (!pbuf)
10670 return status;
10671
10672 int convert_flags = rpc_get_convert_flags();
10673
10674 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10675 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10676 return status;
10677 }
10678#else /* LOCAL_ROUTINES */
10679
10680 return BM_SUCCESS;
10681#endif
10682}
10683
10684/********************************************************************/
10740 if (rpc_is_remote()) {
10741 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10742 }
10743#ifdef LOCAL_ROUTINES
10744 {
10746
10747 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10748
10749 if (!pbuf)
10750 return status;
10751
10752 int convert_flags = rpc_get_convert_flags();
10753
10754 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10755 }
10756#else /* LOCAL_ROUTINES */
10757
10758 return BM_SUCCESS;
10759#endif
10760}
10761
10762/********************************************************************/
10817INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10818 if (rpc_is_remote()) {
10819 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10820 }
10821#ifdef LOCAL_ROUTINES
10822 {
10824
10825 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10826
10827 if (!pbuf)
10828 return status;
10829
10830 int convert_flags = rpc_get_convert_flags();
10831
10832 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10833 }
10834#else /* LOCAL_ROUTINES */
10835 return BM_SUCCESS;
10836#endif
10837}
10838
10839#ifdef LOCAL_ROUTINES
10840
10842{
10843 /* clear read cache */
10844 if (pbuf->read_cache_size > 0) {
10845
10847
10848 if (status != BM_SUCCESS)
10849 return status;
10850
10851 pbuf->read_cache_rp = 0;
10852 pbuf->read_cache_wp = 0;
10853
10854 pbuf->read_cache_mutex.unlock();
10855 }
10856
10858
10859 if (!pbuf_guard.is_locked())
10860 return pbuf_guard.get_status();
10861
10862 BUFFER_HEADER *pheader = pbuf->buffer_header;
10863
10864 /* forward read pointer to global write pointer */
10866 pclient->read_pointer = pheader->write_pointer;
10867
10868 return BM_SUCCESS;
10869}
10870
10871#endif /* LOCAL_ROUTINES */
10872
10873/********************************************************************/
10882INT bm_skip_event(INT buffer_handle) {
10883 if (rpc_is_remote())
10884 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
10885
10886#ifdef LOCAL_ROUTINES
10887 {
10888 int status = 0;
10889
10890 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
10891
10892 if (!pbuf)
10893 return status;
10894
10895 return bm_skip_event(pbuf);
10896 }
10897#endif
10898
10899 return BM_SUCCESS;
10900}
10901
10902#ifdef LOCAL_ROUTINES
10903/********************************************************************/
10910static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
10911 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
10912
10913 /* return immediately if no callback routine is defined */
10914 if (!pbuf->callback)
10915 return BM_SUCCESS;
10916
10917 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
10918}
10919
10920/********************************************************************/
10926static INT bm_push_event(const char *buffer_name)
10927{
10928 std::vector<BUFFER*> mybuffers;
10929
10930 gBuffersMutex.lock();
10932 gBuffersMutex.unlock();
10933
10934 for (size_t i = 0; i < mybuffers.size(); i++) {
10935 BUFFER *pbuf = mybuffers[i];
10936 if (!pbuf || !pbuf->attached)
10937 continue;
10938 // FIXME: unlocked read access to pbuf->buffer_name!
10939 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
10940 return bm_push_buffer(pbuf, i + 1);
10941 }
10942 }
10943
10944 return BM_INVALID_HANDLE;
10945}
10946
10947#else
10948
10949static INT bm_push_event(const char *buffer_name)
10950{
10951 return BM_SUCCESS;
10952}
10953
10954#endif /* LOCAL_ROUTINES */
10955
10956/********************************************************************/
10963#ifdef LOCAL_ROUTINES
10964 {
10965 INT status = 0;
10966 BOOL bMore;
10967 DWORD start_time;
10968 //static DWORD last_time = 0;
10969
10970 /* if running as a server, buffer checking is done by client
10971 via ASYNC bm_receive_event */
10972 if (rpc_is_mserver()) {
10973 return FALSE;
10974 }
10975
10976 bMore = FALSE;
10977 start_time = ss_millitime();
10978
10979 std::vector<BUFFER*> mybuffers;
10980
10981 gBuffersMutex.lock();
10983 gBuffersMutex.unlock();
10984
10985 /* go through all buffers */
10986 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
10988
10989 if (!pbuf || !pbuf->attached)
10990 continue;
10991
10992 //int count_loops = 0;
10993 while (1) {
10994 if (pbuf->attached) {
10995 /* one bm_push_event could cause a run stop and a buffer close, which
10996 * would crash the next call to bm_push_event(). So check for valid
10997 * buffer on each call */
10998
10999 /* this is what happens:
11000 * bm_push_buffer() may call a user callback function
11001 * user callback function may indirectly call bm_close() of this buffer,
11002 * i.e. if it stops the run,
11003 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11004 * here we will see pbuf->attched is false and quit this loop
11005 */
11006
11007 status = bm_push_buffer(pbuf, idx + 1);
11008
11009 if (status == BM_CORRUPTED) {
11010 return status;
11011 }
11012
11013 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11014
11015 if (status != BM_MORE_EVENTS) {
11016 //DWORD t = ss_millitime() - start_time;
11017 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11018 break;
11019 }
11020
11021 // count_loops++;
11022 }
11023
11024 // NB: this code has a logic error: if 2 buffers always have data,
11025 // this timeout will cause us to exit reading the 1st buffer
11026 // after 1000 msec, then we read the 2nd buffer exactly once,
11027 // and exit the loop because the timeout is still active -
11028 // we did not reset "start_time" when we started reading
11029 // from the 2nd buffer. Result is that we always read all
11030 // the data in a loop from the 1st buffer, but read just
11031 // one event from the 2nd buffer, resulting in severe unfairness.
11032
11033 /* stop after one second */
11034 DWORD t = ss_millitime() - start_time;
11035 if (t > 1000) {
11036 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11037 bMore = TRUE;
11038 break;
11039 }
11040 }
11041 }
11042
11043 //last_time = start_time;
11044
11045 return bMore;
11046
11047 }
11048#else /* LOCAL_ROUTINES */
11049
11050 return FALSE;
11051
11052#endif
11053}
11054
11055/********************************************************************/
11057/********************************************************************\
11058
11059 Routine: bm_notify_client
11060
11061 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11062 the connected client. Used by mserver to relay the BM_MSG
11063 buffer message from local UDP socket to the remote connected client.
11064
11065 Input:
11066 char *buffer_name Name of buffer
11067 int client_socket Network socket to client
11068
11069 Output:
11070 none
11071
11072 Function value:
11073 BM_SUCCESS Successful completion
11074
11075\********************************************************************/
11076{
11077 static DWORD last_time = 0;
11079
11080 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11081
11082 BUFFER* fbuf = NULL;
11083
11084 gBuffersMutex.lock();
11085
11086 for (size_t i = 0; i < gBuffers.size(); i++) {
11087 BUFFER* pbuf = gBuffers[i];
11088 if (!pbuf || !pbuf->attached)
11089 continue;
11090 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11091 fbuf = pbuf;
11092 break;
11093 }
11094 }
11095
11096 gBuffersMutex.unlock();
11097
11098 if (!fbuf)
11099 return BM_INVALID_HANDLE;
11100
11101 /* don't send notification if client has no callback defined
11102 to receive events -> client calls bm_receive_event manually */
11103 if (!fbuf->callback)
11104 return DB_SUCCESS;
11105
11106 int convert_flags = rpc_get_convert_flags();
11107
11108 /* only send notification once each 500ms */
11109 if (now - last_time < 500)
11110 return DB_SUCCESS;
11111
11112 last_time = now;
11113
11114 char buffer[32];
11115 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11116
11117 nc->header.routine_id = MSG_BM;
11118 nc->header.param_size = 0;
11119
11120 if (convert_flags) {
11123 }
11124
11125 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11126
11127 /* send the update notification to the client */
11128 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11129
11130 return BM_SUCCESS;
11131}
11132
11133/********************************************************************/
11135/********************************************************************\
11136
11137 Routine: bm_poll_event
11138
11139 Purpose: Poll an event from a remote server. Gets called by
11140 rpc_client_dispatch() and by cm_yield()
11141
11142 Function value:
11143 BM_SUCCESS At least one event was received and dispatched
11144 BM_ASYNC_RETURN No events received
11145 SS_ABORT Network connection broken
11146
11147\********************************************************************/
11148{
11150
11151 //printf("bm_poll_event!\n");
11152
11153 DWORD start_time = ss_millitime();
11154
11155 std::vector<char> vec;
11156
11157 /* loop over all requests */
11158 _request_list_mutex.lock();
11159 bool locked = true;
11160 size_t n = _request_list.size();
11161 for (size_t i = 0; i < n; i++) {
11162 if (!locked) {
11163 _request_list_mutex.lock();
11164 locked = true;
11165 }
11166 /* continue if no dispatcher set (manual bm_receive_event) */
11167 if (_request_list[i].dispatcher == NULL)
11168 continue;
11169
11170 int buffer_handle = _request_list[i].buffer_handle;
11171
11172 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11173 _request_list_mutex.unlock();
11174 locked = false;
11175
11176 do {
11177 /* receive event */
11178 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11179
11180 //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());
11181
11182 /* call user function if successful */
11183 if (status == BM_SUCCESS) {
11184 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11186 }
11187
11188 /* break if no more events */
11189 if (status == BM_ASYNC_RETURN)
11190 break;
11191
11192 /* break if corrupted event buffer */
11193 if (status == BM_TRUNCATED) {
11194 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());
11195 }
11196
11197 /* break if corrupted event buffer */
11198 if (status == BM_CORRUPTED)
11199 return SS_ABORT;
11200
11201 /* break if server died */
11202 if (status == RPC_NET_ERROR) {
11203 return SS_ABORT;
11204 }
11205
11206 /* stop after one second */
11207 if (ss_millitime() - start_time > 1000) {
11208 break;
11209 }
11210
11211 } while (TRUE);
11212 }
11213
11214 if (locked)
11215 _request_list_mutex.unlock();
11216
11218 return BM_SUCCESS;
11219 else
11220 return BM_ASYNC_RETURN;
11221}
11222
11223/********************************************************************/
11249 if (rpc_is_remote())
11251
11252#ifdef LOCAL_ROUTINES
11253 {
11254 std::vector<BUFFER*> mybuffers;
11255
11256 gBuffersMutex.lock();
11258 gBuffersMutex.unlock();
11259
11260 /* go through all buffers */
11261 for (BUFFER* pbuf : mybuffers) {
11262 if (!pbuf)
11263 continue;
11264 if (!pbuf->attached)
11265 continue;
11266
11267 int status = bm_skip_event(pbuf);
11268 if (status != BM_SUCCESS)
11269 return status;
11270 }
11271 }
11272#endif /* LOCAL_ROUTINES */
11273
11274 return BM_SUCCESS;
11275}
11276
11278#ifndef DOXYGEN_SHOULD_SKIP_THIS
11279
11280#define MAX_DEFRAG_EVENTS 10
11281
11288
11290
11291/********************************************************************/
11292static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11293 EVENT_HEADER *pevent, void *pdata,
11294 EVENT_HANDLER *dispatcher)
11295/********************************************************************\
11296
11297 Routine: bm_defragment_event
11298
11299 Purpose: Called internally from the event receiving routines
11300 bm_push_event and bm_poll_event to recombine event
11301 fragments and call the user callback routine upon
11302 completion.
11303
11304 Input:
11305 HNDLE buffer_handle Handle for the buffer containing event
11306 HNDLE request_id Handle for event request
11307 EVENT_HEADER *pevent Pointer to event header
11308 void *pata Pointer to event data
11309 dispatcher() User callback routine
11310
11311 Output:
11312 <calls dispatcher() after successfull recombination of event>
11313
11314 Function value:
11315 void
11316
11317\********************************************************************/
11318{
11319 INT i;
11320
11321 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11322 /*---- start new event ----*/
11323
11324 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11325
11326 /* check if fragments already stored */
11327 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11328 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11329 break;
11330
11331 if (i < MAX_DEFRAG_EVENTS) {
11332 free(defrag_buffer[i].pevent);
11335 cm_msg(MERROR, "bm_defragement_event",
11336 "Received new event with ID %d while old fragments were not completed",
11337 (pevent->event_id & 0x0FFF));
11338 }
11339
11340 /* search new slot */
11341 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11342 if (defrag_buffer[i].event_id == 0)
11343 break;
11344
11345 if (i == MAX_DEFRAG_EVENTS) {
11346 cm_msg(MERROR, "bm_defragment_event",
11347 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11348 return;
11349 }
11350
11351 /* check event size */
11352 if (pevent->data_size != sizeof(DWORD)) {
11353 cm_msg(MERROR, "bm_defragment_event",
11354 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11355 pevent->data_size, (int) sizeof(DWORD));
11356 return;
11357 }
11358
11359 /* setup defragment buffer */
11360 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11364
11365 if (defrag_buffer[i].pevent == NULL) {
11367 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11368 return;
11369 }
11370
11371 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11374
11375 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11376 // pevent->serial_number, defrag_buffer[i].data_size);
11377
11378 return;
11379 }
11380
11381 /* search buffer for that event */
11382 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11383 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11384 break;
11385
11386 if (i == MAX_DEFRAG_EVENTS) {
11387 /* no buffer available -> no first fragment received */
11388 cm_msg(MERROR, "bm_defragement_event",
11389 "Received fragment without first fragment (ID %d) Ser#:%d",
11390 pevent->event_id & 0x0FFF, pevent->serial_number);
11391 return;
11392 }
11393
11394 /* add fragment to buffer */
11396 free(defrag_buffer[i].pevent);
11399 cm_msg(MERROR, "bm_defragement_event",
11400 "Received fragments with more data (%d) than event size (%d)",
11401 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11402 return;
11403 }
11404
11405 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11406 defrag_buffer[i].received, pdata, pevent->data_size);
11407
11408 defrag_buffer[i].received += pevent->data_size;
11409
11410 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11411 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11412
11413 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11414 /* event complete */
11415 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11416 free(defrag_buffer[i].pevent);
11419 }
11420}
11421
11423#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11424
/* end of bmfunctionc */
11427
11433/********************************************************************\
11434* *
11435* RPC functions *
11436* *
11437\********************************************************************/
11438
11440{
11441public:
11442 std::atomic_bool connected{false}; /* socket is connected */
11443 std::string host_name; /* server name */
11444 int port = 0; /* server port */
11445 std::mutex mutex; /* connection lock */
11446 int index = 0; /* index in the connection array */
11447 std::string client_name; /* name of remote client */
11448 int send_sock = 0; /* tcp socket */
11449 int remote_hw_type = 0; /* remote hardware type */
11450 int rpc_timeout = 0; /* timeout in milliseconds */
11451
11452 void print() {
11453 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11454 index,
11455 client_name.c_str(),
11456 host_name.c_str(),
11457 port,
11458 send_sock,
11459 int(connected),
11460 rpc_timeout);
11461 }
11462
11464 if (send_sock > 0) {
11466 }
11467 connected = false;
11468 }
11469};
11470
11471/* globals */
11472
11473//
11474// locking rules for client connections:
11475//
11476// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11477// 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
11478// lock individual connection, check c->connected, work on the connection, unlock connection
11479//
11480// ok to access without locking client connection:
11481//
11482// - c->connected (std::atomic, but must recheck it after taking the lock)
11483// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11484//
11485// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11486// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11487//
11488// lifetime of client connections:
11489//
11490// - client connection slots are allocated by rpc_client_connect()
11491// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11492// - client slots marked NULL are free and will be reused by rpc_client_connect()
11493// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11494// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11495// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11496// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11497// client call will see an empty slot and return an error
11498// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11499// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11500// only when one is sure no other threads are running concurrent rpc client calls.
11501//
11502
11504static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11505
11506static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11507static bool _rpc_is_remote = false;
11508
11509//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11510static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11511static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11512
11514{
11515 assert(idx >= 0);
11516 assert(idx < (int)_server_acceptions.size());
11517 assert(_server_acceptions[idx] != NULL);
11518 return _server_acceptions[idx];
11519}
11520
11525
11527{
11528 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11529 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11530 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11531 return _server_acceptions[idx];
11532 }
11533 }
11534
11536
11537 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11538 if (_server_acceptions[idx] == NULL) {
11539 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11540 _server_acceptions[idx] = sa;
11541 return _server_acceptions[idx];
11542 }
11543 }
11544
11545 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11546 _server_acceptions.push_back(sa);
11547
11548 return sa;
11549}
11550
11552{
11553 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11554
11555 if (is_mserver) {
11556 assert(_mserver_acception == this);
11558 is_mserver = false;
11559 }
11560
11561 /* close server connection */
11562 if (recv_sock)
11564 if (send_sock)
11566 if (event_sock)
11568
11569 /* free TCP cache */
11570 if (net_buffer) {
11571 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11572 free(net_buffer);
11573 net_buffer = NULL;
11574 net_buffer_size = 0;
11575 }
11576
11577 /* mark this entry as invalid */
11578 clear();
11579}
11580
11581static std::vector<RPC_LIST> rpc_list;
11582static std::mutex rpc_list_mutex;
11583
11585
11586
11587/********************************************************************\
11588* conversion functions *
11589\********************************************************************/
11590
11591void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11592 *convert_flags = 0;
11593
11594 /* big/little endian conversion */
11595 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11596 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11597 && (hw_type & DRI_BIG_ENDIAN)))
11598 *convert_flags |= CF_ENDIAN;
11599
11600 /* float conversion between IEEE and VAX G */
11601 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11602 *convert_flags |= CF_VAX2IEEE;
11603
11604 /* float conversion between VAX G and IEEE */
11605 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11606 *convert_flags |= CF_IEEE2VAX;
11607
11609 //if (remote_hw_type & DR_ASCII)
11610 // *convert_flags |= CF_ASCII;
11611}
11612
11613/********************************************************************/
11617
11618/********************************************************************/
11620 unsigned short int lo, hi;
11621
11622 /* swap hi and lo word */
11623 lo = *((short int *) (var) + 1);
11624 hi = *((short int *) (var));
11625
11626 /* correct exponent */
11627 if (lo != 0)
11628 lo += 0x100;
11629
11630 *((short int *) (var) + 1) = hi;
11631 *((short int *) (var)) = lo;
11632}
11633
11635 unsigned short int lo, hi;
11636
11637 /* swap hi and lo word */
11638 lo = *((short int *) (var) + 1);
11639 hi = *((short int *) (var));
11640
11641 /* correct exponent */
11642 if (hi != 0)
11643 hi -= 0x100;
11644
11645 *((short int *) (var) + 1) = hi;
11646 *((short int *) (var)) = lo;
11647
11648}
11649
11651 unsigned short int i1, i2, i3, i4;
11652
11653 /* swap words */
11654 i1 = *((short int *) (var) + 3);
11655 i2 = *((short int *) (var) + 2);
11656 i3 = *((short int *) (var) + 1);
11657 i4 = *((short int *) (var));
11658
11659 /* correct exponent */
11660 if (i4 != 0)
11661 i4 -= 0x20;
11662
11663 *((short int *) (var) + 3) = i4;
11664 *((short int *) (var) + 2) = i3;
11665 *((short int *) (var) + 1) = i2;
11666 *((short int *) (var)) = i1;
11667}
11668
11670 unsigned short int i1, i2, i3, i4;
11671
11672 /* swap words */
11673 i1 = *((short int *) (var) + 3);
11674 i2 = *((short int *) (var) + 2);
11675 i3 = *((short int *) (var) + 1);
11676 i4 = *((short int *) (var));
11677
11678 /* correct exponent */
11679 if (i1 != 0)
11680 i1 += 0x20;
11681
11682 *((short int *) (var) + 3) = i4;
11683 *((short int *) (var) + 2) = i3;
11684 *((short int *) (var) + 1) = i2;
11685 *((short int *) (var)) = i1;
11686}
11687
11688/********************************************************************/
11689void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11690
11691 if (convert_flags & CF_ENDIAN) {
11692 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11693 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11694 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11695 }
11696
11697 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11698 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11699 if (tid == TID_FLOAT)
11700 rpc_ieee2vax_float((float *) data);
11701 if (tid == TID_DOUBLE)
11702 rpc_ieee2vax_double((double *) data);
11703 }
11704
11705 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11706 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11707 if (tid == TID_FLOAT)
11708 rpc_vax2ieee_float((float *) data);
11709 if (tid == TID_DOUBLE)
11710 rpc_vax2ieee_double((double *) data);
11711 }
11712}
11713
11714void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11715/********************************************************************\
11716
11717 Routine: rpc_convert_data
11718
11719 Purpose: Convert data format between differenct computers
11720
11721 Input:
11722 void *data Pointer to data
11723 INT tid Type ID of data, one of TID_xxx
11724 INT flags Combination of following flags:
11725 RPC_IN: data is input parameter
11726 RPC_OUT: data is output variable
11727 RPC_FIXARRAY, RPC_VARARRAY: data is array
11728 of "size" bytes (see next param.)
11729 RPC_OUTGOING: data is outgoing
11730 INT total_size Size of bytes of data. Used for variable
11731 length arrays.
11732 INT convert_flags Flags for data conversion
11733
11734 Output:
11735 void *data Is converted according to _convert_flag
11736 value
11737
11738 Function value:
11739 RPC_SUCCESS Successful completion
11740
11741\********************************************************************/
11742{
11743 /* convert array */
11744 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11745 int single_size = rpc_tid_size(tid);
11746 /* don't convert TID_ARRAY & TID_STRUCT */
11747 if (single_size == 0)
11748 return;
11749
11750 int n = total_size / single_size;
11751
11752 for (int i = 0; i < n; i++) {
11753 char* p = (char *) data + (i * single_size);
11754 rpc_convert_single(p, tid, flags, convert_flags);
11755 }
11756 } else {
11757 rpc_convert_single(data, tid, flags, convert_flags);
11758 }
11759}
11760
11761/********************************************************************\
11762* type ID functions *
11763\********************************************************************/
11764
11766 if (id >= 0 && id < TID_LAST)
11767 return tid_size[id];
11768
11769 return 0;
11770}
11771
11772const char *rpc_tid_name(INT id) {
11773 if (id >= 0 && id < TID_LAST)
11774 return tid_name[id];
11775 else
11776 return "<unknown>";
11777}
11778
11779const char *rpc_tid_name_old(INT id) {
11780 if (id >= 0 && id < TID_LAST)
11781 return tid_name_old[id];
11782 else
11783 return "<unknown>";
11784}
11785
11786int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11787{
11788 for (int i=0; i<TID_LAST; i++) {
11789 if (strcmp(name, tid_name[i]) == 0)
11790 return i;
11791 }
11792
11793 for (int i=0; i<TID_LAST; i++) {
11794 if (strcmp(name, tid_name_old[i]) == 0)
11795 return i;
11796 }
11797
11798 return 0;
11799}
11800
11801/********************************************************************\
11802* client functions *
11803\********************************************************************/
11804
11805/********************************************************************/
11823
11824/********************************************************************/
11836{
11837 for (int i = 0; new_list[i].id != 0; i++) {
11838 /* check valid ID for user functions */
11839 if (new_list != rpc_get_internal_list(0) &&
11841 || new_list[i].id > RPC_MAX_ID)) {
11842 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11843 }
11844 }
11845
11846 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11847
11848 /* check double defined functions */
11849 for (int i = 0; new_list[i].id != 0; i++) {
11850 for (size_t j = 0; j < rpc_list.size(); j++) {
11851 if (rpc_list[j].id == new_list[i].id) {
11852 return RPC_DOUBLE_DEFINED;
11853 }
11854 }
11855 }
11856
11857 /* append new functions */
11858 for (int i = 0; new_list[i].id != 0; i++) {
11859 RPC_LIST e = new_list[i];
11860
11861 /* set default dispatcher */
11862 if (e.dispatch == NULL) {
11863 e.dispatch = func;
11864 }
11865
11866 rpc_list.push_back(e);
11867 }
11868
11869 return RPC_SUCCESS;
11870}
11871
11872
11873
11875#ifndef DOXYGEN_SHOULD_SKIP_THIS
11876
11877/********************************************************************/
11879/********************************************************************\
11880
11881 Routine: rpc_deregister_functions
11882
11883 Purpose: Free memory of previously registered functions
11884
11885 Input:
11886 none
11887
11888 Output:
11889 none
11890
11891 Function value:
11892 RPC_SUCCESS Successful completion
11893
11894\********************************************************************/
11895{
11896 rpc_list_mutex.lock();
11897 rpc_list.clear();
11898 rpc_list_mutex.unlock();
11899
11900 return RPC_SUCCESS;
11901}
11902
11903
11904/********************************************************************/
11905INT rpc_register_function(INT id, INT(*func)(INT, void **))
11906/********************************************************************\
11907
11908 Routine: rpc_register_function
11909
11910 Purpose: Replace a dispatch function for a specific rpc routine
11911
11912 Input:
11913 INT id RPC ID
11914 INT *func New dispatch function
11915
11916 Output:
11917 <implicit: func gets copied to rpc_list>
11918
11919 Function value:
11920 RPC_SUCCESS Successful completion
11921 RPC_INVALID_ID RPC ID not found
11922
11923\********************************************************************/
11924{
11925 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11926
11927 for (size_t i = 0; i < rpc_list.size(); i++) {
11928 if (rpc_list[i].id == id) {
11929 rpc_list[i].dispatch = func;
11930 return RPC_SUCCESS;
11931 }
11932 }
11933
11934 return RPC_INVALID_ID;
11935}
11936
11937/********************************************************************/
11938
11939static int handle_msg_odb(int n, const NET_COMMAND *nc) {
11940 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
11941 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
11942 /* update a changed record */
11943 HNDLE hDB = *((INT *) nc->param);
11944 HNDLE hKeyRoot = *((INT *) nc->param + 1);
11945 HNDLE hKey = *((INT *) nc->param + 2);
11946 int index = *((INT *) nc->param + 3);
11948 }
11949 return CM_VERSION_MISMATCH;
11950}
11951
11952/********************************************************************/
11954/********************************************************************\
11955
11956 Routine: rpc_client_dispatch
11957
11958 Purpose: Receive data from the mserver: watchdog and buffer notification messages
11959
11960\********************************************************************/
11961{
11962 INT status = 0;
11963 char net_buffer[256];
11964
11965 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11966 if (n <= 0)
11967 return SS_ABORT;
11968
11969 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
11970
11971 if (nc->header.routine_id == MSG_ODB) {
11972 status = handle_msg_odb(n, nc);
11973 } else if (nc->header.routine_id == MSG_WATCHDOG) {
11974 nc->header.routine_id = 1;
11975 nc->header.param_size = 0;
11976 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11978 } else if (nc->header.routine_id == MSG_BM) {
11980 struct timeval timeout;
11981
11982 //printf("rpc_client_dispatch: received MSG_BM!\n");
11983
11984 /* receive further messages to empty TCP queue */
11985 do {
11986 FD_ZERO(&readfds);
11987 FD_SET(sock, &readfds);
11988
11989 timeout.tv_sec = 0;
11990 timeout.tv_usec = 0;
11991
11992 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
11993
11994 if (FD_ISSET(sock, &readfds)) {
11995 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11996 if (n <= 0)
11997 return SS_ABORT;
11998
11999 if (nc->header.routine_id == MSG_ODB) {
12000 status = handle_msg_odb(n, nc);
12001 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12002 nc->header.routine_id = 1;
12003 nc->header.param_size = 0;
12004 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12006 }
12007 }
12008
12009 } while (FD_ISSET(sock, &readfds));
12010
12011 /* poll event from server */
12013 }
12014
12015 return status;
12016}
12017
12018
12019/********************************************************************/
12020INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12021/********************************************************************\
12022
12023 Routine: rpc_client_connect
12024
12025 Purpose: Establish a network connection to a remote client
12026
12027 Input:
12028 char *host_name IP address of host to connect to.
12029 INT port TPC port to connect to.
12030 char *clinet_name Client program name
12031
12032 Output:
12033 HNDLE *hConnection Handle for new connection which can be used
12034 in future rpc_call(hConnection....) calls
12035
12036 Function value:
12037 RPC_SUCCESS Successful completion
12038 RPC_NET_ERROR Error in socket call
12039 RPC_NO_CONNECTION Maximum number of connections reached
12040 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12041
12042\********************************************************************/
12043{
12044 INT i, status;
12045 bool debug = false;
12046
12047 /* check if cm_connect_experiment was called */
12048 if (_client_name.length() == 0) {
12049 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12050 return RPC_NOT_REGISTERED;
12051 }
12052
12053 /* refuse connection to port 0 */
12054 if (port == 0) {
12055 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12056 return RPC_NET_ERROR;
12057 }
12058
12060
12061 static std::mutex gHostnameMutex;
12062
12063 {
12064 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12065
12066 if (debug) {
12067 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12068 for (size_t i = 0; i < _client_connections.size(); i++) {
12069 if (_client_connections[i]) {
12070 printf("client connection %d: ", (int)i);
12071 _client_connections[i]->print();
12072 printf("\n");
12073 }
12074 }
12075 }
12076
12077 // slot with index 0 is not used, fill it with a NULL
12078
12079 if (_client_connections.empty()) {
12080 _client_connections.push_back(NULL);
12081 }
12082
12083 bool hostname_locked = false;
12084
12085 /* check if connection already exists */
12086 for (size_t i = 1; i < _client_connections.size(); i++) {
12088 if (c && c->connected) {
12089
12090 if (!hostname_locked) {
12091 gHostnameMutex.lock();
12092 hostname_locked = true;
12093 }
12094
12095 if ((c->host_name == host_name) && (c->port == port)) {
12096 // NB: we must release the hostname lock before taking
12097 // c->mutex to avoid a locking order inversion deadlock:
12098 // later on we lock the hostname mutex while holding the c->mutex
12099 gHostnameMutex.unlock();
12100 hostname_locked = false;
12101 std::lock_guard<std::mutex> cguard(c->mutex);
12102 // check if socket is still connected
12103 if (c->connected) {
12104 // found connection slot with matching hostname and port number
12105 status = ss_socket_wait(c->send_sock, 0);
12106 if (status == SS_TIMEOUT) { // yes, still connected and empty
12107 // so reuse it connection
12108 *hConnection = c->index;
12109 if (debug) {
12110 printf("already connected: ");
12111 c->print();
12112 printf("\n");
12113 }
12114 // implicit unlock of c->mutex
12115 // gHostnameLock is not locked here
12116 return RPC_SUCCESS;
12117 }
12118 //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);
12119 c->close_locked();
12120 }
12121 // implicit unlock of c->mutex
12122 }
12123 }
12124 }
12125
12126 if (hostname_locked) {
12127 gHostnameMutex.unlock();
12128 hostname_locked = false;
12129 }
12130
12131 // only start reusing connections once we have
12132 // a good number of slots allocated.
12133 if (_client_connections.size() > 10) {
12134 static int last_reused = 0;
12135
12136 int size = _client_connections.size();
12137 for (int j = 1; j < size; j++) {
12138 int i = (last_reused + j) % size;
12139 if (_client_connections[i] && !_client_connections[i]->connected) {
12141 if (debug) {
12142 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12143 c->print();
12144 printf("\n");
12145 }
12146 last_reused = i;
12147 break;
12148 }
12149 }
12150 }
12151
12152 // no slots to reuse, allocate a new slot.
12153 if (!c) {
12155
12156 // if empty slot not found, add to end of array
12157 c->index = _client_connections.size();
12158 _client_connections.push_back(c);
12159
12160 if (debug) {
12161 printf("new connection appended to array: ");
12162 c->print();
12163 printf("\n");
12164 }
12165 }
12166
12167 c->mutex.lock();
12168 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12169
12170 // done with the array of connections
12171 // implicit unlock of _client_connections_mutex
12172 }
12173
12174 // locked connection slot for new connection
12175 assert(c != NULL);
12176
12177 std::string errmsg;
12178
12179 /* create a new socket for connecting to remote server */
12180 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12181 if (status != SS_SUCCESS) {
12182 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12183 c->mutex.unlock();
12184 return RPC_NET_ERROR;
12185 }
12186
12187 gHostnameMutex.lock();
12188
12189 c->host_name = host_name;
12190 c->port = port;
12191
12192 gHostnameMutex.unlock();
12193
12194 c->client_name = client_name;
12195 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12196
12197 /* set TCP_NODELAY option for better performance */
12198 i = 1;
12199 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12200
12201 /* send local computer info */
12202 std::string local_prog_name = rpc_get_name();
12203 std::string local_host_name = ss_gethostname();
12204
12205 int hw_type = rpc_get_hw_type();
12206
12207 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12208
12209 int size = cstr.length() + 1;
12210 i = send(c->send_sock, cstr.c_str(), size, 0);
12211 if (i < 0 || i != size) {
12212 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12213 c->mutex.unlock();
12214 return RPC_NET_ERROR;
12215 }
12216
12217 bool restore_watchdog_timeout = false;
12219 DWORD watchdog_timeout;
12220 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12221
12222 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12223
12224 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12227 }
12228
12229 char str[256];
12230
12231 /* receive remote computer info */
12232 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12233
12235 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12236 }
12237
12238 if (i <= 0) {
12239 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12240 c->close_locked();
12241 c->mutex.unlock();
12242 return RPC_NET_ERROR;
12243 }
12244
12245 int remote_hw_type = 0;
12246 char remote_version[32];
12247 remote_version[0] = 0;
12248 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12249
12250 c->remote_hw_type = remote_hw_type;
12251
12252 /* print warning if version patch level doesn't agree */
12253 char v1[32];
12254 mstrlcpy(v1, remote_version, sizeof(v1));
12255 if (strchr(v1, '.'))
12256 if (strchr(strchr(v1, '.') + 1, '.'))
12257 *strchr(strchr(v1, '.') + 1, '.') = 0;
12258
12259 mstrlcpy(str, cm_get_version(), sizeof(str));
12260 if (strchr(str, '.'))
12261 if (strchr(strchr(str, '.') + 1, '.'))
12262 *strchr(strchr(str, '.') + 1, '.') = 0;
12263
12264 if (strcmp(v1, str) != 0) {
12265 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12266 }
12267
12268 c->connected = true;
12269
12270 *hConnection = c->index;
12271
12272 c->mutex.unlock();
12273
12274 return RPC_SUCCESS;
12275}
12276
12277/********************************************************************/
12279/********************************************************************\
12280
12281 Routine: rpc_client_check
12282
12283 Purpose: Check all client connections if remote client closed link
12284
12285\********************************************************************/
12286{
12287#if 0
12288 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12289 if (_client_connection[i].send_sock != 0)
12290 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);
12291#endif
12292
12293 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12294
12295 /* check for broken connections */
12296 for (unsigned i = 0; i < _client_connections.size(); i++) {
12298 if (c && c->connected) {
12299 std::lock_guard<std::mutex> cguard(c->mutex);
12300
12301 if (!c->connected) {
12302 // implicit unlock
12303 continue;
12304 }
12305
12306 //printf("rpc_client_check: connection %d: ", i);
12307 //c->print();
12308 //printf("\n");
12309
12310 int ok = 0;
12311
12313 FD_ZERO(&readfds);
12314 FD_SET(c->send_sock, &readfds);
12315
12316 struct timeval timeout;
12317 timeout.tv_sec = 0;
12318 timeout.tv_usec = 0;
12319
12320 int status;
12321
12322#ifdef OS_WINNT
12323 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12324#else
12325 do {
12326 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12327 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12328#endif
12329
12330 if (!FD_ISSET(c->send_sock, &readfds)) {
12331 // implicit unlock
12332 continue;
12333 }
12334
12335 char buffer[64];
12336
12337 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12338 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12339
12340 if (status < 0) {
12341#ifndef OS_WINNT
12342 if (errno == EAGAIN) { // still connected
12343 ok = 1;
12344 } else
12345#endif
12346 {
12347 // connection error
12348 cm_msg(MERROR, "rpc_client_check",
12349 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12350 c->client_name.c_str(),
12351 c->host_name.c_str(),
12352 errno, strerror(errno));
12353 ok = 0;
12354 }
12355 } else if (status == 0) {
12356 // connection closed by remote end without sending an EXIT message
12357 // this can happen if the remote end has crashed, so this message
12358 // is still necessary as a useful diagnostic for unexpected crashes
12359 // of midas programs. K.O.
12360 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());
12361 ok = 0;
12362 } else {
12363 // read some data
12364 ok = 1;
12365 if (equal_ustring(buffer, "EXIT")) {
12366 /* normal exit */
12367 ok = 0;
12368 }
12369 }
12370
12371 if (!ok) {
12372 //printf("rpc_client_check: closing connection %d: ", i);
12373 //c->print();
12374 //printf("\n");
12375
12376 // connection lost, close the socket
12377 c->close_locked();
12378 }
12379
12380 // implicit unlock
12381 }
12382 }
12383
12384 // implicit unlock of _client_connections_mutex
12385}
12386
12387
12388/********************************************************************/
12389INT rpc_server_connect(const char *host_name, const char *exp_name)
12390/********************************************************************\
12391
12392 Routine: rpc_server_connect
12393
12394 Purpose: Extablish a network connection to a remote MIDAS
12395 server using a callback scheme.
12396
12397 Input:
12398 char *host_name IP address of host to connect to.
12399
12400 INT port TPC port to connect to.
12401
12402 char *exp_name Name of experiment to connect to. By using
12403 this name, several experiments (e.g. online
12404 DAQ and offline analysis) can run simultan-
12405 eously on the same host.
12406
12407 Output:
12408 none
12409
12410 Function value:
12411 RPC_SUCCESS Successful completion
12412 RPC_NET_ERROR Error in socket call
12413 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12414 CM_UNDEF_EXP Undefined experiment on server
12415
12416\********************************************************************/
12417{
12418 INT i, status;
12419 INT remote_hw_type, hw_type;
12420 char str[200], version[32], v1[32];
12422 struct timeval timeout;
12423 int port = MIDAS_TCP_PORT;
12424 char *s;
12425
12426#ifdef OS_WINNT
12427 {
12429
12430 /* Start windows sockets */
12431 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12432 return RPC_NET_ERROR;
12433 }
12434#endif
12435
12436 /* check if local connection */
12437 if (host_name[0] == 0)
12438 return RPC_SUCCESS;
12439
12440 /* register system functions */
12442
12443 /* check if cm_connect_experiment was called */
12444 if (_client_name.length() == 0) {
12445 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12446 return RPC_NOT_REGISTERED;
12447 }
12448
12449 /* check if connection already exists */
12451 return RPC_SUCCESS;
12452
12456
12457 bool listen_localhost = false;
12458
12459 if (strcmp(host_name, "localhost") == 0)
12460 listen_localhost = true;
12461
12462 int lsock1, lport1;
12463 int lsock2, lport2;
12464 int lsock3, lport3;
12465
12466 std::string errmsg;
12467
12469
12470 if (status != SS_SUCCESS) {
12471 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12472 return RPC_NET_ERROR;
12473 }
12474
12476
12477 if (status != SS_SUCCESS) {
12478 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12479 return RPC_NET_ERROR;
12480 }
12481
12483
12484 if (status != SS_SUCCESS) {
12485 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12486 return RPC_NET_ERROR;
12487 }
12488
12489 /* extract port number from host_name */
12490 mstrlcpy(str, host_name, sizeof(str));
12491 s = strchr(str, ':');
12492 if (s) {
12493 *s = 0;
12494 port = strtoul(s + 1, NULL, 0);
12495 }
12496
12497 int sock;
12498
12499 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12500
12501 if (status != SS_SUCCESS) {
12502 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12503 return RPC_NET_ERROR;
12504 }
12505
12506 /* connect to experiment */
12507 if (exp_name[0] == 0)
12508 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12509 else
12510 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12511
12512 send(sock, str, strlen(str) + 1, 0);
12513 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12514 ss_socket_close(&sock);
12515 if (i <= 0) {
12516 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12517 return RPC_NET_ERROR;
12518 }
12519
12520 status = version[0] = 0;
12521 sscanf(str, "%d %s", &status, version);
12522
12523 if (status == 2) {
12524/* message "undefined experiment" should be displayed by application */
12525 return CM_UNDEF_EXP;
12526 }
12527
12528 /* print warning if version patch level doesn't agree */
12529 strcpy(v1, version);
12530 if (strchr(v1, '.'))
12531 if (strchr(strchr(v1, '.') + 1, '.'))
12532 *strchr(strchr(v1, '.') + 1, '.') = 0;
12533
12534 strcpy(str, cm_get_version());
12535 if (strchr(str, '.'))
12536 if (strchr(strchr(str, '.') + 1, '.'))
12537 *strchr(strchr(str, '.') + 1, '.') = 0;
12538
12539 if (strcmp(v1, str) != 0) {
12540 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12541 cm_get_version());
12542 }
12543
12544 /* wait for callback on send and recv socket with timeout */
12545 FD_ZERO(&readfds);
12549
12550 timeout.tv_sec = _rpc_connect_timeout / 1000;
12551 timeout.tv_usec = 0;
12552
12553 do {
12554 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12555
12556 /* if an alarm signal was cought, restart select with reduced timeout */
12557 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12558 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12559
12560 } while (status == -1); /* dont return if an alarm signal was cought */
12561
12562 if (!FD_ISSET(lsock1, &readfds)) {
12563 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12567 return RPC_NET_ERROR;
12568 }
12569
12573
12575 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12576 return RPC_NET_ERROR;
12577 }
12578
12582
12583 /* set TCP_NODELAY option for better performance */
12584 int flag = 1;
12587
12588 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12589 flag = 2 * 1024 * 1024;
12591 if (status != 0)
12592 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12593
12594 /* send local computer info */
12595 std::string local_prog_name = rpc_get_name();
12597 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12598
12600
12601 /* receive remote computer info */
12603 if (i <= 0) {
12604 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12605 return RPC_NET_ERROR;
12606 }
12607
12608 sscanf(str, "%d", &remote_hw_type);
12609 _server_connection.remote_hw_type = remote_hw_type;
12610
12612
12613 _rpc_is_remote = true;
12614
12615 return RPC_SUCCESS;
12616}
12617
12618/********************************************************************/
12619
12621{
12623 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12625 if (c && c->connected) {
12627 c->mutex.lock();
12628 if (!c->connected) {
12629 // disconnected while we were waiting for the lock
12630 c->mutex.unlock();
12631 return NULL;
12632 }
12633 return c;
12634 }
12635 }
12637 return NULL;
12638}
12639
12641{
12642 /* close all open connections */
12643
12645
12646 for (unsigned i = 0; i < _client_connections.size(); i++) {
12648 if (c && c->connected) {
12649 int index = c->index;
12650 // must unlock the array, otherwise we hang -
12651 // rpc_client_disconnect() will do rpc_call_client()
12652 // which needs to lock the array to convert handle
12653 // to connection pointer. Ouch! K.O. Dec 2020.
12657 }
12658 }
12659
12660 for (unsigned i = 0; i < _client_connections.size(); i++) {
12662 //printf("client connection %d %p\n", i, c);
12663 if (c) {
12664 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12665 if (!c->connected) {
12666 delete c;
12668 }
12669 }
12670 }
12671
12673
12674 /* close server connection from other clients */
12675 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12676 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12677 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12678 _server_acceptions[i]->close();
12679 }
12680 }
12681}
12682
12683/********************************************************************/
12685/********************************************************************\
12686
12687 Routine: rpc_client_disconnect
12688
12689 Purpose: Close a rpc connection to a MIDAS client
12690
12691 Input:
12692 HNDLE hConn Handle of connection
12693 BOOL bShutdown Shut down remote server if TRUE
12694
12695 Output:
12696 none
12697
12698 Function value:
12699 RPC_SUCCESS Successful completion
12700
12701\********************************************************************/
12702{
12703 /* notify server about exit */
12704
12705 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12706
12708
12709 return RPC_SUCCESS;
12710}
12711
12712/********************************************************************/
12714/********************************************************************\
12715
12716 Routine: rpc_server_disconnect
12717
12718 Purpose: Close a rpc connection to a MIDAS server and close all
12719 server connections from other clients
12720
12721 Input:
12722 none
12723
12724 Output:
12725 none
12726
12727 Function value:
12728 RPC_SUCCESS Successful completion
12729 RPC_NET_ERROR Error in socket call
12730 RPC_NO_CONNECTION Maximum number of connections reached
12731
12732\********************************************************************/
12733{
12735
12737 return RPC_SUCCESS;
12738
12740
12741 /* flush remaining events */
12743
12744 /* notify server about exit */
12745 if (rpc_is_connected()) {
12747 }
12748
12749 /* close sockets */
12756
12758
12759 /* remove semaphore */
12760 if (_mutex_rpc)
12762 _mutex_rpc = NULL;
12763
12765 return RPC_SUCCESS;
12766}
12767
12768/********************************************************************/
12770/********************************************************************\
12771
12772 Routine: rpc_is_remote
12773
12774 Purpose: Return true if program is connected to a remote server
12775
12776 Input:
12777 none
12778
12779 Output:
12780 none
12781
12782 Function value:
12783 INT true is remote client connected to mserver, false if local connection
12784
12785\********************************************************************/
12786{
12787 return _rpc_is_remote;
12788}
12789
12790/********************************************************************/
12792/********************************************************************\
12793
12794 Routine: rpc_is_connected
12795
12796 Purpose: Return true if connection to mserver is still open
12797
12798 Input:
12799 none
12800
12801 Output:
12802 none
12803
12804 Function value:
12805 INT true if connection to mserver is still open, false if connection to mserver is already closed
12806
12807\********************************************************************/
12808{
12809 return _server_connection.send_sock != 0;
12810}
12811
12812/********************************************************************/
12814/********************************************************************\
12815
12816 Routine: rpc_get_mserver_hostname
12817
12818 Purpose: Return the hostname of the mserver connection (host:port format)
12819
12820\********************************************************************/
12821{
12823}
12824
12825/********************************************************************/
12827/********************************************************************\
12828
12829 Routine: rpc_is_mserver
12830
12831 Purpose: Return true if we are the mserver
12832
12833 Function value:
12834 INT "true" if we are the mserver
12835
12836\********************************************************************/
12837{
12838 return _mserver_acception != NULL;
12839}
12840
12841/********************************************************************/
12843/********************************************************************\
12844
12845 Routine: rpc_get_hw_type
12846
12847 Purpose: get hardware information
12848
12849 Function value:
12850 INT combination of DRI_xxx bits
12851
12852\********************************************************************/
12853{
12854 {
12855 {
12856 INT tmp_type, size;
12857 DWORD dummy;
12858 unsigned char *p;
12859 float f;
12860 double d;
12861
12862 tmp_type = 0;
12863
12864 /* test pointer size */
12865 size = sizeof(p);
12866 if (size == 2)
12867 tmp_type |= DRI_16;
12868 if (size == 4)
12869 tmp_type |= DRI_32;
12870 if (size == 8)
12871 tmp_type |= DRI_64;
12872
12873 /* test if little or big endian machine */
12874 dummy = 0x12345678;
12875 p = (unsigned char *) &dummy;
12876 if (*p == 0x78)
12878 else if (*p == 0x12)
12880 else
12881 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
12882
12883 /* floating point format */
12884 f = (float) 1.2345;
12885 dummy = 0;
12886 memcpy(&dummy, &f, sizeof(f));
12887 if ((dummy & 0xFF) == 0x19 &&
12888 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
12889 && ((dummy >> 24) & 0xFF) == 0x3F)
12890 tmp_type |= DRF_IEEE;
12891 else if ((dummy & 0xFF) == 0x9E &&
12892 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
12893 && ((dummy >> 24) & 0xFF) == 0x04)
12895 else
12896 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12897
12898 d = (double) 1.2345;
12899 dummy = 0;
12900 memcpy(&dummy, &d, sizeof(f));
12901 if ((dummy & 0xFF) == 0x8D && /* little endian */
12902 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
12903 && ((dummy >> 24) & 0xFF) == 0x12)
12904 tmp_type |= DRF_IEEE;
12905 else if ((dummy & 0xFF) == 0x83 && /* big endian */
12906 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
12907 && ((dummy >> 24) & 0xFF) == 0x3F)
12908 tmp_type |= DRF_IEEE;
12909 else if ((dummy & 0xFF) == 0x13 &&
12910 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
12911 && ((dummy >> 24) & 0xFF) == 0xC0)
12913 else if ((dummy & 0xFF) == 0x9E &&
12914 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
12915 && ((dummy >> 24) & 0xFF) == 0x04)
12916 cm_msg(MERROR, "rpc_get_option",
12917 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
12918 else
12919 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12920
12921 return tmp_type;
12922 }
12923 }
12924}
12925
12927#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12928
12929/********************************************************************/
12937#if 0
12939 switch (item) {
12940 case RPC_OTIMEOUT:
12941 if (hConn == -1)
12943 else if (hConn == -2)
12945 else {
12947 if (c) {
12948 c->rpc_timeout = value;
12949 c->mutex.unlock();
12950 }
12951 }
12952 break;
12953
12954 case RPC_NODELAY:
12955 if (hConn == -1) {
12957 } else {
12959 if (c) {
12960 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12961 c->mutex.unlock();
12962 }
12963 }
12964 break;
12965
12966 default:
12967 cm_msg(MERROR, "rpc_set_option", "invalid argument");
12968 break;
12969 }
12970
12971 return 0;
12972}
12973#endif
12974
12975/********************************************************************/
12982{
12983 if (hConn == RPC_HNDLE_MSERVER) {
12985 } else if (hConn == RPC_HNDLE_CONNECT) {
12986 return _rpc_connect_timeout;
12987 } else {
12989 if (c) {
12990 int timeout = c->rpc_timeout;
12991 c->mutex.unlock();
12992 return timeout;
12993 }
12994 }
12995 return 0;
12996}
12997
12998/********************************************************************/
13007{
13008 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13009
13010 if (hConn == RPC_HNDLE_MSERVER) {
13011 if (old_timeout_msec)
13014 } else if (hConn == RPC_HNDLE_CONNECT) {
13015 if (old_timeout_msec)
13018 } else {
13020 if (c) {
13021 if (old_timeout_msec)
13022 *old_timeout_msec = c->rpc_timeout;
13023 c->rpc_timeout = timeout_msec;
13024 c->mutex.unlock();
13025 } else {
13026 if (old_timeout_msec)
13027 *old_timeout_msec = 0;
13028 }
13029 }
13030 return RPC_SUCCESS;
13031}
13032
13033
13035#ifndef DOXYGEN_SHOULD_SKIP_THIS
13036
13037/********************************************************************/
13039/********************************************************************\
13040
13041 Routine: rpc_get_convert_flags
13042
13043 Purpose: Get RPC convert_flags for the mserver connection
13044
13045 Function value:
13046 INT Actual option
13047
13048\********************************************************************/
13049{
13052 else
13053 return 0;
13054}
13055
13056static std::string _mserver_path;
13057
13058/********************************************************************/
13060/********************************************************************\
13061
13062 Routine: rpc_get_mserver_path()
13063
13064 Purpose: Get path of the mserver executable
13065
13066\********************************************************************/
13067{
13068 return _mserver_path.c_str();
13069}
13070
13071/********************************************************************/
13072INT rpc_set_mserver_path(const char *path)
13073/********************************************************************\
13074
13075 Routine: rpc_set_mserver_path
13076
13077 Purpose: Remember the path of the mserver executable
13078
13079 Input:
13080 char *path Full path of the mserver executable
13081
13082 Function value:
13083 RPC_SUCCESS Successful completion
13084
13085\********************************************************************/
13086{
13087 _mserver_path = path;
13088 return RPC_SUCCESS;
13089}
13090
13091/********************************************************************/
13092std::string rpc_get_name()
13093/********************************************************************\
13094
13095 Routine: rpc_get_name
13096
13097 Purpose: Get name set by rpc_set_name
13098
13099 Input:
13100 none
13101
13102 Output:
13103 char* name The location pointed by *name receives a
13104 copy of the _prog_name
13105
13106 Function value:
13107 RPC_SUCCESS Successful completion
13108
13109\********************************************************************/
13110{
13111 return _client_name;
13112}
13113
13114
13115/********************************************************************/
13117/********************************************************************\
13118
13119 Routine: rpc_set_name
13120
13121 Purpose: Set name of actual program for further rpc connections
13122
13123 Input:
13124 char *name Program name, up to NAME_LENGTH chars,
13125 no blanks
13126
13127 Output:
13128 none
13129
13130 Function value:
13131 RPC_SUCCESS Successful completion
13132
13133\********************************************************************/
13134{
13136
13137 return RPC_SUCCESS;
13138}
13139
13140
13141/********************************************************************/
13142INT rpc_set_debug(void (*func)(const char *), INT mode)
13143/********************************************************************\
13144
13145 Routine: rpc_set_debug
13146
13147 Purpose: Set a function which is called on every RPC call to
13148 display the function name and parameters of the RPC
13149 call.
13150
13151 Input:
13152 void *func(char*) Pointer to function.
13153 INT mode Debug mode
13154
13155 Output:
13156 none
13157
13158 Function value:
13159 RPC_SUCCESS Successful completion
13160
13161\********************************************************************/
13162{
13163 _debug_print = func;
13164 _debug_mode = mode;
13165 return RPC_SUCCESS;
13166}
13167
13168/********************************************************************/
13169void rpc_debug_printf(const char *format, ...)
13170/********************************************************************\
13171
13172 Routine: rpc_debug_print
13173
13174 Purpose: Calls function set via rpc_set_debug to output a string.
13175
13176 Input:
13177 char *str Debug string
13178
13179 Output:
13180 none
13181
13182\********************************************************************/
13183{
13185 char str[1000];
13186
13187 if (_debug_mode) {
13188 va_start(argptr, format);
13189 vsprintf(str, (char *) format, argptr);
13190 va_end(argptr);
13191
13192 if (_debug_print) {
13193 strcat(str, "\n");
13195 } else
13196 puts(str);
13197 }
13198}
13199
13200/********************************************************************/
13202 switch (arg_type) {
13203 /* On the stack, the minimum parameter size is sizeof(int).
13204 To avoid problems on little endian systems, treat all
13205 smaller parameters as int's */
13206 case TID_UINT8:
13207 case TID_INT8:
13208 case TID_CHAR:
13209 case TID_UINT16:
13210 case TID_INT16:
13211 *((int *) arg) = va_arg(*arg_ptr, int);
13212 break;
13213
13214 case TID_INT32:
13215 case TID_BOOL:
13216 *((INT *) arg) = va_arg(*arg_ptr, INT);
13217 break;
13218
13219 case TID_UINT32:
13220 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13221 break;
13222
13223 /* float variables are passed as double by the compiler */
13224 case TID_FLOAT:
13225 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13226 break;
13227
13228 case TID_DOUBLE:
13229 *((double *) arg) = va_arg(*arg_ptr, double);
13230 break;
13231
13232 case TID_ARRAY:
13233 *((char **) arg) = va_arg(*arg_ptr, char *);
13234 break;
13235 }
13236}
13237
13238/********************************************************************/
13240{
13241 bool debug = false;
13242
13243 if (debug) {
13244 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13245 for (int i=0; rl.param[i].tid != 0; i++) {
13246 int tid = rl.param[i].tid;
13247 int flags = rl.param[i].flags;
13248 int n = rl.param[i].n;
13249 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13250 }
13251 }
13252
13253 char args[MAX_RPC_PARAMS][8];
13254
13255 for (int i=0; rl.param[i].tid != 0; i++) {
13256 int tid = rl.param[i].tid;
13257 int flags = rl.param[i].flags;
13258 int arg_type = 0;
13259
13260 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13261 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13262 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13263
13264 if (bpointer)
13266 else
13267 arg_type = tid;
13268
13269 /* floats are passed as doubles, at least under NT */
13270 if (tid == TID_FLOAT && !bpointer)
13272
13273 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13274
13276 }
13277
13278 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13279 char* buf = (char *)malloc(buf_size);
13280 assert(buf);
13281
13282 (*nc) = (NET_COMMAND*) buf;
13283
13284 /* find out if we are on a big endian system */
13285 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13286
13287 char* param_ptr = (*nc)->param;
13288
13289 for (int i=0; rl.param[i].tid != 0; i++) {
13290 int tid = rl.param[i].tid;
13291 int flags = rl.param[i].flags;
13292 int arg_type = 0;
13293
13294 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13295 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13296 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13297
13298 if (bpointer)
13300 else
13301 arg_type = tid;
13302
13303 /* floats are passed as doubles, at least under NT */
13304 if (tid == TID_FLOAT && !bpointer)
13306
13307 /* get pointer to argument */
13308 //char arg[8];
13309 //rpc_va_arg(&ap, arg_type, arg);
13310
13311 char* arg = args[i];
13312
13313 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13314 if (bbig) {
13315 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13316 arg[0] = arg[3];
13317 }
13318 if (tid == TID_UINT16 || tid == TID_INT16) {
13319 arg[0] = arg[2];
13320 arg[1] = arg[3];
13321 }
13322 }
13323
13324 if (flags & RPC_IN) {
13325 int arg_size = 0;
13326
13327 if (bpointer)
13328 arg_size = rpc_tid_size(tid);
13329 else
13331
13332 /* for strings, the argument size depends on the string length */
13333 if (tid == TID_STRING || tid == TID_LINK) {
13334 arg_size = 1 + strlen((char *) *((char **) arg));
13335 }
13336
13337 /* for varibale length arrays, the size is given by
13338 the next parameter on the stack */
13339 if (flags & RPC_VARARRAY) {
13340 //va_list aptmp;
13342 //va_copy(aptmp, ap);
13343
13344 //char arg_tmp[8];
13345 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13346
13347 const char* arg_tmp = args[i+1];
13348
13349 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13350 if (flags & RPC_OUT)
13351 arg_size = *((INT *) *((void **) arg_tmp));
13352 else
13353 arg_size = *((INT *) arg_tmp);
13354
13355 *((INT *) param_ptr) = ALIGN8(arg_size);
13356 param_ptr += ALIGN8(sizeof(INT));
13357
13358 //va_end(aptmp);
13359 }
13360
13361 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13362 arg_size = rl.param[i].n;
13363
13364 /* always align parameter size */
13365 int param_size = ALIGN8(arg_size);
13366
13367 {
13368 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13369
13370 if (param_offset + param_size + 16 > buf_size) {
13371 size_t new_size = param_offset + param_size + 1024;
13372 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13373 buf = (char *) realloc(buf, new_size);
13374 assert(buf);
13375 buf_size = new_size;
13376 (*nc) = (NET_COMMAND*) buf;
13377 param_ptr = buf + param_offset;
13378 }
13379 }
13380
13381 if (bpointer) {
13382 if (debug) {
13383 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);
13384 }
13385 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13386 } else if (tid == TID_FLOAT) {
13387 if (debug) {
13388 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);
13389 }
13390 /* floats are passed as doubles on most systems */
13391 *((float *) param_ptr) = (float) *((double *) arg);
13392 } else {
13393 if (debug) {
13394 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);
13395 }
13396 memcpy(param_ptr, arg, arg_size);
13397 }
13398
13399 param_ptr += param_size;
13400 }
13401 }
13402
13403 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13404
13405 if (debug)
13406 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13407}
13408
13409/********************************************************************/
13410static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13411{
13412 bool debug = false;
13413
13414 if (debug)
13415 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13416
13417 /* extract result variables and place it to argument list */
13418
13419 const char* param_ptr = buf;
13420
13421 for (int i = 0; rl.param[i].tid != 0; i++) {
13422 int tid = rl.param[i].tid;
13423 int flags = rl.param[i].flags;
13424 int arg_type = 0;
13425
13426 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13427 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13428 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13429
13430 if (bpointer)
13432 else
13433 arg_type = rl.param[i].tid;
13434
13435 if (tid == TID_FLOAT && !bpointer)
13437
13438 char arg[8];
13439 rpc_va_arg(&ap, arg_type, arg);
13440
13441 if (rl.param[i].flags & RPC_OUT) {
13442
13443 if (param_ptr == NULL) {
13444 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);
13445 return RPC_NET_ERROR;
13446 }
13447
13448 tid = rl.param[i].tid;
13449 int arg_size = rpc_tid_size(tid);
13450
13451 if (tid == TID_STRING || tid == TID_LINK)
13452 arg_size = strlen((char *) (param_ptr)) + 1;
13453
13454 if (flags & RPC_VARARRAY) {
13455 arg_size = *((INT *) param_ptr);
13456 param_ptr += ALIGN8(sizeof(INT));
13457 }
13458
13459 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13460 arg_size = rl.param[i].n;
13461
13462 /* parameter size is always aligned */
13463 int param_size = ALIGN8(arg_size);
13464
13465 /* return parameters are always pointers */
13466 if (*((char **) arg)) {
13467 if (debug)
13468 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);
13469 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13470 }
13471
13472 param_ptr += param_size;
13473 }
13474 }
13475
13476 return RPC_SUCCESS;
13477}
13478
13479/********************************************************************/
13481/********************************************************************\
13482
13483 Routine: rpc_client_call
13484
13485 Purpose: Call a function on a MIDAS client
13486
13487 Input:
13488 INT hConn Client connection
13489 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13490
13491 ... variable argument list
13492
13493 Output:
13494 (depends on argument list)
13495
13496 Function value:
13497 RPC_SUCCESS Successful completion
13498 RPC_NET_ERROR Error in socket call
13499 RPC_NO_CONNECTION No active connection
13500 RPC_TIMEOUT Timeout in RPC call
13501 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13502 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13503
13504\********************************************************************/
13505{
13507
13508 if (!c) {
13509 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13510 return RPC_NO_CONNECTION;
13511 }
13512
13513 //printf("rpc_client_call: handle %d, connection: ", hConn);
13514 //c->print();
13515 //printf("\n");
13516
13517 INT i, status;
13518
13519 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13520 routine_id &= ~RPC_NO_REPLY;
13521
13522 //if (rpc_no_reply)
13523 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13524
13525 // make local copy of the client name just in case _client_connection is erased by another thread
13526
13527 /* find rpc_index */
13528
13529 int rpc_index = -1;
13530 const char *rpc_name = NULL;
13532
13533 rpc_list_mutex.lock();
13534 for (size_t i = 0; i < rpc_list.size(); i++) {
13535 if (rpc_list[i].id == (int) routine_id) {
13536 rpc_index = i;
13539 break;
13540 }
13541 }
13542 rpc_list_mutex.unlock();
13543
13544 if (rpc_index < 0) {
13545 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);
13546 c->mutex.unlock();
13547 return RPC_INVALID_ID;
13548 }
13549
13550 NET_COMMAND *nc = NULL;
13551
13552 /* examine variable argument list and convert it to parameter array */
13553 va_list ap;
13554 va_start(ap, routine_id);
13555
13557
13558 va_end(ap);
13559
13560 nc->header.routine_id = routine_id;
13561
13562 if (rpc_no_reply)
13564
13565 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13566
13567 /* in FAST TCP mode, only send call and return immediately */
13568 if (rpc_no_reply) {
13569 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13570
13571 if (i != send_size) {
13572 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);
13573 free(nc);
13574 c->mutex.unlock();
13575 return RPC_NET_ERROR;
13576 }
13577
13578 free(nc);
13579
13580 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
13581 //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);
13582 //c->print();
13583 //printf("\n");
13584 c->close_locked();
13585 }
13586
13587 c->mutex.unlock();
13588 return RPC_SUCCESS;
13589 }
13590
13591 /* in TCP mode, send and wait for reply on send socket */
13592 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13593 if (i != send_size) {
13594 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);
13595 c->mutex.unlock();
13596 return RPC_NET_ERROR;
13597 }
13598
13599 free(nc);
13600 nc = NULL;
13601
13602 bool restore_watchdog_timeout = false;
13604 DWORD watchdog_timeout;
13605 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13606
13607 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
13608
13609 if (c->rpc_timeout >= (int) watchdog_timeout) {
13611 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
13612 }
13613
13614 DWORD rpc_status = 0;
13615 DWORD buf_size = 0;
13616 char* buf = NULL;
13617
13618 /* receive result on send socket */
13619 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
13620
13622 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
13623 }
13624
13625 if (status == SS_TIMEOUT) {
13626 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);
13627 if (buf)
13628 free(buf);
13629 c->mutex.unlock();
13630 return RPC_TIMEOUT;
13631 }
13632
13633 if (status != SS_SUCCESS) {
13634 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);
13635 if (buf)
13636 free(buf);
13637 c->mutex.unlock();
13638 return RPC_NET_ERROR;
13639 }
13640
13641 c->mutex.unlock();
13642
13643 if (rpc_status == RPC_INVALID_ID) {
13644 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);
13645 if (buf)
13646 free(buf);
13647 return rpc_status;
13648 }
13649
13650 /* extract result variables and place it to argument list */
13651
13652 va_start(ap, routine_id);
13653
13654 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13655
13656 if (status != RPC_SUCCESS) {
13658 }
13659
13660 va_end(ap);
13661
13662 if (buf)
13663 free(buf);
13664 buf = NULL;
13665 buf_size = 0;
13666
13667 return rpc_status;
13668}
13669
13670/********************************************************************/
13671INT rpc_call(DWORD routine_id, ...)
13672/********************************************************************\
13673
13674 Routine: rpc_call
13675
13676 Purpose: Call a function on a MIDAS server
13677
13678 Input:
13679 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13680
13681 ... variable argument list
13682
13683 Output:
13684 (depends on argument list)
13685
13686 Function value:
13687 RPC_SUCCESS Successful completion
13688 RPC_NET_ERROR Error in socket call
13689 RPC_NO_CONNECTION No active connection
13690 RPC_TIMEOUT Timeout in RPC call
13691 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13692 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13693
13694\********************************************************************/
13695{
13696 va_list ap;
13697 INT i, status;
13698
13699 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13700 routine_id &= ~RPC_NO_REPLY;
13701
13702 //if (rpc_no_reply)
13703 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13704
13705 int send_sock = _server_connection.send_sock;
13706 int rpc_timeout = _server_connection.rpc_timeout;
13707
13708 if (!send_sock) {
13709 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
13710 return RPC_NET_ERROR;
13711 }
13712
13713 if (!_mutex_rpc) {
13714 /* create a local mutex for multi-threaded applications */
13716 }
13717
13718 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
13719 if (status != SS_SUCCESS) {
13720 cm_msg(MERROR, "rpc_call", "Mutex timeout");
13721 return RPC_MUTEX_TIMEOUT;
13722 }
13723
13724 /* find rpc definition */
13725
13726 int idx = -1;
13727 const char* rpc_name = NULL;
13729
13730 rpc_list_mutex.lock();
13731
13732 for (size_t i = 0; i < rpc_list.size(); i++) {
13733 if (rpc_list[i].id == (int) routine_id) {
13734 idx = i;
13737 break;
13738 }
13739 }
13740
13741 rpc_list_mutex.unlock();
13742
13743 if (idx < 0) {
13745 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
13746 return RPC_INVALID_ID;
13747 }
13748
13749 /* prepare output buffer */
13750
13751 NET_COMMAND* nc = NULL;
13752
13753 /* examine variable argument list and convert it to parameter array */
13754 va_start(ap, routine_id);
13755
13757
13758 va_end(ap);
13759
13760 nc->header.routine_id = routine_id;
13761
13762 if (rpc_no_reply)
13764
13765 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13766
13767 /* do not wait for reply if requested RPC_NO_REPLY */
13768 if (rpc_no_reply) {
13769 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13770
13771 if (i != send_size) {
13773 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13774 free(nc);
13775 return RPC_NET_ERROR;
13776 }
13777
13779 free(nc);
13780 return RPC_SUCCESS;
13781 }
13782
13783 /* in TCP mode, send and wait for reply on send socket */
13784 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13785 if (i != send_size) {
13787 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13788 free(nc);
13789 return RPC_NET_ERROR;
13790 }
13791
13792 free(nc);
13793 nc = NULL;
13794
13795 bool restore_watchdog_timeout = false;
13797 DWORD watchdog_timeout;
13798 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13799
13800 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
13801
13802 if (!rpc_is_remote()) {
13803 // if RPC is remote, we are connected to an mserver,
13804 // the mserver takes care of watchdog timeouts.
13805 // otherwise we should make sure the watchdog timeout
13806 // is longer than the RPC timeout. K.O.
13807 if (rpc_timeout >= (int) watchdog_timeout) {
13809 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
13810 }
13811 }
13812
13813 DWORD rpc_status = 0;
13814 DWORD buf_size = 0;
13815 char* buf = NULL;
13816
13817 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
13818
13821 }
13822
13823 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
13824
13826
13827 /* check for reply errors */
13828
13829 if (status == SS_TIMEOUT) {
13830 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
13831 if (buf)
13832 free(buf);
13833 abort(); // cannot continue - our mserver is not talking to us!
13834 return RPC_TIMEOUT;
13835 }
13836
13837 if (status != SS_SUCCESS) {
13838 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
13839 if (buf)
13840 free(buf);
13841 abort(); // cannot continue - something is wrong with our mserver connection
13842 return RPC_NET_ERROR;
13843 }
13844
13845 if (rpc_status == RPC_INVALID_ID) {
13846 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
13847 if (buf)
13848 free(buf);
13849 return rpc_status;
13850 }
13851
13852 /* extract result variables and place it to argument list */
13853
13854 va_start(ap, routine_id);
13855
13856 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13857
13858 if (status != RPC_SUCCESS) {
13860 }
13861
13862 va_end(ap);
13863
13864 if (buf)
13865 free(buf);
13866
13867 return rpc_status;
13868}
13869
13870
13871/********************************************************************/
13873 INT old;
13874
13877 return old;
13878}
13879
13881 return _opt_tcp_size;
13882}
13883
13885#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13886
13887/********************************************************************/
13909INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
13910{
13911 if (rpc_is_remote()) {
13912 return rpc_send_event1(buffer_handle, pevent);
13913 } else {
13914 return bm_send_event(buffer_handle, pevent, unused, async_flag);
13915 }
13916}
13917
13918/********************************************************************/
13927INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
13928{
13929 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
13930 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
13931}
13932
13933INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
13934{
13935 if (sg_n < 1) {
13936 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
13937 return BM_INVALID_SIZE;
13938 }
13939
13940 if (sg_ptr[0] == NULL) {
13941 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
13942 return BM_INVALID_SIZE;
13943 }
13944
13945 if (sg_len[0] < sizeof(EVENT_HEADER)) {
13946 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));
13947 return BM_INVALID_SIZE;
13948 }
13949
13950 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
13951
13952 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
13953 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
13954
13955 if (data_size == 0) {
13956 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
13957 return BM_INVALID_SIZE;
13958 }
13959
13960 if (data_size > MAX_DATA_SIZE) {
13961 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);
13962 return BM_INVALID_SIZE;
13963 }
13964
13965 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
13966 const size_t total_size = ALIGN8(event_size);
13967
13968 size_t count = 0;
13969 for (int i=0; i<sg_n; i++) {
13970 count += sg_len[i];
13971 }
13972
13973 if (count != event_size) {
13974 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);
13975 return BM_INVALID_SIZE;
13976 }
13977
13978 // protect non-atomic access to _server_connection.event_sock. K.O.
13979
13980 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
13981
13982 //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);
13983
13984 if (_server_connection.event_sock == 0) {
13985 return RPC_NO_CONNECTION;
13986 }
13987
13988 //
13989 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
13990 //
13991 // 4 bytes of buffer handle
13992 // 16 bytes of event header, includes data_size
13993 // ALIGN8(data_size) bytes of event data
13994 //
13995
13996 int status;
13997
13998 /* send buffer handle */
13999
14000 assert(sizeof(DWORD) == 4);
14001 DWORD bh_buf = buffer_handle;
14002
14003 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14004 if (status != SS_SUCCESS) {
14006 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14007 return RPC_NET_ERROR;
14008 }
14009
14010 /* send data */
14011
14012 for (int i=0; i<sg_n; i++) {
14014 if (status != SS_SUCCESS) {
14016 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14017 return RPC_NET_ERROR;
14018 }
14019 }
14020
14021 /* send padding */
14022
14023 if (count < total_size) {
14024 char padding[8] = { 0,0,0,0,0,0,0,0 };
14025 size_t padlen = total_size - count;
14026 assert(padlen < 8);
14028 if (status != SS_SUCCESS) {
14030 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14031 return RPC_NET_ERROR;
14032 }
14033 }
14034
14035 return RPC_SUCCESS;
14036}
14037
14038/********************************************************************/
14047 return RPC_SUCCESS;
14048}
14049
14050/********************************************************************/
14051
14052struct TR_FIFO {
14053 int transition = 0;
14054 int run_number = 0;
14057};
14058
14059static std::mutex _tr_fifo_mutex;
14061static int _tr_fifo_wp = 0;
14062static int _tr_fifo_rp = 0;
14063
14065/********************************************************************\
14066
14067 Routine: rpc_transition_dispatch
14068
14069 Purpose: Gets called when a transition function was registered and
14070 a transition occured. Internal use only.
14071
14072 Input:
14073 INT idx RPC function ID
14074 void *prpc_param RPC parameters
14075
14076 Output:
14077 none
14078
14079 Function value:
14080 INT return value from called user routine
14081
14082\********************************************************************/
14083{
14084 /* erase error string */
14085 *(CSTRING(2)) = 0;
14086
14087 if (idx == RPC_RC_TRANSITION) {
14088 // find registered handler
14089 // NB: this code should match same code in cm_transition_call_direct()
14090 // NB: only use the first handler, this is how MIDAS always worked
14091 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14092 _trans_table_mutex.lock();
14093 size_t n = _trans_table.size();
14094 _trans_table_mutex.unlock();
14095
14096 for (size_t i = 0; i < n; i++) {
14097 _trans_table_mutex.lock();
14099 _trans_table_mutex.unlock();
14100
14101 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14102 if (tt.func) {
14103 /* execute callback if defined */
14104 return tt.func(CINT(1), CSTRING(2));
14105 } else {
14106 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14107 /* store transition in FIFO */
14112 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14113 // implicit unlock
14114 return RPC_SUCCESS;
14115 }
14116 }
14117 }
14118 // no handler for this transition
14119 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14120 return CM_SUCCESS;
14121 } else {
14122 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14123 return RPC_INVALID_ID;
14124 }
14125}
14126
14127/********************************************************************/
14128int cm_query_transition(int *transition, int *run_number, int *trans_time)
14129/********************************************************************\
14130
14131 Routine: cm_query_transition
14132
14133 Purpose: Query system if transition has occured. Normally, one
14134 registers callbacks for transitions via
14135 cm_register_transition. In some environments however,
14136 callbacks are not possible. In that case one spciefies
14137 a NULL pointer as the callback routine and can query
14138 transitions "manually" by calling this functions. A small
14139 FIFO takes care that no transition is lost if this functions
14140 did not get called between some transitions.
14141
14142 Output:
14143 INT *transition Type of transition, one of TR_xxx
14144 INT *run_nuber Run number for transition
14145 time_t *trans_time Time (in UNIX time) of transition
14146
14147 Function value:
14148 FALSE No transition occured since last call
14149 TRUE Transition occured
14150
14151\********************************************************************/
14152{
14153 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14154
14155 if (_tr_fifo_wp == _tr_fifo_rp)
14156 return FALSE;
14157
14158 if (transition)
14160
14161 if (run_number)
14163
14164 if (trans_time)
14165 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14166
14167 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14168
14169 // implicit unlock
14170 return TRUE;
14171}
14172
14173/********************************************************************\
14174* server functions *
14175\********************************************************************/
14176
14177#if 0
14178void debug_dump(unsigned char *p, int size)
14179{
14180 int i, j;
14181 unsigned char c;
14182
14183 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14184 printf("%p ", p + i * 16);
14185 for (j = 0; j < 16; j++)
14186 if (i * 16 + j < size)
14187 printf("%02X ", p[i * 16 + j]);
14188 else
14189 printf(" ");
14190 printf(" ");
14191
14192 for (j = 0; j < 16; j++) {
14193 c = p[i * 16 + j];
14194 if (i * 16 + j < size)
14195 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14196 }
14197 printf("\n");
14198 }
14199
14200 printf("\n");
14201}
14202#endif
14203
14204/********************************************************************/
14206/********************************************************************\
14207
14208 Routine: recv_net_command
14209
14210 Purpose: TCP receive routine with local cache. To speed up network
14211 performance, a 64k buffer is read in at once and split into
14212 several RPC command on successive calls to recv_net_command.
14213 Therefore, the number of recv() calls is minimized.
14214
14215 This routine is ment to be called by the server process.
14216 Clients should call recv_tcp instead.
14217
14218 Input:
14219 INT idx Index of server connection
14220 DWORD buffer_size Size of the buffer in bytes.
14221 INT flags Flags passed to recv()
14222 INT convert_flags Convert flags needed for big/little
14223 endian conversion
14224
14225 Output:
14226 char *buffer Network receive buffer.
14227 INT *remaining Remaining data in cache
14228
14229 Function value:
14230 INT Same as recv()
14231
14232\********************************************************************/
14233{
14234 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14235
14237
14238 int sock = sa->recv_sock;
14239
14240 if (!sa->net_buffer) {
14241 if (sa->is_mserver)
14243 else
14245
14246 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14247 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14248 sa->write_ptr = 0;
14249 sa->read_ptr = 0;
14250 sa->misalign = 0;
14251 }
14252 if (!sa->net_buffer) {
14253 cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14254 return -1;
14255 }
14256
14257 int copied = 0;
14258 int param_size = -1;
14259
14260 int write_ptr = sa->write_ptr;
14261 int read_ptr = sa->read_ptr;
14262 int misalign = sa->misalign;
14263 char *net_buffer = sa->net_buffer;
14264
14265 do {
14266 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14267 if (param_size == -1) {
14268 if (copied > 0) {
14269 /* assemble split header */
14270 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14271 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14272 param_size = (INT) nc->header.param_size;
14273 } else {
14274 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14275 param_size = (INT) nc->header.param_size;
14276 }
14277
14278 if (sa->convert_flags)
14279 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14280 }
14281
14282 //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14283
14284 /* check if parameters fit in buffer */
14285 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14286 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14287 char *p = (char *) realloc(*pbuf, new_size);
14288 //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14289 if (p == NULL) {
14290 cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14291 sa->read_ptr = 0;
14292 sa->write_ptr = 0;
14293 return -1;
14294 }
14295 *pbuf = p;
14296 *pbufsize = new_size;
14297 }
14298
14299 buffer = *pbuf;
14300
14301 /* check if we have all parameters in buffer */
14302 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14303 break;
14304 }
14305
14306 /* not enough data, so copy partially and get new */
14307 int size = write_ptr - read_ptr;
14308
14309 if (size > 0) {
14310 memcpy(buffer + copied, net_buffer + read_ptr, size);
14311 copied += size;
14312 read_ptr = write_ptr;
14313 }
14314#ifdef OS_UNIX
14315 do {
14316 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14317
14318 /* don't return if an alarm signal was cought */
14319 } while (write_ptr == -1 && errno == EINTR);
14320#else
14321 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14322#endif
14323
14324 /* abort if connection broken */
14325 if (write_ptr <= 0) {
14326 if (write_ptr == 0)
14327 cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14328 else
14329 cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14330
14331 if (remaining)
14332 *remaining = 0;
14333
14334 return write_ptr;
14335 }
14336
14337 read_ptr = misalign;
14338 write_ptr += misalign;
14339
14340 misalign = write_ptr % 8;
14341 } while (TRUE);
14342
14343 /* copy rest of parameters */
14344 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14345 memcpy(buffer + copied, net_buffer + read_ptr, size);
14346 read_ptr += size;
14347
14348 if (remaining) {
14349 /* don't keep rpc_server_receive in an infinite loop */
14350 if (write_ptr - read_ptr < param_size)
14351 *remaining = 0;
14352 else
14353 *remaining = write_ptr - read_ptr;
14354 }
14355
14356 sa->write_ptr = write_ptr;
14357 sa->read_ptr = read_ptr;
14358 sa->misalign = misalign;
14359
14360 return size + copied;
14361}
14362
14363
14364/********************************************************************/
14366/********************************************************************\
14367
14368 Routine: recv_tcp_check
14369
14370 Purpose: Check if in TCP receive buffer associated with sock is
14371 some data. Called by ss_suspend.
14372
14373 Input:
14374 INT sock TCP receive socket
14375
14376 Output:
14377 none
14378
14379 Function value:
14380 INT count Number of bytes remaining in TCP buffer
14381
14382\********************************************************************/
14383{
14384 /* figure out to which connection socket belongs */
14385 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14386 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14387 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14388 }
14389
14390 return 0;
14391}
14392
14393
14394/********************************************************************/
14396/********************************************************************\
14397
14398 Routine: recv_event_server_realloc
14399
14400 Purpose: receive events sent by rpc_send_event()
14401
14402 Input:
14403 INT idx Index of server connection
14404 DWORD buffer_size Size of the buffer in bytes.
14405 INT flags Flags passed to recv()
14406 INT convert_flags Convert flags needed for big/little
14407 endian conversion
14408
14409 Output:
14410 char *buffer Network receive buffer.
14411 INT *remaining Remaining data in cache
14412
14413 Function value:
14414 INT Same as recv()
14415
14416\********************************************************************/
14417{
14418 int sock = psa->event_sock;
14419
14420 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14421
14422 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14423
14424 char header_buf[header_size];
14425
14426 // First read the header.
14427 //
14428 // Data format is:
14429 // INT buffer handle (4 bytes)
14430 // EVENT_HEADER (16 bytes)
14431 // event data
14432 // ALIGN8() padding
14433 // ...next event
14434
14435 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14436
14437 if (hrd == 0) {
14438 // timeout waiting for data
14439 return 0;
14440 }
14441
14442 /* abort if connection broken */
14443 if (hrd < 0) {
14444 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14445 return -1;
14446 }
14447
14448 if (hrd < (int) header_size) {
14449 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14450
14451 /* abort if connection broken */
14452 if (hrd1 <= 0) {
14453 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14454 return -1;
14455 }
14456
14457 hrd += hrd1;
14458 }
14459
14460 /* abort if connection broken */
14461 if (hrd != (int) header_size) {
14462 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14463 return -1;
14464 }
14465
14466 INT *pbh = (INT *) header_buf;
14467 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14468
14469 /* convert header little endian/big endian */
14470 if (psa->convert_flags) {
14471 rpc_convert_single(&pbh, TID_INT32, 0, psa->convert_flags);
14472 rpc_convert_single(&pevent->event_id, TID_INT16, 0, psa->convert_flags);
14473 rpc_convert_single(&pevent->trigger_mask, TID_INT16, 0, psa->convert_flags);
14474 rpc_convert_single(&pevent->serial_number, TID_UINT32, 0, psa->convert_flags);
14475 rpc_convert_single(&pevent->time_stamp, TID_UINT32, 0, psa->convert_flags);
14476 rpc_convert_single(&pevent->data_size, TID_UINT32, 0, psa->convert_flags);
14477 }
14478
14479 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14480 int total_size = ALIGN8(event_size);
14481
14482 //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);
14483
14484 if (pevent->data_size == 0) {
14485 for (int i=0; i<5; i++) {
14486 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14487 }
14488 abort();
14489 }
14490
14491 /* check for sane event size */
14492 if (event_size <= 0 || total_size <= 0) {
14493 cm_msg(MERROR, "recv_event_server",
14494 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14495 event_size, total_size);
14496 return -1;
14497 }
14498
14499 //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);
14500
14501
14502 int bufsize = sizeof(INT) + total_size;
14503
14504 // Second, check that output buffer is big enough
14505
14506 /* check if data part fits in buffer */
14507 if (*pbuffer_size < bufsize) {
14508 int newsize = 1024 + ALIGN8(bufsize);
14509
14510 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14511
14512 char *newbuf = (char *) realloc(*pbuffer, newsize);
14513 if (newbuf == NULL) {
14514 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14515 newsize);
14516 return -1;
14517 }
14518 *pbuffer = newbuf;
14520 }
14521
14522 // Third, copy header into output buffer
14523
14525
14526 // Forth, read the event data
14527
14528 int to_read = sizeof(INT) + total_size - header_size;
14529 int rptr = header_size;
14530
14531 if (to_read > 0) {
14532 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14533
14534 /* abort if connection broken */
14535 if (drd <= 0) {
14536 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14537 return -1;
14538 }
14539 }
14540
14541 return bufsize;
14542}
14543
14544
14545/********************************************************************/
14547/********************************************************************\
14548
14549 Routine: rpc_register_listener
14550
14551 Purpose: Register the calling process as a MIDAS RPC server. Note
14552 that cm_connnect_experiment must be called prior to any call of
14553 rpc_register_server.
14554
14555 Input:
14556 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14557
14558 Output:
14559 int *plsock Listener socket, can be NULL
14560 int *pport Port under which server is listening, can be NULL
14561
14562 Function value:
14563 RPC_SUCCESS Successful completion
14564 RPC_NET_ERROR Error in socket call
14565 RPC_NOT_REGISTERED cm_connect_experiment was not called
14566
14567\********************************************************************/
14568{
14569 int status;
14570 int lsock;
14571
14573 if (status != RPC_SUCCESS)
14574 return status;
14575
14577 if (status != SS_SUCCESS)
14578 return status;
14579
14580 if (plsock)
14581 *plsock = lsock;
14582
14583 return RPC_SUCCESS;
14584}
14585
14586/********************************************************************/
14588/********************************************************************\
14589
14590 Routine: rpc_register_listener
14591
14592 Purpose: Register the calling process as a MIDAS RPC server. Note
14593 that cm_connnect_experiment must be called prior to any call of
14594 rpc_register_listener.
14595
14596 Input:
14597 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14598 INT *func Default dispatch function
14599
14600 Output:
14601 int *plsock Listener socket, should not be NULL
14602 int *pport Port under which server is listening, can be NULL
14603
14604 Function value:
14605 RPC_SUCCESS Successful completion
14606 RPC_NET_ERROR Error in socket call
14607 RPC_NOT_REGISTERED cm_connect_experiment was not called
14608
14609\********************************************************************/
14610{
14611 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
14613
14614 /* create a socket for listening */
14615 int lsock = 0;
14616 int lport = 0;
14617 std::string errmsg;
14618
14620
14621 if (status != SS_SUCCESS) {
14622 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
14623 return RPC_NET_ERROR;
14624 }
14625
14626 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
14627#if defined(F_SETFD) && defined(FD_CLOEXEC)
14629 if (status < 0) {
14630 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
14631 return RPC_NET_ERROR;
14632 }
14633#endif
14634
14635 /* return port wich OS has choosen */
14636 if (pport) {
14637 *pport = lport;
14638 }
14639
14640 if (plsock)
14641 *plsock = lsock;
14642
14643 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
14644
14645 return RPC_SUCCESS;
14646}
14647
14653
14655static int tls_size = 0;
14656
14657/********************************************************************/
14658INT rpc_execute(INT sock, char *buffer, INT convert_flags)
14659/********************************************************************\
14660
14661 Routine: rpc_execute
14662
14663 Purpose: Execute a RPC command received over the network
14664
14665 Input:
14666 INT sock TCP socket to which the result should be
14667 send back
14668
14669 char *buffer Command buffer
14670 INT convert_flags Flags for data conversion
14671
14672 Output:
14673 none
14674
14675 Function value:
14676 RPC_SUCCESS Successful completion
14677 RPC_INVALID_ID Invalid routine_id received
14678 RPC_NET_ERROR Error in socket call
14679 RPC_EXCEED_BUFFER Not enough memory for network buffer
14680 RPC_SHUTDOWN Shutdown requested
14681 SS_ABORT TCP connection broken
14682 SS_EXIT TCP connection closed
14683
14684\********************************************************************/
14685{
14686 INT i, routine_id, status;
14688 INT tid, flags;
14690 INT param_size, max_size;
14691 void *prpc_param[20];
14692 char debug_line[1024], *return_buffer;
14695#ifdef FIXED_BUFFER
14697#else
14698 int initial_buffer_size = 1024;
14699#endif
14700
14701 /* return buffer must must use thread local storage multi-thread servers */
14702 if (!tls_size) {
14706 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14707 tls_size = 1;
14708 }
14709 for (i = 0; i < tls_size; i++)
14710 if (tls_buffer[i].thread_id == ss_gettid())
14711 break;
14712 if (i == tls_size) {
14713 /* new thread -> allocate new buffer */
14717 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14718 tls_size++;
14719 }
14720
14723 return_buffer = tls_buffer[i].buffer;
14724 assert(return_buffer);
14725
14726 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
14727 // and valgrind complains that we transmit uninitialized data
14728 //memset(return_buffer, 0, return_buffer_size);
14729
14730 /* extract pointer array to parameters */
14731 nc_in = (NET_COMMAND *) buffer;
14732
14733 /* convert header format (byte swapping) */
14734 if (convert_flags) {
14735 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
14736 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
14737 }
14738
14739 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
14740 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
14741 //}
14742
14743 /* no result return as requested */
14744 if (nc_in->header.routine_id & RPC_NO_REPLY)
14745 sock = 0;
14746
14747 /* find entry in rpc_list */
14748 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
14749
14750 int idx = -1;
14751 RPC_LIST rl;
14752
14753 rpc_list_mutex.lock();
14754
14755 for (size_t i = 0; i < rpc_list.size(); i++) {
14756 if (rpc_list[i].id == routine_id) {
14757 idx = i;
14758 rl = rpc_list[idx];
14759 break;
14760 }
14761 }
14762
14763 rpc_list_mutex.unlock();
14764
14765 if (idx < 0) {
14766 cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
14767 return RPC_INVALID_ID;
14768 }
14769
14770 again:
14771
14772 in_param_ptr = nc_in->param;
14773
14774 nc_out = (NET_COMMAND *) return_buffer;
14775 out_param_ptr = nc_out->param;
14776
14777 sprintf(debug_line, "%s(", rl.name);
14778
14779 for (i = 0; rl.param[i].tid != 0; i++) {
14780 tid = rl.param[i].tid;
14781 flags = rl.param[i].flags;
14782
14783 if (flags & RPC_IN) {
14784 param_size = ALIGN8(rpc_tid_size(tid));
14785
14786 if (tid == TID_STRING || tid == TID_LINK)
14787 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
14788
14789 if (flags & RPC_VARARRAY) {
14790 /* for arrays, the size is stored as a INT in front of the array */
14791 param_size = *((INT *) in_param_ptr);
14792 if (convert_flags)
14793 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
14794 param_size = ALIGN8(param_size);
14795
14796 in_param_ptr += ALIGN8(sizeof(INT));
14797 }
14798
14799 if (tid == TID_STRUCT)
14800 param_size = ALIGN8(rl.param[i].n);
14801
14803
14804 /* convert data format */
14805 if (convert_flags) {
14806 if (flags & RPC_VARARRAY)
14807 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
14808 else
14809 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
14810 convert_flags);
14811 }
14812
14813 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
14814 if (rl.param[i].tid == TID_STRING) {
14815 /* check for long strings (db_create_record...) */
14816 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
14817 strcat(debug_line, "\"");
14818 strcat(debug_line, str.c_str());
14819 strcat(debug_line, "\"");
14820 } else
14821 strcat(debug_line, "...");
14822 } else
14823 strcat(debug_line, str.c_str());
14824
14825 in_param_ptr += param_size;
14826 }
14827
14828 if (flags & RPC_OUT) {
14829 param_size = ALIGN8(rpc_tid_size(tid));
14830
14831 if (flags & RPC_VARARRAY || tid == TID_STRING) {
14832
14833 /* save maximum array length from the value of the next argument.
14834 * this means RPC_OUT arrays and strings should always be passed like this:
14835 * rpc_call(..., array_ptr, array_max_size, ...); */
14836
14837 max_size = *((INT *) in_param_ptr);
14838
14839 if (convert_flags)
14840 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
14842
14843 *((INT *) out_param_ptr) = max_size;
14844
14845 /* save space for return array length */
14846 out_param_ptr += ALIGN8(sizeof(INT));
14847
14848 /* use maximum array length from input */
14849 param_size = max_size;
14850 }
14851
14852 if (rl.param[i].tid == TID_STRUCT)
14853 param_size = ALIGN8(rl.param[i].n);
14854
14855 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
14856#ifdef FIXED_BUFFER
14857 cm_msg(MERROR, "rpc_execute",
14858 "return parameters (%d) too large for network buffer (%d)",
14860
14861 return RPC_EXCEED_BUFFER;
14862#else
14863 int itls;
14864 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
14865
14866#if 0
14867 cm_msg(MINFO, "rpc_execute",
14868 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
14870#endif
14871
14873
14875 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
14876
14877 if (!tls_buffer[itls].buffer) {
14878 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
14879 return RPC_EXCEED_BUFFER;
14880 }
14881
14883 return_buffer = tls_buffer[itls].buffer;
14884 assert(return_buffer);
14885
14886 goto again;
14887#endif
14888 }
14889
14890 /* if parameter goes both directions, copy input to output */
14891 if (rl.param[i].flags & RPC_IN)
14892 memcpy(out_param_ptr, prpc_param[i], param_size);
14893
14894 if (_debug_print && !(flags & RPC_IN))
14895 strcat(debug_line, "-");
14896
14898 out_param_ptr += param_size;
14899 }
14900
14901 if (rl.param[i + 1].tid)
14902 strcat(debug_line, ", ");
14903 }
14904
14905 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
14906
14907 strcat(debug_line, ")");
14909
14911
14912 /*********************************\
14913 * call dispatch function *
14914 \*********************************/
14915 if (rl.dispatch)
14916 status = rl.dispatch(routine_id, prpc_param);
14917 else
14919
14920 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
14922
14923 /* return immediately for closed down client connections */
14924 if (!sock && routine_id == RPC_ID_EXIT)
14925 return SS_EXIT;
14926
14927 if (!sock && routine_id == RPC_ID_SHUTDOWN)
14928 return RPC_SHUTDOWN;
14929
14930 /* Return if TCP connection broken */
14931 if (status == SS_ABORT)
14932 return SS_ABORT;
14933
14934 /* if sock == 0, we are in FTCP mode and may not sent results */
14935 if (!sock)
14936 return RPC_SUCCESS;
14937
14938 /* compress variable length arrays */
14939 out_param_ptr = nc_out->param;
14940 for (i = 0; rl.param[i].tid != 0; i++)
14941 if (rl.param[i].flags & RPC_OUT) {
14942 tid = rl.param[i].tid;
14943 flags = rl.param[i].flags;
14944 param_size = ALIGN8(rpc_tid_size(tid));
14945
14946 if (tid == TID_STRING) {
14947 max_size = *((INT *) out_param_ptr);
14948 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14949 // and prpc_param() is now pointing to the wrong place. here we know our string data
14950 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
14951 //const char* param_ptr = (char *) prpc_param[i];
14952 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
14953 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
14954 param_size = strlen(param_ptr) + 1;
14955 param_size = ALIGN8(param_size);
14956
14957 /* move string ALIGN8(sizeof(INT)) left */
14958 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
14959
14960 /* move remaining parameters to end of string */
14961 memmove(out_param_ptr + param_size,
14962 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
14964 }
14965
14966 if (flags & RPC_VARARRAY) {
14967 /* store array length at current out_param_ptr */
14968 max_size = *((INT *) out_param_ptr);
14969 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14970 // and prpc_param() is now pointing to the wrong place. instead, compute location
14971 // of next parameter using max_size. K.O.
14972 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
14973 if (rl.param[i+1].flags & RPC_OUT)
14974 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
14975 else
14976 param_size = *((INT *) prpc_param[i + 1]);
14977 *((INT *) out_param_ptr) = param_size; // store new array size
14978 if (convert_flags)
14980
14981 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
14982
14983 param_size = ALIGN8(param_size);
14984
14985 /* move remaining parameters to end of array */
14986 memmove(out_param_ptr + param_size,
14989 }
14990
14991 if (tid == TID_STRUCT)
14992 param_size = ALIGN8(rl.param[i].n);
14993
14994 /* convert data format */
14995 if (convert_flags) {
14996 if (flags & RPC_VARARRAY)
14998 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
14999 else
15001 rl.param[i].flags | RPC_OUTGOING,
15002 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15003 }
15004
15005 out_param_ptr += param_size;
15006 }
15007
15008 /* send return parameters */
15009 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15010 nc_out->header.routine_id = status;
15011 nc_out->header.param_size = param_size;
15012
15013 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15014
15015 /* convert header format (byte swapping) if necessary */
15016 if (convert_flags) {
15017 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15018 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15019 }
15020
15021 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15022 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15023
15024 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15025
15026 if (status < 0) {
15027 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15028 return RPC_NET_ERROR;
15029 }
15030
15031 /* print return buffer */
15032/*
15033 printf("Return buffer, ID %d:\n", routine_id);
15034 for (i=0; i<param_size ; i++)
15035 {
15036 status = (char) nc_out->param[i];
15037 printf("%02X ", status);
15038 if (i%8 == 7)
15039 printf("\n");
15040 }
15041*/
15042 /* return SS_EXIT if RPC_EXIT is called */
15043 if (routine_id == RPC_ID_EXIT)
15044 return SS_EXIT;
15045
15046 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15047 if (routine_id == RPC_ID_SHUTDOWN)
15048 return RPC_SHUTDOWN;
15049
15050 return RPC_SUCCESS;
15051}
15052
15053/********************************************************************/
15055/********************************************************************\
15056 Routine: rpc_test_rpc
15057
15058 Purpose: Test RPC parameters encoding and decoding
15059
15060 Input:
15061 none
15062
15063 Output:
15064 none
15065
15066 Function value:
15067 RPC_SUCCESS Successful completion
15068
15069\********************************************************************/
15070{
15071 int status = RPC_SUCCESS;
15072
15073 printf("rpc_test_rpc!\n");
15074
15075 int int_out = 0;
15076 int int_inout = 456;
15077
15078 char string_out[32];
15079 char string2_out[48];
15080
15081 char string_inout[25];
15082 strcpy(string_inout, "string_inout");
15083
15084 KEY struct_in;
15085
15086 struct_in.type = 111;
15087 struct_in.num_values = 222;
15088 strcpy(struct_in.name, "name");
15089 struct_in.last_written = 333;
15090
15093
15094 struct_inout.type = 111111;
15095 struct_inout.num_values = 222222;
15096 strcpy(struct_inout.name, "name_name");
15097 struct_inout.last_written = 333333;
15098
15100 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15101
15102 for (int i=0; i<10; i++) {
15103 dwordarray_inout[i] = i*10;
15104 }
15105
15106 char array_in[10];
15107
15108 for (size_t i=0; i<sizeof(array_in); i++) {
15109 array_in[i] = 'a' + i;
15110 }
15111
15112 char array_out[16];
15113 size_t array_out_size = sizeof(array_out);
15114
15116 123,
15117 &int_out,
15118 &int_inout,
15119 "test string",
15120 string_out, sizeof(string_out),
15121 string2_out, sizeof(string2_out),
15122 string_inout, sizeof(string_inout),
15123 &struct_in,
15124 &struct_out,
15125 &struct_inout,
15127 array_in, sizeof(array_in),
15129 );
15130
15131 printf("rpc_call(RPC_TEST2) status %d\n", status);
15132
15133 if (int_out != 789) {
15134 printf("int_out mismatch!\n");
15135 status = 0;
15136 }
15137
15138 if (int_inout != 456*2) {
15139 printf("int_inout mismatch!\n");
15140 status = 0;
15141 }
15142
15143 if (strcmp(string_out, "string_out") != 0) {
15144 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
15145 status = 0;
15146 }
15147
15148 if (strcmp(string2_out, "second string_out") != 0) {
15149 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
15150 status = 0;
15151 }
15152
15153 if (strcmp(string_inout, "return string_inout") != 0) {
15154 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
15155 status = 0;
15156 }
15157
15158 KEY* pkey;
15159
15160 pkey = &struct_in;
15161
15162 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15163
15164 pkey = &struct_out;
15165
15166 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
15167 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);
15168 status = 0;
15169 }
15170
15171 pkey = &struct_inout;
15172
15173 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
15174 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);
15175 status = 0;
15176 }
15177
15178#if 0
15179 if (dwordarray_inout_size != 4*5) {
15180 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
15181 status = 0;
15182 }
15183
15184 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
15185 printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
15186 }
15187#endif
15188
15189#if 0
15190 {RPC_TEST_CXX, "test_cxx",
15191 {{TID_INT32, RPC_IN},
15193 {TID_STRING, RPC_IN},
15197 {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
15198 {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
15199 {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
15203 {0}}},
15204#endif
15205
15206#if 0
15208#endif
15209
15210 return status;
15211}
15212
15213static std::atomic_bool gAllowedHostsEnabled(false);
15214static std::vector<std::string> gAllowedHosts;
15215static std::mutex gAllowedHostsMutex;
15216
15217/********************************************************************/
15219/********************************************************************\
15220 Routine: rpc_clear_allowed_hosts
15221
15222 Purpose: Clear list of allowed hosts and permit connections from anybody
15223
15224 Input:
15225 none
15226
15227 Output:
15228 none
15229
15230 Function value:
15231 RPC_SUCCESS Successful completion
15232
15233\********************************************************************/
15234{
15235 gAllowedHostsMutex.lock();
15236 gAllowedHosts.clear();
15237 gAllowedHostsEnabled = false;
15238 gAllowedHostsMutex.unlock();
15239 return RPC_SUCCESS;
15240}
15241
15242/********************************************************************/
15243INT rpc_add_allowed_host(const char *hostname)
15244/********************************************************************\
15245 Routine: rpc_add_allowed_host
15246
15247 Purpose: Permit connections from listed hosts only
15248
15249 Input:
15250 none
15251
15252 Output:
15253 none
15254
15255 Function value:
15256 RPC_SUCCESS Successful completion
15257 RPC_NO_MEMORY Too many allowed hosts
15258
15259\********************************************************************/
15260{
15261 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
15262
15263 gAllowedHostsMutex.lock();
15264 gAllowedHosts.push_back(hostname);
15265 gAllowedHostsEnabled = true;
15266 gAllowedHostsMutex.unlock();
15267
15268 return RPC_SUCCESS;
15269}
15270
15271/********************************************************************/
15272INT rpc_check_allowed_host(const char *hostname)
15273/********************************************************************\
15274 Routine: rpc_check_allowed_host
15275
15276 Purpose: Check if hostname is permitted to connect
15277
15278 Function value:
15279 RPC_SUCCESS hostname is permitted to connect
15280 RPC_NOT_REGISTERED hostname not permitted to connect
15281
15282\********************************************************************/
15283{
15284 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
15285
15287 return RPC_SUCCESS;
15288
15289 if (strcmp(hostname, "localhost") == 0)
15290 return RPC_SUCCESS;
15291
15292 if (strcmp(hostname, "localhost.localdomain") == 0)
15293 return RPC_SUCCESS;
15294
15295 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
15296 return RPC_SUCCESS;
15297
15298 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
15299 return RPC_SUCCESS;
15300
15302
15303 gAllowedHostsMutex.lock();
15304
15305 for (const auto& h: gAllowedHosts) {
15306 if (h == hostname) {
15308 break;
15309 }
15310 }
15311
15312 gAllowedHostsMutex.unlock();
15313
15314 //if (status != RPC_SUCCESS)
15315 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
15316
15317 return status;
15318}
15319
15320/*------------------------------------------------------------------*/
15322{
15323 std::string hostname;
15324
15325 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
15326
15327 if (status != SS_SUCCESS)
15328 return status;
15329
15330 status = rpc_check_allowed_host(hostname.c_str());
15331
15332 if (status == RPC_SUCCESS)
15333 return RPC_SUCCESS;
15334
15335 static std::atomic_int max_report(10);
15336 if (max_report > 0) {
15337 max_report--;
15338 if (max_report == 0) {
15339 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
15340 } else {
15341 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());
15342 }
15343 }
15344
15345 return RPC_NET_ERROR;
15346}
15347
15348/********************************************************************/
15350/********************************************************************\
15351
15352 Routine: rpc_server_accept
15353
15354 Purpose: Accept new incoming connections
15355
15356 Input:
15357 INT lscok Listen socket
15358
15359 Output:
15360 none
15361
15362 Function value:
15363 RPC_SUCCESS Successful completion
15364 RPC_NET_ERROR Error in socket call
15365 RPC_CONNCLOSED Connection was closed
15366 RPC_SHUTDOWN Listener shutdown
15367 RPC_EXCEED_BUFFER Not enough memory for network buffer
15368
15369\********************************************************************/
15370{
15371 INT i;
15372 INT sock;
15373 char version[NAME_LENGTH], v1[32];
15374 char experiment[NAME_LENGTH];
15375 INT port1, port2, port3;
15376 char *ptr;
15377 char net_buffer[256];
15378 struct linger ling;
15379
15380 static struct callback_addr callback;
15381
15382 if (lsock > 0) {
15383 sock = accept(lsock, NULL, NULL);
15384
15385 if (sock == -1)
15386 return RPC_NET_ERROR;
15387 } else {
15388 /* lsock is stdin -> already connected from inetd */
15389
15390 sock = lsock;
15391 }
15392
15393 /* check access control list */
15396
15397 if (status != RPC_SUCCESS) {
15398 ss_socket_close(&sock);
15399 return RPC_NET_ERROR;
15400 }
15401 }
15402
15403 /* receive string with timeout */
15404 i = recv_string(sock, net_buffer, 256, 10000);
15405 rpc_debug_printf("Received command: %s", net_buffer);
15406
15407 if (i > 0) {
15408 char command = (char) toupper(net_buffer[0]);
15409
15410 //printf("rpc_server_accept: command [%c]\n", command);
15411
15412 switch (command) {
15413 case 'S': {
15414
15415 /*----------- shutdown listener ----------------------*/
15416 ss_socket_close(&sock);
15417 return RPC_SHUTDOWN;
15418 }
15419 case 'I': {
15420
15421 /*----------- return available experiments -----------*/
15422#ifdef LOCAL_ROUTINES
15423 exptab_struct exptab;
15424 cm_read_exptab(&exptab); // thread safe!
15425 for (unsigned i=0; i<exptab.exptab.size(); i++) {
15426 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
15427 const char* str = exptab.exptab[i].name.c_str();
15428 send(sock, str, strlen(str) + 1, 0);
15429 }
15430 send(sock, "", 1, 0);
15431#endif
15432 ss_socket_close(&sock);
15433 break;
15434 }
15435 case 'C': {
15436
15437 /*----------- connect to experiment -----------*/
15438
15439 /* get callback information */
15440 callback.experiment[0] = 0;
15441 port1 = port2 = version[0] = 0;
15442
15443 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
15444
15445 /* parse string in format "C port1 port2 port3 version expt" */
15446 /* example: C 51046 45838 56832 2.0.0 alpha */
15447
15448 port1 = strtoul(net_buffer + 2, &ptr, 0);
15449 port2 = strtoul(ptr, &ptr, 0);
15450 port3 = strtoul(ptr, &ptr, 0);
15451
15452 while (*ptr == ' ')
15453 ptr++;
15454
15455 i = 0;
15456 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
15457 version[i++] = *ptr++;
15458
15459 // ensure that we do not overwrite buffer "version"
15460 assert(i < (int) sizeof(version));
15461 version[i] = 0;
15462
15463 // skip wjatever is left from the "version" string
15464 for (; *ptr != 0 && *ptr != ' ';)
15465 ptr++;
15466
15467 while (*ptr == ' ')
15468 ptr++;
15469
15470 i = 0;
15471 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
15472 experiment[i++] = *ptr++;
15473
15474 // ensure that we do not overwrite buffer "experiment"
15475 assert(i < (int) sizeof(experiment));
15476 experiment[i] = 0;
15477
15479
15480 /* print warning if version patch level doesn't agree */
15481 mstrlcpy(v1, version, sizeof(v1));
15482 if (strchr(v1, '.'))
15483 if (strchr(strchr(v1, '.') + 1, '.'))
15484 *strchr(strchr(v1, '.') + 1, '.') = 0;
15485
15486 char str[100];
15487 mstrlcpy(str, cm_get_version(), sizeof(str));
15488 if (strchr(str, '.'))
15489 if (strchr(strchr(str, '.') + 1, '.'))
15490 *strchr(strchr(str, '.') + 1, '.') = 0;
15491
15492 if (strcmp(v1, str) != 0) {
15493 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
15494 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
15495 }
15496
15501
15503
15504 if (status != SS_SUCCESS) {
15505 ss_socket_close(&sock);
15506 break;
15507 }
15508
15509#ifdef LOCAL_ROUTINES
15510 /* update experiment definition */
15511 exptab_struct exptab;
15512 cm_read_exptab(&exptab); // thread safe!
15513
15514 unsigned idx = 0;
15515 bool found = false;
15516 /* lookup experiment */
15517 if (equal_ustring(callback.experiment.c_str(), "Default")) {
15518 found = true;
15519 idx = 0;
15520 } else {
15521 for (idx = 0; idx < exptab.exptab.size(); idx++) {
15522 if (exptab.exptab[idx].name == callback.experiment) {
15523 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
15524 found = true;
15525 break;
15526 }
15527 }
15528 }
15529 }
15530
15531 if (!found) {
15532 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
15533
15534 send(sock, "2", 2, 0); /* 2 means exp. not found */
15535 ss_socket_close(&sock);
15536 break;
15537 }
15538
15539 callback.directory = exptab.exptab[idx].directory;
15540 callback.user = exptab.exptab[idx].user;
15541
15542 /* create a new process */
15543 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
15544 char debug_str[30];
15545
15550
15551 const char *mserver_path = rpc_get_mserver_path();
15552
15553 const char *argv[10];
15554 argv[0] = mserver_path;
15555 argv[1] = callback.host_name.c_str();
15556 argv[2] = host_port1_str;
15557 argv[3] = host_port2_str;
15558 argv[4] = host_port3_str;
15559 argv[5] = debug_str;
15560 argv[6] = callback.experiment.c_str();
15561 argv[7] = callback.directory.c_str();
15562 argv[8] = callback.user.c_str();
15563 argv[9] = NULL;
15564
15565 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
15566 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
15567 argv[9]);
15568
15570
15571 if (status != SS_SUCCESS) {
15572 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
15573
15574 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
15575 send(sock, str, strlen(str) + 1, 0);
15576 ss_socket_close(&sock);
15577 break;
15578 }
15579
15580 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
15581 send(sock, str, strlen(str) + 1, 0);
15582#endif // LOCAL_ROUTINES
15583 ss_socket_close(&sock);
15584
15585 break;
15586 }
15587 default: {
15588 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
15589 ss_socket_close(&sock);
15590 break;
15591 }
15592 }
15593 } else { /* if i>0 */
15594
15595 /* lingering needed for PCTCP */
15596 ling.l_onoff = 1;
15597 ling.l_linger = 0;
15598 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
15599 ss_socket_close(&sock);
15600 }
15601
15602 return RPC_SUCCESS;
15603}
15604
15605/********************************************************************/
15607/********************************************************************\
15608
15609 Routine: rpc_client_accept
15610
15611 Purpose: midas program accept new RPC connection (run transitions, etc)
15612
15613 Input:
15614 INT lsock Listen socket
15615
15616 Output:
15617 none
15618
15619 Function value:
15620 RPC_SUCCESS Successful completion
15621 RPC_NET_ERROR Error in socket call
15622 RPC_CONNCLOSED Connection was closed
15623 RPC_SHUTDOWN Listener shutdown
15624 RPC_EXCEED_BUFFER Not enough memory for network buffer
15625
15626\********************************************************************/
15627{
15628 INT i, status;
15630 std::string client_program;
15631 std::string host_name;
15632 INT convert_flags;
15633 char net_buffer[256], *p;
15634
15635 int sock = accept(lsock, NULL, NULL);
15636
15637 if (sock == -1)
15638 return RPC_NET_ERROR;
15639
15640 /* check access control list */
15643
15644 if (status != RPC_SUCCESS) {
15645 ss_socket_close(&sock);
15646 return RPC_NET_ERROR;
15647 }
15648 }
15649
15650 host_name = "(unknown)";
15651 client_program = "(unknown)";
15652
15653 /* receive string with timeout */
15654 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
15655 if (i <= 0) {
15656 ss_socket_close(&sock);
15657 return RPC_NET_ERROR;
15658 }
15659
15660 /* get remote computer info */
15661 p = strtok(net_buffer, " ");
15662 if (p != NULL) {
15663 client_hw_type = atoi(p);
15664 p = strtok(NULL, " ");
15665 }
15666 if (p != NULL) {
15667 //version = atoi(p);
15668 p = strtok(NULL, " ");
15669 }
15670 if (p != NULL) {
15671 client_program = p;
15672 p = strtok(NULL, " ");
15673 }
15674 if (p != NULL) {
15675 host_name = p;
15676 p = strtok(NULL, " ");
15677 }
15678
15679 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
15680
15682
15683 /* save information in _server_acception structure */
15684 sa->recv_sock = sock;
15685 sa->send_sock = 0;
15686 sa->event_sock = 0;
15688 sa->host_name = host_name;
15691 sa->watchdog_timeout = 0;
15692 sa->is_mserver = FALSE;
15693
15694 /* send my own computer id */
15696 std::string str = msprintf("%d %s", hw_type, cm_get_version());
15697 status = send(sock, str.c_str(), str.length() + 1, 0);
15698 if (status != (INT) str.length() + 1)
15699 return RPC_NET_ERROR;
15700
15702 sa->convert_flags = convert_flags;
15703
15705
15706 return RPC_SUCCESS;
15707}
15708
15709/********************************************************************/
15711/********************************************************************\
15712
15713 Routine: rpc_server_callback
15714
15715 Purpose: Callback a remote client. Setup _server_acception entry
15716 with optional conversion flags and establish two-way
15717 TCP connection.
15718
15719 Input:
15720 callback_addr pcallback Pointer to a callback structure
15721
15722 Output:
15723 none
15724
15725 Function value:
15726 RPC_SUCCESS Successful completion
15727
15728\********************************************************************/
15729{
15730 INT status;
15731 int recv_sock, send_sock, event_sock;
15732 char str[100];
15733 std::string client_program;
15735 INT convert_flags;
15736 char net_buffer[256];
15737 char *p;
15738 int flag;
15739
15740 /* copy callback information */
15742 //idx = callback.index;
15743
15744 std::string errmsg;
15745
15746 /* create new sockets for TCP */
15748
15749 if (status != SS_SUCCESS) {
15750 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());
15751 ss_socket_close(&recv_sock);
15752 //ss_socket_close(&send_sock);
15753 //ss_socket_close(&event_sock);
15754 return RPC_NET_ERROR;
15755 }
15756
15758
15759 if (status != SS_SUCCESS) {
15760 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());
15761 ss_socket_close(&recv_sock);
15762 ss_socket_close(&send_sock);
15763 //ss_socket_close(&event_sock);
15764 return RPC_NET_ERROR;
15765 }
15766
15768
15769 if (status != SS_SUCCESS) {
15770 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());
15771 ss_socket_close(&recv_sock);
15772 ss_socket_close(&send_sock);
15773 ss_socket_close(&event_sock);
15774 return RPC_NET_ERROR;
15775 }
15776#ifndef OS_ULTRIX /* crashes ULTRIX... */
15777 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
15778 flag = 2 * 1024 * 1024;
15779 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
15780 if (status != 0)
15781 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
15782 strerror(errno));
15783#endif
15784
15785 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
15786 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
15787 ss_socket_close(&recv_sock);
15788 ss_socket_close(&send_sock);
15789 ss_socket_close(&event_sock);
15790 return RPC_NET_ERROR;
15791 }
15792 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
15793
15794 /* get remote computer info */
15795 client_hw_type = strtoul(net_buffer, &p, 0);
15796
15797 while (*p == ' ')
15798 p++;
15799
15800 client_program = p;
15801
15802 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
15803
15804 std::string host_name;
15805
15807
15808 if (status != SS_SUCCESS)
15809 host_name = "unknown";
15810
15811 //printf("rpc_server_callback: mserver acception\n");
15812
15814
15815 /* save information in _server_acception structure */
15816 sa->recv_sock = recv_sock;
15817 sa->send_sock = send_sock;
15818 sa->event_sock = event_sock;
15820 sa->host_name = host_name;
15823 sa->watchdog_timeout = 0;
15824 sa->is_mserver = TRUE;
15825
15826 assert(_mserver_acception == NULL);
15827
15828 _mserver_acception = sa;
15829
15830 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
15831
15832 /* send my own computer id */
15834 sprintf(str, "%d", hw_type);
15835 send(recv_sock, str, strlen(str) + 1, 0);
15836
15838 sa->convert_flags = convert_flags;
15839
15841
15842 if (rpc_is_mserver()) {
15843 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
15844 }
15845
15846 return RPC_SUCCESS;
15847}
15848
15849
15850/********************************************************************/
15852/********************************************************************\
15853
15854 Routine: rpc_server_loop
15855
15856 Purpose: mserver main event loop
15857
15858\********************************************************************/
15859{
15860 while (1) {
15861 int status = ss_suspend(1000, 0);
15862
15863 if (status == SS_ABORT || status == SS_EXIT)
15864 break;
15865
15867 break;
15868
15869 /* check alarms, etc */
15871
15873 }
15874
15875 return RPC_SUCCESS;
15876}
15877
15878/********************************************************************/
15880/********************************************************************\
15881
15882 Routine: rpc_server_receive_rpc
15883
15884 Purpose: Receive rpc commands and execute them. Close the connection
15885 if client has broken TCP pipe.
15886
15887 Function value:
15888 RPC_SUCCESS Successful completion
15889 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15890 SS_EXIT Server connection was closed
15891 SS_ABORT Server connection was broken
15892
15893\********************************************************************/
15894{
15895 int status = 0;
15896 int remaining = 0;
15897
15898 char *buf = NULL;
15899 int bufsize = 0;
15900
15901 do {
15903
15904 if (n_received <= 0) {
15905 status = SS_ABORT;
15906 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
15907 goto error;
15908 }
15909
15910 status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
15911
15912 if (status == SS_ABORT) {
15913 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
15914 goto error;
15915 }
15916
15917 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
15918 if (rpc_is_mserver())
15919 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
15920 goto exit;
15921 }
15922
15923 } while (remaining);
15924
15925 if (buf) {
15926 free(buf);
15927 buf = NULL;
15928 bufsize = 0;
15929 }
15930
15931 return RPC_SUCCESS;
15932
15933 error:
15934
15935 {
15936 char str[80];
15937 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
15938 if (strchr(str, '.'))
15939 *strchr(str, '.') = 0;
15940 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
15941 }
15942
15943 exit:
15944
15946
15947 if (buf) {
15948 free(buf);
15949 buf = NULL;
15950 bufsize = 0;
15951 }
15952
15953 /* disconnect from experiment as MIDAS server */
15954 if (rpc_is_mserver()) {
15955 HNDLE hDB, hKey;
15956
15958
15959 /* only disconnect from experiment if previously connected.
15960 Necessary for pure RPC servers (RPC_SRVR) */
15961 if (hDB) {
15965
15967
15969 }
15970 }
15971
15972 bool is_mserver = sa->is_mserver;
15973
15974 sa->close();
15975
15976 /* signal caller a shutdonw */
15977 if (status == RPC_SHUTDOWN)
15978 return status;
15979
15980 /* only the mserver should stop on server connection closure */
15981 if (!is_mserver) {
15982 return SS_SUCCESS;
15983 }
15984
15985 return status;
15986}
15987
15988/********************************************************************/
15990/********************************************************************\
15991
15992 Routine: rpc_server_receive_event
15993
15994 Purpose: Receive event and dispatch it
15995
15996 Function value:
15997 RPC_SUCCESS Successful completion
15998 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15999 SS_EXIT Server connection was closed
16000 SS_ABORT Server connection was broken
16001
16002\********************************************************************/
16003{
16004 int status = 0;
16005
16006 DWORD start_time = ss_millitime();
16007
16008 //
16009 // THIS IS NOT THREAD SAFE!!!
16010 //
16011 // IT IS ONLY USED BY THE MSERVER
16012 // MSERVER IS SINGLE-THREADED!!!
16013 //
16014
16015 static char *xbuf = NULL;
16016 static int xbufsize = 0;
16017 static bool xbufempty = true;
16018
16019 // short cut
16020 if (sa == NULL && xbufempty)
16021 return RPC_SUCCESS;
16022
16023 static bool recurse = false;
16024
16025 if (recurse) {
16026 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
16027 // do not do anything if we are called recursively
16028 // via recursive ss_suspend() or otherwise. K.O.
16029 if (xbufempty)
16030 return RPC_SUCCESS;
16031 else
16032 return BM_ASYNC_RETURN;
16033 }
16034
16035 recurse = true;
16036
16037 do {
16038 if (xbufempty && sa) {
16040
16041 if (n_received < 0) {
16042 status = SS_ABORT;
16043 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
16044 goto error;
16045 }
16046
16047 if (n_received == 0) {
16048 // no more data in the tcp socket
16049 recurse = false;
16050 return RPC_SUCCESS;
16051 }
16052
16053 xbufempty = false;
16054 }
16055
16056 if (xbufempty) {
16057 // no event in xbuf buffer
16058 recurse = false;
16059 return RPC_SUCCESS;
16060 }
16061
16062 /* send event to buffer */
16063 INT *pbh = (INT *) xbuf;
16064 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
16065
16066 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
16067
16068 //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);
16069
16070 if (status == SS_ABORT) {
16071 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
16072 goto error;
16073 }
16074
16075 if (status == BM_ASYNC_RETURN) {
16076 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
16077 recurse = false;
16078 return status;
16079 }
16080
16081 if (status != BM_SUCCESS) {
16082 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
16083 }
16084
16085 xbufempty = true;
16086
16087 /* repeat for maximum 0.5 sec */
16088 } while (ss_millitime() - start_time < 500);
16089
16090 recurse = false;
16091 return RPC_SUCCESS;
16092
16093 error:
16094
16095 {
16096 char str[80];
16097 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
16098 if (strchr(str, '.'))
16099 *strchr(str, '.') = 0;
16100 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
16101 }
16102
16103 //exit:
16104
16106
16107 /* disconnect from experiment as MIDAS server */
16108 if (rpc_is_mserver()) {
16109 HNDLE hDB, hKey;
16110
16112
16113 /* only disconnect from experiment if previously connected.
16114 Necessary for pure RPC servers (RPC_SRVR) */
16115 if (hDB) {
16119
16121
16123 }
16124 }
16125
16126 bool is_mserver = sa->is_mserver;
16127
16128 sa->close();
16129
16130 /* signal caller a shutdonw */
16131 if (status == RPC_SHUTDOWN)
16132 return status;
16133
16134 /* only the mserver should stop on server connection closure */
16135 if (!is_mserver) {
16136 return SS_SUCCESS;
16137 }
16138
16139 return status;
16140}
16141
16142
16143/********************************************************************/
16145/********************************************************************\
16146
16147 Routine: rpc_flush_event_socket
16148
16149 Purpose: Receive and en-buffer events from the mserver event socket
16150
16151 Function value:
16152 BM_SUCCESS Event socket is empty, all data was read an en-buffered
16153 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
16154 SS_EXIT Server connection was closed
16155 SS_ABORT Server connection was broken
16156
16157\********************************************************************/
16158{
16160
16161 //printf("ss_event_socket_has_data() returned %d\n", has_data);
16162
16163 if (has_data) {
16164 if (timeout_msec == BM_NO_WAIT) {
16165 return BM_ASYNC_RETURN;
16166 } else if (timeout_msec == BM_WAIT) {
16167 return BM_ASYNC_RETURN;
16168 } else {
16170 if (status == SS_ABORT || status == SS_EXIT)
16171 return status;
16172 return BM_ASYNC_RETURN;
16173 }
16174 }
16175
16177
16178 //printf("rpc_server_receive_event() status %d\n", status);
16179
16180 if (status == BM_ASYNC_RETURN) {
16181 return BM_ASYNC_RETURN;
16182 }
16183
16184 if (status == SS_ABORT || status == SS_EXIT)
16185 return status;
16186
16187 return BM_SUCCESS;
16188}
16189
16190/********************************************************************/
16192/********************************************************************\
16193
16194 Routine: rpc_server_shutdown
16195
16196 Purpose: Shutdown RPC server, abort all connections
16197
16198 Input:
16199 none
16200
16201 Output:
16202 none
16203
16204 Function value:
16205 RPC_SUCCESS Successful completion
16206
16207\********************************************************************/
16208{
16209 //printf("rpc_server_shutdown!\n");
16210
16211 struct linger ling;
16212
16213 /* close all open connections */
16214 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16215 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
16217 /* lingering needed for PCTCP */
16218 ling.l_onoff = 1;
16219 ling.l_linger = 0;
16220 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16222
16223 if (sa->send_sock) {
16224 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16226 }
16227
16228 if (sa->event_sock) {
16229 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16231 }
16232 }
16233 }
16234
16235 /* avoid memory leak */
16236 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16238 if (sa) {
16239 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
16240 if (sa == _mserver_acception) {
16241 // do not leave behind a stale pointer!
16243 }
16244 delete sa;
16246 }
16247 }
16248
16249 if (_rpc_registered) {
16252 }
16253
16254 /* free suspend structures */
16256
16257 return RPC_SUCCESS;
16258}
16259
16260
16261/********************************************************************/
16263/********************************************************************\
16264
16265 Routine: rpc_check_channels
16266
16267 Purpose: Check open rpc channels by sending watchdog messages
16268
16269 Input:
16270 none
16271
16272 Output:
16273 none
16274
16275 Function value:
16276 RPC_SUCCESS Channel is still alive
16277 RPC_NET_ERROR Connection is broken
16278
16279\********************************************************************/
16280{
16281 INT status;
16282 NET_COMMAND nc;
16284 struct timeval timeout;
16285
16286 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16287 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
16289 if (sa == NULL)
16290 continue;
16291
16292 if (sa->watchdog_timeout == 0) {
16293 continue;
16294 }
16295
16297 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
16298
16299 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
16300
16301 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
16302
16303 /* send a watchdog message */
16305 nc.header.param_size = 0;
16306
16307 int convert_flags = sa->convert_flags;
16308 if (convert_flags) {
16311 }
16312
16313 /* send the header to the client */
16314 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
16315
16316 if (i < 0) {
16317 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
16318 sa->prog_name.c_str(),
16319 sa->host_name.c_str(),
16320 sa->watchdog_timeout / 1000,
16321 i);
16322
16323 /* disconnect from experiment */
16324 if (rpc_is_mserver()) {
16326 return RPC_NET_ERROR;
16327 }
16328
16329 sa->close();
16330 return RPC_NET_ERROR;
16331 }
16332
16333 /* make some timeout checking */
16334 FD_ZERO(&readfds);
16335 FD_SET(sa->send_sock, &readfds);
16336 FD_SET(sa->recv_sock, &readfds);
16337
16338 timeout.tv_sec = sa->watchdog_timeout / 1000;
16339 timeout.tv_usec = (sa->watchdog_timeout % 1000) * 1000;
16340
16341 do {
16342 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
16343
16344 /* if an alarm signal was cought, restart select with reduced timeout */
16345 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
16346 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
16347
16348 } while (status == -1); /* dont return if an alarm signal was cought */
16349
16350 if (!FD_ISSET(sa->send_sock, &readfds) &&
16351 !FD_ISSET(sa->recv_sock, &readfds)) {
16352
16353 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
16354 sa->prog_name.c_str(),
16355 sa->host_name.c_str(),
16356 sa->watchdog_timeout / 1000);
16357
16358 /* disconnect from experiment */
16359 if (rpc_is_mserver()) {
16361 return RPC_NET_ERROR;
16362 }
16363
16364 sa->close();
16365 return RPC_NET_ERROR;
16366 }
16367
16368 /* receive result on send socket */
16369 if (FD_ISSET(sa->send_sock, &readfds)) {
16370 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
16371 if (i <= 0) {
16372 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
16373 sa->prog_name.c_str(),
16374 sa->host_name.c_str(),
16375 sa->watchdog_timeout / 1000,
16376 i);
16377
16378 /* disconnect from experiment */
16379 if (rpc_is_mserver()) {
16381 return RPC_NET_ERROR;
16382 }
16383
16384 sa->close();
16385 return RPC_NET_ERROR;
16386 }
16387 }
16388 }
16389 }
16390 }
16391
16392 return RPC_SUCCESS;
16393}
16394
16402/********************************************************************\
16403* *
16404* Bank functions *
16405* *
16406\********************************************************************/
16407
16408/********************************************************************/
16414void bk_init(void *event) {
16415 ((BANK_HEADER *) event)->data_size = 0;
16416 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
16417}
16418
16420#ifndef DOXYGEN_SHOULD_SKIP_THIS
16421
16422/********************************************************************/
16423BOOL bk_is32(const void *event)
16424/********************************************************************\
16425
16426 Routine: bk_is32
16427
16428 Purpose: Return true if banks inside event are 32-bit banks
16429
16430 Input:
16431 void *event pointer to the event
16432
16433 Output:
16434 none
16435
16436 Function value:
16437 none
16438
16439\********************************************************************/
16440{
16441 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
16442}
16443
16444/********************************************************************/
16445BOOL bk_is32a(const void *event)
16446/********************************************************************\
16447
16448 Routine: bk_is32a
16449
16450 Purpose: Return true if banks inside event are 32-bit banks
16451 and banks are 64-bit aligned
16452
16453 Input:
16454 void *event pointer to the event
16455
16456 Output:
16457 none
16458
16459 Function value:
16460 none
16461
16462\********************************************************************/
16463{
16464 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
16465}
16466
16468#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16469
16470/********************************************************************/
16477void bk_init32(void *event) {
16478 ((BANK_HEADER *) event)->data_size = 0;
16479 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
16480}
16481
16482/********************************************************************/
16490void bk_init32a(void *event) {
16491 ((BANK_HEADER *) event)->data_size = 0;
16493}
16494
16495/********************************************************************/
16503INT bk_size(const void *event) {
16504 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
16505}
16506
16507static void copy_bk_name(char* dst, const char* src)
16508{
16509 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
16510
16511 if (src[0] == 0) {
16512 // invalid empty name
16513 dst[0] = 0;
16514 dst[1] = 0;
16515 dst[2] = 0;
16516 dst[3] = 0;
16517 return;
16518 }
16519
16520 dst[0] = src[0];
16521
16522 if (src[1] == 0) {
16523 dst[1] = 0;
16524 dst[2] = 0;
16525 dst[3] = 0;
16526 return;
16527 }
16528
16529 dst[1] = src[1];
16530
16531 if (src[2] == 0) {
16532 dst[2] = 0;
16533 dst[3] = 0;
16534 return;
16535 }
16536
16537 dst[2] = src[2];
16538
16539 if (src[3] == 0) {
16540 dst[3] = 0;
16541 return;
16542 }
16543
16544 dst[3] = src[3];
16545}
16546
16547/********************************************************************/
16569void bk_create(void *event, const char *name, WORD type, void **pdata) {
16570 if (bk_is32a((BANK_HEADER *) event)) {
16571 if (((PTYPE) event & 0x07) != 0) {
16572 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
16573 return;
16574 }
16575 BANK32A *pbk32a;
16576
16577 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16578 copy_bk_name(pbk32a->name, name);
16579 pbk32a->type = type;
16580 pbk32a->data_size = 0;
16581 *pdata = pbk32a + 1;
16582 } else if (bk_is32((BANK_HEADER *) event)) {
16583 BANK32 *pbk32;
16584
16585 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16586 copy_bk_name(pbk32->name, name);
16587 pbk32->type = type;
16588 pbk32->data_size = 0;
16589 *pdata = pbk32 + 1;
16590 } else {
16591 BANK *pbk;
16592
16593 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16594 copy_bk_name(pbk->name, name);
16595 pbk->type = type;
16596 pbk->data_size = 0;
16597 *pdata = pbk + 1;
16598 }
16599}
16600
16602#ifndef DOXYGEN_SHOULD_SKIP_THIS
16603
16604/********************************************************************/
16612INT bk_copy(char *pevent, char *psrce, const char *bkname) {
16613
16614 INT status;
16617 BANK *psbkh;
16618 char *pdest;
16619 void *psdata;
16620
16621 // source pointing on the BANKxx
16622 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
16623 // Find requested bank
16625 // Return 0 if not found
16626 if (status != SUCCESS) return 0;
16627
16628 // Check bank type...
16629 // You cannot mix BANK and BANK32 so make sure all the FE use either
16630 // bk_init(pevent) or bk_init32(pevent).
16631 if (bk_is32a(psBkh)) {
16632
16633 // pointer to the source bank header
16634 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
16635 // Data size in the bank
16637
16638 // Get to the end of the event
16639 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16640 // Copy from BANK32 to end of Data
16641 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
16642 // Bring pointer to the next free location
16643 pdest += ALIGN8(bksze) + sizeof(BANK32A);
16644
16645 } else if (bk_is32(psBkh)) {
16646
16647 // pointer to the source bank header
16648 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
16649 // Data size in the bank
16651
16652 // Get to the end of the event
16653 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16654 // Copy from BANK32 to end of Data
16655 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
16656 // Bring pointer to the next free location
16657 pdest += ALIGN8(bksze) + sizeof(BANK32);
16658
16659 } else {
16660
16661 // pointer to the source bank header
16662 psbkh = ((BANK *) psdata - 1);
16663 // Data size in the bank
16664 bksze = psbkh->data_size;
16665
16666 // Get to the end of the event
16667 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16668 // Copy from BANK to end of Data
16669 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
16670 // Bring pointer to the next free location
16671 pdest += ALIGN8(bksze) + sizeof(BANK);
16672 }
16673
16674 // Close bank (adjust BANK_HEADER size)
16675 bk_close(pevent, pdest);
16676 // Adjust EVENT_HEADER size
16677 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
16678 return SUCCESS;
16679}
16680
16681/********************************************************************/
16682int bk_delete(void *event, const char *name)
16683/********************************************************************\
16684
16685 Routine: bk_delete
16686
16687 Purpose: Delete a MIDAS bank inside an event
16688
16689 Input:
16690 void *event pointer to the event
16691 char *name Name of bank (exactly four letters)
16692
16693 Function value:
16694 CM_SUCCESS Bank has been deleted
16695 0 Bank has not been found
16696
16697\********************************************************************/
16698{
16699 BANK *pbk;
16700 DWORD dname;
16701 int remaining;
16702
16703 if (bk_is32a((BANK_HEADER *) event)) {
16704 /* locate bank */
16705 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16706 copy_bk_name((char *) &dname, name);
16707 do {
16708 if (*((DWORD *) pbk32a->name) == dname) {
16709 /* bank found, delete it */
16710 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16711 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16712
16713 /* reduce total event size */
16714 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
16715
16716 /* copy remaining bytes */
16717 if (remaining > 0)
16718 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
16719 return CM_SUCCESS;
16720 }
16721
16722 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16723 } while ((DWORD) ((char *) pbk32a - (char *) event) <
16724 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16725 } else if (bk_is32((BANK_HEADER *) event)) {
16726 /* locate bank */
16727 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16728 copy_bk_name((char *) &dname, name);
16729 do {
16730 if (*((DWORD *) pbk32->name) == dname) {
16731 /* bank found, delete it */
16732 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16733 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16734
16735 /* reduce total event size */
16736 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
16737
16738 /* copy remaining bytes */
16739 if (remaining > 0)
16740 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
16741 return CM_SUCCESS;
16742 }
16743
16744 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16745 } while ((DWORD) ((char *) pbk32 - (char *) event) <
16746 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16747 } else {
16748 /* locate bank */
16749 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16750 copy_bk_name((char *) &dname, name);
16751 do {
16752 if (*((DWORD *) pbk->name) == dname) {
16753 /* bank found, delete it */
16754 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16755 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16756
16757 /* reduce total event size */
16758 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
16759
16760 /* copy remaining bytes */
16761 if (remaining > 0)
16762 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
16763 return CM_SUCCESS;
16764 }
16765
16766 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16767 } while ((DWORD) ((char *) pbk - (char *) event) <
16768 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16769 }
16770
16771 return 0;
16772}
16773
16775#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16776
16777/********************************************************************/
16788INT bk_close(void *event, void *pdata) {
16789 if (bk_is32a((BANK_HEADER *) event)) {
16790 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16791 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
16792 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
16793 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]);
16794 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
16795 return pbk32a->data_size;
16796 } else if (bk_is32((BANK_HEADER *) event)) {
16797 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16798 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
16799 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
16800 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]);
16801 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
16802 return pbk32->data_size;
16803 } else {
16804 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16805 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
16806 if (size > 0xFFFF) {
16807 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16808 size = 0;
16809 }
16810 pbk->data_size = (WORD) (size);
16811 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
16812 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]);
16813 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
16814 if (size > 0xFFFF) {
16815 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16816 size = 0;
16817 }
16818 ((BANK_HEADER *) event)->data_size = size;
16819 return pbk->data_size;
16820 }
16821}
16822
16823/********************************************************************/
16848INT bk_list(const void *event, char *bklist) { /* Full event */
16849 INT nbk;
16850 BANK *pmbk = NULL;
16851 BANK32 *pmbk32 = NULL;
16852 BANK32A *pmbk32a = NULL;
16853 char *pdata;
16854
16855 /* compose bank list */
16856 bklist[0] = 0;
16857 nbk = 0;
16858 do {
16859 /* scan all banks for bank name only */
16860 if (bk_is32a(event)) {
16861 bk_iterate32a(event, &pmbk32a, &pdata);
16862 if (pmbk32a == NULL)
16863 break;
16864 } else if (bk_is32(event)) {
16865 bk_iterate32(event, &pmbk32, &pdata);
16866 if (pmbk32 == NULL)
16867 break;
16868 } else {
16869 bk_iterate(event, &pmbk, &pdata);
16870 if (pmbk == NULL)
16871 break;
16872 }
16873 nbk++;
16874
16875 if (nbk > BANKLIST_MAX) {
16876 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
16877 return (nbk - 1);
16878 }
16879 if (bk_is32a(event))
16880 strncat(bklist, (char *) pmbk32a->name, 4);
16881 else if (bk_is32(event))
16882 strncat(bklist, (char *) pmbk32->name, 4);
16883 else
16884 strncat(bklist, (char *) pmbk->name, 4);
16885 } while (1);
16886 return (nbk);
16887}
16888
16889/********************************************************************/
16897INT bk_locate(const void *event, const char *name, void *pdata) {
16898 BANK *pbk;
16899 BANK32 *pbk32;
16900 BANK32A *pbk32a;
16901 DWORD dname;
16902
16903 if (bk_is32a(event)) {
16904 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16905 copy_bk_name((char *) &dname, name);
16906 while ((DWORD) ((char *) pbk32a - (char *) event) <
16907 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16908 if (*((DWORD *) pbk32a->name) == dname) {
16909 *((void **) pdata) = pbk32a + 1;
16910 if (tid_size[pbk32a->type & 0xFF] == 0)
16911 return pbk32a->data_size;
16912 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16913 }
16914 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16915 }
16916 } else if (bk_is32(event)) {
16917 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16918 copy_bk_name((char *) &dname, name);
16919 while ((DWORD) ((char *) pbk32 - (char *) event) <
16920 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16921 if (*((DWORD *) pbk32->name) == dname) {
16922 *((void **) pdata) = pbk32 + 1;
16923 if (tid_size[pbk32->type & 0xFF] == 0)
16924 return pbk32->data_size;
16925 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
16926 }
16927 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16928 }
16929 } else {
16930 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16931 copy_bk_name((char *) &dname, name);
16932 while ((DWORD) ((char *) pbk - (char *) event) <
16933 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16934 if (*((DWORD *) pbk->name) == dname) {
16935 *((void **) pdata) = pbk + 1;
16936 if (tid_size[pbk->type & 0xFF] == 0)
16937 return pbk->data_size;
16938 return pbk->data_size / tid_size[pbk->type & 0xFF];
16939 }
16940 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16941 }
16942
16943 }
16944
16945 /* bank not found */
16946 *((void **) pdata) = NULL;
16947 return 0;
16948}
16949
16950/********************************************************************/
16960INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
16961 DWORD dname;
16962
16963 if (bk_is32a(pbkh)) {
16964 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
16965 copy_bk_name((char *) &dname, name);
16966 do {
16967 if (*((DWORD *) pbk32a->name) == dname) {
16968 *((void **) pdata) = pbk32a + 1;
16969 if (tid_size[pbk32a->type & 0xFF] == 0)
16970 *bklen = pbk32a->data_size;
16971 else
16972 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16973
16974 *bktype = pbk32a->type;
16975 return 1;
16976 }
16977 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16978 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16979 } else if (bk_is32(pbkh)) {
16980 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
16981 copy_bk_name((char *) &dname, name);
16982 do {
16983 if (*((DWORD *) pbk32->name) == dname) {
16984 *((void **) pdata) = pbk32 + 1;
16985 if (tid_size[pbk32->type & 0xFF] == 0)
16986 *bklen = pbk32->data_size;
16987 else
16988 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
16989
16990 *bktype = pbk32->type;
16991 return 1;
16992 }
16993 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16994 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16995 } else {
16996 BANK *pbk = (BANK *) (pbkh + 1);
16997 copy_bk_name((char *) &dname, name);
16998 do {
16999 if (*((DWORD *) pbk->name) == dname) {
17000 *((void **) pdata) = pbk + 1;
17001 if (tid_size[pbk->type & 0xFF] == 0)
17002 *bklen = pbk->data_size;
17003 else
17004 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
17005
17006 *bktype = pbk->type;
17007 return 1;
17008 }
17009 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17010 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17011 }
17012
17013 /* bank not found */
17014 *((void **) pdata) = NULL;
17015 return 0;
17016}
17017
17018/********************************************************************/
17054INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
17055 if (*pbk == NULL)
17056 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
17057 else
17058 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17059
17060 *((void **) pdata) = (*pbk) + 1;
17061
17062 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17063 *pbk = *((BANK **) pdata) = NULL;
17064 return 0;
17065 }
17066
17067 return (*pbk)->data_size;
17068}
17069
17070
17072#ifndef DOXYGEN_SHOULD_SKIP_THIS
17073
17074/********************************************************************/
17075INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
17076/********************************************************************\
17077
17078 Routine: bk_iterate32
17079
17080 Purpose: Iterate through 32 bit MIDAS banks inside an event
17081
17082 Input:
17083 void *event pointer to the event
17084 BANK32 **pbk32 must be NULL for the first call to bk_iterate
17085
17086 Output:
17087 BANK32 **pbk32 pointer to the bank header
17088 void *pdata pointer to data area of the bank
17089
17090 Function value:
17091 INT size of the bank in bytes
17092
17093\********************************************************************/
17094{
17095 if (*pbk == NULL)
17096 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
17097 else
17098 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17099
17100 *((void **) pdata) = (*pbk) + 1;
17101
17102 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17103 *pbk = NULL;
17104 pdata = NULL;
17105 return 0;
17106 }
17107
17108 return (*pbk)->data_size;
17109}
17110
17111INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
17112/********************************************************************\
17113
17114 Routine: bk_iterate32a
17115
17116 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
17117
17118 Input:
17119 void *event pointer to the event
17120 BANK32A **pbk32a must be NULL for the first call to bk_iterate
17121
17122 Output:
17123 BANK32A **pbk32 pointer to the bank header
17124 void *pdata pointer to data area of the bank
17125
17126 Function value:
17127 INT size of the bank in bytes
17128
17129\********************************************************************/
17130{
17131 if (*pbk32a == NULL)
17132 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
17133 else
17134 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
17135
17136 *((void **) pdata) = (*pbk32a) + 1;
17137
17138 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17139 *pbk32a = NULL;
17140 pdata = NULL;
17141 return 0;
17142 }
17143
17144 return (*pbk32a)->data_size;
17145}
17146
17148#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17149
17150/********************************************************************/
17165INT bk_swap(void *event, BOOL force) {
17167 BANK *pbk;
17168 BANK32 *pbk32;
17169 BANK32A *pbk32a;
17170 void *pdata;
17171 WORD type;
17172
17173 pbh = (BANK_HEADER *) event;
17174
17175 /* only swap if flags in high 16-bit */
17176 if (pbh->flags < 0x10000 && !force)
17177 return 0;
17178
17179 /* swap bank header */
17180 DWORD_SWAP(&pbh->data_size);
17181 DWORD_SWAP(&pbh->flags);
17182
17183 pbk = (BANK *) (pbh + 1);
17184 pbk32 = (BANK32 *) pbk;
17185 pbk32a = (BANK32A *) pbk;
17186
17187 /* scan event */
17188 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
17189 /* swap bank header */
17190 if (bk_is32a(event)) {
17191 DWORD_SWAP(&pbk32a->type);
17192 DWORD_SWAP(&pbk32a->data_size);
17193 pdata = pbk32a + 1;
17194 type = (WORD) pbk32a->type;
17195 } else if (bk_is32(event)) {
17196 DWORD_SWAP(&pbk32->type);
17197 DWORD_SWAP(&pbk32->data_size);
17198 pdata = pbk32 + 1;
17199 type = (WORD) pbk32->type;
17200 } else {
17201 WORD_SWAP(&pbk->type);
17202 WORD_SWAP(&pbk->data_size);
17203 pdata = pbk + 1;
17204 type = pbk->type;
17205 }
17206
17207 /* pbk points to next bank */
17208 if (bk_is32a(event)) {
17209 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17210 pbk = (BANK *) pbk32a;
17211 } else if (bk_is32(event)) {
17212 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17213 pbk = (BANK *) pbk32;
17214 } else {
17215 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17216 pbk32 = (BANK32 *) pbk;
17217 }
17218
17219 switch (type) {
17220 case TID_UINT16:
17221 case TID_INT16:
17222 while ((char *) pdata < (char *) pbk) {
17224 pdata = (void *) (((WORD *) pdata) + 1);
17225 }
17226 break;
17227
17228 case TID_UINT32:
17229 case TID_INT32:
17230 case TID_BOOL:
17231 case TID_FLOAT:
17232 while ((char *) pdata < (char *) pbk) {
17234 pdata = (void *) (((DWORD *) pdata) + 1);
17235 }
17236 break;
17237
17238 case TID_DOUBLE:
17239 case TID_INT64:
17240 case TID_UINT64:
17241 while ((char *) pdata < (char *) pbk) {
17243 pdata = (void *) (((double *) pdata) + 1);
17244 }
17245 break;
17246 }
17247 }
17248
17249 return CM_SUCCESS;
17250}
17251
/* end of bkfunctionc */
17255
17256
17263#ifndef DOXYGEN_SHOULD_SKIP_THIS
17264/********************************************************************/
17265
17266/********************************************************************\
17267* *
17268* Ring buffer functions *
17269* *
17270* Provide an inter-thread buffer scheme for handling front-end *
17271* events. This code allows concurrent data acquisition, calibration *
17272* and network transfer on a multi-CPU machine. One thread reads *
17273* out the data, passes it vis the ring buffer functions *
17274* to another thread running on the other CPU, which can then *
17275* calibrate and/or send the data over the network. *
17276* *
17277\********************************************************************/
17278
17279typedef struct {
17280 unsigned char *buffer;
17281 unsigned int size;
17282 unsigned int max_event_size;
17283 unsigned char *rp;
17284 unsigned char *wp;
17285 unsigned char *ep;
17286} RING_BUFFER;
17287
17288#define MAX_RING_BUFFER 100
17289
17291
17292static volatile int _rb_nonblocking = 0;
17293
17295#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17296
17297/********************************************************************/
17304/********************************************************************\
17305
17306 Routine: rb_set_nonblocking
17307
17308 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17309 environments for stopping all theads without deadlock
17310
17311 Input:
17312 NONE
17313
17314 Output:
17315 NONE
17316
17317 Function value:
17318 DB_SUCCESS Successful completion
17319
17320\********************************************************************/
17321{
17322 _rb_nonblocking = 1;
17323
17324 return DB_SUCCESS;
17325}
17326
17327/********************************************************************/
17344int rb_create(int size, int max_event_size, int *handle)
17345/********************************************************************\
17346
17347 Routine: rb_create
17348
17349 Purpose: Create a ring buffer with a given size
17350
17351 Input:
17352 int size Size of ring buffer, must be larger than
17353 2*max_event_size
17354 int max_event_size Maximum event size to be placed into
17355 ring buffer
17356 Output:
17357 int *handle Handle to ring buffer
17358
17359 Function value:
17360 DB_SUCCESS Successful completion
17361 DB_NO_MEMORY Maximum number of ring buffers exceeded
17362 DB_INVALID_PARAM Invalid event size specified
17363
17364\********************************************************************/
17365{
17366 int i;
17367
17368 for (i = 0; i < MAX_RING_BUFFER; i++)
17369 if (rb[i].buffer == NULL)
17370 break;
17371
17372 if (i == MAX_RING_BUFFER)
17373 return DB_NO_MEMORY;
17374
17375 if (size < max_event_size * 2)
17376 return DB_INVALID_PARAM;
17377
17378 memset(&rb[i], 0, sizeof(RING_BUFFER));
17379 rb[i].buffer = (unsigned char *) M_MALLOC(size);
17380 assert(rb[i].buffer);
17381 rb[i].size = size;
17383 rb[i].rp = rb[i].buffer;
17384 rb[i].wp = rb[i].buffer;
17385 rb[i].ep = rb[i].buffer;
17386
17387 *handle = i + 1;
17388
17389 return DB_SUCCESS;
17390}
17391
17392/********************************************************************/
17398int rb_delete(int handle)
17399/********************************************************************\
17400
17401 Routine: rb_delete
17402
17403 Purpose: Delete a ring buffer
17404
17405 Input:
17406 none
17407 Output:
17408 int handle Handle to ring buffer
17409
17410 Function value:
17411 DB_SUCCESS Successful completion
17412
17413\********************************************************************/
17414{
17415 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17416 return DB_INVALID_HANDLE;
17417
17418 M_FREE(rb[handle - 1].buffer);
17419 rb[handle - 1].buffer = NULL;
17420 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
17421
17422 return DB_SUCCESS;
17423}
17424
17425/********************************************************************/
17435int rb_get_wp(int handle, void **p, int millisec)
17436/********************************************************************\
17437
17438Routine: rb_get_wp
17439
17440 Purpose: Retrieve write pointer where new data can be written
17441
17442 Input:
17443 int handle Ring buffer handle
17444 int millisec Optional timeout in milliseconds if
17445 buffer is full. Zero to not wait at
17446 all (non-blocking)
17447
17448 Output:
17449 char **p Write pointer
17450
17451 Function value:
17452 DB_SUCCESS Successful completion
17453
17454\********************************************************************/
17455{
17456 int h, i;
17457 unsigned char *rp;
17458
17459 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17460 return DB_INVALID_HANDLE;
17461
17462 h = handle - 1;
17463
17464 for (i = 0; i <= millisec / 10; i++) {
17465
17466 rp = rb[h].rp; // keep local copy for convenience
17467
17468 /* check if enough size for wp >= rp without wrap-around */
17469 if (rb[h].wp >= rp
17470 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17471 *p = rb[h].wp;
17472 return DB_SUCCESS;
17473 }
17474
17475 /* check if enough size for wp >= rp with wrap-around */
17476 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17477 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
17478 *p = rb[h].wp;
17479 return DB_SUCCESS;
17480 }
17481
17482 /* check if enough size for wp < rp */
17483 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17484 *p = rb[h].wp;
17485 return DB_SUCCESS;
17486 }
17487
17488 if (millisec == 0)
17489 return DB_TIMEOUT;
17490
17491 if (_rb_nonblocking)
17492 return DB_TIMEOUT;
17493
17494 /* wait one time slice */
17495 ss_sleep(10);
17496 }
17497
17498 return DB_TIMEOUT;
17499}
17500
17501/********************************************************************/
17510int rb_increment_wp(int handle, int size)
17511/********************************************************************\
17512
17513 Routine: rb_increment_wp
17514
17515 Purpose: Increment current write pointer, making the data at
17516 the write pointer available to the receiving thread
17517
17518 Input:
17519 int handle Ring buffer handle
17520 int size Number of bytes placed at the WP
17521
17522 Output:
17523 NONE
17524
17525 Function value:
17526 DB_SUCCESS Successful completion
17527 DB_INVALID_PARAM Event size too large or invalid handle
17528\********************************************************************/
17529{
17530 int h;
17531 unsigned char *new_wp;
17532
17533 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17534 return DB_INVALID_HANDLE;
17535
17536 h = handle - 1;
17537
17538 if ((DWORD) size > rb[h].max_event_size) {
17539 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
17540 size/1024/1024, rb[h].max_event_size/1024/1024);
17541 abort();
17542 }
17543
17544 new_wp = rb[h].wp + size;
17545
17546 /* wrap around wp if not enough space */
17547 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17548 rb[h].ep = new_wp;
17549 new_wp = rb[h].buffer;
17550 assert(rb[h].rp != rb[h].buffer);
17551 } else
17552 if (new_wp > rb[h].ep)
17553 rb[h].ep = new_wp;
17554
17555 rb[h].wp = new_wp;
17556
17557 return DB_SUCCESS;
17558}
17559
17560/********************************************************************/
17576int rb_get_rp(int handle, void **p, int millisec)
17577/********************************************************************\
17578
17579 Routine: rb_get_rp
17580
17581 Purpose: Obtain the current read pointer at which new data is
17582 available with optional timeout
17583
17584 Input:
17585 int handle Ring buffer handle
17586 int millisec Optional timeout in milliseconds if
17587 buffer is full. Zero to not wait at
17588 all (non-blocking)
17589
17590 Output:
17591 char **p Address of pointer pointing to newly
17592 available data. If p == NULL, only
17593 return status.
17594
17595 Function value:
17596 DB_SUCCESS Successful completion
17597
17598\********************************************************************/
17599{
17600 int i, h;
17601
17602 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17603 return DB_INVALID_HANDLE;
17604
17605 h = handle - 1;
17606
17607 for (i = 0; i <= millisec / 10; i++) {
17608
17609 if (rb[h].wp != rb[h].rp) {
17610 if (p != NULL)
17611 *p = rb[handle - 1].rp;
17612 return DB_SUCCESS;
17613 }
17614
17615 if (millisec == 0)
17616 return DB_TIMEOUT;
17617
17618 if (_rb_nonblocking)
17619 return DB_TIMEOUT;
17620
17621 /* wait one time slice */
17622 ss_sleep(10);
17623 }
17624
17625 return DB_TIMEOUT;
17626}
17627
17628/********************************************************************/
17638int rb_increment_rp(int handle, int size)
17639/********************************************************************\
17640
17641 Routine: rb_increment_rp
17642
17643 Purpose: Increment current read pointer, freeing up space for
17644 the writing thread.
17645
17646 Input:
17647 int handle Ring buffer handle
17648 int size Number of bytes to free up at current
17649 read pointer
17650
17651 Output:
17652 NONE
17653
17654 Function value:
17655 DB_SUCCESS Successful completion
17656 DB_INVALID_PARAM Event size too large or invalid handle
17657
17658\********************************************************************/
17659{
17660 int h;
17661
17662 unsigned char *new_rp;
17663 unsigned char *ep;
17664
17665 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17666 return DB_INVALID_HANDLE;
17667
17668 h = handle - 1;
17669
17670 if ((DWORD) size > rb[h].max_event_size)
17671 return DB_INVALID_PARAM;
17672
17673 new_rp = rb[h].rp + size;
17674 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
17675
17676 /* wrap around if end pointer reached */
17677 if (new_rp >= ep && rb[h].wp < ep)
17678 new_rp = rb[h].buffer;
17679
17680 rb[handle - 1].rp = new_rp;
17681
17682 return DB_SUCCESS;
17683}
17684
17685/********************************************************************/
17693int rb_get_buffer_level(int handle, int *n_bytes)
17694/********************************************************************\
17695
17696 Routine: rb_get_buffer_level
17697
17698 Purpose: Return number of bytes in a ring buffer
17699
17700 Input:
17701 int handle Handle of the buffer to get the info
17702
17703 Output:
17704 int *n_bytes Number of bytes in buffer
17705
17706 Function value:
17707 DB_SUCCESS Successful completion
17708 DB_INVALID_HANDLE Buffer handle is invalid
17709
17710\********************************************************************/
17711{
17712 int h;
17713
17714 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17715 return DB_INVALID_HANDLE;
17716
17717 h = handle - 1;
17718
17719 if (rb[h].wp >= rb[h].rp)
17720 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
17721 else
17722 *n_bytes =
17723 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
17724
17725 return DB_SUCCESS;
17726}
17727
/* end of rbfunctionc */
17729
17730
17732{
17733 if (format == FORMAT_FIXED) {
17734 int status;
17735 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
17736 if (status != DB_SUCCESS) {
17737 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
17738 return status;
17739 }
17740 return SUCCESS;
17741 } else if (format == FORMAT_MIDAS) {
17742 INT size, i, status, n_data;
17743 int n;
17744 char *pdata, *pdata0;
17745
17746 char name[5];
17748 BANK *pbk;
17749 BANK32 *pbk32;
17750 BANK32A *pbk32a;
17751 DWORD bkname;
17752 WORD bktype;
17754 KEY key;
17755
17756 pbh = (BANK_HEADER *) (pevent + 1);
17757 pbk = NULL;
17758 pbk32 = NULL;
17759 pbk32a = NULL;
17760
17761 /* count number of banks */
17762 for (n=0 ; ; n++) {
17763 if (bk_is32a(pbh)) {
17765 if (pbk32a == NULL)
17766 break;
17767 } else if (bk_is32(pbh)) {
17769 if (pbk32 == NULL)
17770 break;
17771 } else {
17772 bk_iterate(pbh, &pbk, &pdata);
17773 if (pbk == NULL)
17774 break;
17775 }
17776 }
17777
17778 /* build array of keys */
17779 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
17780
17781 pbk = NULL;
17782 pbk32 = NULL;
17783 n = 0;
17784 do {
17785 /* scan all banks */
17786 if (bk_is32a(pbh)) {
17787 size = bk_iterate32a(pbh, &pbk32a, &pdata);
17788 if (pbk32a == NULL)
17789 break;
17790 bkname = *((DWORD *) pbk32a->name);
17791 bktype = (WORD) pbk32a->type;
17792 } else if (bk_is32(pbh)) {
17793 size = bk_iterate32(pbh, &pbk32, &pdata);
17794 if (pbk32 == NULL)
17795 break;
17796 bkname = *((DWORD *) pbk32->name);
17797 bktype = (WORD) pbk32->type;
17798 } else {
17799 size = bk_iterate(pbh, &pbk, &pdata);
17800 if (pbk == NULL)
17801 break;
17802 bkname = *((DWORD *) pbk->name);
17803 bktype = (WORD) pbk->type;
17804 }
17805
17806 n_data = size;
17807 if (rpc_tid_size(bktype & 0xFF))
17808 n_data /= rpc_tid_size(bktype & 0xFF);
17809
17810 /* get bank key */
17811 *((DWORD *) name) = bkname;
17812 name[4] = 0;
17813 /* record the start of the data in case it is struct */
17814 pdata0 = pdata;
17815 if (bktype == TID_STRUCT) {
17817 if (status != DB_SUCCESS) {
17818 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
17819 continue;
17820 }
17821
17822 /* write structured bank */
17823 for (i = 0;; i++) {
17826 break;
17827
17828 db_get_key(hDB, hKeyl, &key);
17829
17830 /* adjust for alignment */
17831 if (key.type != TID_STRING && key.type != TID_LINK)
17833
17835 if (status != DB_SUCCESS) {
17836 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17837 continue;
17838 }
17839 hKeys[n++] = hKeyl;
17840
17841 /* shift data pointer to next item */
17843 }
17844 } else {
17845 /* write variable length bank */
17847 if (status != DB_SUCCESS) {
17849 if (status != DB_SUCCESS) {
17850 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);
17851 continue;
17852 }
17854 if (status != DB_SUCCESS) {
17855 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);
17856 continue;
17857 }
17858 }
17859 if (n_data > 0) {
17860 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
17861 if (status != DB_SUCCESS) {
17862 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17863 }
17864 hKeys[n++] = hKeyRoot;
17865 }
17866 }
17867 } while (1);
17868
17869 /* notify all hot-lined clients in one go */
17871
17872 free(hKeys);
17873
17874 return SUCCESS;
17875 } else {
17876 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
17877 return CM_DB_ERROR;
17878 }
17879}
17880
17881/* emacs
17882 * Local Variables:
17883 * tab-width: 8
17884 * c-basic-offset: 3
17885 * indent-tabs-mode: nil
17886 * End:
17887 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11443
std::atomic_bool connected
Definition midas.cxx:11442
std::string client_name
Definition midas.cxx:11447
BUFFER * get_pbuf() const
Definition midas.cxx:3181
bool is_error() const
Definition midas.cxx:3171
int get_status() const
Definition midas.cxx:3176
bool is_locked() const
Definition midas.cxx:3166
bm_lock_buffer_guard(BUFFER *pbuf, bool do_not_lock=false)
Definition midas.cxx:3085
bm_lock_buffer_guard & operator=(const bm_lock_buffer_guard &)=delete
bm_lock_buffer_guard(const bm_lock_buffer_guard &)=delete
static bool exists(const std::string &name)
Definition odbxx.cxx:75
INT transition(INT run_number, char *error)
Definition consume.cxx:35
#define EXPRT
Definition esone.h:28
TRIGGER_SETTINGS ts
INT al_get_alarms(std::string *presult)
Definition alarm.cxx:844
INT al_check()
Definition alarm.cxx:614
void bk_init32a(void *event)
Definition midas.cxx:16490
INT bk_close(void *event, void *pdata)
Definition midas.cxx:16788
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:17111
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:16507
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:17165
BOOL bk_is32a(const void *event)
Definition midas.cxx:16445
int bk_delete(void *event, const char *name)
Definition midas.cxx:16682
BOOL bk_is32(const void *event)
Definition midas.cxx:16423
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:17075
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:16897
void bk_init(void *event)
Definition midas.cxx:16414
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:16848
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:16612
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:17054
void bk_init32(void *event)
Definition midas.cxx:16477
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:16569
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:16960
INT bk_size(const void *event)
Definition midas.cxx:16503
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8795
static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER **ppevent, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10491
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6725
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6197
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9686
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:9995
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)
Definition midas.cxx:6472
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:10841
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10071
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7288
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11280
INT bm_delete_request(INT request_id)
Definition midas.cxx:8592
INT bm_close_all_buffers(void)
Definition midas.cxx:7251
INT bm_poll_event()
Definition midas.cxx:11134
INT bm_add_event_request(INT buffer_handle, short int event_id, short int trigger_mask, INT sampling_type, EVENT_HANDLER *func, INT request_id)
Definition midas.cxx:8322
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6594
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6231
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10817
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9611
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9597
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8974
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6074
INT bm_empty_buffers()
Definition midas.cxx:11248
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8728
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9077
INT bm_request_event(HNDLE buffer_handle, short int event_id, short int trigger_mask, INT sampling_type, HNDLE *request_id, EVENT_HANDLER *func)
Definition midas.cxx:8473
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5930
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8148
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6315
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10658
INT bm_check_buffers()
Definition midas.cxx:10962
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9000
INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
Definition midas.cxx:8300
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8526
static double _bm_mutex_timeout_sec
Definition midas.cxx:5928
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7104
int bm_send_event_sg(int buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[], int timeout_msec)
Definition midas.cxx:9786
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8289
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)
Definition midas.cxx:9512
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11289
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6264
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6023
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:8944
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8873
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6125
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8863
static DWORD _bm_max_event_size
Definition midas.cxx:5922
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6416
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10215
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8831
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8630
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)
Definition midas.cxx:10272
static int bm_wait_for_free_space_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
Definition midas.cxx:9088
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7083
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9713
static int _bm_lock_timeout
Definition midas.cxx:5927
static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8899
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10739
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6399
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6043
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:10910
static int bm_wait_for_more_events_locked(bm_lock_buffer_guard &pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
Definition midas.cxx:9400
INT cm_set_path(const char *path)
Definition midas.cxx:1497
INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number)
Definition midas.cxx:3601
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7408
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4173
INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:2833
static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition midas.cxx:3361
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7331
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4090
INT cm_yield(INT millisec)
Definition midas.cxx:5650
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3011
INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names)
Definition midas.cxx:2612
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3325
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11292
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2766
INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void(*func)(char *))
Definition midas.cxx:2278
static BUFFER_CLIENT * bm_get_my_client_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:6007
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2586
INT cm_start_watchdog_thread()
Definition midas.cxx:7363
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:10926
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
Definition midas.cxx:3033
static int tr_finish(HNDLE hDB, TrState *tr, int transition, int status, const char *errorstr)
Definition midas.cxx:4004
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3791
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:7912
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4037
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5294
INT cm_stop_watchdog_thread()
Definition midas.cxx:7378
std::string cm_asctime()
Definition midas.cxx:1412
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5798
INT cm_connect_experiment1(const char *host_name, const char *default_exp_name, const char *client_name, void(*func)(char *), INT odb_size, DWORD watchdog_timeout)
Definition midas.cxx:2297
INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:1869
static int xbm_lock_buffer(BUFFER *pbuf)
Definition midas.cxx:7983
INT cm_periodic_tasks()
Definition midas.cxx:5587
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8045
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5393
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5246
INT cm_select_experiment_remote(const char *host_name, std::string *exp_name)
Definition midas.cxx:2719
INT cm_register_server(void)
Definition midas.cxx:3460
static BOOL _ctrlc_pressed
Definition midas.cxx:5447
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3412
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5464
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5731
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3344
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7618
void cm_check_connect(void)
Definition midas.cxx:2201
static BUFFER * bm_get_buffer(const char *who, INT buffer_handle, int *pstatus)
Definition midas.cxx:6630
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2670
std::string cm_expand_env(const char *str)
Definition midas.cxx:7718
std::string cm_get_client_name()
Definition midas.cxx:2059
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7954
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5469
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1545
static void rpc_client_shutdown()
Definition midas.cxx:12640
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7330
static exptab_struct _exptab
Definition midas.cxx:1605
INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name, char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout)
Definition midas.cxx:1893
INT cm_disconnect_experiment(void)
Definition midas.cxx:2846
static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition midas.cxx:6160
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11056
int cm_get_exptab(const char *expname, std::string *dir, std::string *user)
Definition midas.cxx:1799
static void xcm_watchdog_thread()
Definition midas.cxx:7357
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7750
INT cm_synchronize(DWORD *seconds)
Definition midas.cxx:1369
std::string cm_get_exptab_filename()
Definition midas.cxx:1788
std::string cm_get_path()
Definition midas.cxx:1537
static DWORD _deferred_transition_mask
Definition midas.cxx:3827
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5851
void cm_test_expand_env()
Definition midas.cxx:7765
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3845
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2166
static INT _requested_transition
Definition midas.cxx:3826
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2134
const char * cm_get_version()
Definition midas.cxx:1476
INT cm_read_exptab(exptab_struct *exptab)
Definition midas.cxx:1614
static int bm_lock_buffer_write_cache(BUFFER *pbuf)
Definition midas.cxx:7933
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3677
INT cm_check_deferred_transition()
Definition midas.cxx:3897
std::string cm_get_experiment_name()
Definition midas.cxx:1580
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3731
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:3984
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1852
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7329
const char * cm_get_revision()
Definition midas.cxx:1484
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7337
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:2939
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5460
static INT tr_main_thread(void *param)
Definition midas.cxx:5262
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5449
INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3244
INT cm_time(DWORD *t)
Definition midas.cxx:1434
INT cm_transition_cleanup()
Definition midas.cxx:5275
INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3283
static int cm_transition_call_direct(TrClient *tr_client)
Definition midas.cxx:4408
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7528
INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
Definition midas.cxx:2958
static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4531
INT cm_set_experiment_name(const char *name)
Definition midas.cxx:1558
#define CM_SUCCESS
Definition midas.h:582
#define CM_UNDEF_EXP
Definition midas.h:586
#define CM_INVALID_TRANSITION
Definition midas.h:594
#define CM_SET_ERROR
Definition midas.h:583
#define CM_TRUNCATED
Definition midas.h:596
#define CM_DEFERRED_TRANSITION
Definition midas.h:591
#define CM_TRANSITION_IN_PROGRESS
Definition midas.h:592
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define CM_NO_CLIENT
Definition midas.h:584
#define CM_TRANSITION_CANCELED
Definition midas.h:597
#define CM_DB_ERROR
Definition midas.h:585
#define CM_VERSION_MISMATCH
Definition midas.h:587
#define CM_UNDEF_ENVIRON
Definition midas.h:590
#define BM_INVALID_PARAM
Definition midas.h:619
#define BM_MORE_EVENTS
Definition midas.h:620
#define BM_TRUNCATED
Definition midas.h:614
#define BM_NOT_FOUND
Definition midas.h:612
#define BM_NO_MEMORY
Definition midas.h:607
#define BM_INVALID_MIXING
Definition midas.h:621
#define BM_NO_SLOT
Definition midas.h:610
#define BM_ASYNC_RETURN
Definition midas.h:613
#define BM_INVALID_HANDLE
Definition midas.h:609
#define BM_INVALID_SIZE
Definition midas.h:624
#define BM_SUCCESS
Definition midas.h:605
#define BM_CORRUPTED
Definition midas.h:623
#define BM_NO_SHM
Definition midas.h:622
#define BM_CREATED
Definition midas.h:606
#define BM_NO_SEMAPHORE
Definition midas.h:611
#define DB_INVALID_HANDLE
Definition midas.h:635
#define DB_INVALID_PARAM
Definition midas.h:639
#define DB_SUCCESS
Definition midas.h:631
#define DB_NO_MEMORY
Definition midas.h:633
#define DB_NO_KEY
Definition midas.h:642
#define DB_TYPE_MISMATCH
Definition midas.h:645
#define DB_CREATED
Definition midas.h:632
#define DB_TIMEOUT
Definition midas.h:655
#define DB_NO_MORE_SUBKEYS
Definition midas.h:646
#define SS_SUCCESS
Definition midas.h:663
#define SS_ABORT
Definition midas.h:677
#define SS_FILE_ERROR
Definition midas.h:669
#define SS_TIMEOUT
Definition midas.h:674
#define SS_CREATED
Definition midas.h:664
#define SS_EXIT
Definition midas.h:678
#define RPC_SHUTDOWN
Definition midas.h:707
#define RPC_SUCCESS
Definition midas.h:698
#define RPC_EXCEED_BUFFER
Definition midas.h:703
#define RPC_INVALID_ID
Definition midas.h:706
#define RPC_DOUBLE_DEFINED
Definition midas.h:709
#define RPC_NOT_REGISTERED
Definition midas.h:704
#define RPC_MUTEX_TIMEOUT
Definition midas.h:710
#define RPC_TIMEOUT
Definition midas.h:702
#define RPC_NO_CONNECTION
Definition midas.h:700
#define RPC_NET_ERROR
Definition midas.h:701
#define AL_TRIGGERED
Definition midas.h:758
#define FE_ERR_HW
Definition midas.h:719
unsigned short int WORD
Definition mcstd.h:49
unsigned int DWORD
Definition mcstd.h:51
#define SUCCESS
Definition mcstd.h:54
#define TR_RESUME
Definition midas.h:408
#define GET_NONBLOCKING
Definition midas.h:322
#define TR_PAUSE
Definition midas.h:407
#define TID_DOUBLE
Definition midas.h:343
#define MT_LOG_STR
Definition midas.h:555
#define TID_KEY
Definition midas.h:349
#define GET_ALL
Definition midas.h:321
#define TID_BOOL
Definition midas.h:340
#define TRIGGER_ALL
Definition midas.h:538
#define TR_START
Definition midas.h:405
#define TR_SYNC
Definition midas.h:358
#define GET_RECENT
Definition midas.h:323
#define MT_ALL
Definition midas.h:549
#define BM_NO_WAIT
Definition midas.h:366
#define TID_UINT64
Definition midas.h:352
#define FORMAT_FIXED
Definition midas.h:314
#define MT_INFO_STR
Definition midas.h:552
#define TID_INT64
Definition midas.h:351
#define TR_MTHREAD
Definition midas.h:361
#define MT_INFO
Definition midas.h:543
#define TR_STARTABORT
Definition midas.h:409
#define RPC_HNDLE_CONNECT
Definition midas.h:394
#define STATE_STOPPED
Definition midas.h:305
#define MINFO
Definition midas.h:560
#define MODE_DELETE
Definition midas.h:372
#define MT_DEBUG_STR
Definition midas.h:553
#define MT_TALK
Definition midas.h:547
#define RPC_NO_REPLY
Definition midas.h:396
#define TID_STRUCT
Definition midas.h:348
#define MT_USER
Definition midas.h:545
#define RPC_HNDLE_MSERVER
Definition midas.h:393
#define MLOG
Definition midas.h:563
#define MT_USER_STR
Definition midas.h:554
#define TID_INT32
Definition midas.h:339
#define TR_DETACH
Definition midas.h:360
#define MT_LOG
Definition midas.h:546
#define TID_UINT8
Definition midas.h:328
#define TID_LINK
Definition midas.h:350
#define STATE_PAUSED
Definition midas.h:306
#define MT_TALK_STR
Definition midas.h:556
#define TID_STRING
Definition midas.h:346
#define MODE_WRITE
Definition midas.h:371
#define EVENTID_ALL
Definition midas.h:537
#define TR_DEFERRED
Definition midas.h:410
#define MERROR
Definition midas.h:559
#define STATE_RUNNING
Definition midas.h:307
#define MODE_READ
Definition midas.h:370
#define TID_INT8
Definition midas.h:330
#define FORMAT_MIDAS
Definition midas.h:311
#define TID_ARRAY
Definition midas.h:347
#define MT_DEBUG
Definition midas.h:544
#define TID_CHAR
Definition midas.h:331
#define MTALK
Definition midas.h:564
#define MDEBUG
Definition midas.h:561
#define TID_UINT32
Definition midas.h:337
#define TID_UINT16
Definition midas.h:333
#define TR_STOP
Definition midas.h:406
#define TID_INT16
Definition midas.h:335
#define BM_WAIT
Definition midas.h:365
#define TID_FLOAT
Definition midas.h:341
#define TID_LAST
Definition midas.h:354
#define MT_ERROR
Definition midas.h:542
#define MT_ERROR_STR
Definition midas.h:551
#define VALIGN(adr, align)
Definition midas.h:526
#define MIN(a, b)
Definition midas.h:515
#define ALIGN8(x)
Definition midas.h:522
#define MAX(a, b)
Definition midas.h:509
RPC_LIST * rpc_get_internal_list(INT flag)
Definition mrpc.cxx:716
#define DRI_32
Definition msystem.h:46
#define MESSAGE_BUFFER_NAME
Definition msystem.h:111
#define DRI_LITTLE_ENDIAN
Definition msystem.h:48
#define DRF_G_FLOAT
Definition msystem.h:51
#define MAX_STRING_LENGTH
Definition msystem.h:113
#define MESSAGE_BUFFER_SIZE
Definition msystem.h:110
#define MSG_BM
Definition msystem.h:295
#define DRI_16
Definition msystem.h:45
#define NET_BUFFER_SIZE
Definition msystem.h:114
#define FD_SETSIZE
Definition msystem.h:199
#define O_TEXT
Definition msystem.h:220
#define DRI_64
Definition msystem.h:47
#define DRI_BIG_ENDIAN
Definition msystem.h:49
#define DRF_IEEE
Definition msystem.h:50
#define MSG_WATCHDOG
Definition msystem.h:300
#define MSG_ODB
Definition msystem.h:296
void() EVENT_HANDLER(HNDLE buffer_handler, HNDLE request_id, EVENT_HEADER *event_header, void *event_data)
Definition midas.h:918
INT() RPC_HANDLER(INT index, void *prpc_param[])
Definition midas.h:922
std::string ss_gethostname()
Definition system.cxx:5712
INT ss_suspend(INT millisec, INT msg)
Definition system.cxx:4543
INT ss_get_struct_align()
Definition system.cxx:1319
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3157
INT ss_suspend_init_odb_port()
Definition system.cxx:4305
bool ss_event_socket_has_data()
Definition system.cxx:4520
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3365
DWORD ss_millitime()
Definition system.cxx:3393
int ss_file_exist(const char *path)
Definition system.cxx:7124
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2460
INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
Definition system.cxx:5562
INT ss_suspend_set_client_listener(int listen_socket)
Definition system.cxx:4284
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5246
int ss_file_link_exist(const char *path)
Definition system.cxx:7160
std::string ss_getcwd()
Definition system.cxx:5776
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3211
int ss_socket_wait(int sock, INT millisec)
Definition system.cxx:4898
INT ss_suspend_set_server_acceptions(RPC_SERVER_ACCEPTION_LIST *acceptions)
Definition system.cxx:4298
INT ss_getpid(void)
Definition system.cxx:1377
DWORD ss_settime(DWORD seconds)
Definition system.cxx:3475
char * ss_getpass(const char *prompt)
Definition system.cxx:7446
INT ss_suspend_set_client_connection(RPC_SERVER_CONNECTION *connection)
Definition system.cxx:4291
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:2941
void ss_tzset()
Definition system.cxx:3355
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:324
INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
Definition system.cxx:5399
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5352
int ss_dir_exist(const char *path)
Definition system.cxx:7192
bool ss_timed_mutex_wait_for_sec(std::timed_mutex &mutex, const char *mutex_name, double timeout_sec)
Definition system.cxx:3265
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2781
int ss_file_copy(const char *src, const char *dst, bool append)
Definition system.cxx:7225
DWORD ss_time()
Definition system.cxx:3462
INT ss_suspend_exit()
Definition system.cxx:4226
INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5454
INT ss_resume(INT port, const char *message)
Definition system.cxx:4844
midas_thread_t ss_gettid(void)
Definition system.cxx:1519
std::string ss_asctime()
Definition system.cxx:3549
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2869
INT ss_sleep(INT millisec)
Definition system.cxx:3628
INT ss_socket_connect_tcp(const char *hostname, int tcp_port, int *sockp, std::string *error_msg_p)
Definition system.cxx:4967
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2639
INT ss_socket_listen_tcp(bool listen_localhost, int tcp_port, int *sockp, int *tcp_port_p, std::string *error_msg_p)
Definition system.cxx:5062
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7897
INT ss_spawnv(INT mode, const char *cmdname, const char *const argv[])
Definition system.cxx:1630
INT ss_suspend_get_buffer_port(midas_thread_t thread_id, INT *port)
Definition system.cxx:4353
INT ss_socket_close(int *sockp)
Definition system.cxx:5231
char * ss_gets(char *string, int size)
Definition system.cxx:7776
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5635
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:755
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5285
void * ss_ctrlc_handler(void(*func)(int))
Definition system.cxx:3899
BOOL ss_pid_exists(int pid)
Definition system.cxx:1440
std::string ss_get_executable(void)
Definition system.cxx:1488
INT ss_system(const char *command)
Definition system.cxx:2116
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3037
INT ss_file_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6719
static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated, int *num_messages)
Definition midas.cxx:1096
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:973
int cm_msg_early_init(void)
Definition midas.cxx:467
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:504
int cm_msg_open_buffer(void)
Definition midas.cxx:474
int cm_msg_close_buffer(void)
Definition midas.cxx:487
static std::mutex gMsgBufMutex
Definition midas.cxx:857
static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message)
Definition midas.cxx:1065
INT cm_msg_register(EVENT_HANDLER *func)
Definition midas.cxx:1051
INT cm_msg_log(INT message_type, const char *facility, const char *message)
Definition midas.cxx:664
INT cm_msg_flush_buffer()
Definition midas.cxx:865
static std::deque< msg_buffer_entry > gMsgBuf
Definition midas.cxx:856
static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message)
Definition midas.cxx:824
std::string cm_get_error(INT code)
Definition midas.cxx:455
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:915
static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
Definition midas.cxx:751
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1334
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1264
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:539
INT cm_set_msg_print(INT system_mask, INT user_mask, int(*func)(const char *))
Definition midas.cxx:647
#define WORD_SWAP(x)
Definition msystem.h:65
#define QWORD_SWAP(x)
Definition msystem.h:86
#define DWORD_SWAP(x)
Definition msystem.h:74
struct rpc_server_acception_struct RPC_SERVER_ACCEPTION
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3201
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2268
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6893
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3856
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3059
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5415
INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size, WORD access_mode, void(*dispatcher)(INT, INT, void *), void *info)
Definition odb.cxx:13292
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1787
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2827
INT db_lock_database(HNDLE hDB)
Definition odb.cxx:2455
std::string strcomb1(const char **list)
Definition odb.cxx:598
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6539
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3308
INT db_unlock_database(HNDLE hDB)
Definition odb.cxx:2577
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8027
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6019
INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string *s, BOOL create, int create_string_length)
Definition odb.cxx:13935
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3014
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7648
INT db_close_all_records()
Definition odb.cxx:13515
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13814
INT db_close_all_databases(void)
Definition odb.cxx:2360
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7215
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10843
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2692
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7313
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5261
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4079
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13553
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:2976
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13600
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2897
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2791
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2402
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12624
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12292
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5586
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12801
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14006
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2664
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7502
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3167
int rb_get_rp(int handle, void **p, int millisec)
Definition midas.cxx:17576
#define MAX_RING_BUFFER
Definition midas.cxx:17288
int rb_delete(int handle)
Definition midas.cxx:17398
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:17435
int rb_increment_rp(int handle, int size)
Definition midas.cxx:17638
int rb_set_nonblocking()
Definition midas.cxx:17303
static volatile int _rb_nonblocking
Definition midas.cxx:17292
int rb_increment_wp(int handle, int size)
Definition midas.cxx:17510
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:17344
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:17693
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:17290
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:15243
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11714
#define RPC_CM_TIME
Definition mrpc.h:29
INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
Definition midas.cxx:12020
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14546
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:14655
#define RPC_TEST2
Definition mrpc.h:123
#define RPC_CM_CHECK_CLIENT
Definition mrpc.h:34
#define RPC_CM_ASCTIME
Definition mrpc.h:28
static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION *psa, char **pbuffer, int *pbuffer_size)
Definition midas.cxx:14395
INT rpc_get_opt_tcp_size()
Definition midas.cxx:13880
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12684
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11584
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13480
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11835
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:15710
static std::mutex _client_connections_mutex
Definition midas.cxx:11503
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13006
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
static std::mutex gAllowedHostsMutex
Definition midas.cxx:15215
INT recv_tcp_check(int sock)
Definition midas.cxx:14365
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15879
#define RPC_BM_GET_BUFFER_INFO
Definition mrpc.h:39
#define RPC_CM_SET_CLIENT_INFO
Definition mrpc.h:21
const char * rpc_get_mserver_path()
Definition midas.cxx:13059
static RPC_SERVER_ACCEPTION * rpc_get_server_acception(int idx)
Definition midas.cxx:11513
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11521
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11591
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12389
std::string rpc_get_name()
Definition midas.cxx:13092
#define RPC_ID_WATCHDOG
Definition mrpc.h:133
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11526
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
bool rpc_is_remote(void)
Definition midas.cxx:12769
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13169
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11779
static int _tr_fifo_rp
Definition midas.cxx:14062
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14128
#define RPC_RC_TRANSITION
Definition mrpc.h:116
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13201
#define RPC_ID_EXIT
Definition mrpc.h:135
INT rpc_server_loop(void)
Definition midas.cxx:15851
INT rpc_clear_allowed_hosts()
Definition midas.cxx:15218
int rpc_test_rpc()
Definition midas.cxx:15054
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12813
INT rpc_get_hw_type()
Definition midas.cxx:12842
INT rpc_deregister_functions()
Definition midas.cxx:11878
bool rpc_is_connected(void)
Definition midas.cxx:12791
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13072
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11581
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:14654
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11504
#define RPC_ID_SHUTDOWN
Definition mrpc.h:134
static void rpc_call_encode(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13239
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12620
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:15214
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:13671
static std::mutex rpc_list_mutex
Definition midas.cxx:11582
void rpc_client_check()
Definition midas.cxx:12278
const char * rpc_tid_name(INT id)
Definition midas.cxx:11772
INT rpc_flush_event()
Definition midas.cxx:14046
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11816
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11510
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:15321
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:16191
static int _tr_fifo_wp
Definition midas.cxx:14061
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:16144
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:13909
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14060
static std::string _mserver_path
Definition midas.cxx:13056
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:12981
INT rpc_server_disconnect()
Definition midas.cxx:12713
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13142
INT rpc_client_accept(int lsock)
Definition midas.cxx:15606
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11634
INT rpc_execute(INT sock, char *buffer, INT convert_flags)
Definition midas.cxx:14658
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:13872
#define RPC_BM_GET_BUFFER_LEVEL
Definition mrpc.h:40
int rpc_name_tid(const char *name)
Definition midas.cxx:11786
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:14587
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:15989
#define RPC_BM_OPEN_BUFFER
Definition mrpc.h:36
#define RPC_BM_EMPTY_BUFFERS
Definition mrpc.h:49
INT rpc_server_accept(int lsock)
Definition midas.cxx:15349
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14059
bool rpc_is_mserver(void)
Definition midas.cxx:12826
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11511
static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
Definition midas.cxx:14205
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11619
#define RPC_CM_SET_WATCHDOG_PARAMS
Definition mrpc.h:22
#define RPC_CM_MSG_LOG
Definition mrpc.h:25
INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
Definition midas.cxx:13927
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11507
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13410
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:13933
INT rpc_set_name(const char *name)
Definition midas.cxx:13116
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:11905
#define RPC_CM_MSG_RETRIEVE
Definition mrpc.h:32
#define RPC_BM_SKIP_EVENT
Definition mrpc.h:50
INT rpc_client_dispatch(int sock)
Definition midas.cxx:11953
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11506
INT rpc_check_channels(void)
Definition midas.cxx:16262
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14064
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:11939
#define RPC_BM_FLUSH_CACHE
Definition mrpc.h:46
#define RPC_CM_CLEANUP
Definition mrpc.h:23
void rpc_ieee2vax_double(double *var)
Definition midas.cxx:11669
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11650
#define RPC_CM_GET_WATCHDOG_INFO
Definition mrpc.h:24
INT rpc_get_convert_flags(void)
Definition midas.cxx:13038
INT rpc_tid_size(INT id)
Definition midas.cxx:11765
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:15272
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11689
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
char exp_name[NAME_LENGTH]
Definition mana.cxx:243
INT run_number[2]
Definition mana.cxx:246
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
DWORD last_time
Definition mana.cxx:3070
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
INT odb_size
Definition analyzer.cxx:46
BOOL debug
debug printouts
Definition mana.cxx:254
INT type
Definition mana.cxx:269
HNDLE hDB
main ODB handle
Definition mana.cxx:207
char host_name[HOST_NAME_LENGTH]
Definition mana.cxx:242
double count
Definition mdump.cxx:33
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
HNDLE hSubkey
Definition mdump.cxx:35
char expt_name[NAME_LENGTH]
Definition mevb.cxx:44
char buffer_name[NAME_LENGTH]
Definition mevb.cxx:45
DWORD actual_time
Definition mfe.cxx:37
void * event_buffer
Definition mfe.cxx:65
INT max_event_size
Definition mfed.cxx:30
#define DWORD
Definition mhdump.cxx:31
static const int tid_size[]
Definition midas.cxx:66
static INT _n_mem
Definition midas.cxx:287
static std::string join(const char *sep, const std::vector< std::string > &v)
Definition midas.cxx:389
static std::vector< TRANS_TABLE > _trans_table
Definition midas.cxx:248
static std::atomic_int _message_mask_system
Definition midas.cxx:435
INT _semaphore_alarm
Definition midas.cxx:1462
static int disable_bind_rpc_to_localhost
Definition midas.cxx:237
static std::mutex gBuffersMutex
Definition midas.cxx:195
static HNDLE _hKeyClient
Definition midas.cxx:1456
int(* MessagePrintCallback)(const char *)
Definition midas.cxx:431
std::string cm_transition_name(int transition)
Definition midas.cxx:133
static DBG_MEM_LOC * _mem_loc
Definition midas.cxx:286
static std::vector< BUFFER * > gBuffers
Definition midas.cxx:196
static void(* _debug_print)(const char *)
Definition midas.cxx:229
static std::mutex _trans_table_mutex
Definition midas.cxx:247
static std::string _experiment_name
Definition midas.cxx:1458
static std::string _path_name
Definition midas.cxx:1460
INT _database_entries
static std::string _client_name
Definition midas.cxx:1459
static std::vector< EventRequest > _request_list
Definition midas.cxx:220
static int _rpc_connect_timeout
Definition midas.cxx:233
static const char * tid_name[]
Definition midas.cxx:111
static const ERROR_TABLE _error_table[]
Definition midas.cxx:270
static INT _watchdog_timeout
Definition midas.cxx:1461
INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
Definition midas.cxx:7799
static INT _debug_mode
Definition midas.cxx:231
static MUTEX_T * _mutex_rpc
Definition midas.cxx:227
static EVENT_HANDLER * _msg_dispatch
Definition midas.cxx:199
void * dbg_calloc(unsigned int size, unsigned int count, char *file, int line)
Definition midas.cxx:341
static BOOL _rpc_registered
Definition midas.cxx:258
static std::atomic< MessagePrintCallback > _message_print
Definition midas.cxx:433
bool ends_with_char(const std::string &s, char c)
Definition midas.cxx:403
void dbg_free(void *adr, char *file, int line)
Definition midas.cxx:351
static std::atomic_int _message_mask_user
Definition midas.cxx:436
static std::mutex _request_list_mutex
Definition midas.cxx:219
INT _semaphore_elog
Definition midas.cxx:1463
INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
Definition midas.cxx:7846
INT _semaphore_history
Definition midas.cxx:1464
static TRANS_TABLE _deferred_trans_table[]
Definition midas.cxx:250
static int _rpc_listen_socket
Definition midas.cxx:259
static HNDLE _hDB
Definition midas.cxx:1457
std::string msprintf(const char *format,...)
Definition midas.cxx:410
void * dbg_malloc(unsigned int size, char *file, int line)
Definition midas.cxx:306
static const char * tid_name_old[]
Definition midas.cxx:89
const char * mname[]
Definition midas.cxx:144
static std::vector< std::string > split(const char *sep, const std::string &s)
Definition midas.cxx:372
static TR_PARAM _trp
Definition midas.cxx:302
int cm_write_event_to_odb(HNDLE hDB, HNDLE hKey, const EVENT_HEADER *pevent, INT format)
Definition midas.cxx:17731
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8071
#define DIR_SEPARATOR
Definition midas.h:193
#define MAX_CLIENTS
Definition midas.h:274
INT HNDLE
Definition midas.h:132
#define O_LARGEFILE
Definition midas.h:210
#define CF_ENDIAN
Definition midas.h:1609
#define M_MALLOC(x)
Definition midas.h:1550
#define CINT(_i)
Definition midas.h:1620
#define RPC_OUT
Definition midas.h:1579
#define HOST_NAME_LENGTH
Definition midas.h:273
INT midas_thread_t
Definition midas.h:179
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define RPC_IN
Definition midas.h:1578
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1604
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1605
#define NET_TCP_SIZE
Definition midas.h:266
int INT
Definition midas.h:129
#define DEFAULT_RPC_TIMEOUT
Definition midas.h:287
#define MAX_RPC_PARAMS
Definition midas.h:1593
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1446
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:907
#define RPC_OUTGOING
Definition midas.h:1583
#define MAX_WRITE_CACHE_EVENT_SIZE_DIV
Definition midas.h:259
#define MIDAS_TCP_PORT
Definition midas.h:283
#define CSTRING(_i)
Definition midas.h:1644
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1580
#define EVENTID_MESSAGE
Definition midas.h:902
#define MIDAS_VERSION
Definition midas.h:37
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define M_FREE(x)
Definition midas.h:1552
#define TRUE
Definition midas.h:182
#define BANKLIST_MAX
Definition midas.h:278
#define EVENTID_FRAG1
Definition midas.h:906
#define CF_IEEE2VAX
Definition midas.h:1610
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1582
#define POINTER_T
Definition midas.h:166
#define BANK_FORMAT_64BIT_ALIGNED
Definition midas.h:1203
#define BANK_FORMAT_32BIT
Definition midas.h:1202
#define CF_VAX2IEEE
Definition midas.h:1611
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define MAX_WRITE_CACHE_SIZE_DIV
Definition midas.h:258
#define TRANSITION_ERROR_STRING_LENGTH
Definition midas.h:280
#define RPC_FIXARRAY
Definition midas.h:1581
#define BANK_FORMAT_VERSION
Definition midas.h:1201
#define NAME_LENGTH
Definition midas.h:272
#define trigger_mask
#define message(type, str)
#define read(n, a, f)
#define event_id
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
INT serial
Definition minife.c:20
static std::string remove(const std::string s, char c)
Definition mjsonrpc.cxx:253
int gettimeofday(struct timeval *tp, void *tzp)
struct callback_addr callback
Definition mserver.cxx:22
timeval tv
Definition msysmon.cxx:1095
int event_size
Definition msysmon.cxx:527
MUTEX_T * tm
Definition odbedit.cxx:39
char pwd[256]
Definition odbedit.cxx:24
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
DWORD status
Definition odbhist.cxx:39
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
DWORD data_size
Definition midas.h:1228
DWORD data_size
Definition midas.h:1222
Definition midas.h:1213
DWORD watchdog_timeout
Definition midas.h:951
DWORD last_activity
Definition midas.h:950
char name[NAME_LENGTH]
Definition midas.h:935
INT read_pointer
Definition midas.h:940
INT num_in_events
Definition midas.h:964
INT write_pointer
Definition midas.h:963
INT num_clients
Definition midas.h:959
char name[NAME_LENGTH]
Definition midas.h:958
INT max_client_index
Definition midas.h:960
BUFFER_CLIENT client[MAX_CLIENTS]
Definition midas.h:967
INT num_out_events
Definition midas.h:965
INT read_pointer
Definition midas.h:962
int count_sent
Definition midas.cxx:6435
int last_count_lock
Definition midas.cxx:6439
BOOL get_all_flag
Definition midas.cxx:6431
int count_lock
Definition midas.cxx:6434
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6448
int count_write_wait
Definition midas.cxx:6437
double bytes_read
Definition midas.cxx:6444
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6445
int wait_client_index
Definition midas.cxx:6441
DWORD time_write_wait
Definition midas.cxx:6438
int count_read
Definition midas.cxx:6443
double bytes_sent
Definition midas.cxx:6436
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6446
DWORD wait_start_time
Definition midas.cxx:6440
int max_requested_space
Definition midas.cxx:6442
INT client_index
Definition midas.h:989
int client_count_write_wait[MAX_CLIENTS]
Definition midas.h:1022
char buffer_name[NAME_LENGTH]
Definition midas.h:991
void * adr
Definition midas.cxx:280
const char * string
Definition midas.cxx:267
EVENT_HEADER * pevent
Definition midas.cxx:11286
short int event_id
Definition midas.h:852
DWORD data_size
Definition midas.h:856
DWORD serial_number
Definition midas.h:854
DWORD time_stamp
Definition midas.h:855
short int trigger_mask
Definition midas.h:853
INT sampling_type
Definition midas.h:931
BOOL valid
Definition midas.h:928
short int event_id
Definition midas.cxx:206
INT buffer_handle
Definition midas.cxx:205
short int trigger_mask
Definition midas.cxx:207
EVENT_HANDLER * dispatcher
Definition midas.cxx:208
void clear()
Definition midas.cxx:210
Definition midas.h:1026
INT num_values
Definition midas.h:1028
DWORD type
Definition midas.h:1027
char name[NAME_LENGTH]
Definition midas.h:1029
INT item_size
Definition midas.h:1032
NET_COMMAND_HEADER header
Definition msystem.h:286
char param[32]
Definition msystem.h:287
unsigned char * wp
Definition midas.cxx:17284
unsigned char * buffer
Definition midas.cxx:17280
unsigned int max_event_size
Definition midas.cxx:17282
unsigned int size
Definition midas.cxx:17281
unsigned char * rp
Definition midas.cxx:17283
unsigned char * ep
Definition midas.cxx:17285
RPC_HANDLER * dispatch
Definition midas.h:1599
const char * name
Definition midas.h:1597
midas_thread_t thread_id
Definition midas.cxx:14649
int buffer_size
Definition midas.cxx:14650
char * buffer
Definition midas.cxx:14651
int transition
Definition midas.cxx:14053
time_t trans_time
Definition midas.cxx:14055
int run_number
Definition midas.cxx:14054
int sequence_number
Definition midas.cxx:14056
std::atomic< std::thread * > thread
Definition midas.cxx:299
std::atomic_bool finished
Definition midas.cxx:298
INT debug_flag
Definition midas.cxx:296
INT run_number
Definition midas.cxx:292
INT errstr_size
Definition midas.cxx:294
char * errstr
Definition midas.cxx:293
std::atomic_int status
Definition midas.cxx:297
INT async_flag
Definition midas.cxx:295
INT transition
Definition midas.cxx:291
INT sequence_number
Definition midas.cxx:243
INT(* func)(INT, char *)
Definition midas.cxx:244
INT transition
Definition midas.cxx:242
DWORD connect_timeout
Definition midas.cxx:3953
int transition
Definition midas.cxx:3938
std::string host_name
Definition midas.cxx:3944
DWORD connect_end_time
Definition midas.cxx:3955
std::atomic_int status
Definition midas.cxx:3948
~TrClient()
Definition midas.cxx:3966
std::vector< int > wait_for_index
Definition midas.cxx:3943
DWORD init_time
Definition midas.cxx:3951
std::string key_name
Definition midas.cxx:3947
DWORD rpc_end_time
Definition midas.cxx:3958
std::string waiting_for_client
Definition midas.cxx:3952
DWORD rpc_timeout
Definition midas.cxx:3956
int async_flag
Definition midas.cxx:3940
int port
Definition midas.cxx:3946
DWORD rpc_start_time
Definition midas.cxx:3957
int sequence_number
Definition midas.cxx:3942
int debug_flag
Definition midas.cxx:3941
int run_number
Definition midas.cxx:3939
DWORD end_time
Definition midas.cxx:3959
DWORD connect_start_time
Definition midas.cxx:3954
std::string client_name
Definition midas.cxx:3945
std::string errorstr
Definition midas.cxx:3950
void Print() const
Definition midas.cxx:3972
std::thread * thread
Definition midas.cxx:3949
int transition
Definition midas.cxx:3991
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:3999
int async_flag
Definition midas.cxx:3993
DWORD end_time
Definition midas.cxx:3998
int run_number
Definition midas.cxx:3992
int status
Definition midas.cxx:3995
DWORD start_time
Definition midas.cxx:3997
std::string errorstr
Definition midas.cxx:3996
int debug_flag
Definition midas.cxx:3994
unsigned short host_port1
Definition msystem.h:312
std::string user
Definition msystem.h:318
unsigned short host_port2
Definition msystem.h:313
std::string experiment
Definition msystem.h:316
unsigned short host_port3
Definition msystem.h:314
std::string directory
Definition msystem.h:317
std::string host_name
Definition msystem.h:311
Definition midas.cxx:1594
std::string name
Definition midas.cxx:1595
std::string directory
Definition midas.cxx:1596
std::string user
Definition midas.cxx:1597
std::string filename
Definition midas.cxx:1601
std::vector< exptab_entry > exptab
Definition midas.cxx:1602
Definition midas.cxx:850
DWORD ts
Definition midas.cxx:851
int message_type
Definition midas.cxx:852
std::string message
Definition midas.cxx:853
double d
Definition system.cxx:1311
char c
Definition system.cxx:1310
static double e(void)
Definition tinyexpr.c:136
static double fac(double a)
Definition tinyexpr.c:137
static te_expr * list(state *s)
Definition tinyexpr.c:567