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
3290 } else if (rpc_is_mserver()) { // we are the mserver
3292 if (sa)
3293 sa->watchdog_timeout = timeout;
3294
3295 /* write timeout value to client enty in ODB */
3296 HNDLE hDB, hKey;
3298
3299 if (hDB) {
3301 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3303 }
3304 return DB_SUCCESS;
3305 } else {
3307 }
3308}
3309
3310/********************************************************************/
3318 if (call_watchdog)
3320 if (timeout)
3321 *timeout = _watchdog_timeout;
3322
3323 return CM_SUCCESS;
3324}
3325
3326/********************************************************************/
3336INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3337 if (rpc_is_remote())
3338 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3339
3340#ifdef LOCAL_ROUTINES
3341 return db_get_watchdog_info(hDB, client_name, timeout, last);
3342#else /* LOCAL_ROUTINES */
3343 return CM_SUCCESS;
3344#endif /* LOCAL_ROUTINES */
3345}
3346
3347
3349#ifndef DOXYGEN_SHOULD_SKIP_THIS
3350
3351/********************************************************************/
3352
3353static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3354 int status;
3355 int i, last;
3356 KEY key;
3357 int max_size;
3358 char *str;
3359
3360// if (index != -99)
3361// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3362
3364
3365 if (status != DB_SUCCESS)
3366 return;
3367
3368 //printf("clear rpc hosts!\n");
3370
3372 str = (char *) malloc(max_size);
3373
3374 last = 0;
3375 for (i = 0; i < key.num_values; i++) {
3376 int size = max_size;
3378 if (status != DB_SUCCESS)
3379 break;
3380
3381 if (strlen(str) < 1) // skip emties
3382 continue;
3383
3384 if (str[0] == '#') // skip commented-out entries
3385 continue;
3386
3387 //printf("add rpc hosts %d [%s]\n", i, str);
3389 last = i;
3390 }
3391
3392 if (key.num_values - last < 10) {
3393 int new_size = last + 10;
3395 if (status != DB_SUCCESS) {
3396 cm_msg(MERROR, "load_rpc_hosts",
3397 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3398 }
3399 }
3400
3401 free(str);
3402}
3403
3405 int status;
3406 char buf[256];
3407 int size, i;
3408 HNDLE hKey;
3409
3410 strcpy(buf, "localhost");
3411 size = sizeof(buf);
3412
3413 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3414
3415 if (status != DB_SUCCESS) {
3416 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3417 status);
3418 return;
3419 }
3420
3421 size = sizeof(i);
3422 i = 0;
3423 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3424
3425 if (status != DB_SUCCESS) {
3426 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3427 return;
3428 }
3429
3430 if (i != 0) // RPC hosts check is disabled
3431 return;
3432
3433 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3434
3435 if (status != DB_SUCCESS || hKey == 0) {
3436 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3437 status);
3438 return;
3439 }
3440
3441 load_rpc_hosts(hDB, hKey, -99, NULL);
3442
3444
3445 if (status != DB_SUCCESS) {
3446 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3447 return;
3448 }
3449}
3450
3451/********************************************************************/
3453/********************************************************************\
3454
3455 Routine: cm_register_server
3456
3457 Purpose: Register a server which can be called from other clients
3458 of a specific experiment.
3459
3460 Input:
3461 none
3462
3463 Output:
3464 none
3465
3466 Function value:
3467 CM_SUCCESS Successful completion
3468
3469\********************************************************************/
3470{
3471 if (!_rpc_registered) {
3472 INT status;
3473 int size;
3474 HNDLE hDB, hKey;
3475 char name[NAME_LENGTH];
3476 char str[256];
3477 int port = 0;
3478
3480
3481 size = sizeof(name);
3482 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3483
3484 if (status != DB_SUCCESS) {
3485 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3486 return status;
3487 }
3488
3489 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3490 mstrlcat(str, name, sizeof(str));
3491
3492 size = sizeof(port);
3493 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3494
3495 if (status != DB_SUCCESS) {
3496 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3497 return status;
3498 }
3499
3500 int lport = 0; // actual port number assigned to us by the OS
3501
3503 if (status != RPC_SUCCESS) {
3504 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3505 return status;
3506 }
3507
3509
3510 /* register MIDAS library functions */
3512
3513 /* store port number in ODB */
3514
3515 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3516 if (status != DB_SUCCESS) {
3517 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3518 return status;
3519 }
3520
3521 /* unlock database */
3523
3524 /* set value */
3525 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3526 if (status != DB_SUCCESS) {
3527 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3528 return status;
3529 }
3530
3531 /* lock database */
3533
3535 }
3536
3537 return CM_SUCCESS;
3538}
3539
3541#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3542
3543/********************************************************************/
3593INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3594 INT status;
3596 KEY key;
3597 char str[256];
3598
3599 /* check for valid transition */
3601 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3602 return CM_INVALID_TRANSITION;
3603 }
3604
3606
3608
3609 /* register new transition request */
3610
3611 {
3612 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3613
3614 for (size_t i = 0; i < _trans_table.size(); i++) {
3615 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3616 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3617 return CM_INVALID_TRANSITION;
3618 }
3619 }
3620
3621 bool found = false;
3622 for (size_t i = 0; i < _trans_table.size(); i++) {
3623 if (!_trans_table[i].transition) {
3624 _trans_table[i].transition = transition;
3625 _trans_table[i].sequence_number = sequence_number;
3626 _trans_table[i].func = func;
3627 found = true;
3628 break;
3629 }
3630 }
3631
3632 if (!found) {
3635 tt.sequence_number = sequence_number;
3636 tt.func = func;
3637 _trans_table.push_back(tt);
3638 }
3639
3640 // implicit unlock
3641 }
3642
3643 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3644
3645 /* unlock database */
3647
3648 /* set value */
3650 if (!hKeyTrans) {
3651 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3652 if (status != DB_SUCCESS)
3653 return status;
3654 } else {
3656 if (status != DB_SUCCESS)
3657 return status;
3658 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3659 if (status != DB_SUCCESS)
3660 return status;
3661 }
3662
3663 /* re-lock database */
3665
3666 return CM_SUCCESS;
3667}
3668
3670 INT status;
3672 char str[256];
3673
3674 /* check for valid transition */
3676 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3677 return CM_INVALID_TRANSITION;
3678 }
3679
3681
3682 {
3683 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3684
3685 /* remove existing transition request */
3686 for (size_t i = 0; i < _trans_table.size(); i++) {
3688 _trans_table[i].transition = 0;
3689 _trans_table[i].sequence_number = 0;
3690 _trans_table[i].func = NULL;
3691 }
3692 }
3693
3694 // implicit unlock
3695 }
3696
3697 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3698
3699 /* unlock database */
3701
3702 /* set value */
3704 if (hKeyTrans) {
3706 if (status != DB_SUCCESS)
3707 return status;
3708 }
3709
3710 /* re-lock database */
3712
3713 return CM_SUCCESS;
3714}
3715
3716/********************************************************************/
3724 INT status;
3725 HNDLE hDB, hKey;
3726 char str[256];
3727
3728 /* check for valid transition */
3730 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3731 return CM_INVALID_TRANSITION;
3732 }
3733
3734 {
3735 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3736
3737 int count = 0;
3738 for (size_t i = 0; i < _trans_table.size(); i++) {
3740 _trans_table[i].sequence_number = sequence_number;
3741 count++;
3742 }
3743 }
3744
3745 if (count == 0) {
3746 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3747 return CM_INVALID_TRANSITION;
3748 } else if (count > 1) {
3749 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3750 return CM_INVALID_TRANSITION;
3751 }
3752
3753 /* Change local sequence number for this transition type */
3754
3755 for (size_t i = 0; i < _trans_table.size(); i++) {
3757 _trans_table[i].sequence_number = sequence_number;
3758 }
3759 }
3760
3761 // implicit unlock
3762 }
3763
3765
3766 /* unlock database */
3768
3769 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3770
3771 /* set value */
3772 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3773 if (status != DB_SUCCESS)
3774 return status;
3775
3776 /* re-lock database */
3778
3779 return CM_SUCCESS;
3780
3781}
3782
3784 INT status;
3785 HNDLE hDB, hKey;
3786 KEY key;
3787
3789
3790 /* check that hKey is still valid */
3792
3793 if (status != DB_SUCCESS) {
3794 cm_msg(MERROR, "cm_set_client_run_state",
3795 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3796 hKey);
3797 return status;
3798 }
3799
3800 /* unlock database */
3802
3803 /* set value */
3804 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3805 if (status != DB_SUCCESS)
3806 return status;
3807
3808 /* re-lock database */
3810
3811 return CM_SUCCESS;
3812
3813}
3814
3816#ifndef DOXYGEN_SHOULD_SKIP_THIS
3817
3820
3822#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3823
3824/********************************************************************/
3838 INT status, size;
3839 char tr_key_name[256];
3840 HNDLE hDB, hKey;
3841
3843
3844 for (int i = 0; _deferred_trans_table[i].transition; i++)
3846 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3847
3848 /* set new transition mask */
3850
3851 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3852
3853 /* unlock database */
3855
3856 /* set value */
3857 int i = 0;
3858 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3859 if (status != DB_SUCCESS)
3860 return status;
3861
3862 /* re-lock database */
3864
3865 /* hot link requested transition */
3866 size = sizeof(_requested_transition);
3867 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3868 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3870 if (status != DB_SUCCESS) {
3871 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3872 return status;
3873 }
3874
3875 return CM_SUCCESS;
3876}
3877
3878/********************************************************************/
3890 INT i, status;
3891 char str[256];
3892 static BOOL first;
3893
3894 if (_requested_transition == 0)
3895 first = TRUE;
3896
3898 for (i = 0; _deferred_trans_table[i].transition; i++)
3900 break;
3901
3905 if (status != CM_SUCCESS)
3906 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3907
3908 /* bypass hotlink and set _requested_transition directly to zero */
3910
3911 return status;
3912 }
3913 first = FALSE;
3914 }
3915 }
3916
3917 return SUCCESS;
3918}
3919
3920
3922#ifndef DOXYGEN_SHOULD_SKIP_THIS
3923
3924/********************************************************************/
3925
3927#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3928
3929struct TrClient {
3930 int transition = 0;
3931 int run_number = 0;
3932 int async_flag = 0;
3933 int debug_flag = 0;
3935 std::vector<int> wait_for_index;
3936 std::string host_name;
3937 std::string client_name;
3938 int port = 0;
3939 std::string key_name; /* this client key name in /System/Clients */
3940 std::atomic_int status{0};
3941 std::thread* thread = NULL;
3942 std::string errorstr;
3943 DWORD init_time = 0; // time when tr_client created
3944 std::string waiting_for_client; // name of client we are waiting for
3946 DWORD connect_start_time = 0; // time when client rpc connection is started
3947 DWORD connect_end_time = 0; // time when client rpc connection is finished
3949 DWORD rpc_start_time = 0; // time client rpc call is started
3950 DWORD rpc_end_time = 0; // time client rpc call is finished
3951 DWORD end_time = 0; // time client thread is finished
3952
3953 TrClient() // ctor
3954 {
3955 // empty
3956 }
3957
3958 ~TrClient() // dtor
3959 {
3960 //printf("TrClient::dtor: client \"%s\"\n", client_name);
3961 assert(thread == NULL);
3962 }
3963
3964 void Print() const
3965 {
3966 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3967 if (wait_for_index.size() > 0) {
3968 printf(", wait for:");
3969 for (size_t i=0; i<wait_for_index.size(); i++) {
3970 printf(" %d", wait_for_index[i]);
3971 }
3972 }
3973 }
3974};
3975
3976static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
3977 return arg1->sequence_number < arg2->sequence_number;
3978}
3979
3980/*------------------------------------------------------------------*/
3981
3982struct TrState {
3983 int transition = 0;
3984 int run_number = 0;
3985 int async_flag = 0;
3986 int debug_flag = 0;
3987 int status = 0;
3988 std::string errorstr;
3991 std::vector<std::unique_ptr<TrClient>> clients;
3992};
3993
3994/*------------------------------------------------------------------*/
3995
3996static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
3997{
3998 DWORD end_time = ss_millitime();
3999
4000 if (transition != TR_STARTABORT) {
4001 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4002 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4003
4004 if (errorstr) {
4005 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4006 } else if (status == CM_SUCCESS) {
4007 const char *buf = "Success";
4008 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4009 } else {
4010 char buf[256];
4011 sprintf(buf, "status %d", status);
4012 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4013 }
4014 }
4015
4016 tr->status = status;
4017 tr->end_time = end_time;
4018 if (errorstr) {
4019 tr->errorstr = errorstr;
4020 } else {
4021 tr->errorstr = "(null)";
4022 }
4023
4024 return status;
4025}
4026
4027/*------------------------------------------------------------------*/
4028
4030 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4031
4032 int status;
4033 HNDLE hKey;
4034
4035 if (tr_client->transition == TR_STARTABORT) {
4036 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4037 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4038 if (status != DB_SUCCESS)
4039 return;
4040 } else {
4041 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4042 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4043 if (status != DB_SUCCESS)
4044 return;
4045 }
4046
4047 // same client_name can exist with different sequence numbers!
4048 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4049
4051 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4052 if (status != DB_SUCCESS)
4053 return;
4054
4056
4057 //int transition;
4058 //int run_number;
4059 //int async_flag;
4060 //int debug_flag;
4061 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4062 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4063 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4064 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4065 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4066 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);
4067 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4068 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4069 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4070 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4071 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4072 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4073 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4074 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4075 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4076 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4077}
4078
4079/*------------------------------------------------------------------*/
4080
4081/* Perform a detached transition through the external "mtransition" program */
4082static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4083 HNDLE hDB;
4084 int status;
4085 const char *args[100];
4086 std::string path;
4087 char debug_arg[256];
4088 char start_arg[256];
4089 std::string expt_name;
4090 std::string mserver_hostname;
4091
4092 int iarg = 0;
4093
4095
4096 const char *midassys = getenv("MIDASSYS");
4097 if (midassys) {
4098 path += midassys;
4099 path += DIR_SEPARATOR_STR;
4100 path += "bin";
4101 path += DIR_SEPARATOR_STR;
4102 }
4103 path += "mtransition";
4104
4105 args[iarg++] = path.c_str();
4106
4107 if (rpc_is_remote()) {
4108 /* if connected to mserver, pass connection info to mtransition */
4110 args[iarg++] = "-h";
4111 args[iarg++] = mserver_hostname.c_str();
4112 }
4113
4114 /* get experiment name from ODB */
4115 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4116
4117 if (expt_name.length() > 0) {
4118 args[iarg++] = "-e";
4119 args[iarg++] = expt_name.c_str();
4120 }
4121
4122 if (debug_flag) {
4123 args[iarg++] = "-d";
4124
4125 sprintf(debug_arg, "%d", debug_flag);
4126 args[iarg++] = debug_arg;
4127 }
4128
4129 if (transition == TR_STOP)
4130 args[iarg++] = "STOP";
4131 else if (transition == TR_PAUSE)
4132 args[iarg++] = "PAUSE";
4133 else if (transition == TR_RESUME)
4134 args[iarg++] = "RESUME";
4135 else if (transition == TR_START) {
4136 args[iarg++] = "START";
4137
4139 args[iarg++] = start_arg;
4140 }
4141
4142 args[iarg++] = NULL;
4143
4144#if 0
4145 for (iarg = 0; args[iarg] != NULL; iarg++) {
4146 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4147 }
4148#endif
4149
4151
4152 if (status != SS_SUCCESS) {
4153 if (errstr != NULL) {
4154 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4155 }
4156 return CM_SET_ERROR;
4157 }
4158
4159 return CM_SUCCESS;
4160}
4161
4162/*------------------------------------------------------------------*/
4163
4164/* contact a client via RPC and execute the remote transition */
4165static int cm_transition_call(TrState* s, int idx) {
4166 INT old_timeout, status, i, t1, t0, size;
4167 HNDLE hDB;
4168 HNDLE hConn = -1;
4169 int connect_timeout = 10000;
4170 int timeout = 120000;
4171
4173 assert(hDB);
4174
4175 TrClient *tr_client = s->clients[idx].get();
4176
4177 tr_client->errorstr = "";
4178 //tr_client->init_time = ss_millitime();
4179 tr_client->waiting_for_client = "";
4180 tr_client->connect_timeout = 0;
4181 tr_client->connect_start_time = 0;
4182 tr_client->connect_end_time = 0;
4183 tr_client->rpc_timeout = 0;
4184 tr_client->rpc_start_time = 0;
4185 tr_client->rpc_end_time = 0;
4186 tr_client->end_time = 0;
4187
4189
4190 /* wait for predecessor if set */
4191 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4192 while (1) {
4194
4195 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4196 int wait_for_index = tr_client->wait_for_index[i];
4197
4198 assert(wait_for_index >= 0);
4199 assert(wait_for_index < (int)s->clients.size());
4200
4201 TrClient *t = s->clients[wait_for_index].get();
4202
4203 if (!t)
4204 continue;
4205
4206 if (t->status == 0) {
4207 wait_for = t;
4208 break;
4209 }
4210
4211 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4212 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));
4213 tr_client->status = -1;
4214 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4215 tr_client->end_time = ss_millitime();
4217 return CM_SUCCESS;
4218 }
4219 }
4220
4221 if (wait_for == NULL)
4222 break;
4223
4224 tr_client->waiting_for_client = wait_for->client_name;
4226
4227 if (tr_client->debug_flag == 1)
4228 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4229
4230 i = 0;
4231 size = sizeof(i);
4232 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4233
4234 if (status == DB_SUCCESS && i == 0) {
4235 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());
4236 tr_client->status = -1;
4237 tr_client->errorstr = "Canceled";
4238 tr_client->end_time = ss_millitime();
4240 return CM_SUCCESS;
4241 }
4242
4243 ss_sleep(100);
4244 };
4245 }
4246
4247 tr_client->waiting_for_client[0] = 0;
4248
4249 /* contact client if transition mask set */
4250 if (tr_client->debug_flag == 1)
4251 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4252 if (tr_client->debug_flag == 2)
4253 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());
4254
4255 /* get transition timeout for rpc connect */
4256 size = sizeof(timeout);
4257 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4258
4259 if (connect_timeout < 1000)
4260 connect_timeout = 1000;
4261
4262 /* get transition timeout */
4263 size = sizeof(timeout);
4264 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4265
4266 if (timeout < 1000)
4267 timeout = 1000;
4268
4269 /* set our timeout for rpc_client_connect() */
4270 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4271 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4272
4273 tr_client->connect_timeout = connect_timeout;
4274 tr_client->connect_start_time = ss_millitime();
4275
4277
4278 /* client found -> connect to its server port */
4279 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4280
4282
4283 tr_client->connect_end_time = ss_millitime();
4285
4286 if (status != RPC_SUCCESS) {
4287 cm_msg(MERROR, "cm_transition_call",
4288 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4289 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4290 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4291
4292 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4293 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4294 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4295
4296 if (tr_client->transition != TR_STOP) {
4297 /* indicate abort */
4298 i = 1;
4299 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4300 i = 0;
4301 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4302 }
4303
4304 tr_client->status = status;
4305 tr_client->end_time = ss_millitime();
4306
4308 return status;
4309 }
4310
4311 if (tr_client->debug_flag == 1)
4312 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4313 if (tr_client->debug_flag == 2)
4314 cm_msg(MINFO, "cm_transition_call",
4315 "cm_transition: Connection established to client \"%s\" on host %s",
4316 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4317
4318 /* call RC_TRANSITION on remote client with increased timeout */
4319 //old_timeout = rpc_get_timeout(hConn);
4320 rpc_set_timeout(hConn, timeout, &old_timeout);
4321
4322 tr_client->rpc_timeout = timeout;
4323 tr_client->rpc_start_time = ss_millitime();
4325
4326 if (tr_client->debug_flag == 1)
4327 printf("Executing RPC transition client \"%s\" on host %s...\n",
4328 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4329 if (tr_client->debug_flag == 2)
4330 cm_msg(MINFO, "cm_transition_call",
4331 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4332 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4333
4334 t0 = ss_millitime();
4335
4336 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4337 errorstr[0] = 0;
4338
4339 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4340
4341 tr_client->errorstr = errorstr;
4342
4343 t1 = ss_millitime();
4344
4345 tr_client->rpc_end_time = ss_millitime();
4346
4348
4349 /* fix for clients returning 0 as error code */
4350 if (status == 0)
4351 status = FE_ERR_HW;
4352
4353 /* reset timeout */
4355
4356 //DWORD t2 = ss_millitime();
4357
4358 if (tr_client->debug_flag == 1)
4359 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4360 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4361 if (tr_client->debug_flag == 2)
4362 cm_msg(MINFO, "cm_transition_call",
4363 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4364 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4365
4366 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4367 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());
4368 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4369 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4370 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4371 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4372 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());
4373 }
4374
4375 tr_client->status = status;
4376 tr_client->end_time = ss_millitime();
4377
4378 // write updated status and end_time to ODB
4379
4381
4382#if 0
4383 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4384 hConn,
4385 tr_client->client_name.c_str(),
4386 tr_client->init_time - tr_client->init_time,
4387 tr_client->connect_start_time - tr_client->init_time,
4388 tr_client->connect_end_time - tr_client->init_time,
4389 tr_client->rpc_start_time - tr_client->init_time,
4390 tr_client->rpc_end_time - tr_client->init_time,
4391 t2 - tr_client->init_time,
4392 tr_client->end_time - tr_client->init_time);
4393#endif
4394
4395 return CM_SUCCESS;
4396}
4397
4398/*------------------------------------------------------------------*/
4399
4401{
4402 HNDLE hDB;
4403
4405
4407
4408 tr_client->errorstr = "";
4409 //tr_client->init_time = now;
4410 tr_client->waiting_for_client = "";
4411 tr_client->connect_timeout = 0;
4412 tr_client->connect_start_time = now;
4413 tr_client->connect_end_time = now;
4414 tr_client->rpc_timeout = 0;
4415 tr_client->rpc_start_time = 0;
4416 tr_client->rpc_end_time = 0;
4417 tr_client->end_time = 0;
4418
4420
4421 // find registered handler
4422 // NB: this code should match same code in rpc_transition_dispatch()
4423 // NB: only use the first handler, this is how MIDAS always worked
4424 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4425
4426 _trans_table_mutex.lock();
4427 size_t n = _trans_table.size();
4428 _trans_table_mutex.unlock();
4429
4430 for (size_t i = 0; i < n; i++) {
4431 _trans_table_mutex.lock();
4433 _trans_table_mutex.unlock();
4434 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4435 /* call registered function */
4436 if (tt.func) {
4437 if (tr_client->debug_flag == 1)
4438 printf("Calling local transition callback\n");
4439 if (tr_client->debug_flag == 2)
4440 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4441
4442 tr_client->rpc_start_time = ss_millitime();
4443
4445
4446 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4447 errorstr[0] = 0;
4448
4449 tr_client->status = tt.func(tr_client->run_number, errorstr);
4450
4451 tr_client->errorstr = errorstr;
4452
4453 tr_client->rpc_end_time = ss_millitime();
4454
4455 if (tr_client->debug_flag == 1)
4456 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4457 if (tr_client->debug_flag == 2)
4458 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4459
4460 tr_client->end_time = ss_millitime();
4461
4462 // write status and end_time to ODB
4463
4465
4466 return tr_client->status;
4467 }
4468 }
4469 }
4470
4471 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4472
4473 tr_client->status = CM_SUCCESS;
4474 tr_client->end_time = ss_millitime();
4475
4476 // write status and end_time to ODB
4477
4479
4480 return CM_SUCCESS;
4481}
4482
4483/********************************************************************/
4523static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4524{
4525 INT i, status, size, sequence_number, port, state;
4527 DWORD seconds;
4528 char tr_key_name[256];
4529 KEY key;
4530 BOOL deferred;
4532
4533 //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);
4534
4535 /* if needed, use internal error string */
4536 if (!errstr) {
4537 errstr = xerrstr;
4538 errstr_size = sizeof(xerrstr);
4539 }
4540
4541 /* erase error string */
4542 errstr[0] = 0;
4543
4544 /* get key of local client */
4546
4549
4550 /* check for valid transition */
4552 && transition != TR_STARTABORT) {
4553 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4554 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4555 return CM_INVALID_TRANSITION;
4556 }
4557
4558 /* check if transition in progress */
4559 if (!deferred) {
4560 i = 0;
4561 size = sizeof(i);
4562 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4563 if (i == 1) {
4564 if (errstr) {
4565 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4566 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4567 }
4568 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4570 }
4571 }
4572
4573 /* indicate transition in progress */
4574 i = transition;
4575 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4576
4577 /* clear run abort flag */
4578 i = 0;
4579 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4580
4581 /* construct new transition state */
4582
4583 TrState s;
4584
4587 s.async_flag = async_flag;
4588 s.debug_flag = debug_flag;
4589 s.status = 0;
4590 s.errorstr[0] = 0;
4592 s.end_time = 0;
4593
4594 /* construct the ODB tree /System/Transition */
4595
4596 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4597 if (status == DB_SUCCESS) {
4599 }
4600
4601 if (transition != TR_STARTABORT) {
4602 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4603 if (status == DB_SUCCESS) {
4605 }
4606 }
4607
4608 if (transition != TR_STARTABORT) {
4609 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4610 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4611 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4612 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4613 status = 0;
4614 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4615 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4616 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4617 }
4618
4619 /* check for alarms */
4620 i = 0;
4621 size = sizeof(i);
4622 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4623 if (i == TRUE && transition == TR_START) {
4624 al_check();
4625 std::string alarms;
4626 if (al_get_alarms(&alarms) > 0) {
4627 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4628 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4629 mstrlcat(errstr, alarms.c_str(), errstr_size);
4630 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4631 }
4632 }
4633
4634 /* check for required programs */
4635 i = 0;
4636 size = sizeof(i);
4637 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4638 if (i == TRUE && transition == TR_START) {
4639
4641
4642 /* check /programs alarms */
4643 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4644 if (hkeyroot) {
4645 for (i = 0;; i++) {
4649 break;
4650
4651 db_get_key(hDB, hkey, &key);
4652
4653 /* don't check "execute on xxx" */
4654 if (key.type != TID_KEY)
4655 continue;
4656
4657 size = sizeof(program_info_required);
4658 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4659 if (status != DB_SUCCESS) {
4660 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4661 continue;
4662 }
4663
4665 std::string name = rpc_get_name();
4666 std::string str = name;
4667 str.resize(strlen(key.name));
4668 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4669 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4670 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4671 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4672 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4673 }
4674 }
4675 }
4676 }
4677 }
4678
4679 /* do detached transition via mtransition tool */
4680 if (async_flag & TR_DETACH) {
4681 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4682 return tr_finish(hDB, &s, transition, status, errstr);
4683 }
4684
4685 mstrlcpy(errstr, "Unknown error", errstr_size);
4686
4687 if (debug_flag == 0) {
4688 size = sizeof(i);
4689 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4690 }
4691
4692 /* if no run number is given, get it from ODB and increment it */
4693 if (run_number == 0) {
4694 size = sizeof(run_number);
4695 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4696 assert(status == SUCCESS);
4697 if (transition == TR_START) {
4698 run_number++;
4699 }
4701
4702 if (transition != TR_STARTABORT) {
4703 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4704 }
4705 }
4706
4707 if (run_number <= 0) {
4708 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4709 abort();
4710 }
4711
4712 /* Set new run number in ODB */
4713 if (transition == TR_START) {
4714 if (debug_flag == 1)
4715 printf("Setting run number %d in ODB\n", run_number);
4716 if (debug_flag == 2)
4717 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4718
4719 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4720 if (status != DB_SUCCESS) {
4721 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4722 abort();
4723 }
4724 }
4725
4726 if (deferred) {
4727 if (debug_flag == 1)
4728 printf("Clearing /Runinfo/Requested transition\n");
4729 if (debug_flag == 2)
4730 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4731
4732 /* remove transition request */
4733 i = 0;
4734 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4735 } else {
4736 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4737 if (status != DB_SUCCESS) {
4738 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4739 if (errstr)
4740 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4741 return tr_finish(hDB, &s, transition, status, errstr);
4742 }
4743
4744 /* check if deferred transition already in progress */
4745 size = sizeof(i);
4746 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4747 if (i) {
4748 if (errstr) {
4749 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4750 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4751 }
4752 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4753 }
4754
4755 std::string trname = cm_transition_name(transition);
4756
4757 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4758
4759 /* search database for clients with deferred transition request */
4760 for (i = 0, status = 0;; i++) {
4763 break;
4764
4765 if (status == DB_SUCCESS) {
4766 size = sizeof(sequence_number);
4767 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4768
4769 /* if registered for deferred transition, set flag in ODB and return */
4770 if (status == DB_SUCCESS) {
4771 char str[256];
4772 size = NAME_LENGTH;
4773 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4774
4775 if (debug_flag == 1)
4776 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4777 if (debug_flag == 2)
4778 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4779
4780 if (debug_flag == 1)
4781 printf("Setting /Runinfo/Requested transition\n");
4782 if (debug_flag == 2)
4783 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4784
4785 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4786 * will activate the deferred transition code in the frontend.
4787 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4788
4789 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4790
4791 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4792
4793 if (errstr)
4794 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4795
4796 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4797 }
4798 }
4799 }
4800 }
4801
4802 /* execute programs on start */
4803 if (transition == TR_START) {
4804 char str[256];
4805 str[0] = 0;
4806 size = sizeof(str);
4807 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4808 if (str[0])
4809 ss_system(str);
4810
4811 db_find_key(hDB, 0, "/Programs", &hRootKey);
4812 if (hRootKey) {
4813 for (i = 0;; i++) {
4817 break;
4818
4819 db_get_key(hDB, hKey, &key);
4820
4821 /* don't check "execute on xxx" */
4822 if (key.type != TID_KEY)
4823 continue;
4824
4825 size = sizeof(program_info_auto_start);
4826 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4827 if (status != DB_SUCCESS) {
4828 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4829 continue;
4830 }
4831
4833 char start_command[MAX_STRING_LENGTH];
4834 start_command[0] = 0;
4835
4836 size = sizeof(start_command);
4837 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4838 if (status != DB_SUCCESS) {
4839 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4840 continue;
4841 }
4842
4843 if (start_command[0]) {
4844 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4845 start_command);
4846 ss_system(start_command);
4847 }
4848 }
4849 }
4850 }
4851 }
4852
4853 /* execute programs on startabort */
4854 if (transition == TR_STARTABORT) {
4855 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4856 std::string cmd;
4857 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4858
4859 if (!cmd.empty())
4860 ss_system(cmd.c_str());
4861 }
4862
4863 /* set new start time in database */
4864 if (transition == TR_START) {
4865 /* ASCII format */
4866 std::string now = cm_asctime();
4867 now.reserve(32);
4868 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4869
4870 /* reset stop time */
4871 seconds = 0;
4872 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4873
4874 /* Seconds since 1.1.1970 */
4875 cm_time(&seconds);
4876 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4877 }
4878
4879 size = sizeof(state);
4880 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4881
4882 /* set stop time in database */
4883 if (transition == TR_STOP) {
4884 if (status != DB_SUCCESS)
4885 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4886
4887 if (state != STATE_STOPPED) {
4888 /* stop time binary */
4889 cm_time(&seconds);
4890 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4891 if (status != DB_SUCCESS)
4892 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4893
4894 /* stop time ascii */
4895 std::string now = cm_asctime();
4896 now.reserve(32);
4897 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4898 if (status != DB_SUCCESS)
4899 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4900 }
4901 }
4902
4903 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4904 if (status != DB_SUCCESS) {
4905 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4906 if (errstr)
4907 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4908 return tr_finish(hDB, &s, transition, status, errstr);
4909 }
4910
4911 std::string trname = cm_transition_name(transition);
4912
4913 /* check that all transition clients are alive */
4914 for (int i = 0;;) {
4916 if (status != DB_SUCCESS)
4917 break;
4918
4920
4921 if (status == DB_SUCCESS) {
4922 /* this client is alive. Check next one! */
4923 i++;
4924 continue;
4925 }
4926
4927 assert(status == CM_NO_CLIENT);
4928
4929 /* start from scratch: removing odb entries as we iterate over them
4930 * does strange things to db_enum_key() */
4931 i = 0;
4932 }
4933
4934 /* check for broken RPC connections */
4936
4937 if (debug_flag == 1)
4938 printf("---- Transition %s started ----\n", trname.c_str());
4939 if (debug_flag == 2)
4940 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4941
4942 sprintf(tr_key_name, "Transition %s", trname.c_str());
4943
4944 /* search database for clients which registered for transition */
4945
4946 for (int i = 0, status = 0;; i++) {
4947 KEY subkey;
4950 break;
4951
4953 assert(status == DB_SUCCESS);
4954
4955 if (status == DB_SUCCESS) {
4957
4958 if (status == DB_SUCCESS) {
4959
4961
4962 for (int j = 0; j < key.num_values; j++) {
4963 size = sizeof(sequence_number);
4964 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4965 assert(status == DB_SUCCESS);
4966
4967 TrClient *c = new TrClient;
4968
4970 c->transition = transition;
4971 c->run_number = run_number;
4972 c->async_flag = async_flag;
4973 c->debug_flag = debug_flag;
4974 c->sequence_number = sequence_number;
4975 c->status = 0;
4976 c->key_name = subkey.name;
4977
4978 /* get client info */
4979 char client_name[NAME_LENGTH];
4980 size = sizeof(client_name);
4981 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
4982 c->client_name = client_name;
4983
4985 size = sizeof(host_name);
4986 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
4987 c->host_name = host_name;
4988
4989 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
4990
4991 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
4992 /* remember own client */
4993 c->port = 0;
4994 } else {
4995 size = sizeof(port);
4996 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
4997 c->port = port;
4998 }
4999
5000 /* check for duplicates */
5001
5002 bool found = false;
5003 for (size_t k=0; k<s.clients.size(); k++) {
5004 TrClient* cc = s.clients[k].get();
5005 if (cc->client_name == c->client_name)
5006 if (cc->host_name == c->host_name)
5007 if (cc->port == c->port)
5008 if (cc->sequence_number == c->sequence_number)
5009 found = true;
5010 }
5011
5012 if (!found) {
5013 s.clients.push_back(std::unique_ptr<TrClient>(c));
5014 c = NULL;
5015 } else {
5016 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);
5017 delete c;
5018 c = NULL;
5019 }
5020 }
5021 }
5022 }
5023 }
5024
5025 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5026
5027 /* set predecessor for multi-threaded transitions */
5028 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5029 if (s.clients[idx]->sequence_number == 0) {
5030 // sequence number 0 means "don't care"
5031 } else {
5032 /* find clients with smaller sequence number */
5033 if (idx > 0) {
5034 for (size_t i = idx - 1; ; i--) {
5035 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5036 if (s.clients[i]->sequence_number > 0) {
5037 s.clients[idx]->wait_for_index.push_back(i);
5038 }
5039 }
5040 if (i==0)
5041 break;
5042 }
5043 }
5044 }
5045 }
5046
5047 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5049 }
5050
5051#if 0
5052 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5053 printf("TrClient[%d]: ", int(idx));
5054 s.clients[idx]->Print();
5055 printf("\n");
5056 }
5057#endif
5058
5059 /* contact ordered clients for transition -----------------------*/
5061 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5062 if (debug_flag == 1)
5063 printf("\n==== Found client \"%s\" with sequence number %d\n",
5064 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5065 if (debug_flag == 2)
5066 cm_msg(MINFO, "cm_transition",
5067 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5068 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5069
5070 if (async_flag & TR_MTHREAD) {
5072 assert(s.clients[idx]->thread == NULL);
5073 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5074 } else {
5075 if (s.clients[idx]->port == 0) {
5076 /* if own client call transition callback directly */
5078 } else {
5079 /* if other client call transition via RPC layer */
5081 }
5082
5083 if (status == CM_SUCCESS && transition != TR_STOP)
5084 if (s.clients[idx]->status != SUCCESS) {
5085 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5086 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5087 break;
5088 }
5089 }
5090
5091 if (status != CM_SUCCESS)
5092 break;
5093 }
5094
5095 /* wait until all threads have finished */
5096 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5097 if (s.clients[idx]->thread) {
5098 // join() will wait forever until thread finishes
5099 s.clients[idx]->thread->join();
5100 delete s.clients[idx]->thread;
5101 s.clients[idx]->thread = NULL;
5102 }
5103 }
5104
5105 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5106
5107 i = 0;
5108 size = sizeof(i);
5109 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5110
5111 if (status == DB_SUCCESS && i == 0) {
5112 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5113
5114 if (errstr != NULL)
5115 mstrlcpy(errstr, "Canceled", errstr_size);
5116
5117 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5118 }
5119
5120 /* search for any error */
5121 for (size_t idx = 0; idx < s.clients.size(); idx++)
5122 if (s.clients[idx]->status != CM_SUCCESS) {
5123 status = s.clients[idx]->status;
5124 if (errstr)
5125 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5126 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5127 break;
5128 }
5129
5130 if (transition != TR_STOP && status != CM_SUCCESS) {
5131 /* indicate abort */
5132 i = 1;
5133 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5134 i = 0;
5135 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5136
5137 return tr_finish(hDB, &s, transition, status, errstr);
5138 }
5139
5140 if (debug_flag == 1)
5141 printf("\n---- Transition %s finished ----\n", trname.c_str());
5142 if (debug_flag == 2)
5143 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5144
5145 /* set new run state in database */
5148
5149 if (transition == TR_PAUSE)
5151
5152 if (transition == TR_STOP)
5154
5157
5158 size = sizeof(state);
5159 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5160 if (status != DB_SUCCESS)
5161 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5162
5163 /* send notification message */
5164 if (transition == TR_START)
5165 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5166 if (transition == TR_STOP)
5167 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5168 if (transition == TR_PAUSE)
5169 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5170 if (transition == TR_RESUME)
5171 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5173 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5174
5175 /* lock/unlock ODB values if present */
5176 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5177 if (hKey) {
5178 if (state == STATE_STOPPED)
5180 else
5182 }
5183
5184 /* flush online database */
5185 if (transition == TR_STOP)
5187
5188 /* execute/stop programs on stop */
5189 if (transition == TR_STOP) {
5190 std::string cmd;
5191 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5192 if (!cmd.empty())
5193 ss_system(cmd.c_str());
5194
5195 db_find_key(hDB, 0, "/Programs", &hRootKey);
5196 if (hRootKey) {
5197 for (i = 0;; i++) {
5201 break;
5202
5203 db_get_key(hDB, hKey, &key);
5204
5205 /* don't check "execute on xxx" */
5206 if (key.type != TID_KEY)
5207 continue;
5208
5209 size = sizeof(program_info_auto_stop);
5210 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5211 if (status != DB_SUCCESS) {
5212 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5213 continue;
5214 }
5215
5217 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5219 }
5220 }
5221 }
5222 }
5223
5224
5225 /* indicate success */
5226 i = 0;
5227 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5228
5229 if (errstr != NULL)
5230 mstrlcpy(errstr, "Success", errstr_size);
5231
5232 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5233}
5234
5235/*------------------------------------------------------------------*/
5236
5237/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5238static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5239 int status;
5240
5241 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5242
5243 if (transition == TR_START && status != CM_SUCCESS) {
5244 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5245 errstr);
5246 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5247 }
5248
5249 return status;
5250}
5251
5252/*------------------------------------------------------------------*/
5253
5254static INT tr_main_thread(void *param) {
5255 INT status;
5256 TR_PARAM *trp;
5257
5258 trp = (TR_PARAM *) param;
5259 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5260
5261 trp->status = status;
5262 trp->finished = TRUE;
5263
5264 return 0;
5265}
5266
5268{
5269 if (_trp.thread && !_trp.finished) {
5270 //printf("main transition thread did not finish yet!\n");
5272 }
5273
5274 std::thread* t = _trp.thread.exchange(NULL);
5275
5276 if (t) {
5277 t->join();
5278 delete t;
5279 t = NULL;
5280 }
5281
5282 return CM_SUCCESS;
5283}
5284
5285/* wrapper around cm_transition1() for detached multi-threaded transitions */
5286INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5287 int mflag = async_flag & TR_MTHREAD;
5288 int sflag = async_flag & TR_SYNC;
5289
5291
5292 if (status != CM_SUCCESS) {
5293 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5295 }
5296
5297 /* get key of local client */
5298 HNDLE hDB;
5300
5301 bool deferred = (transition & TR_DEFERRED) > 0;
5303
5304 /* check for valid transition */
5306 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5307 if (errstr) {
5308 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5309 }
5310 return CM_INVALID_TRANSITION;
5311 }
5312
5313 /* check if transition in progress */
5314 if (!deferred) {
5315 int i = 0;
5316 int size = sizeof(i);
5317 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5318 if (i == 1) {
5319 if (errstr) {
5320 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5321 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5322 }
5323 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5325 }
5326 }
5327
5328 if (mflag) {
5331 if (sflag) {
5332 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5333 _trp.errstr = errstr;
5334 _trp.errstr_size = errstr_size;
5335 } else {
5336 /* in normal MTHREAD mode, we return right away and
5337 * if errstr is a local variable in the caller and they return too,
5338 * errstr becomes a stale reference and writing into it will corrupt the stack
5339 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5340 * and we definitely corrupt mlogger memory with out this: */
5341 _trp.errstr = NULL;
5342 _trp.errstr_size = 0;
5343 }
5344 _trp.async_flag = async_flag;
5345 _trp.debug_flag = debug_flag;
5346 _trp.status = 0;
5348
5349 if (errstr)
5350 *errstr = 0; // null error string
5351
5352 //ss_thread_create(tr_main_thread, &_trp);
5353
5354 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5355
5356 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5357
5358 if (sflag) {
5359
5360 /* wait until main thread has finished */
5361 do {
5362 ss_sleep(10);
5363 } while (!_trp.finished);
5364
5365 std::thread* t = _trp.thread.exchange(NULL);
5366
5367 if (t) {
5368 t->join();
5369 delete t;
5370 t = NULL;
5371 }
5372
5373 return _trp.status;
5374 }
5375 } else
5376 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5377
5378 return CM_SUCCESS;
5379}
5380
5382#ifndef DOXYGEN_SHOULD_SKIP_THIS
5383
5384/********************************************************************/
5386/********************************************************************\
5387
5388 Routine: cm_dispatch_ipc
5389
5390 Purpose: Called from ss_suspend if an IPC message arrives
5391
5392 Input:
5393 INT msg IPC message we got, MSG_ODB/MSG_BM
5394 INT p1, p2 Optional parameters
5395 int s Optional server socket
5396
5397 Output:
5398 none
5399
5400 Function value:
5401 CM_SUCCESS Successful completion
5402
5403\********************************************************************/
5404{
5405 if (message[0] == 'O') {
5407 INT index;
5408 index = 0;
5409 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5410 if (client_socket) {
5412 } else {
5414 }
5415 }
5416
5417 /* message == "B" means "resume event sender" */
5418 if (message[0] == 'B' && message[2] != ' ') {
5419 char str[NAME_LENGTH];
5420
5421 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5422
5423 mstrlcpy(str, message + 2, sizeof(str));
5424 if (strchr(str, ' '))
5425 *strchr(str, ' ') = 0;
5426
5427 if (client_socket)
5429 else
5430 return bm_push_event(str);
5431 }
5432
5433 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5434
5435 return CM_SUCCESS;
5436}
5437
5438/********************************************************************/
5440
5441void cm_ctrlc_handler(int sig) {
5442 if (_ctrlc_pressed) {
5443 printf("Received 2nd Ctrl-C, hard abort\n");
5444 exit(0);
5445 }
5446 printf("Received Ctrl-C, aborting...\n");
5448
5450}
5451
5455
5459
5460/********************************************************************/
5462/********************************************************************\
5463
5464 Routine: cm_exec_script
5465
5466 Purpose: Execute script from /Script tree
5467
5468 exec_script is enabled by the tree /Script
5469 The /Script struct is composed of list of keys
5470 from which the name of the key is the button name
5471 and the sub-structure is a record as follow:
5472
5473 /Script/<button_name> = <script command> (TID_STRING)
5474
5475 The "Script command", containing possible arguements,
5476 is directly executed.
5477
5478 /Script/<button_name>/<script command>
5479 <soft link1>|<arg1>
5480 <soft link2>|<arg2>
5481 ...
5482
5483 The arguments for the script are derived from the
5484 subtree below <button_name>, where <button_name> must be
5485 TID_KEY. The subtree may then contain arguments or links
5486 to other values in the ODB, like run number etc.
5487
5488\********************************************************************/
5489{
5490 HNDLE hDB, hkey;
5491 KEY key;
5492 int status;
5493
5495 if (status != DB_SUCCESS)
5496 return status;
5497
5499 if (status != DB_SUCCESS)
5500 return status;
5501
5503 if (status != DB_SUCCESS)
5504 return status;
5505
5506 std::string command;
5507
5508 if (key.type == TID_STRING) {
5509 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5510 if (status != DB_SUCCESS) {
5511 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5513 return status;
5514 }
5515 } else if (key.type == TID_KEY) {
5516 for (int i = 0;; i++) {
5517 HNDLE hsubkey;
5518 KEY subkey;
5520 if (!hsubkey)
5521 break;
5523
5524 if (i > 0)
5525 command += " ";
5526
5527 if (subkey.type == TID_KEY) {
5528 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5529 subkey.name);
5530 return DB_TYPE_MISMATCH;
5531 } else {
5532 int size = subkey.item_size;
5533 char *buf = (char *) malloc(size);
5534 assert(buf != NULL);
5535 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5536 if (status != DB_SUCCESS) {
5537 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5538 odb_path_to_script, subkey.name, subkey.type, status);
5539 free(buf);
5540 return status;
5541 }
5542 if (subkey.type == TID_STRING) {
5543 command += buf;
5544 } else {
5545 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5546 }
5547 free(buf);
5548 }
5549 }
5550 } else {
5551 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5553 return DB_TYPE_MISMATCH;
5554 }
5555
5556 // printf("exec_script: %s\n", command.c_str());
5557
5558 if (command.length() > 0) {
5559 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5560 ss_system(command.c_str());
5561 }
5562
5563 return SUCCESS;
5564}
5565
5567#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5568
5569static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5570
5571/********************************************************************/
5580 static DWORD alarm_last_checked_sec = 0;
5581 DWORD now_sec = ss_time();
5582
5584 static DWORD last_millitime = 0;
5586 const DWORD kPeriod = 1000;
5587 if (last_millitime == 0) {
5589 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5590 }
5591
5592 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5593
5594 //if (now_millitime < last_millitime) {
5595 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5596 //}
5597
5598 /* check alarms once every 10 seconds */
5599 if (now_sec - alarm_last_checked_sec > 10) {
5600 al_check();
5602 }
5603
5604 /* run periodic checks previously done by cm_watchdog */
5605
5606 if (tdiff_millitime >= kPeriod) {
5608 if (tdiff_millitime > 60000)
5610
5611 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5612
5613 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5614 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5615
5617
5619 }
5620
5621 /* reap transition thread */
5622
5624
5625 return CM_SUCCESS;
5626}
5627
5628/********************************************************************/
5643 INT status;
5644 INT bMore;
5645 //static DWORD last_yield = 0;
5646 //static DWORD last_yield_time = 0;
5647 //DWORD start_yield = ss_millitime();
5648
5649 /* check for ctrl-c */
5650 if (_ctrlc_pressed)
5651 return RPC_SHUTDOWN;
5652
5653 /* flush the cm_msg buffer */
5655
5656 if (!rpc_is_remote()) {
5657 /* flush the ODB to its binary file */
5658 /* for remote clients, ODB is flushed by the mserver */
5659 HNDLE hDB;
5662 }
5663
5664 /* check for available events */
5665 if (rpc_is_remote()) {
5666 //printf("cm_yield() calling bm_poll_event()\n");
5668
5669 if (status == SS_ABORT) {
5670 return status;
5671 }
5672
5673 if (status == BM_SUCCESS) {
5674 /* one or more events received by bm_poll_event() */
5675 status = ss_suspend(0, 0);
5676 } else {
5678 }
5679
5680 return status;
5681 }
5682
5684
5685 if (status != CM_SUCCESS)
5686 return status;
5687
5688 //DWORD start_check = ss_millitime();
5689
5691
5692 //DWORD end_check = ss_millitime();
5693 //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);
5694 //fflush(stdout);
5695
5696 if (bMore == BM_CORRUPTED) {
5697 status = SS_ABORT;
5698 } else if (bMore) {
5699 /* if events available, quickly check other IPC channels */
5700 status = ss_suspend(0, 0);
5701 } else {
5703 }
5704
5705 /* flush the cm_msg buffer */
5707
5708 //DWORD end_yield = ss_millitime();
5709 //last_yield_time = end_yield - start_yield;
5710 //last_yield = start_yield;
5711
5712 return status;
5713}
5714
5715/********************************************************************/
5723INT cm_execute(const char *command, char *result, INT bufsize) {
5724 char str[256];
5725 INT n;
5726 int fh;
5727 int status = 0;
5728 static int check_cm_execute = 1;
5729 static int enable_cm_execute = 0;
5730
5731 if (rpc_is_remote())
5732 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5733
5734 if (check_cm_execute) {
5735 int status;
5736 int size;
5737 HNDLE hDB;
5738 check_cm_execute = 0;
5739
5741 assert(status == DB_SUCCESS);
5742
5743 size = sizeof(enable_cm_execute);
5744 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5745 assert(status == DB_SUCCESS);
5746
5747 //printf("enable_cm_execute %d\n", enable_cm_execute);
5748 }
5749
5750 if (!enable_cm_execute) {
5751 char buf[32];
5752 mstrlcpy(buf, command, sizeof(buf));
5753 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5754 return CM_WRONG_PASSWORD;
5755 }
5756
5757 if (bufsize > 0) {
5758 strcpy(str, command);
5759 sprintf(str, "%s > %d.tmp", command, ss_getpid());
5760
5761 status = system(str);
5762
5763 sprintf(str, "%d.tmp", ss_getpid());
5764 fh = open(str, O_RDONLY, 0644);
5765 result[0] = 0;
5766 if (fh) {
5767 n = read(fh, result, bufsize - 1);
5768 result[MAX(0, n)] = 0;
5769 close(fh);
5770 }
5771 remove(str);
5772 } else {
5773 status = system(command);
5774 }
5775
5776 if (status < 0) {
5777 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5778 return CM_SET_ERROR;
5779 }
5780
5781 return CM_SUCCESS;
5782}
5783
5784
5785
5787#ifndef DOXYGEN_SHOULD_SKIP_THIS
5788
5789/********************************************************************/
5790INT cm_register_function(INT id, INT(*func)(INT, void **))
5791/********************************************************************\
5792
5793 Routine: cm_register_function
5794
5795 Purpose: Call rpc_register_function and publish the registered
5796 function under system/clients/<pid>/RPC
5797
5798 Input:
5799 INT id RPC ID
5800 INT *func New dispatch function
5801
5802 Output:
5803 <implicit: func gets copied to rpc_list>
5804
5805 Function value:
5806 CM_SUCCESS Successful completion
5807 RPC_INVALID_ID RPC ID not found
5808
5809\********************************************************************/
5810{
5811 HNDLE hDB, hKey;
5812 INT status;
5813 char str[80];
5814
5815 status = rpc_register_function(id, func);
5816 if (status != RPC_SUCCESS)
5817 return status;
5818
5820
5821 /* create new key for this id */
5822 status = 1;
5823 sprintf(str, "RPC/%d", id);
5824
5826 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5828
5829 if (status != DB_SUCCESS)
5830 return status;
5831
5832 return CM_SUCCESS;
5833}
5834
5835
5837#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5838
5839//
5840// Return "/"-terminated file path for given history channel
5841//
5842
5843std::string cm_get_history_path(const char* history_channel)
5844{
5845 int status;
5846 HNDLE hDB;
5847 std::string path;
5848
5850
5851 if (history_channel && (strlen(history_channel) > 0)) {
5852 std::string p;
5853 p += "/Logger/History/";
5854 p += history_channel;
5855 p += "/History dir";
5856
5857 // NB: be careful to avoid creating odb entries under /logger
5858 // for whatever values of "history_channel" we get called with!
5859 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5860 if (status == DB_SUCCESS && path.length() > 0) {
5861 // if not absolute path, prepend with experiment directory
5862 if (path[0] != DIR_SEPARATOR)
5863 path = cm_get_path() + path;
5864 // append directory separator
5865 if (path.back() != DIR_SEPARATOR)
5866 path += DIR_SEPARATOR_STR;
5867 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5868 return path;
5869 }
5870 }
5871
5872 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5873 if (status == DB_SUCCESS && path.length() > 0) {
5874 // if not absolute path, prepend with experiment directory
5875 if (path[0] != DIR_SEPARATOR)
5876 path = cm_get_path() + path;
5877 // append directory separator
5878 if (path.back() != DIR_SEPARATOR)
5879 path += DIR_SEPARATOR_STR;
5880 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5881 return path;
5882 }
5883
5884 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5885 if (status == DB_SUCCESS && path.length() > 0) {
5886 // if not absolute path, prepend with experiment directory
5887 if (path[0] != DIR_SEPARATOR)
5888 path = cm_get_path() + path;
5889 // append directory separator
5890 if (path.back() != DIR_SEPARATOR)
5891 path += DIR_SEPARATOR_STR;
5892 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5893 return path;
5894 }
5895
5896 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5897 return cm_get_path();
5898}
5899
/* end of cmfunctionc */
5902
5908/********************************************************************\
5909* *
5910* bm_xxx - Buffer Manager Functions *
5911* *
5912\********************************************************************/
5913
5915
5916#ifdef LOCAL_ROUTINES
5917
5918// see locking code in xbm_lock_buffer()
5919static int _bm_lock_timeout = 5 * 60 * 1000;
5920static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5921
5923{
5924 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5925
5926 bool badindex = false;
5927 bool badclient = false;
5928
5929 int idx = pbuf->client_index;
5930
5931 if (idx < 0) {
5932 badindex = true;
5933 } else if (idx > pbuf->buffer_header->max_client_index) {
5934 badindex = true;
5935 } else {
5936 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5937 if (pclient->name[0] == 0)
5938 badclient = true;
5939 else if (pclient->pid != ss_getpid())
5940 badclient = true;
5941
5942 //if (strcmp(pclient->name,"mdump")==0) {
5943 // for (int i=0; i<15; i++) {
5944 // printf("sleep %d\n", i);
5945 // ::sleep(1);
5946 // }
5947 //}
5948 }
5949
5950#if 0
5951 if (badindex) {
5952 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5953 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5954 badindex, ss_getpid());
5955 } else if (badclient) {
5956 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",
5957 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5958 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5959 ss_getpid(), badclient);
5960 } else {
5961 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",
5962 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5963 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5964 ss_getpid());
5965 }
5966#endif
5967
5968 if (badindex || badclient) {
5969 static int prevent_recursion = 1;
5970
5971 if (prevent_recursion) {
5973
5974 if (badindex) {
5975 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());
5976 } else {
5977 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());
5978 }
5979
5980 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
5981 }
5982
5983 if (badindex) {
5984 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());
5985 } else {
5986 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());
5987 }
5988
5989 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
5990
5991 pbuf_guard.unlock();
5992
5993 abort();
5994 }
5995
5996 return idx;
5997}
5998
6003
6004#endif // LOCAL_ROUTINES
6005
6006/********************************************************************/
6015INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6016 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6017 // because of mismatch in sign-extension between signed 16-bit event_id and
6018 // unsigned 16-bit constants. K.O.
6019
6020 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)))
6021 /* fragmented event */
6022 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6024
6025 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6027}
6028
6029#ifdef LOCAL_ROUTINES
6030
6031/********************************************************************/
6036 int k, nc;
6038
6039 /* clear entry from client structure in buffer header */
6040 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6041
6042 /* calculate new max_client_index entry */
6043 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6044 if (pheader->client[k].pid != 0)
6045 break;
6046 pheader->max_client_index = k + 1;
6047
6048 /* count new number of clients */
6049 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6050 if (pheader->client[k].pid != 0)
6051 nc++;
6052 pheader->num_clients = nc;
6053
6054 /* check if anyone is waiting and wake him up */
6055 pbctmp = pheader->client;
6056
6057 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6058 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6059 ss_resume(pbctmp->port, "B ");
6060}
6061
6062/********************************************************************/
6067 BUFFER_HEADER *pheader;
6069 int j;
6070
6071 pheader = pbuf->buffer_header;
6072 pbclient = pheader->client;
6073
6074 /* now check other clients */
6075 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6076 if (pbclient->pid) {
6077 if (!ss_pid_exists(pbclient->pid)) {
6078 cm_msg(MINFO, "bm_cleanup",
6079 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6080 pheader->name, who, pbclient->pid);
6081
6082 bm_remove_client_locked(pheader, j);
6083 continue;
6084 }
6085 }
6086
6087 /* If client process has no activity, clear its buffer entry. */
6088 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6089 DWORD tdiff = actual_time - pbclient->last_activity;
6090#if 0
6091 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6092 pheader->name,
6093 pbclient->name,
6094 pbclient->last_activity,
6096 tdiff,
6097 tdiff,
6098 pbclient->watchdog_timeout);
6099#endif
6100 if (actual_time > pbclient->last_activity &&
6101 tdiff > pbclient->watchdog_timeout) {
6102
6103 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6104 pbclient->name, pheader->name, who,
6105 tdiff / 1000.0,
6106 pbclient->watchdog_timeout / 1000.0);
6107
6108 bm_remove_client_locked(pheader, j);
6109 }
6110 }
6111 }
6112}
6113
6118 int pid = ss_getpid();
6119
6120 std::vector<BUFFER*> mybuffers;
6121
6122 gBuffersMutex.lock();
6124 gBuffersMutex.unlock();
6125
6126 for (BUFFER* pbuf : mybuffers) {
6127 if (!pbuf)
6128 continue;
6129 if (pbuf->attached) {
6130
6132
6133 if (!pbuf_guard.is_locked())
6134 continue;
6135
6136 BUFFER_HEADER *pheader = pbuf->buffer_header;
6137 for (int j = 0; j < pheader->max_client_index; j++) {
6138 BUFFER_CLIENT *pclient = pheader->client + j;
6139 if (pclient->pid == pid) {
6141 }
6142 }
6143 }
6144 }
6145}
6146
6147#endif // LOCAL_ROUTINES
6148
6153{
6154#ifdef LOCAL_ROUTINES
6155
6156 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6157
6158 std::vector<BUFFER*> mybuffers;
6159
6160 gBuffersMutex.lock();
6162 gBuffersMutex.unlock();
6163
6164 /* check buffers */
6165 for (BUFFER* pbuf : mybuffers) {
6166 if (!pbuf)
6167 continue;
6168 if (pbuf->attached) {
6169 /* update the last_activity entry to show that we are alive */
6170
6172
6173 if (!pbuf_guard.is_locked())
6174 continue;
6175
6177 pclient->last_activity = actual_time;
6178
6179 /* don't check other clients if interval is strange */
6180 if (!wrong_interval)
6182 }
6183 }
6184#endif // LOCAL_ROUTINES
6185}
6186
6187#ifdef LOCAL_ROUTINES
6188
6189static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6190 if (rp < 0 || rp > pheader->size) {
6191 cm_msg(MERROR, "bm_validate_rp",
6192 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6193 pheader->name,
6194 rp,
6195 pheader->read_pointer,
6196 pheader->write_pointer,
6197 pheader->size,
6198 who);
6199 return FALSE;
6200 }
6201
6202 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6203 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6204 cm_msg(MERROR, "bm_validate_rp",
6205 "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",
6206 pheader->name,
6207 rp,
6208 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6209 pheader->read_pointer,
6210 pheader->write_pointer,
6211 pheader->size,
6212 who);
6213 return FALSE;
6214 }
6215
6216 return TRUE;
6217}
6218
6219#if 0
6220static FILE* gRpLog = NULL;
6221#endif
6222
6223static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6224{
6225#if 0
6226 if (gRpLog == NULL) {
6227 gRpLog = fopen("rp.log", "a");
6228 }
6229 if (gRpLog && (total_size < 16)) {
6230 const char *pdata = (const char *) (pheader + 1);
6231 const DWORD *pevent = (const DWORD*) (pdata + rp);
6232 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,
6233 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6234 }
6235#endif
6236
6237 // these checks are already done before we come here.
6238 // but we check again as last-ressort protection. K.O.
6239 assert(total_size > 0);
6240 assert(total_size >= (int)sizeof(EVENT_HEADER));
6241
6242 rp += total_size;
6243 if (rp >= pheader->size) {
6244 rp -= pheader->size;
6245 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6246 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6247 // if at the end of the buffer, the remaining free space is exactly
6248 // equal to the size of an event header, the event header
6249 // is written there, the pointer is wrapped and the event data
6250 // is written to the beginning of the buffer.
6251 rp = 0;
6252 }
6253 return rp;
6254}
6255
6256static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6257 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6258 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6259 int total_size = ALIGN8(event_size);
6260
6261 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6262 cm_msg(MERROR, "bm_next_rp",
6263 "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",
6264 pheader->name,
6265 rp,
6266 pevent->data_size,
6267 event_size,
6268 total_size,
6269 pheader->read_pointer,
6270 pheader->write_pointer,
6271 pheader->size,
6272 who);
6273 return -1;
6274 }
6275
6276 int remaining = 0;
6277 if (rp < pheader->write_pointer) {
6278 remaining = pheader->write_pointer - rp;
6279 } else {
6280 remaining = pheader->size - rp;
6281 remaining += pheader->write_pointer;
6282 }
6283
6284 //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);
6285
6286 if (total_size > remaining) {
6287 cm_msg(MERROR, "bm_next_rp",
6288 "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",
6289 pheader->name,
6290 rp,
6291 pevent->data_size,
6292 event_size,
6293 total_size,
6294 pheader->read_pointer,
6295 pheader->write_pointer,
6296 pheader->size,
6297 remaining,
6298 who);
6299 return -1;
6300 }
6301
6302 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6303
6304 return rp;
6305}
6306
6308 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6309 const char *pdata = (const char *) (pheader + 1);
6310
6311 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6312
6313 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6314
6315 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6316
6317 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6318 cm_msg(MERROR, "bm_validate_buffer",
6319 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6320 pheader->read_pointer, pheader->size, pheader->write_pointer);
6321 return BM_CORRUPTED;
6322 }
6323
6324 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6325 cm_msg(MERROR, "bm_validate_buffer",
6326 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6327 pheader->write_pointer, pheader->size, pheader->read_pointer);
6328 return BM_CORRUPTED;
6329 }
6330
6331 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6332 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6333 pheader->read_pointer);
6334 return BM_CORRUPTED;
6335 }
6336
6337 int rp = pheader->read_pointer;
6338 int rp0 = -1;
6339 while (rp != pheader->write_pointer) {
6340 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6341 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6342 pheader->name, rp, rp0);
6343 return BM_CORRUPTED;
6344 }
6345 //bm_print_event(pdata, rp);
6346 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6347 if (rp1 < 0) {
6348 cm_msg(MERROR, "bm_validate_buffer",
6349 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6350 return BM_CORRUPTED;
6351 }
6352 rp0 = rp;
6353 rp = rp1;
6354 }
6355
6356 int i;
6357 for (i = 0; i < MAX_CLIENTS; i++) {
6358 const BUFFER_CLIENT *c = &pheader->client[i];
6359 if (c->pid == 0)
6360 continue;
6361 BOOL get_all = FALSE;
6362 int j;
6363 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6364 const EVENT_REQUEST *r = &c->event_request[j];
6365 if (!r->valid)
6366 continue;
6368 get_all = (get_all || xget_all);
6369 //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);
6370 }
6371
6372 int rp = c->read_pointer;
6373 int rp0 = -1;
6374 while (rp != pheader->write_pointer) {
6375 //bm_print_event(pdata, rp);
6376 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6377 if (rp1 < 0) {
6378 cm_msg(MERROR, "bm_validate_buffer",
6379 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6380 pheader->name, c->name, c->read_pointer, rp, rp0);
6381 return BM_CORRUPTED;
6382 }
6383 rp0 = rp;
6384 rp = rp1;
6385 }
6386 }
6387
6388 return BM_SUCCESS;
6389}
6390
6392 BUFFER_HEADER *pheader = pbuf->buffer_header;
6393
6394 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6395
6396 pheader->read_pointer = 0;
6397 pheader->write_pointer = 0;
6398
6399 int i;
6400 for (i = 0; i < pheader->max_client_index; i++) {
6401 BUFFER_CLIENT *pc = pheader->client + i;
6402 if (pc->pid) {
6403 pc->read_pointer = 0;
6404 }
6405 }
6406}
6407
6409 HNDLE hKey;
6410 int status;
6411
6412 char str[256 + 2 * NAME_LENGTH];
6413 sprintf(str, "/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6414 //printf("delete [%s]\n", str);
6415 status = db_find_key(hDB, 0, str, &hKey);
6416 if (status == DB_SUCCESS) {
6418 }
6419}
6420
6422{
6425 /* buffer statistics */
6426 int count_lock = 0;
6427 int count_sent = 0;
6428 double bytes_sent = 0;
6435 int count_read = 0;
6436 double bytes_read = 0;
6441 {
6442 get_all_flag = pbuf->get_all_flag;
6443
6444 /* buffer statistics */
6445 count_lock = pbuf->count_lock;
6446 count_sent = pbuf->count_sent;
6447 bytes_sent = pbuf->bytes_sent;
6448 count_write_wait = pbuf->count_write_wait;
6449 time_write_wait = pbuf->time_write_wait;
6450 last_count_lock = pbuf->last_count_lock;
6451 wait_start_time = pbuf->wait_start_time;
6452 wait_client_index = pbuf->wait_client_index;
6453 max_requested_space = pbuf->max_requested_space;
6454 count_read = pbuf->count_read;
6455 bytes_read = pbuf->bytes_read;
6456
6457 for (int i=0; i<MAX_CLIENTS; i++) {
6458 client_count_write_wait[i] = pbuf->client_count_write_wait[i];
6459 client_time_write_wait[i] = pbuf->client_time_write_wait[i];
6460 }
6461 };
6462};
6463
6464static 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)
6465{
6466 int status;
6467
6469
6470 HNDLE hKey;
6471 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6472 if (status != DB_SUCCESS) {
6473 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6474 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6475 if (status != DB_SUCCESS)
6476 return;
6477 }
6478
6481 if (status != DB_SUCCESS) {
6484 if (status != DB_SUCCESS)
6485 return;
6486 }
6487
6488 double buf_size = pheader->size;
6489 double buf_rptr = pheader->read_pointer;
6490 double buf_wptr = pheader->write_pointer;
6491
6492 double buf_fill = 0;
6493 double buf_cptr = 0;
6494 double buf_cused = 0;
6495 double buf_cused_pct = 0;
6496
6497 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6498 buf_cptr = pheader->client[client_index].read_pointer;
6499
6500 if (buf_wptr == buf_cptr) {
6501 buf_cused = 0;
6502 } else if (buf_wptr > buf_cptr) {
6504 } else {
6505 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6506 }
6507
6508 buf_cused_pct = buf_cused / buf_size * 100.0;
6509
6510 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6511 // because some other GET_ALL client may have different buf_cused & etc,
6512 // so they must be written into the per-client statistics
6513 // and the web page should look at all the GET_ALL clients and used
6514 // the biggest buf_cused as the whole-buffer "bytes used" value.
6515 }
6516
6517 if (buf_wptr == buf_rptr) {
6518 buf_fill = 0;
6519 } else if (buf_wptr > buf_rptr) {
6521 } else {
6522 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6523 }
6524
6525 double buf_fill_pct = buf_fill / buf_size * 100.0;
6526
6527 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6528 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6529 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6530 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6531 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6532
6533 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6534 if (status != DB_SUCCESS) {
6535 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6536 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6537 if (status != DB_SUCCESS)
6538 return;
6539 }
6540
6542 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6543 if (status != DB_SUCCESS) {
6544 db_create_key(hDB, hKey, client_name, TID_KEY);
6545 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6546 if (status != DB_SUCCESS)
6547 return;
6548 }
6549
6550 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6551 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6552 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6553 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6554 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6555 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6556 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6557 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6558 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6559 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6560 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6561 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6562
6563 for (int i = 0; i < MAX_CLIENTS; i++) {
6564 if (!pbuf->client_count_write_wait[i])
6565 continue;
6566
6567 if (pheader->client[i].pid == 0)
6568 continue;
6569
6570 if (pheader->client[i].name[0] == 0)
6571 continue;
6572
6573 char str[100 + NAME_LENGTH];
6574
6575 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6576 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6577
6578 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6579 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6580 }
6581
6582 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6583 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6584}
6585
6587{
6588 //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);
6589
6591
6592 if (!pbuf_guard.is_locked())
6593 return;
6594
6595 if (!force) {
6596 if (pbuf->count_lock == pbuf->last_count_lock) {
6597 return;
6598 }
6599 }
6600
6601 std::string buffer_name = pbuf->buffer_name;
6602 std::string client_name = pbuf->client_name;
6603
6604 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6605 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6606 pbuf_guard.unlock(); // unlock before cm_msg()
6607 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());
6608 return;
6609 }
6610
6611 pbuf->last_count_lock = pbuf->count_lock;
6612
6614 BUFFER_HEADER xheader = *pbuf->buffer_header;
6615 int client_index = pbuf->client_index;
6616
6617 pbuf_guard.unlock();
6618
6619 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6620}
6621
6622static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6623{
6624 size_t sbuffer_handle = buffer_handle;
6625
6626 size_t nbuf = 0;
6627 BUFFER* pbuf = NULL;
6628
6629 gBuffersMutex.lock();
6630
6631 nbuf = gBuffers.size();
6632 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6633 pbuf = gBuffers[buffer_handle-1];
6634 }
6635
6636 gBuffersMutex.unlock();
6637
6638 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6639 if (who)
6640 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6641 if (pstatus)
6643 return NULL;
6644 }
6645
6646 if (!pbuf) {
6647 if (who)
6648 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6649 if (pstatus)
6651 return NULL;
6652 }
6653
6654 if (!pbuf->attached) {
6655 if (who)
6656 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6657 if (pstatus)
6659 return NULL;
6660 }
6661
6662 if (pstatus)
6664
6665 return pbuf;
6666}
6667
6668#endif // LOCAL_ROUTINES
6669
6670/********************************************************************/
6717INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6718 INT status;
6719
6720 if (rpc_is_remote()) {
6721 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6722
6723 HNDLE hDB;
6725 if (status != SUCCESS || hDB == 0) {
6726 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6727 return BM_NO_SHM;
6728 }
6729
6731
6732 int size = sizeof(INT);
6733 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6734
6735 if (status != DB_SUCCESS) {
6736 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6737 status);
6738 return status;
6739 }
6740
6741 return status;
6742 }
6743#ifdef LOCAL_ROUTINES
6744 {
6745 HNDLE shm_handle;
6746 size_t shm_size;
6747 HNDLE hDB;
6748 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6749
6750 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6751
6752 if (!buffer_name || !buffer_name[0]) {
6753 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6754 return BM_INVALID_PARAM;
6755 }
6756
6757 if (strlen(buffer_name) >= NAME_LENGTH) {
6758 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6759 return BM_INVALID_PARAM;
6760 }
6761
6763
6764 if (status != SUCCESS || hDB == 0) {
6765 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6766 return BM_NO_SHM;
6767 }
6768
6769 /* get buffer size from ODB, user parameter as default if not present in ODB */
6770 std::string odb_path;
6771 odb_path += "/Experiment/Buffer sizes/";
6772 odb_path += buffer_name;
6773
6774 int size = sizeof(INT);
6775 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6776
6778 cm_msg(MERROR, "bm_open_buffer",
6779 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6780 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6781 return BM_INVALID_PARAM;
6782 }
6783
6785
6786 size = sizeof(INT);
6787 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6788
6789 if (status != DB_SUCCESS) {
6790 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6791 status);
6792 return status;
6793 }
6794
6795 /* check if buffer already is open */
6796 gBuffersMutex.lock();
6797 for (size_t i = 0; i < gBuffers.size(); i++) {
6798 BUFFER* pbuf = gBuffers[i];
6799 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6800 *buffer_handle = i + 1;
6801 gBuffersMutex.unlock();
6802 return BM_SUCCESS;
6803 }
6804 }
6805 gBuffersMutex.unlock();
6806
6807 // only one thread at a time should create new buffers
6808
6809 static std::mutex gNewBufferMutex;
6810 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6811
6812 // if we had a race against another thread
6813 // and while we were waiting for gNewBufferMutex
6814 // the other thread created this buffer, we return it.
6815
6816 gBuffersMutex.lock();
6817 for (size_t i = 0; i < gBuffers.size(); i++) {
6818 BUFFER* pbuf = gBuffers[i];
6819 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6820 *buffer_handle = i + 1;
6821 gBuffersMutex.unlock();
6822 return BM_SUCCESS;
6823 }
6824 }
6825 gBuffersMutex.unlock();
6826
6827 /* allocate new BUFFER object */
6828
6829 BUFFER* pbuf = new BUFFER;
6830
6831 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6832
6833 for (int i=0; i<MAX_CLIENTS; i++) {
6835 pbuf->client_time_write_wait[i] = 0;
6836 }
6837
6838 /* create buffer semaphore */
6839
6840 status = ss_semaphore_create(buffer_name, &(pbuf->semaphore));
6841
6842 if (status != SS_CREATED && status != SS_SUCCESS) {
6843 *buffer_handle = 0;
6844 delete pbuf;
6845 return BM_NO_SEMAPHORE;
6846 }
6847
6848 std::string client_name = cm_get_client_name();
6849
6850 /* store client name */
6851 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6852
6853 /* store buffer name */
6854 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6855
6856 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6857
6858 pbuf->attached = true; // required by bm_lock_buffer()
6859
6861
6862 if (!pbuf_guard.is_locked()) {
6863 // cannot happen, no other thread can see this pbuf
6864 abort();
6865 return BM_NO_SEMAPHORE;
6866 }
6867
6868 /* open shared memory */
6869
6870 void *p = NULL;
6871 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6872
6873 if (status != SS_SUCCESS && status != SS_CREATED) {
6874 *buffer_handle = 0;
6875 pbuf_guard.unlock();
6876 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6877 delete pbuf;
6878 return BM_NO_SHM;
6879 }
6880
6881 pbuf->buffer_header = (BUFFER_HEADER *) p;
6882
6883 BUFFER_HEADER *pheader = pbuf->buffer_header;
6884
6885 bool shm_created = (status == SS_CREATED);
6886
6887 if (shm_created) {
6888 /* initialize newly created shared memory */
6889
6890 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6891
6892 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6893 pheader->size = buffer_size;
6894
6895 } else {
6896 /* validate existing shared memory */
6897
6898 if (!equal_ustring(pheader->name, buffer_name)) {
6899 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6900 pbuf_guard.unlock();
6901 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6902 cm_msg(MERROR, "bm_open_buffer",
6903 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6904 pheader->name);
6905 *buffer_handle = 0;
6906 delete pbuf;
6907 return BM_CORRUPTED;
6908 }
6909
6910 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6911 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6912 pbuf_guard.unlock();
6913 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6914 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6916 *buffer_handle = 0;
6917 delete pbuf;
6918 return BM_CORRUPTED;
6919 }
6920
6921 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6922 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6923 pbuf_guard.unlock();
6924 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6925 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6927 *buffer_handle = 0;
6928 delete pbuf;
6929 return BM_CORRUPTED;
6930 }
6931
6932 /* check if buffer size is identical */
6933 if (pheader->size != buffer_size) {
6934 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6935 buffer_name, buffer_size, pheader->size);
6936
6937 buffer_size = pheader->size;
6938
6939 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6940
6941 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6942
6943 if (status != SS_SUCCESS) {
6944 *buffer_handle = 0;
6945 pbuf_guard.unlock();
6946 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6947 delete pbuf;
6948 return BM_NO_SHM;
6949 }
6950
6951 pbuf->buffer_header = (BUFFER_HEADER *) p;
6952 pheader = pbuf->buffer_header;
6953 }
6954 }
6955
6956 /* shared memory is good from here down */
6957
6958 pbuf->attached = true;
6959
6960 pbuf->shm_handle = shm_handle;
6961 pbuf->shm_size = shm_size;
6962 pbuf->callback = FALSE;
6963
6964 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6965
6967 if (status != BM_SUCCESS) {
6968 cm_msg(MERROR, "bm_open_buffer",
6969 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6970 status);
6972 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6973 }
6974
6975 /* add our client BUFFER_HEADER */
6976
6977 int iclient = 0;
6978 for (; iclient < MAX_CLIENTS; iclient++)
6979 if (pheader->client[iclient].pid == 0)
6980 break;
6981
6982 if (iclient == MAX_CLIENTS) {
6983 *buffer_handle = 0;
6984 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6985 pbuf_guard.unlock();
6986 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6987 delete pbuf;
6988 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
6989 return BM_NO_SLOT;
6990 }
6991
6992 /* store slot index in _buffer structure */
6993 pbuf->client_index = iclient;
6994
6995 /*
6996 Save the index of the last client of that buffer so that later only
6997 the clients 0..max_client_index-1 have to be searched through.
6998 */
6999 pheader->num_clients++;
7000 if (iclient + 1 > pheader->max_client_index)
7001 pheader->max_client_index = iclient + 1;
7002
7003 /* setup buffer header and client structure */
7004 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7005
7006 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7007
7008 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7009
7010 pclient->pid = ss_getpid();
7011
7013
7014 pclient->read_pointer = pheader->write_pointer;
7015 pclient->last_activity = ss_millitime();
7016
7017 cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
7018
7019 pbuf_guard.unlock();
7020
7021 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7022
7023 pheader = NULL;
7024
7025 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7026
7029
7030 /* add pbuf to buffer list */
7031
7032 gBuffersMutex.lock();
7033
7034 bool added = false;
7035 for (size_t i=0; i<gBuffers.size(); i++) {
7036 if (gBuffers[i] == NULL) {
7037 gBuffers[i] = pbuf;
7038 added = true;
7039 *buffer_handle = i+1;
7040 break;
7041 }
7042 }
7043 if (!added) {
7044 *buffer_handle = gBuffers.size() + 1;
7045 gBuffers.push_back(pbuf);
7046 }
7047
7048 /* from here down we should not touch pbuf without locking it */
7049
7050 pbuf = NULL;
7051
7052 gBuffersMutex.unlock();
7053
7054 /* new buffer is now ready for use */
7055
7056 /* initialize buffer counters */
7057 bm_init_buffer_counters(*buffer_handle);
7058
7059 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7060
7061 if (shm_created)
7062 return BM_CREATED;
7063 }
7064#endif /* LOCAL_ROUTINES */
7065
7066 return BM_SUCCESS;
7067}
7068
7069/********************************************************************/
7075INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7076{
7077 gBuffersMutex.lock();
7078 for (size_t i = 0; i < gBuffers.size(); i++) {
7079 BUFFER* pbuf = gBuffers[i];
7080 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7081 *buffer_handle = i + 1;
7082 gBuffersMutex.unlock();
7083 return BM_SUCCESS;
7084 }
7085 }
7086 gBuffersMutex.unlock();
7087 return BM_NOT_FOUND;
7088}
7089
7090/********************************************************************/
7096INT bm_close_buffer(INT buffer_handle) {
7097 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7098
7099 if (rpc_is_remote())
7100 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7101
7102#ifdef LOCAL_ROUTINES
7103 {
7104 int status = 0;
7105
7106 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7107
7108 if (!pbuf)
7109 return status;
7110
7111 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7112
7113 int i;
7114
7115 { /* delete all requests for this buffer */
7116 _request_list_mutex.lock();
7117 std::vector<EventRequest> request_list_copy = _request_list;
7118 _request_list_mutex.unlock();
7119 for (size_t i = 0; i < request_list_copy.size(); i++) {
7120 if (request_list_copy[i].buffer_handle == buffer_handle) {
7122 }
7123 }
7124 }
7125
7126 HNDLE hDB;
7128
7129 if (hDB) {
7130 /* write statistics to odb */
7132 }
7133
7134 /* lock buffer in correct order */
7135
7137
7138 if (status != BM_SUCCESS) {
7139 return status;
7140 }
7141
7143
7144 if (status != BM_SUCCESS) {
7145 pbuf->read_cache_mutex.unlock();
7146 return status;
7147 }
7148
7150
7151 if (!pbuf_guard.is_locked()) {
7152 pbuf->write_cache_mutex.unlock();
7153 pbuf->read_cache_mutex.unlock();
7154 return pbuf_guard.get_status();
7155 }
7156
7157 BUFFER_HEADER *pheader = pbuf->buffer_header;
7158
7159 /* mark entry in _buffer as empty */
7160 pbuf->attached = false;
7161
7163
7164 if (pclient) {
7165 /* clear entry from client structure in buffer header */
7166 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7167 }
7168
7169 /* calculate new max_client_index entry */
7170 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7171 if (pheader->client[i].pid != 0)
7172 break;
7173 pheader->max_client_index = i + 1;
7174
7175 /* count new number of clients */
7176 int j = 0;
7177 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7178 if (pheader->client[i].pid != 0)
7179 j++;
7180 pheader->num_clients = j;
7181
7182 int destroy_flag = (pheader->num_clients == 0);
7183
7184 // we hold the locks on the read cache and the write cache.
7185
7186 /* free cache */
7187 if (pbuf->read_cache_size > 0) {
7188 free(pbuf->read_cache);
7189 pbuf->read_cache = NULL;
7190 pbuf->read_cache_size = 0;
7191 pbuf->read_cache_rp = 0;
7192 pbuf->read_cache_wp = 0;
7193 }
7194
7195 if (pbuf->write_cache_size > 0) {
7196 free(pbuf->write_cache);
7197 pbuf->write_cache = NULL;
7198 pbuf->write_cache_size = 0;
7199 pbuf->write_cache_rp = 0;
7200 pbuf->write_cache_wp = 0;
7201 }
7202
7203 /* check if anyone is waiting and wake him up */
7204
7205 for (int i = 0; i < pheader->max_client_index; i++) {
7206 BUFFER_CLIENT *pclient = pheader->client + i;
7207 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7208 ss_resume(pclient->port, "B ");
7209 }
7210
7211 /* unmap shared memory, delete it if we are the last */
7212
7213 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7214
7215 /* after ss_shm_close() these are invalid: */
7216
7217 pheader = NULL;
7218 pbuf->buffer_header = NULL;
7219 pbuf->shm_size = 0;
7220 pbuf->shm_handle = 0;
7221
7222 /* unlock buffer in correct order */
7223
7224 pbuf_guard.unlock();
7225
7226 pbuf->write_cache_mutex.unlock();
7227 pbuf->read_cache_mutex.unlock();
7228
7229 /* delete semaphore */
7230
7232 }
7233#endif /* LOCAL_ROUTINES */
7234
7235 return BM_SUCCESS;
7236}
7237
7238/********************************************************************/
7244 if (rpc_is_remote())
7246
7247#ifdef LOCAL_ROUTINES
7248 {
7250
7251 gBuffersMutex.lock();
7252 size_t nbuf = gBuffers.size();
7253 gBuffersMutex.unlock();
7254
7255 for (size_t i = nbuf; i > 0; i--) {
7257 }
7258
7259 gBuffersMutex.lock();
7260 for (size_t i=0; i< gBuffers.size(); i++) {
7261 BUFFER* pbuf = gBuffers[i];
7262 if (!pbuf)
7263 continue;
7264 delete pbuf;
7265 pbuf = NULL;
7266 gBuffers[i] = NULL;
7267 }
7268 gBuffersMutex.unlock();
7269 }
7270#endif /* LOCAL_ROUTINES */
7271
7272 return BM_SUCCESS;
7273}
7274
7275/********************************************************************/
7281#ifdef LOCAL_ROUTINES
7282 {
7283 int status;
7284 HNDLE hDB;
7285
7287
7288 if (status != CM_SUCCESS) {
7289 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7290 return BM_SUCCESS;
7291 }
7292
7293 std::vector<BUFFER*> mybuffers;
7294
7295 gBuffersMutex.lock();
7297 gBuffersMutex.unlock();
7298
7299 for (BUFFER* pbuf : mybuffers) {
7300 if (!pbuf || !pbuf->attached)
7301 continue;
7303 }
7304 }
7305#endif /* LOCAL_ROUTINES */
7306
7307 return BM_SUCCESS;
7308}
7309
/* end of bmfunctionc */
7312
7318/*-- Watchdog routines ---------------------------------------------*/
7319#ifdef LOCAL_ROUTINES
7320
7321static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7322static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7323static std::atomic<std::thread*> _watchdog_thread{NULL};
7324
7325/********************************************************************/
7331 //printf("cm_watchdog_thread started!\n");
7332 while (_watchdog_thread_run) {
7333 //printf("cm_watchdog_thread runs!\n");
7337 int i;
7338 for (i = 0; i < 20; i++) {
7339 ss_sleep(100);
7341 break;
7342 }
7343 }
7344 //printf("cm_watchdog_thread stopped!\n");
7346 return 0;
7347}
7348
7349static void xcm_watchdog_thread() {
7351}
7352
7353#endif
7354
7356 /* watchdog does not run inside remote clients.
7357 * watchdog timeout timers are maintained by the mserver */
7358 if (rpc_is_remote())
7359 return CM_SUCCESS;
7360#ifdef LOCAL_ROUTINES
7361 /* only start once */
7362 if (_watchdog_thread)
7363 return CM_SUCCESS;
7364 _watchdog_thread_run = true;
7365 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7366#endif
7367 return CM_SUCCESS;
7368}
7369
7371 /* watchdog does not run inside remote clients.
7372 * watchdog timeout timers are maintained by the mserver */
7373 if (rpc_is_remote())
7374 return CM_SUCCESS;
7375#ifdef LOCAL_ROUTINES
7376 _watchdog_thread_run = false;
7378 //printf("waiting for watchdog thread to shut down\n");
7379 ss_sleep(10);
7380 }
7381 if (_watchdog_thread != NULL) {
7382 _watchdog_thread.load()->join();
7383 delete static_cast<std::thread *>(_watchdog_thread);
7385 }
7386#endif
7387 return CM_SUCCESS;
7388}
7389
7390/********************************************************************/
7401 INT status, return_status, i, size;
7403 KEY key;
7404 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7405 INT port;
7406 DWORD start_time;
7407 DWORD timeout;
7408 DWORD last;
7409
7411
7412 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7413 if (status != DB_SUCCESS)
7414 return DB_NO_KEY;
7415
7417
7418 /* loop over all clients */
7419 for (i = 0;; i++) {
7422 break;
7423
7424 /* don't shutdown ourselves */
7425 if (hSubkey == hKeyClient)
7426 continue;
7427
7428 if (status == DB_SUCCESS) {
7430
7431 /* contact client */
7432 size = sizeof(client_name);
7433 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7434 if (status != DB_SUCCESS)
7435 continue;
7436
7437 if (!bUnique)
7438 client_name[strlen(name)] = 0; /* strip number */
7439
7440 /* check if individual client */
7441 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7442 continue;
7443
7444 size = sizeof(port);
7445 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7446
7447 size = sizeof(remote_host);
7448 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7449
7450 cm_get_watchdog_info(hDB, name, &timeout, &last);
7451 if (timeout == 0)
7452 timeout = 5000;
7453
7454 /* client found -> connect to its server port */
7455 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7456 if (status != RPC_SUCCESS) {
7457 int client_pid = atoi(key.name);
7459 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7460 client_name, remote_host, port);
7461#ifdef SIGKILL
7462 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7463 client_pid);
7467 if (status != CM_SUCCESS)
7468 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7470#endif
7471 } else {
7472 /* call disconnect with shutdown=TRUE */
7474
7475 /* wait until client has shut down */
7476 start_time = ss_millitime();
7477 do {
7478 ss_sleep(100);
7480 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7481
7482 if (status == DB_SUCCESS) {
7483 int client_pid = atoi(key.name);
7485 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7486#ifdef SIGKILL
7487 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7488 client_pid);
7491 if (status != CM_SUCCESS)
7492 cm_msg(MERROR, "cm_shutdown",
7493 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7494 status);
7495#endif
7497 } else {
7499 i--;
7500 }
7501 }
7502 }
7503
7504 /* display any message created during each shutdown */
7506 }
7507
7508 return return_status;
7509}
7510
7511/********************************************************************/
7521 INT status, i, size;
7523 char client_name[NAME_LENGTH];
7524
7525 if (rpc_is_remote())
7527
7529
7530 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7531 if (status != DB_SUCCESS)
7532 return DB_NO_KEY;
7533
7535
7536 /* loop over all clients */
7537 for (i = 0;; i++) {
7540 break;
7541
7542 if (hSubkey == hKeyClient)
7543 continue;
7544
7545 if (status == DB_SUCCESS) {
7546 /* get client name */
7547 size = sizeof(client_name);
7548 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7549
7550 if (status != DB_SUCCESS) {
7551 //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);
7552 continue;
7553 }
7554
7555 if (equal_ustring(client_name, name)) {
7557 return CM_SUCCESS;
7558 }
7559
7560 if (!bUnique) {
7561 client_name[strlen(name)] = 0; /* strip number */
7562 if (equal_ustring(client_name, name)) {
7564 return CM_SUCCESS;
7565 }
7566 }
7567 }
7568 }
7569
7571
7572 return CM_NO_CLIENT;
7573}
7574
7575/********************************************************************/
7610INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7611 if (rpc_is_remote())
7612 return rpc_call(RPC_CM_CLEANUP, client_name);
7613
7614#ifdef LOCAL_ROUTINES
7615 {
7618
7619 std::vector<BUFFER*> mybuffers;
7620
7621 gBuffersMutex.lock();
7623 gBuffersMutex.unlock();
7624
7625 /* check buffers */
7626 for (BUFFER* pbuf : mybuffers) {
7627 if (!pbuf)
7628 continue;
7629 if (pbuf->attached) {
7630 std::string msg;
7631
7633
7634 if (!pbuf_guard.is_locked())
7635 continue;
7636
7637 /* update the last_activity entry to show that we are alive */
7638 BUFFER_HEADER *pheader = pbuf->buffer_header;
7640 pclient->last_activity = ss_millitime();
7641
7642 /* now check other clients */
7643 for (int j = 0; j < pheader->max_client_index; j++) {
7644 BUFFER_CLIENT *pbclient = &pheader->client[j];
7645 if (j != pbuf->client_index && pbclient->pid &&
7646 (client_name == NULL || client_name[0] == 0
7647 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7648 if (ignore_timeout)
7650 else
7652
7653 /* If client process has no activity, clear its buffer entry. */
7654 if (interval > 0
7655 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7656
7657 /* now make again the check with the buffer locked */
7658 if (interval > 0
7659 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7660 msg = msprintf(
7661 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7662 pbclient->name, pheader->name,
7663 (ss_millitime() - pbclient->last_activity) / 1000.0,
7664 interval / 1000.0);
7665
7666 bm_remove_client_locked(pheader, j);
7667 }
7668
7669 /* go again through whole list */
7670 j = 0;
7671 }
7672 }
7673 }
7674
7675 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7676 pbuf_guard.unlock();
7677
7678 /* display info message after unlocking buffer */
7679 if (!msg.empty())
7680 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7681 }
7682 }
7683
7684 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7685 }
7686#endif /* LOCAL_ROUTINES */
7687
7688 return CM_SUCCESS;
7689}
7690
7691/********************************************************************/
7710std::string cm_expand_env(const char *str) {
7711 const char *s = str;
7712 std::string r;
7713 for (; *s;) {
7714 if (*s == '$') {
7715 s++;
7716 std::string envname;
7717 for (; *s;) {
7718 if (*s == DIR_SEPARATOR)
7719 break;
7720 envname += *s;
7721 s++;
7722 }
7723 const char *e = getenv(envname.c_str());
7724 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7725 if (!e) {
7726 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7727 r += '$';
7728 r += envname;
7729 } else {
7730 r += e;
7731 //if (r[r.length()-1] != DIR_SEPARATOR)
7732 //r += DIR_SEPARATOR_STR;
7733 }
7734 } else {
7735 r += *s;
7736 s++;
7737 }
7738 }
7739 return r;
7740}
7741
7742static bool test_cm_expand_env1(const char *str, const char *expected) {
7743 std::string s = cm_expand_env(str);
7744 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7745 str,
7746 s.c_str(),
7747 expected);
7748 if (s != expected) {
7749 printf(", MISMATCH!\n");
7750 return false;
7751 }
7752
7753 printf("\n");
7754 return true;
7755}
7756
7758 printf("Test expand_end()\n");
7759 setenv("FOO", "foo", 1);
7760 setenv("BAR", "bar", 1);
7761 setenv("EMPTY", "", 1);
7762 unsetenv("UNDEF");
7763
7764 bool ok = true;
7765
7766 ok &= test_cm_expand_env1("aaa", "aaa");
7767 ok &= test_cm_expand_env1("$FOO", "foo");
7768 ok &= test_cm_expand_env1("/$FOO", "/foo");
7769 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7770 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7771 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7772 ok &= test_cm_expand_env1("1$FOO", "1foo");
7773 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7774 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7775
7776 if (ok) {
7777 printf("test_expand_env: all tests passed!\n");
7778 } else {
7779 printf("test_expand_env: test FAILED!\n");
7780 }
7781}
7782
/* end of cmfunctionc */
7786
7788#ifndef DOXYGEN_SHOULD_SKIP_THIS
7789
7790/********************************************************************/
7791INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7792/********************************************************************\
7793
7794 Routine: bm_buffer_info
7795
7796 Purpose: Copies the current buffer header referenced by buffer_handle
7797 into the *buffer_header structure which must be supplied
7798 by the calling routine.
7799
7800 Input:
7801 INT buffer_handle Handle of the buffer to get the header from
7802
7803 Output:
7804 BUFFER_HEADER *buffer_header Destination address which gets a copy
7805 of the buffer header structure.
7806
7807 Function value:
7808 BM_SUCCESS Successful completion
7809 BM_INVALID_HANDLE Buffer handle is invalid
7810 RPC_NET_ERROR Network error
7811
7812\********************************************************************/
7813{
7814 if (rpc_is_remote())
7815 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7816
7817#ifdef LOCAL_ROUTINES
7818
7819 int status = 0;
7820 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7821
7822 if (!pbuf)
7823 return status;
7824
7826
7827 if (!pbuf_guard.is_locked())
7828 return pbuf_guard.get_status();
7829
7830 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7831
7832#endif /* LOCAL_ROUTINES */
7833
7834 return BM_SUCCESS;
7835}
7836
7837/********************************************************************/
7838INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7839/********************************************************************\
7840
7841 Routine: bm_get_buffer_level
7842
7843 Purpose: Return number of bytes in buffer or in cache
7844
7845 Input:
7846 INT buffer_handle Handle of the buffer to get the info
7847
7848 Output:
7849 INT *n_bytes Number of bytes in buffer
7850
7851 Function value:
7852 BM_SUCCESS Successful completion
7853 BM_INVALID_HANDLE Buffer handle is invalid
7854 RPC_NET_ERROR Network error
7855
7856\********************************************************************/
7857{
7858 if (rpc_is_remote())
7859 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7860
7861#ifdef LOCAL_ROUTINES
7862 {
7863 int status = 0;
7864
7865 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7866
7867 if (!pbuf)
7868 return status;
7869
7871
7872 if (!pbuf_guard.is_locked())
7873 return pbuf_guard.get_status();
7874
7875 BUFFER_HEADER *pheader = pbuf->buffer_header;
7876
7878
7879 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7880 if (*n_bytes < 0)
7881 *n_bytes += pheader->size;
7882
7883 pbuf_guard.unlock();
7884
7885 if (pbuf->read_cache_size) {
7887 if (status == BM_SUCCESS) {
7888 /* add bytes in cache */
7889 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7890 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7891 pbuf->read_cache_mutex.unlock();
7892 }
7893 }
7894 }
7895#endif /* LOCAL_ROUTINES */
7896
7897 return BM_SUCCESS;
7898}
7899
7900
7901#ifdef LOCAL_ROUTINES
7902
7903/********************************************************************/
7905{
7906 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7907
7908 if (!locked) {
7909 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);
7910 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);
7911 abort();
7912 /* DOES NOT RETURN */
7913 }
7914
7915 if (!pbuf->attached) {
7916 pbuf->read_cache_mutex.unlock();
7917 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);
7918 return BM_INVALID_HANDLE;
7919 }
7920
7921 return BM_SUCCESS;
7922}
7923
7924/********************************************************************/
7926{
7927 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7928
7929 if (!locked) {
7930 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);
7931 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);
7932 abort();
7933 /* DOES NOT RETURN */
7934 }
7935
7936 if (!pbuf->attached) {
7937 pbuf->write_cache_mutex.unlock();
7938 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);
7939 return BM_INVALID_HANDLE;
7940 }
7941
7942 return BM_SUCCESS;
7943}
7944
7945/********************************************************************/
7947{
7948 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7949
7950 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7951
7952 if (!locked) {
7953 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7954 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7955 abort();
7956 /* DOES NOT RETURN */
7957 }
7958
7959 if (!pbuf->attached) {
7960 pbuf->buffer_mutex.unlock();
7961 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);
7962 return BM_INVALID_HANDLE;
7963 }
7964
7965 //static int counter = 0;
7966 //counter++;
7967 //printf("locked %d!\n", counter);
7968 //if (counter > 50)
7969 // ::sleep(3);
7970
7971 return BM_SUCCESS;
7972}
7973
7974/********************************************************************/
7976{
7977 int status;
7978
7979 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
7980
7981 //if (pbuf->locked) {
7982 // fprintf(stderr, "double lock, abort!\n");
7983 // abort();
7984 //}
7985
7987
7988 if (status != BM_SUCCESS)
7989 return status;
7990
7991 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
7992
7993 if (status != SS_SUCCESS) {
7994 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
7995
7996 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
7997
7998 if (status != SS_SUCCESS) {
7999 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);
8000
8001 if (pbuf->buffer_header) {
8002 for (int i=0; i<MAX_CLIENTS; i++) {
8003 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);
8004 }
8005 }
8006
8008
8009 if (status != SS_SUCCESS) {
8010 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8011 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8012 abort();
8013 /* DOES NOT RETURN */
8014 }
8015 }
8016 }
8017
8018 // protect against double lock
8019 assert(!pbuf->locked);
8020 pbuf->locked = TRUE;
8021
8022#if 0
8023 int x = MAX_CLIENTS - 1;
8024 if (pbuf->buffer_header->client[x].unused1 != 0) {
8025 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8026 }
8027 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8028 pbuf->buffer_header->client[x].unused1 = getpid();
8029#endif
8030
8031 pbuf->count_lock++;
8032
8033 return BM_SUCCESS;
8034}
8035
8036/********************************************************************/
8038 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8039
8040#if 0
8041 int x = MAX_CLIENTS-1;
8042 if (pbuf->attached) {
8043 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8044 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8045 }
8046 pbuf->buffer_header->client[x].unused1 = 0;
8047 } else {
8048 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8049 }
8050#endif
8051
8052 // protect against double unlock
8053 assert(pbuf->locked);
8054 pbuf->locked = FALSE;
8055
8056 ss_semaphore_release(pbuf->semaphore);
8057 pbuf->buffer_mutex.unlock();
8058}
8059
8060#endif /* LOCAL_ROUTINES */
8061
8062/********************************************************************/
8064/********************************************************************\
8065
8066 Routine: bm_init_event_counters
8067
8068 Purpose: Initialize counters for a specific buffer. This routine
8069 should be called at the beginning of a run.
8070
8071 Input:
8072 INT buffer_handle Handle to the buffer to be
8073 initialized.
8074 Output:
8075 none
8076
8077 Function value:
8078 BM_SUCCESS Successful completion
8079 BM_INVALID_HANDLE Buffer handle is invalid
8080
8081\********************************************************************/
8082{
8083 if (rpc_is_remote())
8084 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8085
8086#ifdef LOCAL_ROUTINES
8087
8088 int status = 0;
8089
8090 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8091
8092 if (!pbuf)
8093 return status;
8094
8096
8097 if (!pbuf_guard.is_locked())
8098 return pbuf_guard.get_status();
8099
8100 pbuf->buffer_header->num_in_events = 0;
8101 pbuf->buffer_header->num_out_events = 0;
8102
8103#endif /* LOCAL_ROUTINES */
8104
8105 return BM_SUCCESS;
8106}
8107
8109#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8110
8116/********************************************************************/
8140INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8141/*------------------------------------------------------------------*/
8142{
8143 if (rpc_is_remote())
8144 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8145
8146#ifdef LOCAL_ROUTINES
8147 {
8148 int status = 0;
8149
8150 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8151
8152 if (!pbuf)
8153 return status;
8154
8155 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8156
8158
8159 if (status != BM_SUCCESS)
8160 return status;
8161
8162 if (write_size < 0)
8163 write_size = 0;
8164
8165 if (write_size > 0) {
8167 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);
8169 }
8170 }
8171
8172 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8173
8174 if (write_size > max_write_size) {
8176 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);
8178 }
8179
8180 pbuf->buffer_mutex.unlock();
8181
8182 /* resize read cache */
8183
8185
8186 if (status != BM_SUCCESS) {
8187 return status;
8188 }
8189
8190 if (pbuf->read_cache_size > 0) {
8191 free(pbuf->read_cache);
8192 pbuf->read_cache = NULL;
8193 }
8194
8195 if (read_size > 0) {
8196 pbuf->read_cache = (char *) malloc(read_size);
8197 if (pbuf->read_cache == NULL) {
8198 pbuf->read_cache_size = 0;
8199 pbuf->read_cache_rp = 0;
8200 pbuf->read_cache_wp = 0;
8201 pbuf->read_cache_mutex.unlock();
8202 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);
8203 return BM_NO_MEMORY;
8204 }
8205 }
8206
8207 pbuf->read_cache_size = read_size;
8208 pbuf->read_cache_rp = 0;
8209 pbuf->read_cache_wp = 0;
8210
8211 pbuf->read_cache_mutex.unlock();
8212
8213 /* resize the write cache */
8214
8216
8217 if (status != BM_SUCCESS)
8218 return status;
8219
8220 // FIXME: should flush the write cache!
8221 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8222 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8223 }
8224
8225 /* manage write cache */
8226 if (pbuf->write_cache_size > 0) {
8227 free(pbuf->write_cache);
8228 pbuf->write_cache = NULL;
8229 }
8230
8231 if (write_size > 0) {
8232 pbuf->write_cache = (char *) M_MALLOC(write_size);
8233 if (pbuf->write_cache == NULL) {
8234 pbuf->write_cache_size = 0;
8235 pbuf->write_cache_rp = 0;
8236 pbuf->write_cache_wp = 0;
8237 pbuf->write_cache_mutex.unlock();
8238 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);
8239 return BM_NO_MEMORY;
8240 }
8241 }
8242
8243 pbuf->write_cache_size = write_size;
8244 pbuf->write_cache_rp = 0;
8245 pbuf->write_cache_wp = 0;
8246
8247 pbuf->write_cache_mutex.unlock();
8248 }
8249#endif /* LOCAL_ROUTINES */
8250
8251 return BM_SUCCESS;
8252}
8253
8254/********************************************************************/
8282{
8283 event_header->event_id = event_id;
8284 event_header->trigger_mask = trigger_mask;
8285 event_header->data_size = data_size;
8286 event_header->time_stamp = ss_time();
8287 event_header->serial_number = serial;
8288
8289 return BM_SUCCESS;
8290}
8291
8293{
8294 static std::mutex mutex;
8295
8296 event_header->event_id = event_id;
8297 event_header->trigger_mask = trigger_mask;
8298 event_header->data_size = data_size;
8299 event_header->time_stamp = ss_time();
8300 {
8301 std::lock_guard<std::mutex> lock(mutex);
8302 event_header->serial_number = *serial;
8303 *serial = *serial + 1;
8304 // implicit unlock
8305 }
8306
8307 return BM_SUCCESS;
8308}
8309
8311#ifndef DOXYGEN_SHOULD_SKIP_THIS
8312
8313/********************************************************************/
8314INT bm_add_event_request(INT buffer_handle, short int event_id,
8315 short int trigger_mask,
8316 INT sampling_type,
8317 EVENT_HANDLER *func,
8318 INT request_id)
8319/********************************************************************\
8320
8321 Routine: bm_add_event_request
8322
8323 Purpose: Place a request for a specific event type in the client
8324 structure of the buffer refereced by buffer_handle.
8325
8326 Input:
8327 INT buffer_handle Handle to the buffer where the re-
8328 quest should be placed in
8329
8330 short int event_id Event ID \
8331 short int trigger_mask Trigger mask / Event specification
8332
8333 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8334
8335
8336 Note: to request all types of events, use
8337 event_id = 0 (all others should be !=0 !)
8338 trigger_mask = TRIGGER_ALL
8339 sampling_typ = GET_ALL
8340
8341
8342 void *func Callback function
8343 INT request_id Request id (unique number assigned
8344 by bm_request_event)
8345
8346 Output:
8347 none
8348
8349 Function value:
8350 BM_SUCCESS Successful completion
8351 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8352 MIDAS.H should be increased.
8353 BM_INVALID_HANDLE Buffer handle is invalid
8354 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8355 RPC_NET_ERROR Network error
8356
8357\********************************************************************/
8358{
8359 if (rpc_is_remote())
8360 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8361 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8362
8363#ifdef LOCAL_ROUTINES
8364 {
8365 int status = 0;
8366
8367 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8368
8369 if (!pbuf)
8370 return status;
8371
8372 /* lock buffer */
8374
8375 if (!pbuf_guard.is_locked())
8376 return pbuf_guard.get_status();
8377
8378 /* avoid callback/non callback requests */
8379 if (func == NULL && pbuf->callback) {
8380 pbuf_guard.unlock(); // unlock before cm_msg()
8381 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8382 return BM_INVALID_MIXING;
8383 }
8384
8385 /* do not allow GET_RECENT with nonzero cache size */
8386 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8387 pbuf_guard.unlock(); // unlock before cm_msg()
8388 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8389 return BM_INVALID_PARAM;
8390 }
8391
8392 /* get a pointer to the proper client structure */
8394
8395 /* look for a empty request entry */
8396 int i;
8397 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8398 if (!pclient->event_request[i].valid)
8399 break;
8400
8401 if (i == MAX_EVENT_REQUESTS) {
8402 // implicit unlock
8403 return BM_NO_MEMORY;
8404 }
8405
8406 /* setup event_request structure */
8407 pclient->event_request[i].id = request_id;
8408 pclient->event_request[i].valid = TRUE;
8409 pclient->event_request[i].event_id = event_id;
8410 pclient->event_request[i].trigger_mask = trigger_mask;
8411 pclient->event_request[i].sampling_type = sampling_type;
8412
8413 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8414
8415 pbuf->get_all_flag = pclient->all_flag;
8416
8417 /* set callback flag in buffer structure */
8418 if (func != NULL)
8419 pbuf->callback = TRUE;
8420
8421 /*
8422 Save the index of the last request in the list so that later only the
8423 requests 0..max_request_index-1 have to be searched through.
8424 */
8425
8426 if (i + 1 > pclient->max_request_index)
8427 pclient->max_request_index = i + 1;
8428 }
8429#endif /* LOCAL_ROUTINES */
8430
8431 return BM_SUCCESS;
8432}
8433
8435#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8436
8437/********************************************************************/
8465INT bm_request_event(HNDLE buffer_handle, short int event_id,
8466 short int trigger_mask,
8467 INT sampling_type, HNDLE *request_id,
8468 EVENT_HANDLER *func)
8469{
8470 assert(request_id != NULL);
8471
8472 EventRequest r;
8473 r.buffer_handle = buffer_handle;
8474 r.event_id = event_id;
8476 r.dispatcher = func;
8477
8478 {
8479 std::lock_guard<std::mutex> guard(_request_list_mutex);
8480
8481 bool found = false;
8482
8483 // find deleted entry
8484 for (size_t i = 0; i < _request_list.size(); i++) {
8485 if (_request_list[i].buffer_handle == 0) {
8486 _request_list[i] = r;
8487 *request_id = i;
8488 found = true;
8489 break;
8490 }
8491 }
8492
8493 if (!found) { // not found
8494 *request_id = _request_list.size();
8495 _request_list.push_back(r);
8496 }
8497
8498 // implicit unlock()
8499 }
8500
8501 /* add request in buffer structure */
8502 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8503 if (status != BM_SUCCESS)
8504 return status;
8505
8506 return BM_SUCCESS;
8507}
8508
8509/********************************************************************/
8518INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8519 if (rpc_is_remote())
8520 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8521
8522#ifdef LOCAL_ROUTINES
8523 {
8524 int status = 0;
8525
8526 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8527
8528 if (!pbuf)
8529 return status;
8530
8531 /* lock buffer */
8533
8534 if (!pbuf_guard.is_locked())
8535 return pbuf_guard.get_status();
8536
8537 INT i, deleted;
8538
8539 /* get a pointer to the proper client structure */
8541
8542 /* check all requests and set to zero if matching */
8543 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8544 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8545 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8546 deleted++;
8547 }
8548
8549 /* calculate new max_request_index entry */
8550 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8551 if (pclient->event_request[i].valid)
8552 break;
8553
8554 pclient->max_request_index = i + 1;
8555
8556 /* calculate new all_flag */
8557 pclient->all_flag = FALSE;
8558
8559 for (i = 0; i < pclient->max_request_index; i++)
8560 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8561 pclient->all_flag = TRUE;
8562 break;
8563 }
8564
8565 pbuf->get_all_flag = pclient->all_flag;
8566
8567 if (!deleted)
8568 return BM_NOT_FOUND;
8569 }
8570#endif /* LOCAL_ROUTINES */
8571
8572 return BM_SUCCESS;
8573}
8574
8575/********************************************************************/
8585{
8586 _request_list_mutex.lock();
8587
8588 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8589 _request_list_mutex.unlock();
8590 return BM_INVALID_HANDLE;
8591 }
8592
8593 int buffer_handle = _request_list[request_id].buffer_handle;
8594
8595 _request_list[request_id].clear();
8596
8597 _request_list_mutex.unlock();
8598
8599 /* remove request entry from buffer */
8600 return bm_remove_event_request(buffer_handle, request_id);
8601}
8602
8603#if 0 // currently not used
8604static void bm_show_pointers(const BUFFER_HEADER * pheader)
8605{
8606 int i;
8607 const BUFFER_CLIENT *pclient;
8608
8609 pclient = pheader->client;
8610
8611 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8612 pheader->write_pointer, pheader->size);
8613 for (i = 0; i < pheader->max_client_index; i++)
8614 if (pclient[i].pid) {
8615 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8616 }
8617
8618 printf("done\n");
8619}
8620#endif
8621
8623 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8624 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8625
8626 if (pheader->read_pointer <= pheader->write_pointer) {
8627
8628 if (pclient->read_pointer < pheader->read_pointer) {
8629 cm_msg(MINFO, "bm_validate_client_pointers",
8630 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8631 pclient->name,
8632 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8633
8634 pclient->read_pointer = pheader->read_pointer;
8635 }
8636
8637 if (pclient->read_pointer > pheader->write_pointer) {
8638 cm_msg(MINFO, "bm_validate_client_pointers",
8639 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8640 pclient->name,
8641 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8642
8643 pclient->read_pointer = pheader->write_pointer;
8644 }
8645
8646 } else {
8647
8648 if (pclient->read_pointer < 0) {
8649 cm_msg(MINFO, "bm_validate_client_pointers",
8650 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8651 pclient->name,
8652 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8653
8654 pclient->read_pointer = pheader->read_pointer;
8655 }
8656
8657 if (pclient->read_pointer >= pheader->size) {
8658 cm_msg(MINFO, "bm_validate_client_pointers",
8659 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8660 pclient->name,
8661 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8662
8663 pclient->read_pointer = pheader->read_pointer;
8664 }
8665
8666 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8667 cm_msg(MINFO, "bm_validate_client_pointers",
8668 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8669 pclient->name,
8670 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8671
8672 pclient->read_pointer = pheader->read_pointer;
8673 }
8674 }
8675}
8676
8677#if 0 // currently not used
8678static void bm_validate_pointers(BUFFER_HEADER * pheader)
8679{
8680 BUFFER_CLIENT *pclient = pheader->client;
8681 int i;
8682
8683 for (i = 0; i < pheader->max_client_index; i++)
8684 if (pclient[i].pid) {
8686 }
8687}
8688#endif
8689
8690//
8691// Buffer pointers
8692//
8693// normal:
8694//
8695// zero -->
8696// ... free space
8697// read_pointer -->
8698// client1 rp -->
8699// client2 rp -->
8700// ... buffered data
8701// write_pointer -->
8702// ... free space
8703// pheader->size -->
8704//
8705// inverted:
8706//
8707// zero -->
8708// client3 rp -->
8709// ... buffered data
8710// client4 rp -->
8711// write_pointer -->
8712// ... free space
8713// read_pointer -->
8714// client1 rp -->
8715// client2 rp -->
8716// ... buffered data
8717// pheader->size -->
8718//
8719
8721 assert(caller_name);
8722
8723 /* calculate global read pointer as "minimum" of client read pointers */
8724 int min_rp = pheader->write_pointer;
8725
8726 int i;
8727 for (i = 0; i < pheader->max_client_index; i++) {
8728 BUFFER_CLIENT *pc = pheader->client + i;
8729 if (pc->pid) {
8731
8732#if 0
8733 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8734 pheader->name,
8735 pheader->read_pointer,
8736 pheader->write_pointer,
8737 pheader->size,
8738 min_rp,
8739 pc->name,
8740 pc->read_pointer);
8741#endif
8742
8743 if (pheader->read_pointer <= pheader->write_pointer) {
8744 // normal pointers
8745 if (pc->read_pointer < min_rp)
8746 min_rp = pc->read_pointer;
8747 } else {
8748 // inverted pointers
8749 if (pc->read_pointer <= pheader->write_pointer) {
8750 // clients 3 and 4
8751 if (pc->read_pointer < min_rp)
8752 min_rp = pc->read_pointer;
8753 } else {
8754 // clients 1 and 2
8755 int xptr = pc->read_pointer - pheader->size;
8756 if (xptr < min_rp)
8757 min_rp = xptr;
8758 }
8759 }
8760 }
8761 }
8762
8763 if (min_rp < 0)
8764 min_rp += pheader->size;
8765
8766 assert(min_rp >= 0);
8767 assert(min_rp < pheader->size);
8768
8769 if (min_rp == pheader->read_pointer) {
8770 return FALSE;
8771 }
8772
8773#if 0
8774 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8775 pheader->name,
8776 pheader->read_pointer,
8777 pheader->write_pointer,
8778 pheader->size,
8779 min_rp);
8780#endif
8781
8782 pheader->read_pointer = min_rp;
8783
8784 return TRUE;
8785}
8786
8787static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8788 int i;
8789 int have_get_all_requests = 0;
8790
8791 for (i = 0; i < pc->max_request_index; i++)
8792 if (pc->event_request[i].valid)
8793 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8794
8795 /* only GET_ALL requests actually free space in the event buffer */
8797 return;
8798
8799 /*
8800 If read pointer has been changed, it may have freed up some space
8801 for waiting producers. So check if free space is now more than 50%
8802 of the buffer size and wake waiting producers.
8803 */
8804
8805 int free_space = pc->read_pointer - pheader->write_pointer;
8806 if (free_space <= 0)
8807 free_space += pheader->size;
8808
8809 if (free_space >= pheader->size * 0.5) {
8810 for (i = 0; i < pheader->max_client_index; i++) {
8811 const BUFFER_CLIENT *pc = pheader->client + i;
8812 if (pc->pid && pc->write_wait) {
8813 BOOL send_wakeup = (pc->write_wait < free_space);
8814 //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);
8815 if (send_wakeup) {
8816 ss_resume(pc->port, "B ");
8817 }
8818 }
8819 }
8820 }
8821}
8822
8823static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8824{
8825 _request_list_mutex.lock();
8826 bool locked = true;
8827 size_t n = _request_list.size();
8828 /* call dispatcher */
8829 for (size_t i = 0; i < n; i++) {
8830 if (!locked) {
8831 _request_list_mutex.lock();
8832 locked = true;
8833 }
8835 if (r.buffer_handle != buffer_handle)
8836 continue;
8837 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8838 continue;
8839 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8840 _request_list_mutex.unlock();
8841 locked = false;
8842 /* if event is fragmented, call defragmenter */
8843 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))) {
8844 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8845 } else {
8846 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8847 }
8848 }
8849 if (locked)
8850 _request_list_mutex.unlock();
8851}
8852
8853#ifdef LOCAL_ROUTINES
8854
8855static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8856 /* increment read cache read pointer */
8857 pbuf->read_cache_rp += total_size;
8858
8859 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8860 pbuf->read_cache_rp = 0;
8861 pbuf->read_cache_wp = 0;
8862 }
8863}
8864
8866{
8867 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8868 return FALSE;
8869
8870 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8871 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8872 int total_size = ALIGN8(event_size);
8873
8874 if (ppevent)
8875 *ppevent = pevent;
8876 if (pevent_size)
8878 if (ptotal_size)
8879 *ptotal_size = total_size;
8880
8881 return TRUE;
8882}
8883
8884//
8885// return values:
8886// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8887// BM_ASYNC_RETURN - buffer is empty
8888// BM_CORRUPTED - buffer is corrupted
8889//
8890
8892{
8893 if (pc->read_pointer == pheader->write_pointer) {
8894 /* no more events buffered for this client */
8895 if (!pc->read_wait) {
8896 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8897 pc->read_wait = TRUE;
8898 }
8899 return BM_ASYNC_RETURN;
8900 }
8901
8902 if (pc->read_wait) {
8903 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8904 pc->read_wait = FALSE;
8905 }
8906
8907 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8908 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);
8909 return BM_CORRUPTED;
8910 }
8911
8912 char *pdata = (char *) (pheader + 1);
8913
8914 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8915 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8916 int total_size = ALIGN8(event_size);
8917
8918 if ((total_size <= 0) || (total_size > pheader->size)) {
8919 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);
8920 return BM_CORRUPTED;
8921 }
8922
8923 assert(total_size > 0);
8924 assert(total_size <= pheader->size);
8925
8926 if (ppevent)
8927 *ppevent = pevent;
8928 if (pevent_size)
8930 if (ptotal_size)
8931 *ptotal_size = total_size;
8932
8933 return BM_SUCCESS;
8934}
8935
8936static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8937{
8938 const char *pdata = (const char *) (pheader + 1);
8939
8940 if (rp + event_size <= pheader->size) {
8941 /* copy event to cache */
8942 memcpy(buf, pdata + rp, event_size);
8943 } else {
8944 /* event is splitted */
8945 int size = pheader->size - rp;
8946 memcpy(buf, pdata + rp, size);
8947 memcpy(buf + size, pdata, event_size - size);
8948 }
8949}
8950
8951static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8952{
8953 const char *pdata = (const char *) (pheader + 1);
8954
8955 if (rp + event_size <= pheader->size) {
8956 /* copy event to cache */
8957 vecptr->assign(pdata + rp, pdata + rp + event_size);
8958 } else {
8959 /* event is splitted */
8960 int size = pheader->size - rp;
8961 vecptr->assign(pdata + rp, pdata + rp + size);
8962 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8963 }
8964}
8965
8966static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8967
8969 int i;
8970 for (i = 0; i < pc->max_request_index; i++) {
8971 const EVENT_REQUEST *prequest = pc->event_request + i;
8972 if (prequest->valid) {
8973 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8974 /* check if this is a recent event */
8975 if (prequest->sampling_type == GET_RECENT) {
8976 if (ss_time() - pevent->time_stamp > 1) {
8977 /* skip that event */
8978 continue;
8979 }
8980 }
8981
8983 break;
8984 }
8985 }
8986 }
8987 return is_requested;
8988}
8989
8991
8993{
8994 BUFFER* pbuf = pbuf_guard.get_pbuf();
8995 BUFFER_HEADER* pheader = pbuf->buffer_header;
8998
8999 //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);
9000
9001 /* loop over all events in the buffer */
9002
9003 while (1) {
9004 EVENT_HEADER *pevent = NULL;
9005 int event_size = 3; // poison value
9006 int total_size = 3; // poison value
9007
9008 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9009 if (status == BM_CORRUPTED) {
9010 return status;
9011 } else if (status != BM_SUCCESS) {
9012 /* event buffer is empty */
9013 if (timeout_msec == BM_NO_WAIT) {
9014 if (need_wakeup)
9016 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9017 // read cache is empty
9018 return BM_ASYNC_RETURN;
9019 }
9020 return BM_SUCCESS;
9021 }
9022
9024
9025 if (status != BM_SUCCESS) {
9026 // we only come here with SS_ABORT & co
9027 return status;
9028 }
9029
9030 // make sure we wait for new event only once
9032 // go back to bm_peek_buffer_locked
9033 continue;
9034 }
9035
9036 /* loop over all requests: if this event matches a request,
9037 * copy it to the read cache */
9038
9040
9041 if (is_requested) {
9042 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9043 /* read cache is full */
9044 if (need_wakeup)
9046 return BM_SUCCESS;
9047 }
9048
9049 bm_read_from_buffer_locked(pheader, pc->read_pointer, pbuf->read_cache + pbuf->read_cache_wp, event_size);
9050
9051 pbuf->read_cache_wp += total_size;
9052
9053 /* update statistics */
9054 pheader->num_out_events++;
9055 pbuf->count_read++;
9056 pbuf->bytes_read += event_size;
9057 }
9058
9059 /* shift read pointer */
9060
9061 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9062 pc->read_pointer = new_read_pointer;
9063
9064 need_wakeup = TRUE;
9065 }
9066 /* NOT REACHED */
9067}
9068
9069static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9070 /* now convert event header */
9071 if (convert_flags) {
9072 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9073 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9074 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9075 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9076 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9077 }
9078}
9079
9081{
9082 // return values:
9083 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9084 // BM_CORRUPTED - shared memory is corrupted
9085 // BM_NO_MEMORY - asked for more than buffer size
9086 // BM_ASYNC_RETURN - timeout waiting for free space
9087 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9088 // SS_ABORT - we are told to shutdown (locks releases)
9089
9090 int status;
9091 BUFFER* pbuf = pbuf_guard.get_pbuf();
9092 BUFFER_HEADER *pheader = pbuf->buffer_header;
9093 char *pdata = (char *) (pheader + 1);
9094
9095 /* make sure the buffer never completely full:
9096 * read pointer and write pointer would coincide
9097 * and the code cannot tell if it means the
9098 * buffer is 100% full or 100% empty. It will explode
9099 * or lose events */
9100 requested_space += 100;
9101
9102 if (requested_space >= pheader->size)
9103 return BM_NO_MEMORY;
9104
9107
9108 //DWORD blocking_time = 0;
9109 //int blocking_loops = 0;
9110 int blocking_client_index = -1;
9112 blocking_client_name[0] = 0;
9113
9114 while (1) {
9115 while (1) {
9116 /* check if enough space in buffer */
9117
9118 int free = pheader->read_pointer - pheader->write_pointer;
9119 if (free <= 0)
9120 free += pheader->size;
9121
9122 //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);
9123
9124 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9125 //if (blocking_loops) {
9126 // DWORD wait_time = ss_millitime() - blocking_time;
9127 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9128 //}
9129
9130 if (pbuf->wait_start_time != 0) {
9132 DWORD wait_time = now - pbuf->wait_start_time;
9133 pbuf->time_write_wait += wait_time;
9134 pbuf->wait_start_time = 0;
9135 int iclient = pbuf->wait_client_index;
9136 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9137 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9138 pbuf->client_count_write_wait[iclient] += 1;
9139 pbuf->client_time_write_wait[iclient] += wait_time;
9140 }
9141 }
9142
9143 //if (blocking_loops > 0) {
9144 // 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);
9145 //}
9146
9147 return BM_SUCCESS;
9148 }
9149
9150 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9151 cm_msg(MERROR, "bm_wait_for_free_space",
9152 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9153 pheader->name,
9154 pheader->read_pointer,
9155 pheader->write_pointer,
9156 pheader->size,
9157 free,
9159 return BM_CORRUPTED;
9160 }
9161
9162 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9163 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9164 int total_size = ALIGN8(event_size);
9165
9166#if 0
9167 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);
9168#endif
9169
9170 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9171 cm_msg(MERROR, "bm_wait_for_free_space",
9172 "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",
9173 pheader->name,
9174 pheader->read_pointer,
9175 pheader->write_pointer,
9176 pheader->size,
9177 free,
9179 pevent->data_size,
9180 event_size,
9181 total_size);
9182 return BM_CORRUPTED;
9183 }
9184
9185 int blocking_client = -1;
9186
9187 int i;
9188 for (i = 0; i < pheader->max_client_index; i++) {
9189 BUFFER_CLIENT *pc = pheader->client + i;
9190 if (pc->pid) {
9191 if (pc->read_pointer == pheader->read_pointer) {
9192 /*
9193 First assume that the client with the "minimum" read pointer
9194 is not really blocking due to a GET_ALL request.
9195 */
9197 //int blocking_request_id = -1;
9198
9199 int j;
9200 for (j = 0; j < pc->max_request_index; j++) {
9201 const EVENT_REQUEST *prequest = pc->event_request + j;
9202 if (prequest->valid
9203 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9204 if (prequest->sampling_type & GET_ALL) {
9205 blocking = TRUE;
9206 //blocking_request_id = prequest->id;
9207 break;
9208 }
9209 }
9210 }
9211
9212 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9213
9214 if (blocking) {
9216 break;
9217 }
9218
9219 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9220 }
9221 }
9222 } /* client loop */
9223
9224 if (blocking_client >= 0) {
9227 //if (!blocking_time) {
9228 // blocking_time = ss_millitime();
9229 //}
9230
9231 //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);
9232
9233 // from this "break" we go into timeout check and sleep/wait.
9234 break;
9235 }
9236
9237 /* no blocking clients. move the read pointer and again check for free space */
9238
9239 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9240
9241 if (!moved) {
9242 cm_msg(MERROR, "bm_wait_for_free_space",
9243 "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",
9244 pheader->name,
9245 pheader->read_pointer,
9246 pheader->write_pointer,
9247 pheader->size,
9248 free,
9250 return BM_CORRUPTED;
9251 }
9252
9253 /* we freed one event, loop back to the check for free space */
9254 }
9255
9256 //blocking_loops++;
9257
9258 /* at least one client is blocking */
9259
9261 pc->write_wait = requested_space;
9262
9263 if (pbuf->wait_start_time == 0) {
9264 pbuf->wait_start_time = ss_millitime();
9265 pbuf->count_write_wait++;
9266 if (requested_space > pbuf->max_requested_space)
9267 pbuf->max_requested_space = requested_space;
9268 pbuf->wait_client_index = blocking_client_index;
9269 }
9270
9272
9273 //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);
9274
9275 int sleep_time_msec = 1000;
9276
9277 if (timeout_msec == BM_WAIT) {
9278 // wait forever
9279 } else if (timeout_msec == BM_NO_WAIT) {
9280 // no wait
9281 return BM_ASYNC_RETURN;
9282 } else {
9283 // check timeout
9284 if (now >= time_end) {
9285 // timeout!
9286 return BM_ASYNC_RETURN;
9287 }
9288
9290
9291 if (sleep_time_msec <= 0) {
9292 sleep_time_msec = 10;
9293 } else if (sleep_time_msec > 1000) {
9294 sleep_time_msec = 1000;
9295 }
9296 }
9297
9299
9300 /* before waiting, unlock everything in the correct order */
9301
9302 pbuf_guard.unlock();
9303
9305 pbuf->write_cache_mutex.unlock();
9306
9307 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9308
9309#ifdef DEBUG_MSG
9310 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9311#endif
9312
9314 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9315 //if (idx >= 0)
9316 // pheader->client[idx].write_wait = requested_space;
9317
9318 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9319
9321
9322 /* we are told to shutdown */
9323 if (status == SS_ABORT) {
9324 // NB: buffer is locked!
9325 return SS_ABORT;
9326 }
9327
9328 /* make sure we do sleep in this loop:
9329 * if we are the mserver receiving data on the event
9330 * socket and the data buffer is full, ss_suspend() will
9331 * never sleep: it will detect data on the event channel,
9332 * call rpc_server_receive() (recursively, we already *are* in
9333 * rpc_server_receive()) and return without sleeping. Result
9334 * is a busy loop waiting for free space in data buffer */
9335
9336 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9337 * the event socket, and should sleep now, so this sleep below
9338 * maybe is not needed now. but for safety, I keep it. K.O. */
9339
9340 if (status != SS_TIMEOUT) {
9341 //printf("ss_suspend: status %d\n", status);
9342 ss_sleep(1);
9343 }
9344
9345 /* we may be stuck in this loop for an arbitrary long time,
9346 * depending on how other buffer clients read the accumulated data
9347 * so we should update all the timeouts & etc. K.O. */
9348
9350
9351 /* lock things again in the correct order */
9352
9353 if (unlock_write_cache) {
9355
9356 if (status != BM_SUCCESS) {
9357 // bail out with all locks released
9358 return status;
9359 }
9360 }
9361
9362 if (!pbuf_guard.relock()) {
9363 if (unlock_write_cache) {
9364 pbuf->write_cache_mutex.unlock();
9365 }
9366
9367 // bail out with all locks released
9368 return pbuf_guard.get_status();
9369 }
9370
9371 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9373
9374 pc->write_wait = 0;
9375
9377 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9378 //if (idx >= 0)
9379 // pheader->client[idx].write_wait = 0;
9380 //else {
9381 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9382 // status = SS_ABORT;
9383 //}
9384
9385#ifdef DEBUG_MSG
9386 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);
9387#endif
9388
9389 }
9390}
9391
9393{
9394 BUFFER* pbuf = pbuf_guard.get_pbuf();
9395 BUFFER_HEADER* pheader = pbuf->buffer_header;
9396
9397 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9398
9399 if (pc->read_pointer != pheader->write_pointer) {
9400 // buffer has data
9401 return BM_SUCCESS;
9402 }
9403
9404 if (timeout_msec == BM_NO_WAIT) {
9405 /* event buffer is empty and we are told to not wait */
9406 if (!pc->read_wait) {
9407 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9408 pc->read_wait = TRUE;
9409 }
9410 return BM_ASYNC_RETURN;
9411 }
9412
9415 DWORD sleep_time = 1000;
9416 if (timeout_msec == BM_NO_WAIT) {
9417 // default sleep time
9418 } else if (timeout_msec == BM_WAIT) {
9419 // default sleep time
9420 } else {
9423 }
9424
9425 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9426
9427 while (pc->read_pointer == pheader->write_pointer) {
9428 /* wait until there is data in the buffer (write pointer moves) */
9429
9430 if (!pc->read_wait) {
9431 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9432 pc->read_wait = TRUE;
9433 }
9434
9435 pc->last_activity = ss_millitime();
9436
9438
9439 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9440
9441 pbuf_guard.unlock();
9442
9444 pbuf->read_cache_mutex.unlock();
9445
9447
9448 if (timeout_msec == BM_NO_WAIT) {
9449 // return immediately
9450 } else if (timeout_msec == BM_WAIT) {
9451 // wait forever
9452 } else {
9454 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9455 if (now >= time_wait) {
9456 timeout_msec = BM_NO_WAIT; // cause immediate return
9457 } else {
9459 if (sleep_time > 1000)
9460 sleep_time = 1000;
9461 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9462 }
9463 }
9464
9465 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9466
9467 if (unlock_read_cache) {
9469 if (status != BM_SUCCESS) {
9470 // bail out with all locks released
9471 return status;
9472 }
9473 }
9474
9475 if (!pbuf_guard.relock()) {
9476 if (unlock_read_cache) {
9477 pbuf->read_cache_mutex.unlock();
9478 }
9479 // bail out with all locks released
9480 return pbuf_guard.get_status();
9481 }
9482
9483 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9484 * because we may have been removed from the buffer by bm_cleanup() & co
9485 * due to a timeout or whatever. */
9487
9488 /* return if TCP connection broken */
9489 if (status == SS_ABORT)
9490 return SS_ABORT;
9491
9492 if (timeout_msec == BM_NO_WAIT)
9493 return BM_ASYNC_RETURN;
9494 }
9495
9496 if (pc->read_wait) {
9497 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9498 pc->read_wait = FALSE;
9499 }
9500
9501 return BM_SUCCESS;
9502}
9503
9504static 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)
9505{
9506 char *pdata = (char *) (pheader + 1);
9507
9508 //int old_write_pointer = pheader->write_pointer;
9509
9510 /* new event fits into the remaining space? */
9511 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9512 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9513 char* wptr = pdata + pheader->write_pointer;
9514 for (int i=0; i<sg_n; i++) {
9515 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9516 memcpy(wptr, sg_ptr[i], sg_len[i]);
9517 wptr += sg_len[i];
9518 }
9519 pheader->write_pointer = pheader->write_pointer + total_size;
9520 assert(pheader->write_pointer <= pheader->size);
9521 /* remaining space is smaller than size of an event header? */
9522 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9523 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9524 // equal to the event header size, we will write the next event header here,
9525 // then wrap the pointer and write the event data at the beginning of the buffer.
9526 //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);
9527 pheader->write_pointer = 0;
9528 }
9529 } else {
9530 /* split event */
9531 size_t size = pheader->size - pheader->write_pointer;
9532
9533 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9534
9535 //memcpy(pdata + pheader->write_pointer, pevent, size);
9536 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9537
9538 char* wptr = pdata + pheader->write_pointer;
9539 size_t count = 0;
9540
9541 // copy first part
9542
9543 int i = 0;
9544 for (; i<sg_n; i++) {
9545 if (count + sg_len[i] > size)
9546 break;
9547 memcpy(wptr, sg_ptr[i], sg_len[i]);
9548 wptr += sg_len[i];
9549 count += sg_len[i];
9550 }
9551
9552 //printf("wptr %d, count %d\n", wptr-pdata, count);
9553
9554 // split segment
9555
9556 size_t first = size - count;
9557 size_t second = sg_len[i] - first;
9558 assert(first + second == sg_len[i]);
9559 assert(count + first == size);
9560
9561 //printf("first %d, second %d\n", first, second);
9562
9563 memcpy(wptr, sg_ptr[i], first);
9564 wptr = pdata + 0;
9565 count += first;
9567 wptr += second;
9568 count += second;
9569 i++;
9570
9571 // copy remaining
9572
9573 for (; i<sg_n; i++) {
9574 memcpy(wptr, sg_ptr[i], sg_len[i]);
9575 wptr += sg_len[i];
9576 count += sg_len[i];
9577 }
9578
9579 //printf("wptr %d, count %d\n", wptr-pdata, count);
9580
9581 //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);
9582
9583 pheader->write_pointer = total_size - size;
9584 }
9585
9586 //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);
9587}
9588
9590 if (pc->pid) {
9591 int j;
9592 for (j = 0; j < pc->max_request_index; j++) {
9593 const EVENT_REQUEST *prequest = pc->event_request + j;
9594 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9595 return prequest->id;
9596 }
9597 }
9598 }
9599
9600 return -1;
9601}
9602
9603static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9604 if (request_id >= 0) {
9605 /* if that client has a request and is suspended, wake it up */
9606 if (pc->read_wait) {
9607 char str[80];
9608 sprintf(str, "B %s %d", pheader->name, request_id);
9609 ss_resume(pc->port, str);
9610 //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);
9611 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9612 pc->read_wait = FALSE;
9613 }
9614 }
9615}
9616
9617#endif // LOCAL_ROUTINES
9618
9619#if 0
9620INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9621{
9622 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9623
9626
9628
9629 while (1) {
9630 if (timeout_msec == BM_WAIT) {
9631 xtimeout_msec = 1000;
9632 } else if (timeout_msec == BM_NO_WAIT) {
9634 } else {
9635 if (xtimeout_msec > 1000) {
9636 xtimeout_msec = 1000;
9637 }
9638 }
9639
9640 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9641
9642 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9643
9644 if (status == BM_ASYNC_RETURN) {
9645 if (timeout_msec == BM_WAIT) {
9646 // BM_WAIT means wait forever
9647 continue;
9648 } else if (timeout_msec == BM_NO_WAIT) {
9649 // BM_NO_WAIT means do not wait
9650 return status;
9651 } else {
9653 if (now >= time_end) {
9654 // timeout, return BM_ASYNC_RETURN
9655 return status;
9656 }
9657
9659
9660 if (remain < xtimeout_msec) {
9662 }
9663
9664 // keep asking for event...
9665 continue;
9666 }
9667 } else if (status == BM_SUCCESS) {
9668 // success, return BM_SUCCESS
9669 return status;
9670 } else {
9671 // error
9672 return status;
9673 }
9674 }
9675}
9676#endif
9677
9678INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9679{
9680 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9681 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9682
9683 if (data_size == 0) {
9684 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9685 return BM_INVALID_SIZE;
9686 }
9687
9688 if (data_size > MAX_DATA_SIZE) {
9689 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);
9690 return BM_INVALID_SIZE;
9691 }
9692
9693 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9694
9695 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9696
9697 if (rpc_is_remote()) {
9698 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9699 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9700 } else {
9701 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9702 }
9703}
9704
9705int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9706{
9707 const char* cptr = event.data();
9708 size_t clen = event.size();
9709 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9710}
9711
9712int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9713{
9714 int sg_n = event.size();
9715 const char* sg_ptr[sg_n];
9716 size_t sg_len[sg_n];
9717 for (int i=0; i<sg_n; i++) {
9718 sg_ptr[i] = event[i].data();
9719 sg_len[i] = event[i].size();
9720 }
9721 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9722}
9723
9724#ifdef LOCAL_ROUTINES
9726#endif
9727
9728/********************************************************************/
9778int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9779{
9780 if (rpc_is_remote())
9781 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9782
9783 if (sg_n < 1) {
9784 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9785 return BM_INVALID_SIZE;
9786 }
9787
9788 if (sg_ptr[0] == NULL) {
9789 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9790 return BM_INVALID_SIZE;
9791 }
9792
9793 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9794 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));
9795 return BM_INVALID_SIZE;
9796 }
9797
9798 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9799
9800 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9801 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9802
9803 if (data_size == 0) {
9804 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9805 return BM_INVALID_SIZE;
9806 }
9807
9808 if (data_size > MAX_DATA_SIZE) {
9809 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);
9810 return BM_INVALID_SIZE;
9811 }
9812
9813 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9814
9815 size_t count = 0;
9816 for (int i=0; i<sg_n; i++) {
9817 count += sg_len[i];
9818 }
9819
9820 if (count != event_size) {
9821 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);
9822 return BM_INVALID_SIZE;
9823 }
9824
9825 //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);
9826
9827#ifdef LOCAL_ROUTINES
9828 {
9829 int status = 0;
9830 const size_t total_size = ALIGN8(event_size);
9831
9832 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9833
9834 if (!pbuf)
9835 return status;
9836
9837 /* round up total_size to next DWORD boundary */
9838 //int total_size = ALIGN8(event_size);
9839
9840 /* check if write cache is enabled */
9841 if (pbuf->write_cache_size) {
9843
9844 if (status != BM_SUCCESS)
9845 return status;
9846
9847 /* check if write cache is enabled */
9848 if (pbuf->write_cache_size) {
9849 size_t max_event_size = pbuf->write_cache_size/MAX_WRITE_CACHE_EVENT_SIZE_DIV;
9851
9852 //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);
9853
9854 /* if this event does not fit into the write cache, flush the write cache */
9855 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9856 //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);
9857
9859
9860 if (!pbuf_guard.is_locked()) {
9861 pbuf->write_cache_mutex.unlock();
9862 return pbuf_guard.get_status();
9863 }
9864
9866
9867 if (pbuf_guard.is_locked()) {
9868 // check if bm_wait_for_free_space() failed to relock the buffer
9869 pbuf_guard.unlock();
9870 }
9871
9872 if (status != BM_SUCCESS) {
9873 pbuf->write_cache_mutex.unlock();
9874 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9875 if (status == BM_NO_MEMORY)
9876 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9877 return status;
9878 }
9879
9880 // write cache must be empty here
9881 assert(pbuf->write_cache_wp == 0);
9882 }
9883
9884 /* write this event into the write cache, if it is not too big and if it fits */
9885 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9886 //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);
9887
9888 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9889
9890 for (int i=0; i<sg_n; i++) {
9891 memcpy(wptr, sg_ptr[i], sg_len[i]);
9892 wptr += sg_len[i];
9893 }
9894
9895 pbuf->write_cache_wp += total_size;
9896
9897 pbuf->write_cache_mutex.unlock();
9898 return BM_SUCCESS;
9899 }
9900 }
9901
9902 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9903 pbuf->write_cache_mutex.unlock();
9904 }
9905
9906 /* we come here only for events that are too big to fit into the cache */
9907
9908 /* lock the buffer */
9910
9911 if (!pbuf_guard.is_locked()) {
9912 return pbuf_guard.get_status();
9913 }
9914
9915 /* calculate some shorthands */
9916 BUFFER_HEADER *pheader = pbuf->buffer_header;
9917
9918#if 0
9920 if (status != BM_SUCCESS) {
9921 printf("bm_send_event: corrupted 111!\n");
9922 abort();
9923 }
9924#endif
9925
9926 /* check if buffer is large enough */
9927 if (total_size >= (size_t)pheader->size) {
9928 pbuf_guard.unlock(); // unlock before cm_msg()
9929 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9930 return BM_NO_MEMORY;
9931 }
9932
9934
9935 if (status != BM_SUCCESS) {
9936 // implicit unlock
9937 return status;
9938 }
9939
9940#if 0
9942 if (status != BM_SUCCESS) {
9943 printf("bm_send_event: corrupted 222!\n");
9944 abort();
9945 }
9946#endif
9947
9948 int old_write_pointer = pheader->write_pointer;
9949
9950 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9951
9952 /* write pointer was incremented, but there should
9953 * always be some free space in the buffer and the
9954 * write pointer should never cacth up to the read pointer:
9955 * the rest of the code gets confused this happens (buffer 100% full)
9956 * as it is write_pointer == read_pointer can be either
9957 * 100% full or 100% empty. My solution: never fill
9958 * the buffer to 100% */
9959 assert(pheader->write_pointer != pheader->read_pointer);
9960
9961 /* send wake up messages to all clients that want this event */
9962 int i;
9963 for (i = 0; i < pheader->max_client_index; i++) {
9964 BUFFER_CLIENT *pc = pheader->client + i;
9965 int request_id = bm_find_first_request_locked(pc, pevent);
9966 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9967 }
9968
9969#if 0
9971 if (status != BM_SUCCESS) {
9972 printf("bm_send_event: corrupted 333!\n");
9973 abort();
9974 }
9975#endif
9976
9977 /* update statistics */
9978 pheader->num_in_events++;
9979 pbuf->count_sent += 1;
9980 pbuf->bytes_sent += total_size;
9981 }
9982#endif /* LOCAL_ROUTINES */
9983
9984 return BM_SUCCESS;
9985}
9986
9987static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
9988{
9989 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
9990
9993
9995
9996 while (1) {
9997 if (timeout_msec == BM_WAIT) {
9998 xtimeout_msec = 1000;
9999 } else if (timeout_msec == BM_NO_WAIT) {
10001 } else {
10002 if (xtimeout_msec > 1000) {
10003 xtimeout_msec = 1000;
10004 }
10005 }
10006
10007 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10008
10009 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10010
10011 if (status == BM_ASYNC_RETURN) {
10012 if (timeout_msec == BM_WAIT) {
10013 // BM_WAIT means wait forever
10014 continue;
10015 } else if (timeout_msec == BM_NO_WAIT) {
10016 // BM_NO_WAIT means do not wait
10017 return status;
10018 } else {
10020 if (now >= time_end) {
10021 // timeout, return BM_ASYNC_RETURN
10022 return status;
10023 }
10024
10026
10027 if (remain < (DWORD)xtimeout_msec) {
10029 }
10030
10031 // keep asking for event...
10032 continue;
10033 }
10034 } else if (status == BM_SUCCESS) {
10035 // success, return BM_SUCCESS
10036 return status;
10037 } else {
10038 // error
10039 return status;
10040 }
10041 }
10042}
10043
10044/********************************************************************/
10062#ifdef LOCAL_ROUTINES
10064{
10065 // NB we come here with write cache locked and buffer locked.
10066
10067 {
10068 INT status = 0;
10069
10070 //printf("bm_flush_cache_locked!\n");
10071
10072 BUFFER* pbuf = pbuf_guard.get_pbuf();
10073 BUFFER_HEADER* pheader = pbuf->buffer_header;
10074
10075 //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);
10076
10077 int old_write_pointer = pheader->write_pointer;
10078
10079 int request_id[MAX_CLIENTS];
10080 for (int i = 0; i < pheader->max_client_index; i++) {
10081 request_id[i] = -1;
10082 }
10083
10084 size_t ask_rp = pbuf->write_cache_rp;
10085 size_t ask_wp = pbuf->write_cache_wp;
10086
10087 if (ask_wp == 0) { // nothing to do
10088 return BM_SUCCESS;
10089 }
10090
10091 if (ask_rp == ask_wp) { // nothing to do
10092 return BM_SUCCESS;
10093 }
10094
10095 assert(ask_rp < ask_wp);
10096
10097 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10098
10099 if (ask_free == 0) { // nothing to do
10100 return BM_SUCCESS;
10101 }
10102
10103#if 0
10105 if (status != BM_SUCCESS) {
10106 printf("bm_flush_cache: corrupted 111!\n");
10107 abort();
10108 }
10109#endif
10110
10112
10113 if (status != BM_SUCCESS) {
10114 return status;
10115 }
10116
10117 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10118 //
10119 // wait_for_free_space() will sleep with all locks released,
10120 // during this time, another thread may call bm_send_event() that will
10121 // add one or more events to the write cache and after wait_for_free_space()
10122 // returns, size of data in cache will be bigger than the amount
10123 // of free space we requested. so we need to keep track of how
10124 // much data we write to the buffer and ask for more data
10125 // if we run short. This is the reason for the big loop
10126 // around wait_for_free_space(). We ask for slightly too little free
10127 // space to make sure all this code is always used and does work. K.O.
10128
10129 if (pbuf->write_cache_wp == 0) {
10130 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10131 return BM_SUCCESS;
10132 }
10133
10134 //size_t written = 0;
10135 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10136 /* loop over all events in cache */
10137
10138 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10139 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10140 size_t total_size = ALIGN8(event_size);
10141
10142#if 0
10143 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",
10144 int(pbuf->write_cache_size),
10145 int(pbuf->write_cache_wp),
10146 int(pbuf->write_cache_rp),
10147 int(pevent->data_size),
10148 int(event_size),
10149 int(total_size),
10150 int(ask_free),
10151 int(written));
10152#endif
10153
10154 // check for crazy event size
10155 assert(total_size >= sizeof(EVENT_HEADER));
10156 assert(total_size <= (size_t)pheader->size);
10157
10158 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10159
10160 /* update statistics */
10161 pheader->num_in_events++;
10162 pbuf->count_sent += 1;
10163 pbuf->bytes_sent += total_size;
10164
10165 /* see comment for the same code in bm_send_event().
10166 * We make sure the buffer is never 100% full */
10167 assert(pheader->write_pointer != pheader->read_pointer);
10168
10169 /* check if anybody has a request for this event */
10170 for (int i = 0; i < pheader->max_client_index; i++) {
10171 BUFFER_CLIENT *pc = pheader->client + i;
10172 int r = bm_find_first_request_locked(pc, pevent);
10173 if (r >= 0) {
10174 request_id[i] = r;
10175 }
10176 }
10177
10178 /* this loop does not loop forever because rp
10179 * is monotonously incremented here. write_cache_wp does
10180 * not change */
10181
10182 pbuf->write_cache_rp += total_size;
10183 //written += total_size;
10184
10185 assert(pbuf->write_cache_rp > 0);
10186 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10187 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10188 }
10189
10190 /* the write cache is now empty */
10191 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10192 pbuf->write_cache_wp = 0;
10193 pbuf->write_cache_rp = 0;
10194
10195 /* check which clients are waiting */
10196 for (int i = 0; i < pheader->max_client_index; i++) {
10197 BUFFER_CLIENT *pc = pheader->client + i;
10198 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10199 }
10200 }
10201
10202 return BM_SUCCESS;
10203}
10204
10205#endif /* LOCAL_ROUTINES */
10206
10207INT bm_flush_cache(int buffer_handle, int timeout_msec)
10208{
10209 if (rpc_is_remote()) {
10210 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10211 }
10212
10213#ifdef LOCAL_ROUTINES
10214 {
10215 INT status = 0;
10216
10217 //printf("bm_flush_cache!\n");
10218
10219 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10220
10221 if (!pbuf)
10222 return status;
10223
10224 if (pbuf->write_cache_size == 0)
10225 return BM_SUCCESS;
10226
10228
10229 if (status != BM_SUCCESS)
10230 return status;
10231
10232 /* check if anything needs to be flushed */
10233 if (pbuf->write_cache_wp == 0) {
10234 pbuf->write_cache_mutex.unlock();
10235 return BM_SUCCESS;
10236 }
10237
10238 /* lock the buffer */
10240
10241 if (!pbuf_guard.is_locked())
10242 return pbuf_guard.get_status();
10243
10245
10246 /* unlock in correct order */
10247
10248 if (pbuf_guard.is_locked()) {
10249 // check if bm_wait_for_free_space() failed to relock the buffer
10250 pbuf_guard.unlock();
10251 }
10252
10253 pbuf->write_cache_mutex.unlock();
10254
10255 return status;
10256 }
10257#endif /* LOCAL_ROUTINES */
10258
10259 return BM_SUCCESS;
10260}
10261
10262#ifdef LOCAL_ROUTINES
10263
10264static 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) {
10266
10267 int max_size = 0;
10268 if (buf_size) {
10269 max_size = *buf_size;
10270 *buf_size = 0;
10271 }
10272
10273 //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);
10274
10275 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10276
10277 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10278
10279 /* look if there is anything in the cache */
10280 if (pbuf->read_cache_size > 0) {
10281
10283
10284 if (status != BM_SUCCESS)
10285 return status;
10286
10287 if (pbuf->read_cache_wp == 0) {
10288
10289 // lock buffer for the first time
10290
10291 if (!pbuf_guard.relock()) {
10292 pbuf->read_cache_mutex.unlock();
10293 return pbuf_guard.get_status();
10294 }
10295
10297 if (status != BM_SUCCESS) {
10298 // unlock in correct order
10299 if (pbuf_guard.is_locked()) {
10300 // check if bm_wait_for_more_events() failed to relock the buffer
10301 pbuf_guard.unlock();
10302 }
10303 pbuf->read_cache_mutex.unlock();
10304 return status;
10305 }
10306
10307 // buffer remains locked here
10308 }
10309 EVENT_HEADER *pevent;
10310 int event_size;
10311 int total_size;
10312 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10313 if (pbuf_guard.is_locked()) {
10314 // do not need to keep the event buffer locked
10315 // when reading from the read cache
10316 pbuf_guard.unlock();
10317 }
10318 //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);
10320 if (buf) {
10321 if (event_size > max_size) {
10322 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);
10325 }
10326
10327 memcpy(buf, pevent, event_size);
10328
10329 if (buf_size) {
10330 *buf_size = event_size;
10331 }
10332 if (convert_flags) {
10333 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10334 }
10335 } else if (bufptr) {
10337 memcpy(*bufptr, pevent, event_size);
10339 } else if (vecptr) {
10340 vecptr->resize(0);
10341 char* cptr = (char*)pevent;
10342 vecptr->assign(cptr, cptr+event_size);
10343 }
10344 bm_incr_read_cache_locked(pbuf, total_size);
10345 pbuf->read_cache_mutex.unlock();
10346 if (dispatch) {
10347 // FIXME need to protect currently dispatched event against
10348 // another thread overwriting it by refilling the read cache
10349 bm_dispatch_event(buffer_handle, pevent);
10350 return BM_MORE_EVENTS;
10351 }
10352 // buffer is unlocked here
10353 return status;
10354 }
10355 pbuf->read_cache_mutex.unlock();
10356 }
10357
10358 /* we come here if the read cache is disabled */
10359 /* we come here if the next event is too big to fit into the read cache */
10360
10361 if (!pbuf_guard.is_locked()) {
10362 if (!pbuf_guard.relock())
10363 return pbuf_guard.get_status();
10364 }
10365
10367
10368 BUFFER_HEADER *pheader = pbuf->buffer_header;
10369
10371
10372 while (1) {
10373 /* loop over events in the event buffer */
10374
10376
10377 if (status != BM_SUCCESS) {
10378 // implicit unlock
10379 return status;
10380 }
10381
10382 /* check if event at current read pointer matches a request */
10383
10384 EVENT_HEADER *pevent;
10385 int event_size;
10386 int total_size;
10387
10388 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10389 if (status == BM_CORRUPTED) {
10390 // implicit unlock
10391 return status;
10392 } else if (status != BM_SUCCESS) {
10393 /* event buffer is empty */
10394 break;
10395 }
10396
10398
10399 if (is_requested) {
10400 //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);
10401
10403
10404 if (buf) {
10405 if (event_size > max_size) {
10406 cm_msg(MERROR, "bm_read_buffer",
10407 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10408 event_size, pheader->name);
10411 }
10412
10413 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10414
10415 if (buf_size) {
10416 *buf_size = event_size;
10417 }
10418
10419 if (convert_flags) {
10420 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10421 }
10422
10423 pbuf->count_read++;
10424 pbuf->bytes_read += event_size;
10425 } else if (dispatch || bufptr) {
10426 assert(event_buffer == NULL); // make sure we only come here once
10429 pbuf->count_read++;
10430 pbuf->bytes_read += event_size;
10431 } else if (vecptr) {
10433 pbuf->count_read++;
10434 pbuf->bytes_read += event_size;
10435 }
10436
10437 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10438 pc->read_pointer = new_read_pointer;
10439
10440 pheader->num_out_events++;
10441 /* exit loop over events */
10442 break;
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 pheader->num_out_events++;
10448 }
10449
10450 /*
10451 If read pointer has been changed, it may have freed up some space
10452 for waiting producers. So check if free space is now more than 50%
10453 of the buffer size and wake waiting producers.
10454 */
10455
10457
10458 pbuf_guard.unlock();
10459
10460 if (dispatch && event_buffer) {
10461 bm_dispatch_event(buffer_handle, event_buffer);
10462 free(event_buffer);
10464 return BM_MORE_EVENTS;
10465 }
10466
10467 if (bufptr && event_buffer) {
10471 }
10472
10473 if (event_buffer) {
10474 free(event_buffer);
10476 }
10477
10478 return status;
10479}
10480
10481#endif
10482
10483static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10484{
10485 //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);
10486
10487 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10488
10489 void *xbuf = NULL;
10490 int xbuf_size = 0;
10491
10492 if (buf) {
10493 xbuf = buf;
10494 xbuf_size = *buf_size;
10495 } else if (ppevent) {
10498 } else if (pvec) {
10499 pvec->resize(_bm_max_event_size);
10500 xbuf = pvec->data();
10501 xbuf_size = pvec->size();
10502 } else {
10503 assert(!"incorrect call to bm_receivent_event_rpc()");
10504 }
10505
10506 int status;
10509
10511
10512 int zbuf_size = xbuf_size;
10513
10514 while (1) {
10515 if (timeout_msec == BM_WAIT) {
10516 xtimeout_msec = 1000;
10517 } else if (timeout_msec == BM_NO_WAIT) {
10519 } else {
10520 if (xtimeout_msec > 1000) {
10521 xtimeout_msec = 1000;
10522 }
10523 }
10524
10526
10528
10529 //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);
10530
10531 if (status == BM_ASYNC_RETURN) {
10532 if (timeout_msec == BM_WAIT) {
10533 // BM_WAIT means wait forever
10534 continue;
10535 } else if (timeout_msec == BM_NO_WAIT) {
10536 // BM_NO_WAIT means do not wait
10537 break;
10538 } else {
10540 if (now >= time_end) {
10541 // timeout, return BM_ASYNC_RETURN
10542 break;
10543 }
10544
10546
10547 if (remain < (DWORD)xtimeout_msec) {
10549 }
10550
10551 // keep asking for event...
10552 continue;
10553 }
10554 } else if (status == BM_SUCCESS) {
10555 // success, return BM_SUCCESS
10556 break;
10557 }
10558
10559 // RPC error
10560
10561 if (buf) {
10562 *buf_size = 0;
10563 } else if (ppevent) {
10564 free(*ppevent);
10565 *ppevent = NULL;
10566 } else if (pvec) {
10567 pvec->resize(0);
10568 } else {
10569 assert(!"incorrect call to bm_receivent_event_rpc()");
10570 }
10571
10572 return status;
10573 }
10574
10575 // status is BM_SUCCESS or BM_ASYNC_RETURN
10576
10577 if (buf) {
10578 *buf_size = zbuf_size;
10579 } else if (ppevent) {
10580 // nothing to do
10581 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10582 } else if (pvec) {
10583 pvec->resize(zbuf_size);
10584 } else {
10585 assert(!"incorrect call to bm_receivent_event_rpc()");
10586 }
10587
10588 return status;
10589}
10590
10591/********************************************************************/
10650INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10651 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10652 if (rpc_is_remote()) {
10653 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10654 }
10655#ifdef LOCAL_ROUTINES
10656 {
10658
10659 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10660
10661 if (!pbuf)
10662 return status;
10663
10664 int convert_flags = rpc_get_convert_flags();
10665
10666 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10667 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10668 return status;
10669 }
10670#else /* LOCAL_ROUTINES */
10671
10672 return BM_SUCCESS;
10673#endif
10674}
10675
10676/********************************************************************/
10732 if (rpc_is_remote()) {
10733 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10734 }
10735#ifdef LOCAL_ROUTINES
10736 {
10738
10739 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10740
10741 if (!pbuf)
10742 return status;
10743
10744 int convert_flags = rpc_get_convert_flags();
10745
10746 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10747 }
10748#else /* LOCAL_ROUTINES */
10749
10750 return BM_SUCCESS;
10751#endif
10752}
10753
10754/********************************************************************/
10809INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10810 if (rpc_is_remote()) {
10811 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10812 }
10813#ifdef LOCAL_ROUTINES
10814 {
10816
10817 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10818
10819 if (!pbuf)
10820 return status;
10821
10822 int convert_flags = rpc_get_convert_flags();
10823
10824 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10825 }
10826#else /* LOCAL_ROUTINES */
10827 return BM_SUCCESS;
10828#endif
10829}
10830
10831#ifdef LOCAL_ROUTINES
10832
10834{
10835 /* clear read cache */
10836 if (pbuf->read_cache_size > 0) {
10837
10839
10840 if (status != BM_SUCCESS)
10841 return status;
10842
10843 pbuf->read_cache_rp = 0;
10844 pbuf->read_cache_wp = 0;
10845
10846 pbuf->read_cache_mutex.unlock();
10847 }
10848
10850
10851 if (!pbuf_guard.is_locked())
10852 return pbuf_guard.get_status();
10853
10854 BUFFER_HEADER *pheader = pbuf->buffer_header;
10855
10856 /* forward read pointer to global write pointer */
10858 pclient->read_pointer = pheader->write_pointer;
10859
10860 return BM_SUCCESS;
10861}
10862
10863#endif /* LOCAL_ROUTINES */
10864
10865/********************************************************************/
10874INT bm_skip_event(INT buffer_handle) {
10875 if (rpc_is_remote())
10876 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
10877
10878#ifdef LOCAL_ROUTINES
10879 {
10880 int status = 0;
10881
10882 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
10883
10884 if (!pbuf)
10885 return status;
10886
10887 return bm_skip_event(pbuf);
10888 }
10889#endif
10890
10891 return BM_SUCCESS;
10892}
10893
10894#ifdef LOCAL_ROUTINES
10895/********************************************************************/
10902static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
10903 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
10904
10905 /* return immediately if no callback routine is defined */
10906 if (!pbuf->callback)
10907 return BM_SUCCESS;
10908
10909 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
10910}
10911
10912/********************************************************************/
10918static INT bm_push_event(const char *buffer_name)
10919{
10920 std::vector<BUFFER*> mybuffers;
10921
10922 gBuffersMutex.lock();
10924 gBuffersMutex.unlock();
10925
10926 for (size_t i = 0; i < mybuffers.size(); i++) {
10927 BUFFER *pbuf = mybuffers[i];
10928 if (!pbuf || !pbuf->attached)
10929 continue;
10930 // FIXME: unlocked read access to pbuf->buffer_name!
10931 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
10932 return bm_push_buffer(pbuf, i + 1);
10933 }
10934 }
10935
10936 return BM_INVALID_HANDLE;
10937}
10938
10939#else
10940
10941static INT bm_push_event(const char *buffer_name)
10942{
10943 return BM_SUCCESS;
10944}
10945
10946#endif /* LOCAL_ROUTINES */
10947
10948/********************************************************************/
10955#ifdef LOCAL_ROUTINES
10956 {
10957 INT status = 0;
10958 BOOL bMore;
10959 DWORD start_time;
10960 //static DWORD last_time = 0;
10961
10962 /* if running as a server, buffer checking is done by client
10963 via ASYNC bm_receive_event */
10964 if (rpc_is_mserver()) {
10965 return FALSE;
10966 }
10967
10968 bMore = FALSE;
10969 start_time = ss_millitime();
10970
10971 std::vector<BUFFER*> mybuffers;
10972
10973 gBuffersMutex.lock();
10975 gBuffersMutex.unlock();
10976
10977 /* go through all buffers */
10978 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
10980
10981 if (!pbuf || !pbuf->attached)
10982 continue;
10983
10984 //int count_loops = 0;
10985 while (1) {
10986 if (pbuf->attached) {
10987 /* one bm_push_event could cause a run stop and a buffer close, which
10988 * would crash the next call to bm_push_event(). So check for valid
10989 * buffer on each call */
10990
10991 /* this is what happens:
10992 * bm_push_buffer() may call a user callback function
10993 * user callback function may indirectly call bm_close() of this buffer,
10994 * i.e. if it stops the run,
10995 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
10996 * here we will see pbuf->attched is false and quit this loop
10997 */
10998
10999 status = bm_push_buffer(pbuf, idx + 1);
11000
11001 if (status == BM_CORRUPTED) {
11002 return status;
11003 }
11004
11005 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11006
11007 if (status != BM_MORE_EVENTS) {
11008 //DWORD t = ss_millitime() - start_time;
11009 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11010 break;
11011 }
11012
11013 // count_loops++;
11014 }
11015
11016 // NB: this code has a logic error: if 2 buffers always have data,
11017 // this timeout will cause us to exit reading the 1st buffer
11018 // after 1000 msec, then we read the 2nd buffer exactly once,
11019 // and exit the loop because the timeout is still active -
11020 // we did not reset "start_time" when we started reading
11021 // from the 2nd buffer. Result is that we always read all
11022 // the data in a loop from the 1st buffer, but read just
11023 // one event from the 2nd buffer, resulting in severe unfairness.
11024
11025 /* stop after one second */
11026 DWORD t = ss_millitime() - start_time;
11027 if (t > 1000) {
11028 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11029 bMore = TRUE;
11030 break;
11031 }
11032 }
11033 }
11034
11035 //last_time = start_time;
11036
11037 return bMore;
11038
11039 }
11040#else /* LOCAL_ROUTINES */
11041
11042 return FALSE;
11043
11044#endif
11045}
11046
11047/********************************************************************/
11049/********************************************************************\
11050
11051 Routine: bm_notify_client
11052
11053 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11054 the connected client. Used by mserver to relay the BM_MSG
11055 buffer message from local UDP socket to the remote connected client.
11056
11057 Input:
11058 char *buffer_name Name of buffer
11059 int client_socket Network socket to client
11060
11061 Output:
11062 none
11063
11064 Function value:
11065 BM_SUCCESS Successful completion
11066
11067\********************************************************************/
11068{
11069 static DWORD last_time = 0;
11071
11072 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11073
11074 BUFFER* fbuf = NULL;
11075
11076 gBuffersMutex.lock();
11077
11078 for (size_t i = 0; i < gBuffers.size(); i++) {
11079 BUFFER* pbuf = gBuffers[i];
11080 if (!pbuf || !pbuf->attached)
11081 continue;
11082 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11083 fbuf = pbuf;
11084 break;
11085 }
11086 }
11087
11088 gBuffersMutex.unlock();
11089
11090 if (!fbuf)
11091 return BM_INVALID_HANDLE;
11092
11093 /* don't send notification if client has no callback defined
11094 to receive events -> client calls bm_receive_event manually */
11095 if (!fbuf->callback)
11096 return DB_SUCCESS;
11097
11098 int convert_flags = rpc_get_convert_flags();
11099
11100 /* only send notification once each 500ms */
11101 if (now - last_time < 500)
11102 return DB_SUCCESS;
11103
11104 last_time = now;
11105
11106 char buffer[32];
11107 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11108
11109 nc->header.routine_id = MSG_BM;
11110 nc->header.param_size = 0;
11111
11112 if (convert_flags) {
11115 }
11116
11117 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11118
11119 /* send the update notification to the client */
11120 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11121
11122 return BM_SUCCESS;
11123}
11124
11125/********************************************************************/
11127/********************************************************************\
11128
11129 Routine: bm_poll_event
11130
11131 Purpose: Poll an event from a remote server. Gets called by
11132 rpc_client_dispatch() and by cm_yield()
11133
11134 Function value:
11135 BM_SUCCESS At least one event was received and dispatched
11136 BM_ASYNC_RETURN No events received
11137 SS_ABORT Network connection broken
11138
11139\********************************************************************/
11140{
11142
11143 //printf("bm_poll_event!\n");
11144
11145 DWORD start_time = ss_millitime();
11146
11147 std::vector<char> vec;
11148
11149 /* loop over all requests */
11150 _request_list_mutex.lock();
11151 bool locked = true;
11152 size_t n = _request_list.size();
11153 for (size_t i = 0; i < n; i++) {
11154 if (!locked) {
11155 _request_list_mutex.lock();
11156 locked = true;
11157 }
11158 /* continue if no dispatcher set (manual bm_receive_event) */
11159 if (_request_list[i].dispatcher == NULL)
11160 continue;
11161
11162 int buffer_handle = _request_list[i].buffer_handle;
11163
11164 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11165 _request_list_mutex.unlock();
11166 locked = false;
11167
11168 do {
11169 /* receive event */
11170 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11171
11172 //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());
11173
11174 /* call user function if successful */
11175 if (status == BM_SUCCESS) {
11176 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11178 }
11179
11180 /* break if no more events */
11181 if (status == BM_ASYNC_RETURN)
11182 break;
11183
11184 /* break if corrupted event buffer */
11185 if (status == BM_TRUNCATED) {
11186 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());
11187 }
11188
11189 /* break if corrupted event buffer */
11190 if (status == BM_CORRUPTED)
11191 return SS_ABORT;
11192
11193 /* break if server died */
11194 if (status == RPC_NET_ERROR) {
11195 return SS_ABORT;
11196 }
11197
11198 /* stop after one second */
11199 if (ss_millitime() - start_time > 1000) {
11200 break;
11201 }
11202
11203 } while (TRUE);
11204 }
11205
11206 if (locked)
11207 _request_list_mutex.unlock();
11208
11210 return BM_SUCCESS;
11211 else
11212 return BM_ASYNC_RETURN;
11213}
11214
11215/********************************************************************/
11241 if (rpc_is_remote())
11243
11244#ifdef LOCAL_ROUTINES
11245 {
11246 std::vector<BUFFER*> mybuffers;
11247
11248 gBuffersMutex.lock();
11250 gBuffersMutex.unlock();
11251
11252 /* go through all buffers */
11253 for (BUFFER* pbuf : mybuffers) {
11254 if (!pbuf)
11255 continue;
11256 if (!pbuf->attached)
11257 continue;
11258
11259 int status = bm_skip_event(pbuf);
11260 if (status != BM_SUCCESS)
11261 return status;
11262 }
11263 }
11264#endif /* LOCAL_ROUTINES */
11265
11266 return BM_SUCCESS;
11267}
11268
11270#ifndef DOXYGEN_SHOULD_SKIP_THIS
11271
11272#define MAX_DEFRAG_EVENTS 10
11273
11280
11282
11283/********************************************************************/
11284static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11285 EVENT_HEADER *pevent, void *pdata,
11286 EVENT_HANDLER *dispatcher)
11287/********************************************************************\
11288
11289 Routine: bm_defragment_event
11290
11291 Purpose: Called internally from the event receiving routines
11292 bm_push_event and bm_poll_event to recombine event
11293 fragments and call the user callback routine upon
11294 completion.
11295
11296 Input:
11297 HNDLE buffer_handle Handle for the buffer containing event
11298 HNDLE request_id Handle for event request
11299 EVENT_HEADER *pevent Pointer to event header
11300 void *pata Pointer to event data
11301 dispatcher() User callback routine
11302
11303 Output:
11304 <calls dispatcher() after successfull recombination of event>
11305
11306 Function value:
11307 void
11308
11309\********************************************************************/
11310{
11311 INT i;
11312
11313 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11314 /*---- start new event ----*/
11315
11316 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11317
11318 /* check if fragments already stored */
11319 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11320 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11321 break;
11322
11323 if (i < MAX_DEFRAG_EVENTS) {
11324 free(defrag_buffer[i].pevent);
11327 cm_msg(MERROR, "bm_defragement_event",
11328 "Received new event with ID %d while old fragments were not completed",
11329 (pevent->event_id & 0x0FFF));
11330 }
11331
11332 /* search new slot */
11333 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11334 if (defrag_buffer[i].event_id == 0)
11335 break;
11336
11337 if (i == MAX_DEFRAG_EVENTS) {
11338 cm_msg(MERROR, "bm_defragment_event",
11339 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11340 return;
11341 }
11342
11343 /* check event size */
11344 if (pevent->data_size != sizeof(DWORD)) {
11345 cm_msg(MERROR, "bm_defragment_event",
11346 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11347 pevent->data_size, (int) sizeof(DWORD));
11348 return;
11349 }
11350
11351 /* setup defragment buffer */
11352 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11356
11357 if (defrag_buffer[i].pevent == NULL) {
11359 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11360 return;
11361 }
11362
11363 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11366
11367 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11368 // pevent->serial_number, defrag_buffer[i].data_size);
11369
11370 return;
11371 }
11372
11373 /* search buffer for that event */
11374 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11375 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11376 break;
11377
11378 if (i == MAX_DEFRAG_EVENTS) {
11379 /* no buffer available -> no first fragment received */
11380 cm_msg(MERROR, "bm_defragement_event",
11381 "Received fragment without first fragment (ID %d) Ser#:%d",
11382 pevent->event_id & 0x0FFF, pevent->serial_number);
11383 return;
11384 }
11385
11386 /* add fragment to buffer */
11388 free(defrag_buffer[i].pevent);
11391 cm_msg(MERROR, "bm_defragement_event",
11392 "Received fragments with more data (%d) than event size (%d)",
11393 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11394 return;
11395 }
11396
11397 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11398 defrag_buffer[i].received, pdata, pevent->data_size);
11399
11400 defrag_buffer[i].received += pevent->data_size;
11401
11402 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11403 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11404
11405 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11406 /* event complete */
11407 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11408 free(defrag_buffer[i].pevent);
11411 }
11412}
11413
11415#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11416
/* end of bmfunctionc */
11419
11425/********************************************************************\
11426* *
11427* RPC functions *
11428* *
11429\********************************************************************/
11430
11432{
11433public:
11434 std::atomic_bool connected{false}; /* socket is connected */
11435 std::string host_name; /* server name */
11436 int port = 0; /* server port */
11437 std::mutex mutex; /* connection lock */
11438 int index = 0; /* index in the connection array */
11439 std::string client_name; /* name of remote client */
11440 int send_sock = 0; /* tcp socket */
11441 int remote_hw_type = 0; /* remote hardware type */
11442 int rpc_timeout = 0; /* timeout in milliseconds */
11443
11444 void print() {
11445 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11446 index,
11447 client_name.c_str(),
11448 host_name.c_str(),
11449 port,
11450 send_sock,
11451 int(connected),
11452 rpc_timeout);
11453 }
11454
11456 if (send_sock > 0) {
11458 }
11459 connected = false;
11460 }
11461};
11462
11463/* globals */
11464
11465//
11466// locking rules for client connections:
11467//
11468// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11469// 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
11470// lock individual connection, check c->connected, work on the connection, unlock connection
11471//
11472// ok to access without locking client connection:
11473//
11474// - c->connected (std::atomic, but must recheck it after taking the lock)
11475// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11476//
11477// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11478// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11479//
11480// lifetime of client connections:
11481//
11482// - client connection slots are allocated by rpc_client_connect()
11483// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11484// - client slots marked NULL are free and will be reused by rpc_client_connect()
11485// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11486// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11487// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11488// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11489// client call will see an empty slot and return an error
11490// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11491// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11492// only when one is sure no other threads are running concurrent rpc client calls.
11493//
11494
11496static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11497
11498static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11499static bool _rpc_is_remote = false;
11500
11501//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11502static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11503static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11504
11506{
11507 assert(idx >= 0);
11508 assert(idx < (int)_server_acceptions.size());
11509 assert(_server_acceptions[idx] != NULL);
11510 return _server_acceptions[idx];
11511}
11512
11517
11519{
11520 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11521 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11522 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11523 return _server_acceptions[idx];
11524 }
11525 }
11526
11528
11529 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11530 if (_server_acceptions[idx] == NULL) {
11531 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11532 _server_acceptions[idx] = sa;
11533 return _server_acceptions[idx];
11534 }
11535 }
11536
11537 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11538 _server_acceptions.push_back(sa);
11539
11540 return sa;
11541}
11542
11544{
11545 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11546
11547 if (is_mserver) {
11548 assert(_mserver_acception == this);
11550 is_mserver = false;
11551 }
11552
11553 /* close server connection */
11554 if (recv_sock)
11556 if (send_sock)
11558 if (event_sock)
11560
11561 /* free TCP cache */
11562 if (net_buffer) {
11563 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11564 free(net_buffer);
11565 net_buffer = NULL;
11566 net_buffer_size = 0;
11567 }
11568
11569 /* mark this entry as invalid */
11570 clear();
11571}
11572
11573static std::vector<RPC_LIST> rpc_list;
11574static std::mutex rpc_list_mutex;
11575
11577
11578
11579/********************************************************************\
11580* conversion functions *
11581\********************************************************************/
11582
11583void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11584 *convert_flags = 0;
11585
11586 /* big/little endian conversion */
11587 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11588 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11589 && (hw_type & DRI_BIG_ENDIAN)))
11590 *convert_flags |= CF_ENDIAN;
11591
11592 /* float conversion between IEEE and VAX G */
11593 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11594 *convert_flags |= CF_VAX2IEEE;
11595
11596 /* float conversion between VAX G and IEEE */
11597 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11598 *convert_flags |= CF_IEEE2VAX;
11599
11601 //if (remote_hw_type & DR_ASCII)
11602 // *convert_flags |= CF_ASCII;
11603}
11604
11605/********************************************************************/
11609
11610/********************************************************************/
11612 unsigned short int lo, hi;
11613
11614 /* swap hi and lo word */
11615 lo = *((short int *) (var) + 1);
11616 hi = *((short int *) (var));
11617
11618 /* correct exponent */
11619 if (lo != 0)
11620 lo += 0x100;
11621
11622 *((short int *) (var) + 1) = hi;
11623 *((short int *) (var)) = lo;
11624}
11625
11627 unsigned short int lo, hi;
11628
11629 /* swap hi and lo word */
11630 lo = *((short int *) (var) + 1);
11631 hi = *((short int *) (var));
11632
11633 /* correct exponent */
11634 if (hi != 0)
11635 hi -= 0x100;
11636
11637 *((short int *) (var) + 1) = hi;
11638 *((short int *) (var)) = lo;
11639
11640}
11641
11643 unsigned short int i1, i2, i3, i4;
11644
11645 /* swap words */
11646 i1 = *((short int *) (var) + 3);
11647 i2 = *((short int *) (var) + 2);
11648 i3 = *((short int *) (var) + 1);
11649 i4 = *((short int *) (var));
11650
11651 /* correct exponent */
11652 if (i4 != 0)
11653 i4 -= 0x20;
11654
11655 *((short int *) (var) + 3) = i4;
11656 *((short int *) (var) + 2) = i3;
11657 *((short int *) (var) + 1) = i2;
11658 *((short int *) (var)) = i1;
11659}
11660
11662 unsigned short int i1, i2, i3, i4;
11663
11664 /* swap words */
11665 i1 = *((short int *) (var) + 3);
11666 i2 = *((short int *) (var) + 2);
11667 i3 = *((short int *) (var) + 1);
11668 i4 = *((short int *) (var));
11669
11670 /* correct exponent */
11671 if (i1 != 0)
11672 i1 += 0x20;
11673
11674 *((short int *) (var) + 3) = i4;
11675 *((short int *) (var) + 2) = i3;
11676 *((short int *) (var) + 1) = i2;
11677 *((short int *) (var)) = i1;
11678}
11679
11680/********************************************************************/
11681void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11682
11683 if (convert_flags & CF_ENDIAN) {
11684 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11685 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11686 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11687 }
11688
11689 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11690 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11691 if (tid == TID_FLOAT)
11692 rpc_ieee2vax_float((float *) data);
11693 if (tid == TID_DOUBLE)
11694 rpc_ieee2vax_double((double *) 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_vax2ieee_float((float *) data);
11701 if (tid == TID_DOUBLE)
11702 rpc_vax2ieee_double((double *) data);
11703 }
11704}
11705
11706void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11707/********************************************************************\
11708
11709 Routine: rpc_convert_data
11710
11711 Purpose: Convert data format between differenct computers
11712
11713 Input:
11714 void *data Pointer to data
11715 INT tid Type ID of data, one of TID_xxx
11716 INT flags Combination of following flags:
11717 RPC_IN: data is input parameter
11718 RPC_OUT: data is output variable
11719 RPC_FIXARRAY, RPC_VARARRAY: data is array
11720 of "size" bytes (see next param.)
11721 RPC_OUTGOING: data is outgoing
11722 INT total_size Size of bytes of data. Used for variable
11723 length arrays.
11724 INT convert_flags Flags for data conversion
11725
11726 Output:
11727 void *data Is converted according to _convert_flag
11728 value
11729
11730 Function value:
11731 RPC_SUCCESS Successful completion
11732
11733\********************************************************************/
11734{
11735 /* convert array */
11736 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11737 int single_size = rpc_tid_size(tid);
11738 /* don't convert TID_ARRAY & TID_STRUCT */
11739 if (single_size == 0)
11740 return;
11741
11742 int n = total_size / single_size;
11743
11744 for (int i = 0; i < n; i++) {
11745 char* p = (char *) data + (i * single_size);
11746 rpc_convert_single(p, tid, flags, convert_flags);
11747 }
11748 } else {
11749 rpc_convert_single(data, tid, flags, convert_flags);
11750 }
11751}
11752
11753/********************************************************************\
11754* type ID functions *
11755\********************************************************************/
11756
11758 if (id >= 0 && id < TID_LAST)
11759 return tid_size[id];
11760
11761 return 0;
11762}
11763
11764const char *rpc_tid_name(INT id) {
11765 if (id >= 0 && id < TID_LAST)
11766 return tid_name[id];
11767 else
11768 return "<unknown>";
11769}
11770
11771const char *rpc_tid_name_old(INT id) {
11772 if (id >= 0 && id < TID_LAST)
11773 return tid_name_old[id];
11774 else
11775 return "<unknown>";
11776}
11777
11778int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11779{
11780 for (int i=0; i<TID_LAST; i++) {
11781 if (strcmp(name, tid_name[i]) == 0)
11782 return i;
11783 }
11784
11785 for (int i=0; i<TID_LAST; i++) {
11786 if (strcmp(name, tid_name_old[i]) == 0)
11787 return i;
11788 }
11789
11790 return 0;
11791}
11792
11793/********************************************************************\
11794* client functions *
11795\********************************************************************/
11796
11797/********************************************************************/
11815
11816/********************************************************************/
11828{
11829 for (int i = 0; new_list[i].id != 0; i++) {
11830 /* check valid ID for user functions */
11831 if (new_list != rpc_get_internal_list(0) &&
11833 || new_list[i].id > RPC_MAX_ID)) {
11834 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11835 }
11836 }
11837
11838 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11839
11840 /* check double defined functions */
11841 for (int i = 0; new_list[i].id != 0; i++) {
11842 for (size_t j = 0; j < rpc_list.size(); j++) {
11843 if (rpc_list[j].id == new_list[i].id) {
11844 return RPC_DOUBLE_DEFINED;
11845 }
11846 }
11847 }
11848
11849 /* append new functions */
11850 for (int i = 0; new_list[i].id != 0; i++) {
11851 RPC_LIST e = new_list[i];
11852
11853 /* set default dispatcher */
11854 if (e.dispatch == NULL) {
11855 e.dispatch = func;
11856 }
11857
11858 rpc_list.push_back(e);
11859 }
11860
11861 return RPC_SUCCESS;
11862}
11863
11864
11865
11867#ifndef DOXYGEN_SHOULD_SKIP_THIS
11868
11869/********************************************************************/
11871/********************************************************************\
11872
11873 Routine: rpc_deregister_functions
11874
11875 Purpose: Free memory of previously registered functions
11876
11877 Input:
11878 none
11879
11880 Output:
11881 none
11882
11883 Function value:
11884 RPC_SUCCESS Successful completion
11885
11886\********************************************************************/
11887{
11888 rpc_list_mutex.lock();
11889 rpc_list.clear();
11890 rpc_list_mutex.unlock();
11891
11892 return RPC_SUCCESS;
11893}
11894
11895
11896/********************************************************************/
11897INT rpc_register_function(INT id, INT(*func)(INT, void **))
11898/********************************************************************\
11899
11900 Routine: rpc_register_function
11901
11902 Purpose: Replace a dispatch function for a specific rpc routine
11903
11904 Input:
11905 INT id RPC ID
11906 INT *func New dispatch function
11907
11908 Output:
11909 <implicit: func gets copied to rpc_list>
11910
11911 Function value:
11912 RPC_SUCCESS Successful completion
11913 RPC_INVALID_ID RPC ID not found
11914
11915\********************************************************************/
11916{
11917 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11918
11919 for (size_t i = 0; i < rpc_list.size(); i++) {
11920 if (rpc_list[i].id == id) {
11921 rpc_list[i].dispatch = func;
11922 return RPC_SUCCESS;
11923 }
11924 }
11925
11926 return RPC_INVALID_ID;
11927}
11928
11929/********************************************************************/
11930
11931static int handle_msg_odb(int n, const NET_COMMAND *nc) {
11932 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
11933 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
11934 /* update a changed record */
11935 HNDLE hDB = *((INT *) nc->param);
11936 HNDLE hKeyRoot = *((INT *) nc->param + 1);
11937 HNDLE hKey = *((INT *) nc->param + 2);
11938 int index = *((INT *) nc->param + 3);
11940 }
11941 return CM_VERSION_MISMATCH;
11942}
11943
11944/********************************************************************/
11946/********************************************************************\
11947
11948 Routine: rpc_client_dispatch
11949
11950 Purpose: Receive data from the mserver: watchdog and buffer notification messages
11951
11952\********************************************************************/
11953{
11954 INT status = 0;
11955 char net_buffer[256];
11956
11957 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11958 if (n <= 0)
11959 return SS_ABORT;
11960
11961 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
11962
11963 if (nc->header.routine_id == MSG_ODB) {
11964 status = handle_msg_odb(n, nc);
11965 } else if (nc->header.routine_id == MSG_WATCHDOG) {
11966 nc->header.routine_id = 1;
11967 nc->header.param_size = 0;
11968 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11970 } else if (nc->header.routine_id == MSG_BM) {
11972 struct timeval timeout;
11973
11974 //printf("rpc_client_dispatch: received MSG_BM!\n");
11975
11976 /* receive further messages to empty TCP queue */
11977 do {
11978 FD_ZERO(&readfds);
11979 FD_SET(sock, &readfds);
11980
11981 timeout.tv_sec = 0;
11982 timeout.tv_usec = 0;
11983
11984 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
11985
11986 if (FD_ISSET(sock, &readfds)) {
11987 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11988 if (n <= 0)
11989 return SS_ABORT;
11990
11991 if (nc->header.routine_id == MSG_ODB) {
11992 status = handle_msg_odb(n, nc);
11993 } else if (nc->header.routine_id == MSG_WATCHDOG) {
11994 nc->header.routine_id = 1;
11995 nc->header.param_size = 0;
11996 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11998 }
11999 }
12000
12001 } while (FD_ISSET(sock, &readfds));
12002
12003 /* poll event from server */
12005 }
12006
12007 return status;
12008}
12009
12010
12011/********************************************************************/
12012INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12013/********************************************************************\
12014
12015 Routine: rpc_client_connect
12016
12017 Purpose: Establish a network connection to a remote client
12018
12019 Input:
12020 char *host_name IP address of host to connect to.
12021 INT port TPC port to connect to.
12022 char *clinet_name Client program name
12023
12024 Output:
12025 HNDLE *hConnection Handle for new connection which can be used
12026 in future rpc_call(hConnection....) calls
12027
12028 Function value:
12029 RPC_SUCCESS Successful completion
12030 RPC_NET_ERROR Error in socket call
12031 RPC_NO_CONNECTION Maximum number of connections reached
12032 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12033
12034\********************************************************************/
12035{
12036 INT i, status;
12037 bool debug = false;
12038
12039 /* check if cm_connect_experiment was called */
12040 if (_client_name.length() == 0) {
12041 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12042 return RPC_NOT_REGISTERED;
12043 }
12044
12045 /* refuse connection to port 0 */
12046 if (port == 0) {
12047 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12048 return RPC_NET_ERROR;
12049 }
12050
12052
12053 static std::mutex gHostnameMutex;
12054
12055 {
12056 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12057
12058 if (debug) {
12059 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12060 for (size_t i = 0; i < _client_connections.size(); i++) {
12061 if (_client_connections[i]) {
12062 printf("client connection %d: ", (int)i);
12063 _client_connections[i]->print();
12064 printf("\n");
12065 }
12066 }
12067 }
12068
12069 // slot with index 0 is not used, fill it with a NULL
12070
12071 if (_client_connections.empty()) {
12072 _client_connections.push_back(NULL);
12073 }
12074
12075 bool hostname_locked = false;
12076
12077 /* check if connection already exists */
12078 for (size_t i = 1; i < _client_connections.size(); i++) {
12080 if (c && c->connected) {
12081
12082 if (!hostname_locked) {
12083 gHostnameMutex.lock();
12084 hostname_locked = true;
12085 }
12086
12087 if ((c->host_name == host_name) && (c->port == port)) {
12088 // NB: we must release the hostname lock before taking
12089 // c->mutex to avoid a locking order inversion deadlock:
12090 // later on we lock the hostname mutex while holding the c->mutex
12091 gHostnameMutex.unlock();
12092 hostname_locked = false;
12093 std::lock_guard<std::mutex> cguard(c->mutex);
12094 // check if socket is still connected
12095 if (c->connected) {
12096 // found connection slot with matching hostname and port number
12097 status = ss_socket_wait(c->send_sock, 0);
12098 if (status == SS_TIMEOUT) { // yes, still connected and empty
12099 // so reuse it connection
12100 *hConnection = c->index;
12101 if (debug) {
12102 printf("already connected: ");
12103 c->print();
12104 printf("\n");
12105 }
12106 // implicit unlock of c->mutex
12107 // gHostnameLock is not locked here
12108 return RPC_SUCCESS;
12109 }
12110 //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);
12111 c->close_locked();
12112 }
12113 // implicit unlock of c->mutex
12114 }
12115 }
12116 }
12117
12118 if (hostname_locked) {
12119 gHostnameMutex.unlock();
12120 hostname_locked = false;
12121 }
12122
12123 // only start reusing connections once we have
12124 // a good number of slots allocated.
12125 if (_client_connections.size() > 10) {
12126 static int last_reused = 0;
12127
12128 int size = _client_connections.size();
12129 for (int j = 1; j < size; j++) {
12130 int i = (last_reused + j) % size;
12131 if (_client_connections[i] && !_client_connections[i]->connected) {
12133 if (debug) {
12134 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12135 c->print();
12136 printf("\n");
12137 }
12138 last_reused = i;
12139 break;
12140 }
12141 }
12142 }
12143
12144 // no slots to reuse, allocate a new slot.
12145 if (!c) {
12147
12148 // if empty slot not found, add to end of array
12149 c->index = _client_connections.size();
12150 _client_connections.push_back(c);
12151
12152 if (debug) {
12153 printf("new connection appended to array: ");
12154 c->print();
12155 printf("\n");
12156 }
12157 }
12158
12159 c->mutex.lock();
12160 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12161
12162 // done with the array of connections
12163 // implicit unlock of _client_connections_mutex
12164 }
12165
12166 // locked connection slot for new connection
12167 assert(c != NULL);
12168
12169 std::string errmsg;
12170
12171 /* create a new socket for connecting to remote server */
12172 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12173 if (status != SS_SUCCESS) {
12174 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12175 c->mutex.unlock();
12176 return RPC_NET_ERROR;
12177 }
12178
12179 gHostnameMutex.lock();
12180
12181 c->host_name = host_name;
12182 c->port = port;
12183
12184 gHostnameMutex.unlock();
12185
12186 c->client_name = client_name;
12187 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12188
12189 /* set TCP_NODELAY option for better performance */
12190 i = 1;
12191 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12192
12193 /* send local computer info */
12194 std::string local_prog_name = rpc_get_name();
12195 std::string local_host_name = ss_gethostname();
12196
12197 int hw_type = rpc_get_hw_type();
12198
12199 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12200
12201 int size = cstr.length() + 1;
12202 i = send(c->send_sock, cstr.c_str(), size, 0);
12203 if (i < 0 || i != size) {
12204 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12205 c->mutex.unlock();
12206 return RPC_NET_ERROR;
12207 }
12208
12209 bool restore_watchdog_timeout = false;
12211 DWORD watchdog_timeout;
12212 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12213
12214 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12215
12216 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12219 }
12220
12221 char str[256];
12222
12223 /* receive remote computer info */
12224 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12225
12227 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12228 }
12229
12230 if (i <= 0) {
12231 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12232 c->close_locked();
12233 c->mutex.unlock();
12234 return RPC_NET_ERROR;
12235 }
12236
12237 int remote_hw_type = 0;
12238 char remote_version[32];
12239 remote_version[0] = 0;
12240 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12241
12242 c->remote_hw_type = remote_hw_type;
12243
12244 /* print warning if version patch level doesn't agree */
12245 char v1[32];
12246 mstrlcpy(v1, remote_version, sizeof(v1));
12247 if (strchr(v1, '.'))
12248 if (strchr(strchr(v1, '.') + 1, '.'))
12249 *strchr(strchr(v1, '.') + 1, '.') = 0;
12250
12251 mstrlcpy(str, cm_get_version(), sizeof(str));
12252 if (strchr(str, '.'))
12253 if (strchr(strchr(str, '.') + 1, '.'))
12254 *strchr(strchr(str, '.') + 1, '.') = 0;
12255
12256 if (strcmp(v1, str) != 0) {
12257 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12258 }
12259
12260 c->connected = true;
12261
12262 *hConnection = c->index;
12263
12264 c->mutex.unlock();
12265
12266 return RPC_SUCCESS;
12267}
12268
12269/********************************************************************/
12271/********************************************************************\
12272
12273 Routine: rpc_client_check
12274
12275 Purpose: Check all client connections if remote client closed link
12276
12277\********************************************************************/
12278{
12279#if 0
12280 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12281 if (_client_connection[i].send_sock != 0)
12282 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);
12283#endif
12284
12285 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12286
12287 /* check for broken connections */
12288 for (unsigned i = 0; i < _client_connections.size(); i++) {
12290 if (c && c->connected) {
12291 std::lock_guard<std::mutex> cguard(c->mutex);
12292
12293 if (!c->connected) {
12294 // implicit unlock
12295 continue;
12296 }
12297
12298 //printf("rpc_client_check: connection %d: ", i);
12299 //c->print();
12300 //printf("\n");
12301
12302 int ok = 0;
12303
12305 FD_ZERO(&readfds);
12306 FD_SET(c->send_sock, &readfds);
12307
12308 struct timeval timeout;
12309 timeout.tv_sec = 0;
12310 timeout.tv_usec = 0;
12311
12312 int status;
12313
12314#ifdef OS_WINNT
12315 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12316#else
12317 do {
12318 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12319 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12320#endif
12321
12322 if (!FD_ISSET(c->send_sock, &readfds)) {
12323 // implicit unlock
12324 continue;
12325 }
12326
12327 char buffer[64];
12328
12329 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12330 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12331
12332 if (status < 0) {
12333#ifndef OS_WINNT
12334 if (errno == EAGAIN) { // still connected
12335 ok = 1;
12336 } else
12337#endif
12338 {
12339 // connection error
12340 cm_msg(MERROR, "rpc_client_check",
12341 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12342 c->client_name.c_str(),
12343 c->host_name.c_str(),
12344 errno, strerror(errno));
12345 ok = 0;
12346 }
12347 } else if (status == 0) {
12348 // connection closed by remote end without sending an EXIT message
12349 // this can happen if the remote end has crashed, so this message
12350 // is still necessary as a useful diagnostic for unexpected crashes
12351 // of midas programs. K.O.
12352 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());
12353 ok = 0;
12354 } else {
12355 // read some data
12356 ok = 1;
12357 if (equal_ustring(buffer, "EXIT")) {
12358 /* normal exit */
12359 ok = 0;
12360 }
12361 }
12362
12363 if (!ok) {
12364 //printf("rpc_client_check: closing connection %d: ", i);
12365 //c->print();
12366 //printf("\n");
12367
12368 // connection lost, close the socket
12369 c->close_locked();
12370 }
12371
12372 // implicit unlock
12373 }
12374 }
12375
12376 // implicit unlock of _client_connections_mutex
12377}
12378
12379
12380/********************************************************************/
12381INT rpc_server_connect(const char *host_name, const char *exp_name)
12382/********************************************************************\
12383
12384 Routine: rpc_server_connect
12385
12386 Purpose: Extablish a network connection to a remote MIDAS
12387 server using a callback scheme.
12388
12389 Input:
12390 char *host_name IP address of host to connect to.
12391
12392 INT port TPC port to connect to.
12393
12394 char *exp_name Name of experiment to connect to. By using
12395 this name, several experiments (e.g. online
12396 DAQ and offline analysis) can run simultan-
12397 eously on the same host.
12398
12399 Output:
12400 none
12401
12402 Function value:
12403 RPC_SUCCESS Successful completion
12404 RPC_NET_ERROR Error in socket call
12405 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12406 CM_UNDEF_EXP Undefined experiment on server
12407
12408\********************************************************************/
12409{
12410 INT i, status;
12411 INT remote_hw_type, hw_type;
12412 char str[200], version[32], v1[32];
12414 struct timeval timeout;
12415 int port = MIDAS_TCP_PORT;
12416 char *s;
12417
12418#ifdef OS_WINNT
12419 {
12421
12422 /* Start windows sockets */
12423 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12424 return RPC_NET_ERROR;
12425 }
12426#endif
12427
12428 /* check if local connection */
12429 if (host_name[0] == 0)
12430 return RPC_SUCCESS;
12431
12432 /* register system functions */
12434
12435 /* check if cm_connect_experiment was called */
12436 if (_client_name.length() == 0) {
12437 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12438 return RPC_NOT_REGISTERED;
12439 }
12440
12441 /* check if connection already exists */
12443 return RPC_SUCCESS;
12444
12448
12449 bool listen_localhost = false;
12450
12451 if (strcmp(host_name, "localhost") == 0)
12452 listen_localhost = true;
12453
12454 int lsock1, lport1;
12455 int lsock2, lport2;
12456 int lsock3, lport3;
12457
12458 std::string errmsg;
12459
12461
12462 if (status != SS_SUCCESS) {
12463 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12464 return RPC_NET_ERROR;
12465 }
12466
12468
12469 if (status != SS_SUCCESS) {
12470 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12471 return RPC_NET_ERROR;
12472 }
12473
12475
12476 if (status != SS_SUCCESS) {
12477 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12478 return RPC_NET_ERROR;
12479 }
12480
12481 /* extract port number from host_name */
12482 mstrlcpy(str, host_name, sizeof(str));
12483 s = strchr(str, ':');
12484 if (s) {
12485 *s = 0;
12486 port = strtoul(s + 1, NULL, 0);
12487 }
12488
12489 int sock;
12490
12491 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12492
12493 if (status != SS_SUCCESS) {
12494 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12495 return RPC_NET_ERROR;
12496 }
12497
12498 /* connect to experiment */
12499 if (exp_name[0] == 0)
12500 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12501 else
12502 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12503
12504 send(sock, str, strlen(str) + 1, 0);
12505 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12506 ss_socket_close(&sock);
12507 if (i <= 0) {
12508 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12509 return RPC_NET_ERROR;
12510 }
12511
12512 status = version[0] = 0;
12513 sscanf(str, "%d %s", &status, version);
12514
12515 if (status == 2) {
12516/* message "undefined experiment" should be displayed by application */
12517 return CM_UNDEF_EXP;
12518 }
12519
12520 /* print warning if version patch level doesn't agree */
12521 strcpy(v1, version);
12522 if (strchr(v1, '.'))
12523 if (strchr(strchr(v1, '.') + 1, '.'))
12524 *strchr(strchr(v1, '.') + 1, '.') = 0;
12525
12526 strcpy(str, cm_get_version());
12527 if (strchr(str, '.'))
12528 if (strchr(strchr(str, '.') + 1, '.'))
12529 *strchr(strchr(str, '.') + 1, '.') = 0;
12530
12531 if (strcmp(v1, str) != 0) {
12532 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12533 cm_get_version());
12534 }
12535
12536 /* wait for callback on send and recv socket with timeout */
12537 FD_ZERO(&readfds);
12541
12542 timeout.tv_sec = _rpc_connect_timeout / 1000;
12543 timeout.tv_usec = 0;
12544
12545 do {
12546 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12547
12548 /* if an alarm signal was cought, restart select with reduced timeout */
12549 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12550 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12551
12552 } while (status == -1); /* dont return if an alarm signal was cought */
12553
12554 if (!FD_ISSET(lsock1, &readfds)) {
12555 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12559 return RPC_NET_ERROR;
12560 }
12561
12565
12567 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12568 return RPC_NET_ERROR;
12569 }
12570
12574
12575 /* set TCP_NODELAY option for better performance */
12576 int flag = 1;
12579
12580 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12581 flag = 2 * 1024 * 1024;
12583 if (status != 0)
12584 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12585
12586 /* send local computer info */
12587 std::string local_prog_name = rpc_get_name();
12589 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12590
12592
12593 /* receive remote computer info */
12595 if (i <= 0) {
12596 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12597 return RPC_NET_ERROR;
12598 }
12599
12600 sscanf(str, "%d", &remote_hw_type);
12601 _server_connection.remote_hw_type = remote_hw_type;
12602
12604
12605 _rpc_is_remote = true;
12606
12607 return RPC_SUCCESS;
12608}
12609
12610/********************************************************************/
12611
12613{
12615 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12617 if (c && c->connected) {
12619 c->mutex.lock();
12620 if (!c->connected) {
12621 // disconnected while we were waiting for the lock
12622 c->mutex.unlock();
12623 return NULL;
12624 }
12625 return c;
12626 }
12627 }
12629 return NULL;
12630}
12631
12633{
12634 /* close all open connections */
12635
12637
12638 for (unsigned i = 0; i < _client_connections.size(); i++) {
12640 if (c && c->connected) {
12641 int index = c->index;
12642 // must unlock the array, otherwise we hang -
12643 // rpc_client_disconnect() will do rpc_call_client()
12644 // which needs to lock the array to convert handle
12645 // to connection pointer. Ouch! K.O. Dec 2020.
12649 }
12650 }
12651
12652 for (unsigned i = 0; i < _client_connections.size(); i++) {
12654 //printf("client connection %d %p\n", i, c);
12655 if (c) {
12656 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12657 if (!c->connected) {
12658 delete c;
12660 }
12661 }
12662 }
12663
12665
12666 /* close server connection from other clients */
12667 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12668 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12669 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12670 _server_acceptions[i]->close();
12671 }
12672 }
12673}
12674
12675/********************************************************************/
12677/********************************************************************\
12678
12679 Routine: rpc_client_disconnect
12680
12681 Purpose: Close a rpc connection to a MIDAS client
12682
12683 Input:
12684 HNDLE hConn Handle of connection
12685 BOOL bShutdown Shut down remote server if TRUE
12686
12687 Output:
12688 none
12689
12690 Function value:
12691 RPC_SUCCESS Successful completion
12692
12693\********************************************************************/
12694{
12695 /* notify server about exit */
12696
12697 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12698
12700
12701 return RPC_SUCCESS;
12702}
12703
12704/********************************************************************/
12706/********************************************************************\
12707
12708 Routine: rpc_server_disconnect
12709
12710 Purpose: Close a rpc connection to a MIDAS server and close all
12711 server connections from other clients
12712
12713 Input:
12714 none
12715
12716 Output:
12717 none
12718
12719 Function value:
12720 RPC_SUCCESS Successful completion
12721 RPC_NET_ERROR Error in socket call
12722 RPC_NO_CONNECTION Maximum number of connections reached
12723
12724\********************************************************************/
12725{
12727
12729 return RPC_SUCCESS;
12730
12732
12733 /* flush remaining events */
12735
12736 /* notify server about exit */
12737 if (rpc_is_connected()) {
12739 }
12740
12741 /* close sockets */
12748
12750
12751 /* remove semaphore */
12752 if (_mutex_rpc)
12754 _mutex_rpc = NULL;
12755
12757 return RPC_SUCCESS;
12758}
12759
12760/********************************************************************/
12762/********************************************************************\
12763
12764 Routine: rpc_is_remote
12765
12766 Purpose: Return true if program is connected to a remote server
12767
12768 Input:
12769 none
12770
12771 Output:
12772 none
12773
12774 Function value:
12775 INT true is remote client connected to mserver, false if local connection
12776
12777\********************************************************************/
12778{
12779 return _rpc_is_remote;
12780}
12781
12782/********************************************************************/
12784/********************************************************************\
12785
12786 Routine: rpc_is_connected
12787
12788 Purpose: Return true if connection to mserver is still open
12789
12790 Input:
12791 none
12792
12793 Output:
12794 none
12795
12796 Function value:
12797 INT true if connection to mserver is still open, false if connection to mserver is already closed
12798
12799\********************************************************************/
12800{
12801 return _server_connection.send_sock != 0;
12802}
12803
12804/********************************************************************/
12806/********************************************************************\
12807
12808 Routine: rpc_get_mserver_hostname
12809
12810 Purpose: Return the hostname of the mserver connection (host:port format)
12811
12812\********************************************************************/
12813{
12815}
12816
12817/********************************************************************/
12819/********************************************************************\
12820
12821 Routine: rpc_is_mserver
12822
12823 Purpose: Return true if we are the mserver
12824
12825 Function value:
12826 INT "true" if we are the mserver
12827
12828\********************************************************************/
12829{
12830 return _mserver_acception != NULL;
12831}
12832
12833/********************************************************************/
12835/********************************************************************\
12836
12837 Routine: rpc_get_hw_type
12838
12839 Purpose: get hardware information
12840
12841 Function value:
12842 INT combination of DRI_xxx bits
12843
12844\********************************************************************/
12845{
12846 {
12847 {
12848 INT tmp_type, size;
12849 DWORD dummy;
12850 unsigned char *p;
12851 float f;
12852 double d;
12853
12854 tmp_type = 0;
12855
12856 /* test pointer size */
12857 size = sizeof(p);
12858 if (size == 2)
12859 tmp_type |= DRI_16;
12860 if (size == 4)
12861 tmp_type |= DRI_32;
12862 if (size == 8)
12863 tmp_type |= DRI_64;
12864
12865 /* test if little or big endian machine */
12866 dummy = 0x12345678;
12867 p = (unsigned char *) &dummy;
12868 if (*p == 0x78)
12870 else if (*p == 0x12)
12872 else
12873 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
12874
12875 /* floating point format */
12876 f = (float) 1.2345;
12877 dummy = 0;
12878 memcpy(&dummy, &f, sizeof(f));
12879 if ((dummy & 0xFF) == 0x19 &&
12880 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
12881 && ((dummy >> 24) & 0xFF) == 0x3F)
12882 tmp_type |= DRF_IEEE;
12883 else if ((dummy & 0xFF) == 0x9E &&
12884 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
12885 && ((dummy >> 24) & 0xFF) == 0x04)
12887 else
12888 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12889
12890 d = (double) 1.2345;
12891 dummy = 0;
12892 memcpy(&dummy, &d, sizeof(f));
12893 if ((dummy & 0xFF) == 0x8D && /* little endian */
12894 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
12895 && ((dummy >> 24) & 0xFF) == 0x12)
12896 tmp_type |= DRF_IEEE;
12897 else if ((dummy & 0xFF) == 0x83 && /* big endian */
12898 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
12899 && ((dummy >> 24) & 0xFF) == 0x3F)
12900 tmp_type |= DRF_IEEE;
12901 else if ((dummy & 0xFF) == 0x13 &&
12902 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
12903 && ((dummy >> 24) & 0xFF) == 0xC0)
12905 else if ((dummy & 0xFF) == 0x9E &&
12906 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
12907 && ((dummy >> 24) & 0xFF) == 0x04)
12908 cm_msg(MERROR, "rpc_get_option",
12909 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
12910 else
12911 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12912
12913 return tmp_type;
12914 }
12915 }
12916}
12917
12919#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12920
12921/********************************************************************/
12929#if 0
12931 switch (item) {
12932 case RPC_OTIMEOUT:
12933 if (hConn == -1)
12935 else if (hConn == -2)
12937 else {
12939 if (c) {
12940 c->rpc_timeout = value;
12941 c->mutex.unlock();
12942 }
12943 }
12944 break;
12945
12946 case RPC_NODELAY:
12947 if (hConn == -1) {
12949 } else {
12951 if (c) {
12952 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12953 c->mutex.unlock();
12954 }
12955 }
12956 break;
12957
12958 default:
12959 cm_msg(MERROR, "rpc_set_option", "invalid argument");
12960 break;
12961 }
12962
12963 return 0;
12964}
12965#endif
12966
12967/********************************************************************/
12974{
12975 if (hConn == RPC_HNDLE_MSERVER) {
12977 } else if (hConn == RPC_HNDLE_CONNECT) {
12978 return _rpc_connect_timeout;
12979 } else {
12981 if (c) {
12982 int timeout = c->rpc_timeout;
12983 c->mutex.unlock();
12984 return timeout;
12985 }
12986 }
12987 return 0;
12988}
12989
12990/********************************************************************/
12999{
13000 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13001
13002 if (hConn == RPC_HNDLE_MSERVER) {
13003 if (old_timeout_msec)
13006 } else if (hConn == RPC_HNDLE_CONNECT) {
13007 if (old_timeout_msec)
13010 } else {
13012 if (c) {
13013 if (old_timeout_msec)
13014 *old_timeout_msec = c->rpc_timeout;
13015 c->rpc_timeout = timeout_msec;
13016 c->mutex.unlock();
13017 } else {
13018 if (old_timeout_msec)
13019 *old_timeout_msec = 0;
13020 }
13021 }
13022 return RPC_SUCCESS;
13023}
13024
13025
13027#ifndef DOXYGEN_SHOULD_SKIP_THIS
13028
13029/********************************************************************/
13031/********************************************************************\
13032
13033 Routine: rpc_get_convert_flags
13034
13035 Purpose: Get RPC convert_flags for the mserver connection
13036
13037 Function value:
13038 INT Actual option
13039
13040\********************************************************************/
13041{
13044 else
13045 return 0;
13046}
13047
13048static std::string _mserver_path;
13049
13050/********************************************************************/
13052/********************************************************************\
13053
13054 Routine: rpc_get_mserver_path()
13055
13056 Purpose: Get path of the mserver executable
13057
13058\********************************************************************/
13059{
13060 return _mserver_path.c_str();
13061}
13062
13063/********************************************************************/
13064INT rpc_set_mserver_path(const char *path)
13065/********************************************************************\
13066
13067 Routine: rpc_set_mserver_path
13068
13069 Purpose: Remember the path of the mserver executable
13070
13071 Input:
13072 char *path Full path of the mserver executable
13073
13074 Function value:
13075 RPC_SUCCESS Successful completion
13076
13077\********************************************************************/
13078{
13079 _mserver_path = path;
13080 return RPC_SUCCESS;
13081}
13082
13083/********************************************************************/
13084std::string rpc_get_name()
13085/********************************************************************\
13086
13087 Routine: rpc_get_name
13088
13089 Purpose: Get name set by rpc_set_name
13090
13091 Input:
13092 none
13093
13094 Output:
13095 char* name The location pointed by *name receives a
13096 copy of the _prog_name
13097
13098 Function value:
13099 RPC_SUCCESS Successful completion
13100
13101\********************************************************************/
13102{
13103 return _client_name;
13104}
13105
13106
13107/********************************************************************/
13109/********************************************************************\
13110
13111 Routine: rpc_set_name
13112
13113 Purpose: Set name of actual program for further rpc connections
13114
13115 Input:
13116 char *name Program name, up to NAME_LENGTH chars,
13117 no blanks
13118
13119 Output:
13120 none
13121
13122 Function value:
13123 RPC_SUCCESS Successful completion
13124
13125\********************************************************************/
13126{
13128
13129 return RPC_SUCCESS;
13130}
13131
13132
13133/********************************************************************/
13134INT rpc_set_debug(void (*func)(const char *), INT mode)
13135/********************************************************************\
13136
13137 Routine: rpc_set_debug
13138
13139 Purpose: Set a function which is called on every RPC call to
13140 display the function name and parameters of the RPC
13141 call.
13142
13143 Input:
13144 void *func(char*) Pointer to function.
13145 INT mode Debug mode
13146
13147 Output:
13148 none
13149
13150 Function value:
13151 RPC_SUCCESS Successful completion
13152
13153\********************************************************************/
13154{
13155 _debug_print = func;
13156 _debug_mode = mode;
13157 return RPC_SUCCESS;
13158}
13159
13160/********************************************************************/
13161void rpc_debug_printf(const char *format, ...)
13162/********************************************************************\
13163
13164 Routine: rpc_debug_print
13165
13166 Purpose: Calls function set via rpc_set_debug to output a string.
13167
13168 Input:
13169 char *str Debug string
13170
13171 Output:
13172 none
13173
13174\********************************************************************/
13175{
13177 char str[1000];
13178
13179 if (_debug_mode) {
13180 va_start(argptr, format);
13181 vsprintf(str, (char *) format, argptr);
13182 va_end(argptr);
13183
13184 if (_debug_print) {
13185 strcat(str, "\n");
13187 } else
13188 puts(str);
13189 }
13190}
13191
13192/********************************************************************/
13194 switch (arg_type) {
13195 /* On the stack, the minimum parameter size is sizeof(int).
13196 To avoid problems on little endian systems, treat all
13197 smaller parameters as int's */
13198 case TID_UINT8:
13199 case TID_INT8:
13200 case TID_CHAR:
13201 case TID_UINT16:
13202 case TID_INT16:
13203 *((int *) arg) = va_arg(*arg_ptr, int);
13204 break;
13205
13206 case TID_INT32:
13207 case TID_BOOL:
13208 *((INT *) arg) = va_arg(*arg_ptr, INT);
13209 break;
13210
13211 case TID_UINT32:
13212 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13213 break;
13214
13215 /* float variables are passed as double by the compiler */
13216 case TID_FLOAT:
13217 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13218 break;
13219
13220 case TID_DOUBLE:
13221 *((double *) arg) = va_arg(*arg_ptr, double);
13222 break;
13223
13224 case TID_ARRAY:
13225 *((char **) arg) = va_arg(*arg_ptr, char *);
13226 break;
13227 }
13228}
13229
13230/********************************************************************/
13232{
13233 bool debug = false;
13234
13235 if (debug) {
13236 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13237 for (int i=0; rl.param[i].tid != 0; i++) {
13238 int tid = rl.param[i].tid;
13239 int flags = rl.param[i].flags;
13240 int n = rl.param[i].n;
13241 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13242 }
13243 }
13244
13245 char args[MAX_RPC_PARAMS][8];
13246
13247 for (int i=0; rl.param[i].tid != 0; i++) {
13248 int tid = rl.param[i].tid;
13249 int flags = rl.param[i].flags;
13250 int arg_type = 0;
13251
13252 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13253 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13254 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13255
13256 if (bpointer)
13258 else
13259 arg_type = tid;
13260
13261 /* floats are passed as doubles, at least under NT */
13262 if (tid == TID_FLOAT && !bpointer)
13264
13265 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13266
13268 }
13269
13270 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13271 char* buf = (char *)malloc(buf_size);
13272 assert(buf);
13273
13274 (*nc) = (NET_COMMAND*) buf;
13275
13276 /* find out if we are on a big endian system */
13277 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13278
13279 char* param_ptr = (*nc)->param;
13280
13281 for (int i=0; rl.param[i].tid != 0; i++) {
13282 int tid = rl.param[i].tid;
13283 int flags = rl.param[i].flags;
13284 int arg_type = 0;
13285
13286 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13287 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13288 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13289
13290 if (bpointer)
13292 else
13293 arg_type = tid;
13294
13295 /* floats are passed as doubles, at least under NT */
13296 if (tid == TID_FLOAT && !bpointer)
13298
13299 /* get pointer to argument */
13300 //char arg[8];
13301 //rpc_va_arg(&ap, arg_type, arg);
13302
13303 char* arg = args[i];
13304
13305 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13306 if (bbig) {
13307 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13308 arg[0] = arg[3];
13309 }
13310 if (tid == TID_UINT16 || tid == TID_INT16) {
13311 arg[0] = arg[2];
13312 arg[1] = arg[3];
13313 }
13314 }
13315
13316 if (flags & RPC_IN) {
13317 int arg_size = 0;
13318
13319 if (bpointer)
13320 arg_size = rpc_tid_size(tid);
13321 else
13323
13324 /* for strings, the argument size depends on the string length */
13325 if (tid == TID_STRING || tid == TID_LINK) {
13326 arg_size = 1 + strlen((char *) *((char **) arg));
13327 }
13328
13329 /* for varibale length arrays, the size is given by
13330 the next parameter on the stack */
13331 if (flags & RPC_VARARRAY) {
13332 //va_list aptmp;
13334 //va_copy(aptmp, ap);
13335
13336 //char arg_tmp[8];
13337 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13338
13339 const char* arg_tmp = args[i+1];
13340
13341 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13342 if (flags & RPC_OUT)
13343 arg_size = *((INT *) *((void **) arg_tmp));
13344 else
13345 arg_size = *((INT *) arg_tmp);
13346
13347 *((INT *) param_ptr) = ALIGN8(arg_size);
13348 param_ptr += ALIGN8(sizeof(INT));
13349
13350 //va_end(aptmp);
13351 }
13352
13353 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13354 arg_size = rl.param[i].n;
13355
13356 /* always align parameter size */
13357 int param_size = ALIGN8(arg_size);
13358
13359 {
13360 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13361
13362 if (param_offset + param_size + 16 > buf_size) {
13363 size_t new_size = param_offset + param_size + 1024;
13364 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13365 buf = (char *) realloc(buf, new_size);
13366 assert(buf);
13367 buf_size = new_size;
13368 (*nc) = (NET_COMMAND*) buf;
13369 param_ptr = buf + param_offset;
13370 }
13371 }
13372
13373 if (bpointer) {
13374 if (debug) {
13375 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);
13376 }
13377 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13378 } else if (tid == TID_FLOAT) {
13379 if (debug) {
13380 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);
13381 }
13382 /* floats are passed as doubles on most systems */
13383 *((float *) param_ptr) = (float) *((double *) arg);
13384 } else {
13385 if (debug) {
13386 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);
13387 }
13388 memcpy(param_ptr, arg, arg_size);
13389 }
13390
13391 param_ptr += param_size;
13392 }
13393 }
13394
13395 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13396
13397 if (debug)
13398 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13399}
13400
13401/********************************************************************/
13402static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13403{
13404 bool debug = false;
13405
13406 if (debug)
13407 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13408
13409 /* extract result variables and place it to argument list */
13410
13411 const char* param_ptr = buf;
13412
13413 for (int i = 0; rl.param[i].tid != 0; i++) {
13414 int tid = rl.param[i].tid;
13415 int flags = rl.param[i].flags;
13416 int arg_type = 0;
13417
13418 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13419 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13420 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13421
13422 if (bpointer)
13424 else
13425 arg_type = rl.param[i].tid;
13426
13427 if (tid == TID_FLOAT && !bpointer)
13429
13430 char arg[8];
13431 rpc_va_arg(&ap, arg_type, arg);
13432
13433 if (rl.param[i].flags & RPC_OUT) {
13434
13435 if (param_ptr == NULL) {
13436 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);
13437 return RPC_NET_ERROR;
13438 }
13439
13440 tid = rl.param[i].tid;
13441 int arg_size = rpc_tid_size(tid);
13442
13443 if (tid == TID_STRING || tid == TID_LINK)
13444 arg_size = strlen((char *) (param_ptr)) + 1;
13445
13446 if (flags & RPC_VARARRAY) {
13447 arg_size = *((INT *) param_ptr);
13448 param_ptr += ALIGN8(sizeof(INT));
13449 }
13450
13451 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13452 arg_size = rl.param[i].n;
13453
13454 /* parameter size is always aligned */
13455 int param_size = ALIGN8(arg_size);
13456
13457 /* return parameters are always pointers */
13458 if (*((char **) arg)) {
13459 if (debug)
13460 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);
13461 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13462 }
13463
13464 param_ptr += param_size;
13465 }
13466 }
13467
13468 return RPC_SUCCESS;
13469}
13470
13471/********************************************************************/
13473/********************************************************************\
13474
13475 Routine: rpc_client_call
13476
13477 Purpose: Call a function on a MIDAS client
13478
13479 Input:
13480 INT hConn Client connection
13481 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13482
13483 ... variable argument list
13484
13485 Output:
13486 (depends on argument list)
13487
13488 Function value:
13489 RPC_SUCCESS Successful completion
13490 RPC_NET_ERROR Error in socket call
13491 RPC_NO_CONNECTION No active connection
13492 RPC_TIMEOUT Timeout in RPC call
13493 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13494 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13495
13496\********************************************************************/
13497{
13499
13500 if (!c) {
13501 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13502 return RPC_NO_CONNECTION;
13503 }
13504
13505 //printf("rpc_client_call: handle %d, connection: ", hConn);
13506 //c->print();
13507 //printf("\n");
13508
13509 INT i, status;
13510
13511 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13512 routine_id &= ~RPC_NO_REPLY;
13513
13514 //if (rpc_no_reply)
13515 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13516
13517 // make local copy of the client name just in case _client_connection is erased by another thread
13518
13519 /* find rpc_index */
13520
13521 int rpc_index = -1;
13522 const char *rpc_name = NULL;
13524
13525 rpc_list_mutex.lock();
13526 for (size_t i = 0; i < rpc_list.size(); i++) {
13527 if (rpc_list[i].id == (int) routine_id) {
13528 rpc_index = i;
13531 break;
13532 }
13533 }
13534 rpc_list_mutex.unlock();
13535
13536 if (rpc_index < 0) {
13537 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);
13538 c->mutex.unlock();
13539 return RPC_INVALID_ID;
13540 }
13541
13542 NET_COMMAND *nc = NULL;
13543
13544 /* examine variable argument list and convert it to parameter array */
13545 va_list ap;
13546 va_start(ap, routine_id);
13547
13549
13550 va_end(ap);
13551
13552 nc->header.routine_id = routine_id;
13553
13554 if (rpc_no_reply)
13556
13557 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13558
13559 /* in FAST TCP mode, only send call and return immediately */
13560 if (rpc_no_reply) {
13561 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13562
13563 if (i != send_size) {
13564 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);
13565 free(nc);
13566 c->mutex.unlock();
13567 return RPC_NET_ERROR;
13568 }
13569
13570 free(nc);
13571
13572 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
13573 //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);
13574 //c->print();
13575 //printf("\n");
13576 c->close_locked();
13577 }
13578
13579 c->mutex.unlock();
13580 return RPC_SUCCESS;
13581 }
13582
13583 /* in TCP mode, send and wait for reply on send socket */
13584 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13585 if (i != send_size) {
13586 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);
13587 c->mutex.unlock();
13588 return RPC_NET_ERROR;
13589 }
13590
13591 free(nc);
13592 nc = NULL;
13593
13594 bool restore_watchdog_timeout = false;
13596 DWORD watchdog_timeout;
13597 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13598
13599 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
13600
13601 if (c->rpc_timeout >= (int) watchdog_timeout) {
13603 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
13604 }
13605
13606 DWORD rpc_status = 0;
13607 DWORD buf_size = 0;
13608 char* buf = NULL;
13609
13610 /* receive result on send socket */
13611 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
13612
13614 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
13615 }
13616
13617 if (status == SS_TIMEOUT) {
13618 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);
13619 if (buf)
13620 free(buf);
13621 c->mutex.unlock();
13622 return RPC_TIMEOUT;
13623 }
13624
13625 if (status != SS_SUCCESS) {
13626 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);
13627 if (buf)
13628 free(buf);
13629 c->mutex.unlock();
13630 return RPC_NET_ERROR;
13631 }
13632
13633 c->mutex.unlock();
13634
13635 if (rpc_status == RPC_INVALID_ID) {
13636 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);
13637 if (buf)
13638 free(buf);
13639 return rpc_status;
13640 }
13641
13642 /* extract result variables and place it to argument list */
13643
13644 va_start(ap, routine_id);
13645
13646 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13647
13648 if (status != RPC_SUCCESS) {
13650 }
13651
13652 va_end(ap);
13653
13654 if (buf)
13655 free(buf);
13656 buf = NULL;
13657 buf_size = 0;
13658
13659 return rpc_status;
13660}
13661
13662/********************************************************************/
13663INT rpc_call(DWORD routine_id, ...)
13664/********************************************************************\
13665
13666 Routine: rpc_call
13667
13668 Purpose: Call a function on a MIDAS server
13669
13670 Input:
13671 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13672
13673 ... variable argument list
13674
13675 Output:
13676 (depends on argument list)
13677
13678 Function value:
13679 RPC_SUCCESS Successful completion
13680 RPC_NET_ERROR Error in socket call
13681 RPC_NO_CONNECTION No active connection
13682 RPC_TIMEOUT Timeout in RPC call
13683 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13684 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13685
13686\********************************************************************/
13687{
13688 va_list ap;
13689 INT i, status;
13690
13691 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13692 routine_id &= ~RPC_NO_REPLY;
13693
13694 //if (rpc_no_reply)
13695 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13696
13697 int send_sock = _server_connection.send_sock;
13698 int rpc_timeout = _server_connection.rpc_timeout;
13699
13700 if (!send_sock) {
13701 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
13702 return RPC_NET_ERROR;
13703 }
13704
13705 if (!_mutex_rpc) {
13706 /* create a local mutex for multi-threaded applications */
13708 }
13709
13710 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
13711 if (status != SS_SUCCESS) {
13712 cm_msg(MERROR, "rpc_call", "Mutex timeout");
13713 return RPC_MUTEX_TIMEOUT;
13714 }
13715
13716 /* find rpc definition */
13717
13718 int idx = -1;
13719 const char* rpc_name = NULL;
13721
13722 rpc_list_mutex.lock();
13723
13724 for (size_t i = 0; i < rpc_list.size(); i++) {
13725 if (rpc_list[i].id == (int) routine_id) {
13726 idx = i;
13729 break;
13730 }
13731 }
13732
13733 rpc_list_mutex.unlock();
13734
13735 if (idx < 0) {
13737 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
13738 return RPC_INVALID_ID;
13739 }
13740
13741 /* prepare output buffer */
13742
13743 NET_COMMAND* nc = NULL;
13744
13745 /* examine variable argument list and convert it to parameter array */
13746 va_start(ap, routine_id);
13747
13749
13750 va_end(ap);
13751
13752 nc->header.routine_id = routine_id;
13753
13754 if (rpc_no_reply)
13756
13757 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13758
13759 /* do not wait for reply if requested RPC_NO_REPLY */
13760 if (rpc_no_reply) {
13761 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13762
13763 if (i != send_size) {
13765 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13766 free(nc);
13767 return RPC_NET_ERROR;
13768 }
13769
13771 free(nc);
13772 return RPC_SUCCESS;
13773 }
13774
13775 /* in TCP mode, send and wait for reply on send socket */
13776 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13777 if (i != send_size) {
13779 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13780 free(nc);
13781 return RPC_NET_ERROR;
13782 }
13783
13784 free(nc);
13785 nc = NULL;
13786
13787 bool restore_watchdog_timeout = false;
13789 DWORD watchdog_timeout;
13790 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13791
13792 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
13793
13794 if (!rpc_is_remote()) {
13795 // if RPC is remote, we are connected to an mserver,
13796 // the mserver takes care of watchdog timeouts.
13797 // otherwise we should make sure the watchdog timeout
13798 // is longer than the RPC timeout. K.O.
13799 if (rpc_timeout >= (int) watchdog_timeout) {
13801 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
13802 }
13803 }
13804
13805 DWORD rpc_status = 0;
13806 DWORD buf_size = 0;
13807 char* buf = NULL;
13808
13809 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
13810
13813 }
13814
13815 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
13816
13818
13819 /* check for reply errors */
13820
13821 if (status == SS_TIMEOUT) {
13822 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
13823 if (buf)
13824 free(buf);
13825 abort(); // cannot continue - our mserver is not talking to us!
13826 return RPC_TIMEOUT;
13827 }
13828
13829 if (status != SS_SUCCESS) {
13830 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
13831 if (buf)
13832 free(buf);
13833 abort(); // cannot continue - something is wrong with our mserver connection
13834 return RPC_NET_ERROR;
13835 }
13836
13837 if (rpc_status == RPC_INVALID_ID) {
13838 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
13839 if (buf)
13840 free(buf);
13841 return rpc_status;
13842 }
13843
13844 /* extract result variables and place it to argument list */
13845
13846 va_start(ap, routine_id);
13847
13848 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13849
13850 if (status != RPC_SUCCESS) {
13852 }
13853
13854 va_end(ap);
13855
13856 if (buf)
13857 free(buf);
13858
13859 return rpc_status;
13860}
13861
13862
13863/********************************************************************/
13865 INT old;
13866
13869 return old;
13870}
13871
13873 return _opt_tcp_size;
13874}
13875
13877#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13878
13879/********************************************************************/
13901INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
13902{
13903 if (rpc_is_remote()) {
13904 return rpc_send_event1(buffer_handle, pevent);
13905 } else {
13906 return bm_send_event(buffer_handle, pevent, unused, async_flag);
13907 }
13908}
13909
13910/********************************************************************/
13919INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
13920{
13921 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
13922 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
13923}
13924
13925INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
13926{
13927 if (sg_n < 1) {
13928 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
13929 return BM_INVALID_SIZE;
13930 }
13931
13932 if (sg_ptr[0] == NULL) {
13933 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
13934 return BM_INVALID_SIZE;
13935 }
13936
13937 if (sg_len[0] < sizeof(EVENT_HEADER)) {
13938 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));
13939 return BM_INVALID_SIZE;
13940 }
13941
13942 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
13943
13944 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
13945 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
13946
13947 if (data_size == 0) {
13948 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
13949 return BM_INVALID_SIZE;
13950 }
13951
13952 if (data_size > MAX_DATA_SIZE) {
13953 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);
13954 return BM_INVALID_SIZE;
13955 }
13956
13957 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
13958 const size_t total_size = ALIGN8(event_size);
13959
13960 size_t count = 0;
13961 for (int i=0; i<sg_n; i++) {
13962 count += sg_len[i];
13963 }
13964
13965 if (count != event_size) {
13966 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);
13967 return BM_INVALID_SIZE;
13968 }
13969
13970 // protect non-atomic access to _server_connection.event_sock. K.O.
13971
13972 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
13973
13974 //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);
13975
13976 if (_server_connection.event_sock == 0) {
13977 return RPC_NO_CONNECTION;
13978 }
13979
13980 //
13981 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
13982 //
13983 // 4 bytes of buffer handle
13984 // 16 bytes of event header, includes data_size
13985 // ALIGN8(data_size) bytes of event data
13986 //
13987
13988 int status;
13989
13990 /* send buffer handle */
13991
13992 assert(sizeof(DWORD) == 4);
13993 DWORD bh_buf = buffer_handle;
13994
13995 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
13996 if (status != SS_SUCCESS) {
13998 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
13999 return RPC_NET_ERROR;
14000 }
14001
14002 /* send data */
14003
14004 for (int i=0; i<sg_n; i++) {
14006 if (status != SS_SUCCESS) {
14008 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14009 return RPC_NET_ERROR;
14010 }
14011 }
14012
14013 /* send padding */
14014
14015 if (count < total_size) {
14016 char padding[8] = { 0,0,0,0,0,0,0,0 };
14017 size_t padlen = total_size - count;
14018 assert(padlen < 8);
14020 if (status != SS_SUCCESS) {
14022 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14023 return RPC_NET_ERROR;
14024 }
14025 }
14026
14027 return RPC_SUCCESS;
14028}
14029
14030/********************************************************************/
14039 return RPC_SUCCESS;
14040}
14041
14042/********************************************************************/
14043
14044struct TR_FIFO {
14045 int transition = 0;
14046 int run_number = 0;
14049};
14050
14051static std::mutex _tr_fifo_mutex;
14053static int _tr_fifo_wp = 0;
14054static int _tr_fifo_rp = 0;
14055
14057/********************************************************************\
14058
14059 Routine: rpc_transition_dispatch
14060
14061 Purpose: Gets called when a transition function was registered and
14062 a transition occured. Internal use only.
14063
14064 Input:
14065 INT idx RPC function ID
14066 void *prpc_param RPC parameters
14067
14068 Output:
14069 none
14070
14071 Function value:
14072 INT return value from called user routine
14073
14074\********************************************************************/
14075{
14076 /* erase error string */
14077 *(CSTRING(2)) = 0;
14078
14079 if (idx == RPC_RC_TRANSITION) {
14080 // find registered handler
14081 // NB: this code should match same code in cm_transition_call_direct()
14082 // NB: only use the first handler, this is how MIDAS always worked
14083 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14084 _trans_table_mutex.lock();
14085 size_t n = _trans_table.size();
14086 _trans_table_mutex.unlock();
14087
14088 for (size_t i = 0; i < n; i++) {
14089 _trans_table_mutex.lock();
14091 _trans_table_mutex.unlock();
14092
14093 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14094 if (tt.func) {
14095 /* execute callback if defined */
14096 return tt.func(CINT(1), CSTRING(2));
14097 } else {
14098 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14099 /* store transition in FIFO */
14104 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14105 // implicit unlock
14106 return RPC_SUCCESS;
14107 }
14108 }
14109 }
14110 // no handler for this transition
14111 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14112 return CM_SUCCESS;
14113 } else {
14114 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14115 return RPC_INVALID_ID;
14116 }
14117}
14118
14119/********************************************************************/
14120int cm_query_transition(int *transition, int *run_number, int *trans_time)
14121/********************************************************************\
14122
14123 Routine: cm_query_transition
14124
14125 Purpose: Query system if transition has occured. Normally, one
14126 registers callbacks for transitions via
14127 cm_register_transition. In some environments however,
14128 callbacks are not possible. In that case one spciefies
14129 a NULL pointer as the callback routine and can query
14130 transitions "manually" by calling this functions. A small
14131 FIFO takes care that no transition is lost if this functions
14132 did not get called between some transitions.
14133
14134 Output:
14135 INT *transition Type of transition, one of TR_xxx
14136 INT *run_nuber Run number for transition
14137 time_t *trans_time Time (in UNIX time) of transition
14138
14139 Function value:
14140 FALSE No transition occured since last call
14141 TRUE Transition occured
14142
14143\********************************************************************/
14144{
14145 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14146
14147 if (_tr_fifo_wp == _tr_fifo_rp)
14148 return FALSE;
14149
14150 if (transition)
14152
14153 if (run_number)
14155
14156 if (trans_time)
14157 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14158
14159 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14160
14161 // implicit unlock
14162 return TRUE;
14163}
14164
14165/********************************************************************\
14166* server functions *
14167\********************************************************************/
14168
14169#if 0
14170void debug_dump(unsigned char *p, int size)
14171{
14172 int i, j;
14173 unsigned char c;
14174
14175 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14176 printf("%p ", p + i * 16);
14177 for (j = 0; j < 16; j++)
14178 if (i * 16 + j < size)
14179 printf("%02X ", p[i * 16 + j]);
14180 else
14181 printf(" ");
14182 printf(" ");
14183
14184 for (j = 0; j < 16; j++) {
14185 c = p[i * 16 + j];
14186 if (i * 16 + j < size)
14187 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14188 }
14189 printf("\n");
14190 }
14191
14192 printf("\n");
14193}
14194#endif
14195
14196/********************************************************************/
14198/********************************************************************\
14199
14200 Routine: recv_net_command
14201
14202 Purpose: TCP receive routine with local cache. To speed up network
14203 performance, a 64k buffer is read in at once and split into
14204 several RPC command on successive calls to recv_net_command.
14205 Therefore, the number of recv() calls is minimized.
14206
14207 This routine is ment to be called by the server process.
14208 Clients should call recv_tcp instead.
14209
14210 Input:
14211 INT idx Index of server connection
14212 DWORD buffer_size Size of the buffer in bytes.
14213 INT flags Flags passed to recv()
14214 INT convert_flags Convert flags needed for big/little
14215 endian conversion
14216
14217 Output:
14218 char *buffer Network receive buffer.
14219 INT *remaining Remaining data in cache
14220
14221 Function value:
14222 INT Same as recv()
14223
14224\********************************************************************/
14225{
14226 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14227
14229
14230 int sock = sa->recv_sock;
14231
14232 if (!sa->net_buffer) {
14233 if (sa->is_mserver)
14235 else
14237
14238 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14239 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14240 sa->write_ptr = 0;
14241 sa->read_ptr = 0;
14242 sa->misalign = 0;
14243 }
14244 if (!sa->net_buffer) {
14245 cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14246 return -1;
14247 }
14248
14249 int copied = 0;
14250 int param_size = -1;
14251
14252 int write_ptr = sa->write_ptr;
14253 int read_ptr = sa->read_ptr;
14254 int misalign = sa->misalign;
14255 char *net_buffer = sa->net_buffer;
14256
14257 do {
14258 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14259 if (param_size == -1) {
14260 if (copied > 0) {
14261 /* assemble split header */
14262 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14263 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14264 param_size = (INT) nc->header.param_size;
14265 } else {
14266 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14267 param_size = (INT) nc->header.param_size;
14268 }
14269
14270 if (sa->convert_flags)
14271 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14272 }
14273
14274 //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14275
14276 /* check if parameters fit in buffer */
14277 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14278 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14279 char *p = (char *) realloc(*pbuf, new_size);
14280 //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14281 if (p == NULL) {
14282 cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14283 sa->read_ptr = 0;
14284 sa->write_ptr = 0;
14285 return -1;
14286 }
14287 *pbuf = p;
14288 *pbufsize = new_size;
14289 }
14290
14291 buffer = *pbuf;
14292
14293 /* check if we have all parameters in buffer */
14294 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14295 break;
14296 }
14297
14298 /* not enough data, so copy partially and get new */
14299 int size = write_ptr - read_ptr;
14300
14301 if (size > 0) {
14302 memcpy(buffer + copied, net_buffer + read_ptr, size);
14303 copied += size;
14304 read_ptr = write_ptr;
14305 }
14306#ifdef OS_UNIX
14307 do {
14308 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14309
14310 /* don't return if an alarm signal was cought */
14311 } while (write_ptr == -1 && errno == EINTR);
14312#else
14313 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14314#endif
14315
14316 /* abort if connection broken */
14317 if (write_ptr <= 0) {
14318 if (write_ptr == 0)
14319 cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14320 else
14321 cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14322
14323 if (remaining)
14324 *remaining = 0;
14325
14326 return write_ptr;
14327 }
14328
14329 read_ptr = misalign;
14330 write_ptr += misalign;
14331
14332 misalign = write_ptr % 8;
14333 } while (TRUE);
14334
14335 /* copy rest of parameters */
14336 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14337 memcpy(buffer + copied, net_buffer + read_ptr, size);
14338 read_ptr += size;
14339
14340 if (remaining) {
14341 /* don't keep rpc_server_receive in an infinite loop */
14342 if (write_ptr - read_ptr < param_size)
14343 *remaining = 0;
14344 else
14345 *remaining = write_ptr - read_ptr;
14346 }
14347
14348 sa->write_ptr = write_ptr;
14349 sa->read_ptr = read_ptr;
14350 sa->misalign = misalign;
14351
14352 return size + copied;
14353}
14354
14355
14356/********************************************************************/
14358/********************************************************************\
14359
14360 Routine: recv_tcp_check
14361
14362 Purpose: Check if in TCP receive buffer associated with sock is
14363 some data. Called by ss_suspend.
14364
14365 Input:
14366 INT sock TCP receive socket
14367
14368 Output:
14369 none
14370
14371 Function value:
14372 INT count Number of bytes remaining in TCP buffer
14373
14374\********************************************************************/
14375{
14376 /* figure out to which connection socket belongs */
14377 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14378 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14379 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14380 }
14381
14382 return 0;
14383}
14384
14385
14386/********************************************************************/
14388/********************************************************************\
14389
14390 Routine: recv_event_server_realloc
14391
14392 Purpose: receive events sent by rpc_send_event()
14393
14394 Input:
14395 INT idx Index of server connection
14396 DWORD buffer_size Size of the buffer in bytes.
14397 INT flags Flags passed to recv()
14398 INT convert_flags Convert flags needed for big/little
14399 endian conversion
14400
14401 Output:
14402 char *buffer Network receive buffer.
14403 INT *remaining Remaining data in cache
14404
14405 Function value:
14406 INT Same as recv()
14407
14408\********************************************************************/
14409{
14410 int sock = psa->event_sock;
14411
14412 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14413
14414 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14415
14416 char header_buf[header_size];
14417
14418 // First read the header.
14419 //
14420 // Data format is:
14421 // INT buffer handle (4 bytes)
14422 // EVENT_HEADER (16 bytes)
14423 // event data
14424 // ALIGN8() padding
14425 // ...next event
14426
14427 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14428
14429 if (hrd == 0) {
14430 // timeout waiting for data
14431 return 0;
14432 }
14433
14434 /* abort if connection broken */
14435 if (hrd < 0) {
14436 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14437 return -1;
14438 }
14439
14440 if (hrd < (int) header_size) {
14441 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14442
14443 /* abort if connection broken */
14444 if (hrd1 <= 0) {
14445 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14446 return -1;
14447 }
14448
14449 hrd += hrd1;
14450 }
14451
14452 /* abort if connection broken */
14453 if (hrd != (int) header_size) {
14454 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14455 return -1;
14456 }
14457
14458 INT *pbh = (INT *) header_buf;
14459 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14460
14461 /* convert header little endian/big endian */
14462 if (psa->convert_flags) {
14463 rpc_convert_single(&pbh, TID_INT32, 0, psa->convert_flags);
14464 rpc_convert_single(&pevent->event_id, TID_INT16, 0, psa->convert_flags);
14465 rpc_convert_single(&pevent->trigger_mask, TID_INT16, 0, psa->convert_flags);
14466 rpc_convert_single(&pevent->serial_number, TID_UINT32, 0, psa->convert_flags);
14467 rpc_convert_single(&pevent->time_stamp, TID_UINT32, 0, psa->convert_flags);
14468 rpc_convert_single(&pevent->data_size, TID_UINT32, 0, psa->convert_flags);
14469 }
14470
14471 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14472 int total_size = ALIGN8(event_size);
14473
14474 //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);
14475
14476 if (pevent->data_size == 0) {
14477 for (int i=0; i<5; i++) {
14478 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14479 }
14480 abort();
14481 }
14482
14483 /* check for sane event size */
14484 if (event_size <= 0 || total_size <= 0) {
14485 cm_msg(MERROR, "recv_event_server",
14486 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14487 event_size, total_size);
14488 return -1;
14489 }
14490
14491 //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);
14492
14493
14494 int bufsize = sizeof(INT) + total_size;
14495
14496 // Second, check that output buffer is big enough
14497
14498 /* check if data part fits in buffer */
14499 if (*pbuffer_size < bufsize) {
14500 int newsize = 1024 + ALIGN8(bufsize);
14501
14502 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14503
14504 char *newbuf = (char *) realloc(*pbuffer, newsize);
14505 if (newbuf == NULL) {
14506 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14507 newsize);
14508 return -1;
14509 }
14510 *pbuffer = newbuf;
14512 }
14513
14514 // Third, copy header into output buffer
14515
14517
14518 // Forth, read the event data
14519
14520 int to_read = sizeof(INT) + total_size - header_size;
14521 int rptr = header_size;
14522
14523 if (to_read > 0) {
14524 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14525
14526 /* abort if connection broken */
14527 if (drd <= 0) {
14528 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14529 return -1;
14530 }
14531 }
14532
14533 return bufsize;
14534}
14535
14536
14537/********************************************************************/
14539/********************************************************************\
14540
14541 Routine: rpc_register_listener
14542
14543 Purpose: Register the calling process as a MIDAS RPC server. Note
14544 that cm_connnect_experiment must be called prior to any call of
14545 rpc_register_server.
14546
14547 Input:
14548 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14549
14550 Output:
14551 int *plsock Listener socket, can be NULL
14552 int *pport Port under which server is listening, can be NULL
14553
14554 Function value:
14555 RPC_SUCCESS Successful completion
14556 RPC_NET_ERROR Error in socket call
14557 RPC_NOT_REGISTERED cm_connect_experiment was not called
14558
14559\********************************************************************/
14560{
14561 int status;
14562 int lsock;
14563
14565 if (status != RPC_SUCCESS)
14566 return status;
14567
14569 if (status != SS_SUCCESS)
14570 return status;
14571
14572 if (plsock)
14573 *plsock = lsock;
14574
14575 return RPC_SUCCESS;
14576}
14577
14578/********************************************************************/
14580/********************************************************************\
14581
14582 Routine: rpc_register_listener
14583
14584 Purpose: Register the calling process as a MIDAS RPC server. Note
14585 that cm_connnect_experiment must be called prior to any call of
14586 rpc_register_listener.
14587
14588 Input:
14589 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14590 INT *func Default dispatch function
14591
14592 Output:
14593 int *plsock Listener socket, should not be NULL
14594 int *pport Port under which server is listening, can be NULL
14595
14596 Function value:
14597 RPC_SUCCESS Successful completion
14598 RPC_NET_ERROR Error in socket call
14599 RPC_NOT_REGISTERED cm_connect_experiment was not called
14600
14601\********************************************************************/
14602{
14603 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
14605
14606 /* create a socket for listening */
14607 int lsock = 0;
14608 int lport = 0;
14609 std::string errmsg;
14610
14612
14613 if (status != SS_SUCCESS) {
14614 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
14615 return RPC_NET_ERROR;
14616 }
14617
14618 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
14619#if defined(F_SETFD) && defined(FD_CLOEXEC)
14621 if (status < 0) {
14622 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
14623 return RPC_NET_ERROR;
14624 }
14625#endif
14626
14627 /* return port wich OS has choosen */
14628 if (pport) {
14629 *pport = lport;
14630 }
14631
14632 if (plsock)
14633 *plsock = lsock;
14634
14635 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
14636
14637 return RPC_SUCCESS;
14638}
14639
14645
14647static int tls_size = 0;
14648
14649/********************************************************************/
14650INT rpc_execute(INT sock, char *buffer, INT convert_flags)
14651/********************************************************************\
14652
14653 Routine: rpc_execute
14654
14655 Purpose: Execute a RPC command received over the network
14656
14657 Input:
14658 INT sock TCP socket to which the result should be
14659 send back
14660
14661 char *buffer Command buffer
14662 INT convert_flags Flags for data conversion
14663
14664 Output:
14665 none
14666
14667 Function value:
14668 RPC_SUCCESS Successful completion
14669 RPC_INVALID_ID Invalid routine_id received
14670 RPC_NET_ERROR Error in socket call
14671 RPC_EXCEED_BUFFER Not enough memory for network buffer
14672 RPC_SHUTDOWN Shutdown requested
14673 SS_ABORT TCP connection broken
14674 SS_EXIT TCP connection closed
14675
14676\********************************************************************/
14677{
14678 INT i, routine_id, status;
14680 INT tid, flags;
14682 INT param_size, max_size;
14683 void *prpc_param[20];
14684 char debug_line[1024], *return_buffer;
14687#ifdef FIXED_BUFFER
14689#else
14690 int initial_buffer_size = 1024;
14691#endif
14692
14693 /* return buffer must must use thread local storage multi-thread servers */
14694 if (!tls_size) {
14698 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14699 tls_size = 1;
14700 }
14701 for (i = 0; i < tls_size; i++)
14702 if (tls_buffer[i].thread_id == ss_gettid())
14703 break;
14704 if (i == tls_size) {
14705 /* new thread -> allocate new buffer */
14709 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14710 tls_size++;
14711 }
14712
14715 return_buffer = tls_buffer[i].buffer;
14716 assert(return_buffer);
14717
14718 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
14719 // and valgrind complains that we transmit uninitialized data
14720 //memset(return_buffer, 0, return_buffer_size);
14721
14722 /* extract pointer array to parameters */
14723 nc_in = (NET_COMMAND *) buffer;
14724
14725 /* convert header format (byte swapping) */
14726 if (convert_flags) {
14727 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
14728 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
14729 }
14730
14731 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
14732 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
14733 //}
14734
14735 /* no result return as requested */
14736 if (nc_in->header.routine_id & RPC_NO_REPLY)
14737 sock = 0;
14738
14739 /* find entry in rpc_list */
14740 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
14741
14742 int idx = -1;
14743 RPC_LIST rl;
14744
14745 rpc_list_mutex.lock();
14746
14747 for (size_t i = 0; i < rpc_list.size(); i++) {
14748 if (rpc_list[i].id == routine_id) {
14749 idx = i;
14750 rl = rpc_list[idx];
14751 break;
14752 }
14753 }
14754
14755 rpc_list_mutex.unlock();
14756
14757 if (idx < 0) {
14758 cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
14759 return RPC_INVALID_ID;
14760 }
14761
14762 again:
14763
14764 in_param_ptr = nc_in->param;
14765
14766 nc_out = (NET_COMMAND *) return_buffer;
14767 out_param_ptr = nc_out->param;
14768
14769 sprintf(debug_line, "%s(", rl.name);
14770
14771 for (i = 0; rl.param[i].tid != 0; i++) {
14772 tid = rl.param[i].tid;
14773 flags = rl.param[i].flags;
14774
14775 if (flags & RPC_IN) {
14776 param_size = ALIGN8(rpc_tid_size(tid));
14777
14778 if (tid == TID_STRING || tid == TID_LINK)
14779 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
14780
14781 if (flags & RPC_VARARRAY) {
14782 /* for arrays, the size is stored as a INT in front of the array */
14783 param_size = *((INT *) in_param_ptr);
14784 if (convert_flags)
14785 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
14786 param_size = ALIGN8(param_size);
14787
14788 in_param_ptr += ALIGN8(sizeof(INT));
14789 }
14790
14791 if (tid == TID_STRUCT)
14792 param_size = ALIGN8(rl.param[i].n);
14793
14795
14796 /* convert data format */
14797 if (convert_flags) {
14798 if (flags & RPC_VARARRAY)
14799 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
14800 else
14801 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
14802 convert_flags);
14803 }
14804
14805 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
14806 if (rl.param[i].tid == TID_STRING) {
14807 /* check for long strings (db_create_record...) */
14808 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
14809 strcat(debug_line, "\"");
14810 strcat(debug_line, str.c_str());
14811 strcat(debug_line, "\"");
14812 } else
14813 strcat(debug_line, "...");
14814 } else
14815 strcat(debug_line, str.c_str());
14816
14817 in_param_ptr += param_size;
14818 }
14819
14820 if (flags & RPC_OUT) {
14821 param_size = ALIGN8(rpc_tid_size(tid));
14822
14823 if (flags & RPC_VARARRAY || tid == TID_STRING) {
14824
14825 /* save maximum array length from the value of the next argument.
14826 * this means RPC_OUT arrays and strings should always be passed like this:
14827 * rpc_call(..., array_ptr, array_max_size, ...); */
14828
14829 max_size = *((INT *) in_param_ptr);
14830
14831 if (convert_flags)
14832 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
14834
14835 *((INT *) out_param_ptr) = max_size;
14836
14837 /* save space for return array length */
14838 out_param_ptr += ALIGN8(sizeof(INT));
14839
14840 /* use maximum array length from input */
14841 param_size = max_size;
14842 }
14843
14844 if (rl.param[i].tid == TID_STRUCT)
14845 param_size = ALIGN8(rl.param[i].n);
14846
14847 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
14848#ifdef FIXED_BUFFER
14849 cm_msg(MERROR, "rpc_execute",
14850 "return parameters (%d) too large for network buffer (%d)",
14852
14853 return RPC_EXCEED_BUFFER;
14854#else
14855 int itls;
14856 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
14857
14858#if 0
14859 cm_msg(MINFO, "rpc_execute",
14860 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
14862#endif
14863
14865
14867 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
14868
14869 if (!tls_buffer[itls].buffer) {
14870 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
14871 return RPC_EXCEED_BUFFER;
14872 }
14873
14875 return_buffer = tls_buffer[itls].buffer;
14876 assert(return_buffer);
14877
14878 goto again;
14879#endif
14880 }
14881
14882 /* if parameter goes both directions, copy input to output */
14883 if (rl.param[i].flags & RPC_IN)
14884 memcpy(out_param_ptr, prpc_param[i], param_size);
14885
14886 if (_debug_print && !(flags & RPC_IN))
14887 strcat(debug_line, "-");
14888
14890 out_param_ptr += param_size;
14891 }
14892
14893 if (rl.param[i + 1].tid)
14894 strcat(debug_line, ", ");
14895 }
14896
14897 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
14898
14899 strcat(debug_line, ")");
14901
14903
14904 /*********************************\
14905 * call dispatch function *
14906 \*********************************/
14907 if (rl.dispatch)
14908 status = rl.dispatch(routine_id, prpc_param);
14909 else
14911
14912 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
14914
14915 /* return immediately for closed down client connections */
14916 if (!sock && routine_id == RPC_ID_EXIT)
14917 return SS_EXIT;
14918
14919 if (!sock && routine_id == RPC_ID_SHUTDOWN)
14920 return RPC_SHUTDOWN;
14921
14922 /* Return if TCP connection broken */
14923 if (status == SS_ABORT)
14924 return SS_ABORT;
14925
14926 /* if sock == 0, we are in FTCP mode and may not sent results */
14927 if (!sock)
14928 return RPC_SUCCESS;
14929
14930 /* compress variable length arrays */
14931 out_param_ptr = nc_out->param;
14932 for (i = 0; rl.param[i].tid != 0; i++)
14933 if (rl.param[i].flags & RPC_OUT) {
14934 tid = rl.param[i].tid;
14935 flags = rl.param[i].flags;
14936 param_size = ALIGN8(rpc_tid_size(tid));
14937
14938 if (tid == TID_STRING) {
14939 max_size = *((INT *) out_param_ptr);
14940 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14941 // and prpc_param() is now pointing to the wrong place. here we know our string data
14942 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
14943 //const char* param_ptr = (char *) prpc_param[i];
14944 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
14945 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
14946 param_size = strlen(param_ptr) + 1;
14947 param_size = ALIGN8(param_size);
14948
14949 /* move string ALIGN8(sizeof(INT)) left */
14950 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
14951
14952 /* move remaining parameters to end of string */
14953 memmove(out_param_ptr + param_size,
14954 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
14956 }
14957
14958 if (flags & RPC_VARARRAY) {
14959 /* store array length at current out_param_ptr */
14960 max_size = *((INT *) out_param_ptr);
14961 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14962 // and prpc_param() is now pointing to the wrong place. instead, compute location
14963 // of next parameter using max_size. K.O.
14964 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
14965 if (rl.param[i+1].flags & RPC_OUT)
14966 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
14967 else
14968 param_size = *((INT *) prpc_param[i + 1]);
14969 *((INT *) out_param_ptr) = param_size; // store new array size
14970 if (convert_flags)
14972
14973 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
14974
14975 param_size = ALIGN8(param_size);
14976
14977 /* move remaining parameters to end of array */
14978 memmove(out_param_ptr + param_size,
14981 }
14982
14983 if (tid == TID_STRUCT)
14984 param_size = ALIGN8(rl.param[i].n);
14985
14986 /* convert data format */
14987 if (convert_flags) {
14988 if (flags & RPC_VARARRAY)
14990 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
14991 else
14993 rl.param[i].flags | RPC_OUTGOING,
14994 rl.param[i].n * rpc_tid_size(tid), convert_flags);
14995 }
14996
14997 out_param_ptr += param_size;
14998 }
14999
15000 /* send return parameters */
15001 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15002 nc_out->header.routine_id = status;
15003 nc_out->header.param_size = param_size;
15004
15005 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15006
15007 /* convert header format (byte swapping) if necessary */
15008 if (convert_flags) {
15009 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15010 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15011 }
15012
15013 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15014 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15015
15016 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15017
15018 if (status < 0) {
15019 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15020 return RPC_NET_ERROR;
15021 }
15022
15023 /* print return buffer */
15024/*
15025 printf("Return buffer, ID %d:\n", routine_id);
15026 for (i=0; i<param_size ; i++)
15027 {
15028 status = (char) nc_out->param[i];
15029 printf("%02X ", status);
15030 if (i%8 == 7)
15031 printf("\n");
15032 }
15033*/
15034 /* return SS_EXIT if RPC_EXIT is called */
15035 if (routine_id == RPC_ID_EXIT)
15036 return SS_EXIT;
15037
15038 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15039 if (routine_id == RPC_ID_SHUTDOWN)
15040 return RPC_SHUTDOWN;
15041
15042 return RPC_SUCCESS;
15043}
15044
15045/********************************************************************/
15047/********************************************************************\
15048 Routine: rpc_test_rpc
15049
15050 Purpose: Test RPC parameters encoding and decoding
15051
15052 Input:
15053 none
15054
15055 Output:
15056 none
15057
15058 Function value:
15059 RPC_SUCCESS Successful completion
15060
15061\********************************************************************/
15062{
15063 int status = RPC_SUCCESS;
15064
15065 printf("rpc_test_rpc!\n");
15066
15067 int int_out = 0;
15068 int int_inout = 456;
15069
15070 char string_out[32];
15071 char string2_out[48];
15072
15073 char string_inout[25];
15074 strcpy(string_inout, "string_inout");
15075
15076 KEY struct_in;
15077
15078 struct_in.type = 111;
15079 struct_in.num_values = 222;
15080 strcpy(struct_in.name, "name");
15081 struct_in.last_written = 333;
15082
15085
15086 struct_inout.type = 111111;
15087 struct_inout.num_values = 222222;
15088 strcpy(struct_inout.name, "name_name");
15089 struct_inout.last_written = 333333;
15090
15092 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15093
15094 for (int i=0; i<10; i++) {
15095 dwordarray_inout[i] = i*10;
15096 }
15097
15098 char array_in[10];
15099
15100 for (size_t i=0; i<sizeof(array_in); i++) {
15101 array_in[i] = 'a' + i;
15102 }
15103
15104 char array_out[16];
15105 size_t array_out_size = sizeof(array_out);
15106
15108 123,
15109 &int_out,
15110 &int_inout,
15111 "test string",
15112 string_out, sizeof(string_out),
15113 string2_out, sizeof(string2_out),
15114 string_inout, sizeof(string_inout),
15115 &struct_in,
15116 &struct_out,
15117 &struct_inout,
15119 array_in, sizeof(array_in),
15121 );
15122
15123 printf("rpc_call(RPC_TEST2) status %d\n", status);
15124
15125 if (int_out != 789) {
15126 printf("int_out mismatch!\n");
15127 status = 0;
15128 }
15129
15130 if (int_inout != 456*2) {
15131 printf("int_inout mismatch!\n");
15132 status = 0;
15133 }
15134
15135 if (strcmp(string_out, "string_out") != 0) {
15136 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
15137 status = 0;
15138 }
15139
15140 if (strcmp(string2_out, "second string_out") != 0) {
15141 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
15142 status = 0;
15143 }
15144
15145 if (strcmp(string_inout, "return string_inout") != 0) {
15146 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
15147 status = 0;
15148 }
15149
15150 KEY* pkey;
15151
15152 pkey = &struct_in;
15153
15154 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15155
15156 pkey = &struct_out;
15157
15158 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
15159 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);
15160 status = 0;
15161 }
15162
15163 pkey = &struct_inout;
15164
15165 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
15166 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);
15167 status = 0;
15168 }
15169
15170#if 0
15171 if (dwordarray_inout_size != 4*5) {
15172 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
15173 status = 0;
15174 }
15175
15176 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
15177 printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
15178 }
15179#endif
15180
15181#if 0
15182 {RPC_TEST_CXX, "test_cxx",
15183 {{TID_INT32, RPC_IN},
15185 {TID_STRING, RPC_IN},
15189 {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
15190 {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
15191 {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
15195 {0}}},
15196#endif
15197
15198#if 0
15200#endif
15201
15202 return status;
15203}
15204
15205static std::atomic_bool gAllowedHostsEnabled(false);
15206static std::vector<std::string> gAllowedHosts;
15207static std::mutex gAllowedHostsMutex;
15208
15209/********************************************************************/
15211/********************************************************************\
15212 Routine: rpc_clear_allowed_hosts
15213
15214 Purpose: Clear list of allowed hosts and permit connections from anybody
15215
15216 Input:
15217 none
15218
15219 Output:
15220 none
15221
15222 Function value:
15223 RPC_SUCCESS Successful completion
15224
15225\********************************************************************/
15226{
15227 gAllowedHostsMutex.lock();
15228 gAllowedHosts.clear();
15229 gAllowedHostsEnabled = false;
15230 gAllowedHostsMutex.unlock();
15231 return RPC_SUCCESS;
15232}
15233
15234/********************************************************************/
15235INT rpc_add_allowed_host(const char *hostname)
15236/********************************************************************\
15237 Routine: rpc_add_allowed_host
15238
15239 Purpose: Permit connections from listed hosts only
15240
15241 Input:
15242 none
15243
15244 Output:
15245 none
15246
15247 Function value:
15248 RPC_SUCCESS Successful completion
15249 RPC_NO_MEMORY Too many allowed hosts
15250
15251\********************************************************************/
15252{
15253 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
15254
15255 gAllowedHostsMutex.lock();
15256 gAllowedHosts.push_back(hostname);
15257 gAllowedHostsEnabled = true;
15258 gAllowedHostsMutex.unlock();
15259
15260 return RPC_SUCCESS;
15261}
15262
15263/********************************************************************/
15264INT rpc_check_allowed_host(const char *hostname)
15265/********************************************************************\
15266 Routine: rpc_check_allowed_host
15267
15268 Purpose: Check if hostname is permitted to connect
15269
15270 Function value:
15271 RPC_SUCCESS hostname is permitted to connect
15272 RPC_NOT_REGISTERED hostname not permitted to connect
15273
15274\********************************************************************/
15275{
15276 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
15277
15279 return RPC_SUCCESS;
15280
15281 if (strcmp(hostname, "localhost") == 0)
15282 return RPC_SUCCESS;
15283
15284 if (strcmp(hostname, "localhost.localdomain") == 0)
15285 return RPC_SUCCESS;
15286
15287 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
15288 return RPC_SUCCESS;
15289
15290 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
15291 return RPC_SUCCESS;
15292
15294
15295 gAllowedHostsMutex.lock();
15296
15297 for (const auto& h: gAllowedHosts) {
15298 if (h == hostname) {
15300 break;
15301 }
15302 }
15303
15304 gAllowedHostsMutex.unlock();
15305
15306 //if (status != RPC_SUCCESS)
15307 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
15308
15309 return status;
15310}
15311
15312/*------------------------------------------------------------------*/
15314{
15315 std::string hostname;
15316
15317 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
15318
15319 if (status != SS_SUCCESS)
15320 return status;
15321
15322 status = rpc_check_allowed_host(hostname.c_str());
15323
15324 if (status == RPC_SUCCESS)
15325 return RPC_SUCCESS;
15326
15327 static std::atomic_int max_report(10);
15328 if (max_report > 0) {
15329 max_report--;
15330 if (max_report == 0) {
15331 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
15332 } else {
15333 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());
15334 }
15335 }
15336
15337 return RPC_NET_ERROR;
15338}
15339
15340/********************************************************************/
15342/********************************************************************\
15343
15344 Routine: rpc_server_accept
15345
15346 Purpose: Accept new incoming connections
15347
15348 Input:
15349 INT lscok Listen socket
15350
15351 Output:
15352 none
15353
15354 Function value:
15355 RPC_SUCCESS Successful completion
15356 RPC_NET_ERROR Error in socket call
15357 RPC_CONNCLOSED Connection was closed
15358 RPC_SHUTDOWN Listener shutdown
15359 RPC_EXCEED_BUFFER Not enough memory for network buffer
15360
15361\********************************************************************/
15362{
15363 INT i;
15364 INT sock;
15365 char version[NAME_LENGTH], v1[32];
15366 char experiment[NAME_LENGTH];
15367 INT port1, port2, port3;
15368 char *ptr;
15369 char net_buffer[256];
15370 struct linger ling;
15371
15372 static struct callback_addr callback;
15373
15374 if (lsock > 0) {
15375 sock = accept(lsock, NULL, NULL);
15376
15377 if (sock == -1)
15378 return RPC_NET_ERROR;
15379 } else {
15380 /* lsock is stdin -> already connected from inetd */
15381
15382 sock = lsock;
15383 }
15384
15385 /* check access control list */
15388
15389 if (status != RPC_SUCCESS) {
15390 ss_socket_close(&sock);
15391 return RPC_NET_ERROR;
15392 }
15393 }
15394
15395 /* receive string with timeout */
15396 i = recv_string(sock, net_buffer, 256, 10000);
15397 rpc_debug_printf("Received command: %s", net_buffer);
15398
15399 if (i > 0) {
15400 char command = (char) toupper(net_buffer[0]);
15401
15402 //printf("rpc_server_accept: command [%c]\n", command);
15403
15404 switch (command) {
15405 case 'S': {
15406
15407 /*----------- shutdown listener ----------------------*/
15408 ss_socket_close(&sock);
15409 return RPC_SHUTDOWN;
15410 }
15411 case 'I': {
15412
15413 /*----------- return available experiments -----------*/
15414#ifdef LOCAL_ROUTINES
15415 exptab_struct exptab;
15416 cm_read_exptab(&exptab); // thread safe!
15417 for (unsigned i=0; i<exptab.exptab.size(); i++) {
15418 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
15419 const char* str = exptab.exptab[i].name.c_str();
15420 send(sock, str, strlen(str) + 1, 0);
15421 }
15422 send(sock, "", 1, 0);
15423#endif
15424 ss_socket_close(&sock);
15425 break;
15426 }
15427 case 'C': {
15428
15429 /*----------- connect to experiment -----------*/
15430
15431 /* get callback information */
15432 callback.experiment[0] = 0;
15433 port1 = port2 = version[0] = 0;
15434
15435 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
15436
15437 /* parse string in format "C port1 port2 port3 version expt" */
15438 /* example: C 51046 45838 56832 2.0.0 alpha */
15439
15440 port1 = strtoul(net_buffer + 2, &ptr, 0);
15441 port2 = strtoul(ptr, &ptr, 0);
15442 port3 = strtoul(ptr, &ptr, 0);
15443
15444 while (*ptr == ' ')
15445 ptr++;
15446
15447 i = 0;
15448 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
15449 version[i++] = *ptr++;
15450
15451 // ensure that we do not overwrite buffer "version"
15452 assert(i < (int) sizeof(version));
15453 version[i] = 0;
15454
15455 // skip wjatever is left from the "version" string
15456 for (; *ptr != 0 && *ptr != ' ';)
15457 ptr++;
15458
15459 while (*ptr == ' ')
15460 ptr++;
15461
15462 i = 0;
15463 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
15464 experiment[i++] = *ptr++;
15465
15466 // ensure that we do not overwrite buffer "experiment"
15467 assert(i < (int) sizeof(experiment));
15468 experiment[i] = 0;
15469
15471
15472 /* print warning if version patch level doesn't agree */
15473 mstrlcpy(v1, version, sizeof(v1));
15474 if (strchr(v1, '.'))
15475 if (strchr(strchr(v1, '.') + 1, '.'))
15476 *strchr(strchr(v1, '.') + 1, '.') = 0;
15477
15478 char str[100];
15479 mstrlcpy(str, cm_get_version(), sizeof(str));
15480 if (strchr(str, '.'))
15481 if (strchr(strchr(str, '.') + 1, '.'))
15482 *strchr(strchr(str, '.') + 1, '.') = 0;
15483
15484 if (strcmp(v1, str) != 0) {
15485 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
15486 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
15487 }
15488
15493
15495
15496 if (status != SS_SUCCESS) {
15497 ss_socket_close(&sock);
15498 break;
15499 }
15500
15501#ifdef LOCAL_ROUTINES
15502 /* update experiment definition */
15503 exptab_struct exptab;
15504 cm_read_exptab(&exptab); // thread safe!
15505
15506 unsigned idx = 0;
15507 bool found = false;
15508 /* lookup experiment */
15509 if (equal_ustring(callback.experiment.c_str(), "Default")) {
15510 found = true;
15511 idx = 0;
15512 } else {
15513 for (idx = 0; idx < exptab.exptab.size(); idx++) {
15514 if (exptab.exptab[idx].name == callback.experiment) {
15515 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
15516 found = true;
15517 break;
15518 }
15519 }
15520 }
15521 }
15522
15523 if (!found) {
15524 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
15525
15526 send(sock, "2", 2, 0); /* 2 means exp. not found */
15527 ss_socket_close(&sock);
15528 break;
15529 }
15530
15531 callback.directory = exptab.exptab[idx].directory;
15532 callback.user = exptab.exptab[idx].user;
15533
15534 /* create a new process */
15535 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
15536 char debug_str[30];
15537
15542
15543 const char *mserver_path = rpc_get_mserver_path();
15544
15545 const char *argv[10];
15546 argv[0] = mserver_path;
15547 argv[1] = callback.host_name.c_str();
15548 argv[2] = host_port1_str;
15549 argv[3] = host_port2_str;
15550 argv[4] = host_port3_str;
15551 argv[5] = debug_str;
15552 argv[6] = callback.experiment.c_str();
15553 argv[7] = callback.directory.c_str();
15554 argv[8] = callback.user.c_str();
15555 argv[9] = NULL;
15556
15557 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
15558 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
15559 argv[9]);
15560
15562
15563 if (status != SS_SUCCESS) {
15564 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
15565
15566 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
15567 send(sock, str, strlen(str) + 1, 0);
15568 ss_socket_close(&sock);
15569 break;
15570 }
15571
15572 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
15573 send(sock, str, strlen(str) + 1, 0);
15574#endif // LOCAL_ROUTINES
15575 ss_socket_close(&sock);
15576
15577 break;
15578 }
15579 default: {
15580 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
15581 ss_socket_close(&sock);
15582 break;
15583 }
15584 }
15585 } else { /* if i>0 */
15586
15587 /* lingering needed for PCTCP */
15588 ling.l_onoff = 1;
15589 ling.l_linger = 0;
15590 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
15591 ss_socket_close(&sock);
15592 }
15593
15594 return RPC_SUCCESS;
15595}
15596
15597/********************************************************************/
15599/********************************************************************\
15600
15601 Routine: rpc_client_accept
15602
15603 Purpose: midas program accept new RPC connection (run transitions, etc)
15604
15605 Input:
15606 INT lsock Listen socket
15607
15608 Output:
15609 none
15610
15611 Function value:
15612 RPC_SUCCESS Successful completion
15613 RPC_NET_ERROR Error in socket call
15614 RPC_CONNCLOSED Connection was closed
15615 RPC_SHUTDOWN Listener shutdown
15616 RPC_EXCEED_BUFFER Not enough memory for network buffer
15617
15618\********************************************************************/
15619{
15620 INT i, status;
15622 std::string client_program;
15623 std::string host_name;
15624 INT convert_flags;
15625 char net_buffer[256], *p;
15626
15627 int sock = accept(lsock, NULL, NULL);
15628
15629 if (sock == -1)
15630 return RPC_NET_ERROR;
15631
15632 /* check access control list */
15635
15636 if (status != RPC_SUCCESS) {
15637 ss_socket_close(&sock);
15638 return RPC_NET_ERROR;
15639 }
15640 }
15641
15642 host_name = "(unknown)";
15643 client_program = "(unknown)";
15644
15645 /* receive string with timeout */
15646 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
15647 if (i <= 0) {
15648 ss_socket_close(&sock);
15649 return RPC_NET_ERROR;
15650 }
15651
15652 /* get remote computer info */
15653 p = strtok(net_buffer, " ");
15654 if (p != NULL) {
15655 client_hw_type = atoi(p);
15656 p = strtok(NULL, " ");
15657 }
15658 if (p != NULL) {
15659 //version = atoi(p);
15660 p = strtok(NULL, " ");
15661 }
15662 if (p != NULL) {
15663 client_program = p;
15664 p = strtok(NULL, " ");
15665 }
15666 if (p != NULL) {
15667 host_name = p;
15668 p = strtok(NULL, " ");
15669 }
15670
15671 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
15672
15674
15675 /* save information in _server_acception structure */
15676 sa->recv_sock = sock;
15677 sa->send_sock = 0;
15678 sa->event_sock = 0;
15680 sa->host_name = host_name;
15683 sa->watchdog_timeout = 0;
15684 sa->is_mserver = FALSE;
15685
15686 /* send my own computer id */
15688 std::string str = msprintf("%d %s", hw_type, cm_get_version());
15689 status = send(sock, str.c_str(), str.length() + 1, 0);
15690 if (status != (INT) str.length() + 1)
15691 return RPC_NET_ERROR;
15692
15694 sa->convert_flags = convert_flags;
15695
15697
15698 return RPC_SUCCESS;
15699}
15700
15701/********************************************************************/
15703/********************************************************************\
15704
15705 Routine: rpc_server_callback
15706
15707 Purpose: Callback a remote client. Setup _server_acception entry
15708 with optional conversion flags and establish two-way
15709 TCP connection.
15710
15711 Input:
15712 callback_addr pcallback Pointer to a callback structure
15713
15714 Output:
15715 none
15716
15717 Function value:
15718 RPC_SUCCESS Successful completion
15719
15720\********************************************************************/
15721{
15722 INT status;
15723 int recv_sock, send_sock, event_sock;
15724 char str[100];
15725 std::string client_program;
15727 INT convert_flags;
15728 char net_buffer[256];
15729 char *p;
15730 int flag;
15731
15732 /* copy callback information */
15734 //idx = callback.index;
15735
15736 std::string errmsg;
15737
15738 /* create new sockets for TCP */
15740
15741 if (status != SS_SUCCESS) {
15742 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());
15743 ss_socket_close(&recv_sock);
15744 //ss_socket_close(&send_sock);
15745 //ss_socket_close(&event_sock);
15746 return RPC_NET_ERROR;
15747 }
15748
15750
15751 if (status != SS_SUCCESS) {
15752 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());
15753 ss_socket_close(&recv_sock);
15754 ss_socket_close(&send_sock);
15755 //ss_socket_close(&event_sock);
15756 return RPC_NET_ERROR;
15757 }
15758
15760
15761 if (status != SS_SUCCESS) {
15762 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());
15763 ss_socket_close(&recv_sock);
15764 ss_socket_close(&send_sock);
15765 ss_socket_close(&event_sock);
15766 return RPC_NET_ERROR;
15767 }
15768#ifndef OS_ULTRIX /* crashes ULTRIX... */
15769 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
15770 flag = 2 * 1024 * 1024;
15771 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
15772 if (status != 0)
15773 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
15774 strerror(errno));
15775#endif
15776
15777 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
15778 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
15779 ss_socket_close(&recv_sock);
15780 ss_socket_close(&send_sock);
15781 ss_socket_close(&event_sock);
15782 return RPC_NET_ERROR;
15783 }
15784 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
15785
15786 /* get remote computer info */
15787 client_hw_type = strtoul(net_buffer, &p, 0);
15788
15789 while (*p == ' ')
15790 p++;
15791
15792 client_program = p;
15793
15794 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
15795
15796 std::string host_name;
15797
15799
15800 if (status != SS_SUCCESS)
15801 host_name = "unknown";
15802
15803 //printf("rpc_server_callback: mserver acception\n");
15804
15806
15807 /* save information in _server_acception structure */
15808 sa->recv_sock = recv_sock;
15809 sa->send_sock = send_sock;
15810 sa->event_sock = event_sock;
15812 sa->host_name = host_name;
15815 sa->watchdog_timeout = 0;
15816 sa->is_mserver = TRUE;
15817
15818 assert(_mserver_acception == NULL);
15819
15820 _mserver_acception = sa;
15821
15822 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
15823
15824 /* send my own computer id */
15826 sprintf(str, "%d", hw_type);
15827 send(recv_sock, str, strlen(str) + 1, 0);
15828
15830 sa->convert_flags = convert_flags;
15831
15833
15834 if (rpc_is_mserver()) {
15835 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
15836 }
15837
15838 return RPC_SUCCESS;
15839}
15840
15841
15842/********************************************************************/
15844/********************************************************************\
15845
15846 Routine: rpc_server_loop
15847
15848 Purpose: mserver main event loop
15849
15850\********************************************************************/
15851{
15852 while (1) {
15853 int status = ss_suspend(1000, 0);
15854
15855 if (status == SS_ABORT || status == SS_EXIT)
15856 break;
15857
15859 break;
15860
15861 /* check alarms, etc */
15863
15865 }
15866
15867 return RPC_SUCCESS;
15868}
15869
15870/********************************************************************/
15872/********************************************************************\
15873
15874 Routine: rpc_server_receive_rpc
15875
15876 Purpose: Receive rpc commands and execute them. Close the connection
15877 if client has broken TCP pipe.
15878
15879 Function value:
15880 RPC_SUCCESS Successful completion
15881 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15882 SS_EXIT Server connection was closed
15883 SS_ABORT Server connection was broken
15884
15885\********************************************************************/
15886{
15887 int status = 0;
15888 int remaining = 0;
15889
15890 char *buf = NULL;
15891 int bufsize = 0;
15892
15893 do {
15895
15896 if (n_received <= 0) {
15897 status = SS_ABORT;
15898 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
15899 goto error;
15900 }
15901
15902 status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
15903
15904 if (status == SS_ABORT) {
15905 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
15906 goto error;
15907 }
15908
15909 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
15910 if (rpc_is_mserver())
15911 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
15912 goto exit;
15913 }
15914
15915 } while (remaining);
15916
15917 if (buf) {
15918 free(buf);
15919 buf = NULL;
15920 bufsize = 0;
15921 }
15922
15923 return RPC_SUCCESS;
15924
15925 error:
15926
15927 {
15928 char str[80];
15929 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
15930 if (strchr(str, '.'))
15931 *strchr(str, '.') = 0;
15932 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
15933 }
15934
15935 exit:
15936
15938
15939 if (buf) {
15940 free(buf);
15941 buf = NULL;
15942 bufsize = 0;
15943 }
15944
15945 /* disconnect from experiment as MIDAS server */
15946 if (rpc_is_mserver()) {
15947 HNDLE hDB, hKey;
15948
15950
15951 /* only disconnect from experiment if previously connected.
15952 Necessary for pure RPC servers (RPC_SRVR) */
15953 if (hDB) {
15957
15959
15961 }
15962 }
15963
15964 bool is_mserver = sa->is_mserver;
15965
15966 sa->close();
15967
15968 /* signal caller a shutdonw */
15969 if (status == RPC_SHUTDOWN)
15970 return status;
15971
15972 /* only the mserver should stop on server connection closure */
15973 if (!is_mserver) {
15974 return SS_SUCCESS;
15975 }
15976
15977 return status;
15978}
15979
15980/********************************************************************/
15982/********************************************************************\
15983
15984 Routine: rpc_server_receive_event
15985
15986 Purpose: Receive event and dispatch it
15987
15988 Function value:
15989 RPC_SUCCESS Successful completion
15990 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15991 SS_EXIT Server connection was closed
15992 SS_ABORT Server connection was broken
15993
15994\********************************************************************/
15995{
15996 int status = 0;
15997
15998 DWORD start_time = ss_millitime();
15999
16000 //
16001 // THIS IS NOT THREAD SAFE!!!
16002 //
16003 // IT IS ONLY USED BY THE MSERVER
16004 // MSERVER IS SINGLE-THREADED!!!
16005 //
16006
16007 static char *xbuf = NULL;
16008 static int xbufsize = 0;
16009 static bool xbufempty = true;
16010
16011 // short cut
16012 if (sa == NULL && xbufempty)
16013 return RPC_SUCCESS;
16014
16015 static bool recurse = false;
16016
16017 if (recurse) {
16018 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
16019 // do not do anything if we are called recursively
16020 // via recursive ss_suspend() or otherwise. K.O.
16021 if (xbufempty)
16022 return RPC_SUCCESS;
16023 else
16024 return BM_ASYNC_RETURN;
16025 }
16026
16027 recurse = true;
16028
16029 do {
16030 if (xbufempty && sa) {
16032
16033 if (n_received < 0) {
16034 status = SS_ABORT;
16035 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
16036 goto error;
16037 }
16038
16039 if (n_received == 0) {
16040 // no more data in the tcp socket
16041 recurse = false;
16042 return RPC_SUCCESS;
16043 }
16044
16045 xbufempty = false;
16046 }
16047
16048 if (xbufempty) {
16049 // no event in xbuf buffer
16050 recurse = false;
16051 return RPC_SUCCESS;
16052 }
16053
16054 /* send event to buffer */
16055 INT *pbh = (INT *) xbuf;
16056 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
16057
16058 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
16059
16060 //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);
16061
16062 if (status == SS_ABORT) {
16063 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
16064 goto error;
16065 }
16066
16067 if (status == BM_ASYNC_RETURN) {
16068 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
16069 recurse = false;
16070 return status;
16071 }
16072
16073 if (status != BM_SUCCESS) {
16074 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
16075 }
16076
16077 xbufempty = true;
16078
16079 /* repeat for maximum 0.5 sec */
16080 } while (ss_millitime() - start_time < 500);
16081
16082 recurse = false;
16083 return RPC_SUCCESS;
16084
16085 error:
16086
16087 {
16088 char str[80];
16089 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
16090 if (strchr(str, '.'))
16091 *strchr(str, '.') = 0;
16092 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
16093 }
16094
16095 //exit:
16096
16098
16099 /* disconnect from experiment as MIDAS server */
16100 if (rpc_is_mserver()) {
16101 HNDLE hDB, hKey;
16102
16104
16105 /* only disconnect from experiment if previously connected.
16106 Necessary for pure RPC servers (RPC_SRVR) */
16107 if (hDB) {
16111
16113
16115 }
16116 }
16117
16118 bool is_mserver = sa->is_mserver;
16119
16120 sa->close();
16121
16122 /* signal caller a shutdonw */
16123 if (status == RPC_SHUTDOWN)
16124 return status;
16125
16126 /* only the mserver should stop on server connection closure */
16127 if (!is_mserver) {
16128 return SS_SUCCESS;
16129 }
16130
16131 return status;
16132}
16133
16134
16135/********************************************************************/
16137/********************************************************************\
16138
16139 Routine: rpc_flush_event_socket
16140
16141 Purpose: Receive and en-buffer events from the mserver event socket
16142
16143 Function value:
16144 BM_SUCCESS Event socket is empty, all data was read an en-buffered
16145 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
16146 SS_EXIT Server connection was closed
16147 SS_ABORT Server connection was broken
16148
16149\********************************************************************/
16150{
16152
16153 //printf("ss_event_socket_has_data() returned %d\n", has_data);
16154
16155 if (has_data) {
16156 if (timeout_msec == BM_NO_WAIT) {
16157 return BM_ASYNC_RETURN;
16158 } else if (timeout_msec == BM_WAIT) {
16159 return BM_ASYNC_RETURN;
16160 } else {
16162 if (status == SS_ABORT || status == SS_EXIT)
16163 return status;
16164 return BM_ASYNC_RETURN;
16165 }
16166 }
16167
16169
16170 //printf("rpc_server_receive_event() status %d\n", status);
16171
16172 if (status == BM_ASYNC_RETURN) {
16173 return BM_ASYNC_RETURN;
16174 }
16175
16176 if (status == SS_ABORT || status == SS_EXIT)
16177 return status;
16178
16179 return BM_SUCCESS;
16180}
16181
16182/********************************************************************/
16184/********************************************************************\
16185
16186 Routine: rpc_server_shutdown
16187
16188 Purpose: Shutdown RPC server, abort all connections
16189
16190 Input:
16191 none
16192
16193 Output:
16194 none
16195
16196 Function value:
16197 RPC_SUCCESS Successful completion
16198
16199\********************************************************************/
16200{
16201 //printf("rpc_server_shutdown!\n");
16202
16203 struct linger ling;
16204
16205 /* close all open connections */
16206 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16207 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
16209 /* lingering needed for PCTCP */
16210 ling.l_onoff = 1;
16211 ling.l_linger = 0;
16212 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16214
16215 if (sa->send_sock) {
16216 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16218 }
16219
16220 if (sa->event_sock) {
16221 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16223 }
16224 }
16225 }
16226
16227 /* avoid memory leak */
16228 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16230 if (sa) {
16231 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
16232 if (sa == _mserver_acception) {
16233 // do not leave behind a stale pointer!
16235 }
16236 delete sa;
16238 }
16239 }
16240
16241 if (_rpc_registered) {
16244 }
16245
16246 /* free suspend structures */
16248
16249 return RPC_SUCCESS;
16250}
16251
16252
16253/********************************************************************/
16255/********************************************************************\
16256
16257 Routine: rpc_check_channels
16258
16259 Purpose: Check open rpc channels by sending watchdog messages
16260
16261 Input:
16262 none
16263
16264 Output:
16265 none
16266
16267 Function value:
16268 RPC_SUCCESS Channel is still alive
16269 RPC_NET_ERROR Connection is broken
16270
16271\********************************************************************/
16272{
16273 INT status;
16274 NET_COMMAND nc;
16276 struct timeval timeout;
16277
16278 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16279 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
16281 if (sa == NULL)
16282 continue;
16283
16284 if (sa->watchdog_timeout == 0) {
16285 continue;
16286 }
16287
16289 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
16290
16291 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
16292
16293 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
16294
16295 /* send a watchdog message */
16297 nc.header.param_size = 0;
16298
16299 int convert_flags = sa->convert_flags;
16300 if (convert_flags) {
16303 }
16304
16305 /* send the header to the client */
16306 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
16307
16308 if (i < 0) {
16309 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
16310 sa->prog_name.c_str(),
16311 sa->host_name.c_str(),
16312 sa->watchdog_timeout / 1000,
16313 i);
16314
16315 /* disconnect from experiment */
16316 if (rpc_is_mserver()) {
16318 return RPC_NET_ERROR;
16319 }
16320
16321 sa->close();
16322 return RPC_NET_ERROR;
16323 }
16324
16325 /* make some timeout checking */
16326 FD_ZERO(&readfds);
16327 FD_SET(sa->send_sock, &readfds);
16328 FD_SET(sa->recv_sock, &readfds);
16329
16330 timeout.tv_sec = sa->watchdog_timeout / 1000;
16331 timeout.tv_usec = (sa->watchdog_timeout % 1000) * 1000;
16332
16333 do {
16334 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
16335
16336 /* if an alarm signal was cought, restart select with reduced timeout */
16337 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
16338 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
16339
16340 } while (status == -1); /* dont return if an alarm signal was cought */
16341
16342 if (!FD_ISSET(sa->send_sock, &readfds) &&
16343 !FD_ISSET(sa->recv_sock, &readfds)) {
16344
16345 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
16346 sa->prog_name.c_str(),
16347 sa->host_name.c_str(),
16348 sa->watchdog_timeout / 1000);
16349
16350 /* disconnect from experiment */
16351 if (rpc_is_mserver()) {
16353 return RPC_NET_ERROR;
16354 }
16355
16356 sa->close();
16357 return RPC_NET_ERROR;
16358 }
16359
16360 /* receive result on send socket */
16361 if (FD_ISSET(sa->send_sock, &readfds)) {
16362 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
16363 if (i <= 0) {
16364 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
16365 sa->prog_name.c_str(),
16366 sa->host_name.c_str(),
16367 sa->watchdog_timeout / 1000,
16368 i);
16369
16370 /* disconnect from experiment */
16371 if (rpc_is_mserver()) {
16373 return RPC_NET_ERROR;
16374 }
16375
16376 sa->close();
16377 return RPC_NET_ERROR;
16378 }
16379 }
16380 }
16381 }
16382 }
16383
16384 return RPC_SUCCESS;
16385}
16386
16394/********************************************************************\
16395* *
16396* Bank functions *
16397* *
16398\********************************************************************/
16399
16400/********************************************************************/
16406void bk_init(void *event) {
16407 ((BANK_HEADER *) event)->data_size = 0;
16408 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
16409}
16410
16412#ifndef DOXYGEN_SHOULD_SKIP_THIS
16413
16414/********************************************************************/
16415BOOL bk_is32(const void *event)
16416/********************************************************************\
16417
16418 Routine: bk_is32
16419
16420 Purpose: Return true if banks inside event are 32-bit banks
16421
16422 Input:
16423 void *event pointer to the event
16424
16425 Output:
16426 none
16427
16428 Function value:
16429 none
16430
16431\********************************************************************/
16432{
16433 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
16434}
16435
16436/********************************************************************/
16437BOOL bk_is32a(const void *event)
16438/********************************************************************\
16439
16440 Routine: bk_is32a
16441
16442 Purpose: Return true if banks inside event are 32-bit banks
16443 and banks are 64-bit aligned
16444
16445 Input:
16446 void *event pointer to the event
16447
16448 Output:
16449 none
16450
16451 Function value:
16452 none
16453
16454\********************************************************************/
16455{
16456 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
16457}
16458
16460#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16461
16462/********************************************************************/
16469void bk_init32(void *event) {
16470 ((BANK_HEADER *) event)->data_size = 0;
16471 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
16472}
16473
16474/********************************************************************/
16482void bk_init32a(void *event) {
16483 ((BANK_HEADER *) event)->data_size = 0;
16485}
16486
16487/********************************************************************/
16495INT bk_size(const void *event) {
16496 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
16497}
16498
16499static void copy_bk_name(char* dst, const char* src)
16500{
16501 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
16502
16503 if (src[0] == 0) {
16504 // invalid empty name
16505 dst[0] = 0;
16506 dst[1] = 0;
16507 dst[2] = 0;
16508 dst[3] = 0;
16509 return;
16510 }
16511
16512 dst[0] = src[0];
16513
16514 if (src[1] == 0) {
16515 dst[1] = 0;
16516 dst[2] = 0;
16517 dst[3] = 0;
16518 return;
16519 }
16520
16521 dst[1] = src[1];
16522
16523 if (src[2] == 0) {
16524 dst[2] = 0;
16525 dst[3] = 0;
16526 return;
16527 }
16528
16529 dst[2] = src[2];
16530
16531 if (src[3] == 0) {
16532 dst[3] = 0;
16533 return;
16534 }
16535
16536 dst[3] = src[3];
16537}
16538
16539/********************************************************************/
16561void bk_create(void *event, const char *name, WORD type, void **pdata) {
16562 if (bk_is32a((BANK_HEADER *) event)) {
16563 if (((PTYPE) event & 0x07) != 0) {
16564 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
16565 return;
16566 }
16567 BANK32A *pbk32a;
16568
16569 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16570 copy_bk_name(pbk32a->name, name);
16571 pbk32a->type = type;
16572 pbk32a->data_size = 0;
16573 *pdata = pbk32a + 1;
16574 } else if (bk_is32((BANK_HEADER *) event)) {
16575 BANK32 *pbk32;
16576
16577 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16578 copy_bk_name(pbk32->name, name);
16579 pbk32->type = type;
16580 pbk32->data_size = 0;
16581 *pdata = pbk32 + 1;
16582 } else {
16583 BANK *pbk;
16584
16585 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16586 copy_bk_name(pbk->name, name);
16587 pbk->type = type;
16588 pbk->data_size = 0;
16589 *pdata = pbk + 1;
16590 }
16591}
16592
16594#ifndef DOXYGEN_SHOULD_SKIP_THIS
16595
16596/********************************************************************/
16604INT bk_copy(char *pevent, char *psrce, const char *bkname) {
16605
16606 INT status;
16609 BANK *psbkh;
16610 char *pdest;
16611 void *psdata;
16612
16613 // source pointing on the BANKxx
16614 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
16615 // Find requested bank
16617 // Return 0 if not found
16618 if (status != SUCCESS) return 0;
16619
16620 // Check bank type...
16621 // You cannot mix BANK and BANK32 so make sure all the FE use either
16622 // bk_init(pevent) or bk_init32(pevent).
16623 if (bk_is32a(psBkh)) {
16624
16625 // pointer to the source bank header
16626 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
16627 // Data size in the bank
16629
16630 // Get to the end of the event
16631 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16632 // Copy from BANK32 to end of Data
16633 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
16634 // Bring pointer to the next free location
16635 pdest += ALIGN8(bksze) + sizeof(BANK32A);
16636
16637 } else if (bk_is32(psBkh)) {
16638
16639 // pointer to the source bank header
16640 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
16641 // Data size in the bank
16643
16644 // Get to the end of the event
16645 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16646 // Copy from BANK32 to end of Data
16647 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
16648 // Bring pointer to the next free location
16649 pdest += ALIGN8(bksze) + sizeof(BANK32);
16650
16651 } else {
16652
16653 // pointer to the source bank header
16654 psbkh = ((BANK *) psdata - 1);
16655 // Data size in the bank
16656 bksze = psbkh->data_size;
16657
16658 // Get to the end of the event
16659 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16660 // Copy from BANK to end of Data
16661 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
16662 // Bring pointer to the next free location
16663 pdest += ALIGN8(bksze) + sizeof(BANK);
16664 }
16665
16666 // Close bank (adjust BANK_HEADER size)
16667 bk_close(pevent, pdest);
16668 // Adjust EVENT_HEADER size
16669 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
16670 return SUCCESS;
16671}
16672
16673/********************************************************************/
16674int bk_delete(void *event, const char *name)
16675/********************************************************************\
16676
16677 Routine: bk_delete
16678
16679 Purpose: Delete a MIDAS bank inside an event
16680
16681 Input:
16682 void *event pointer to the event
16683 char *name Name of bank (exactly four letters)
16684
16685 Function value:
16686 CM_SUCCESS Bank has been deleted
16687 0 Bank has not been found
16688
16689\********************************************************************/
16690{
16691 BANK *pbk;
16692 DWORD dname;
16693 int remaining;
16694
16695 if (bk_is32a((BANK_HEADER *) event)) {
16696 /* locate bank */
16697 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16698 copy_bk_name((char *) &dname, name);
16699 do {
16700 if (*((DWORD *) pbk32a->name) == dname) {
16701 /* bank found, delete it */
16702 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16703 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16704
16705 /* reduce total event size */
16706 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
16707
16708 /* copy remaining bytes */
16709 if (remaining > 0)
16710 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
16711 return CM_SUCCESS;
16712 }
16713
16714 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16715 } while ((DWORD) ((char *) pbk32a - (char *) event) <
16716 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16717 } else if (bk_is32((BANK_HEADER *) event)) {
16718 /* locate bank */
16719 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16720 copy_bk_name((char *) &dname, name);
16721 do {
16722 if (*((DWORD *) pbk32->name) == dname) {
16723 /* bank found, delete it */
16724 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16725 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16726
16727 /* reduce total event size */
16728 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
16729
16730 /* copy remaining bytes */
16731 if (remaining > 0)
16732 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
16733 return CM_SUCCESS;
16734 }
16735
16736 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16737 } while ((DWORD) ((char *) pbk32 - (char *) event) <
16738 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16739 } else {
16740 /* locate bank */
16741 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16742 copy_bk_name((char *) &dname, name);
16743 do {
16744 if (*((DWORD *) pbk->name) == dname) {
16745 /* bank found, delete it */
16746 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16747 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16748
16749 /* reduce total event size */
16750 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
16751
16752 /* copy remaining bytes */
16753 if (remaining > 0)
16754 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
16755 return CM_SUCCESS;
16756 }
16757
16758 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16759 } while ((DWORD) ((char *) pbk - (char *) event) <
16760 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16761 }
16762
16763 return 0;
16764}
16765
16767#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16768
16769/********************************************************************/
16780INT bk_close(void *event, void *pdata) {
16781 if (bk_is32a((BANK_HEADER *) event)) {
16782 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16783 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
16784 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
16785 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]);
16786 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
16787 return pbk32a->data_size;
16788 } else if (bk_is32((BANK_HEADER *) event)) {
16789 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16790 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
16791 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
16792 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]);
16793 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
16794 return pbk32->data_size;
16795 } else {
16796 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16797 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
16798 if (size > 0xFFFF) {
16799 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16800 size = 0;
16801 }
16802 pbk->data_size = (WORD) (size);
16803 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
16804 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]);
16805 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
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 ((BANK_HEADER *) event)->data_size = size;
16811 return pbk->data_size;
16812 }
16813}
16814
16815/********************************************************************/
16840INT bk_list(const void *event, char *bklist) { /* Full event */
16841 INT nbk;
16842 BANK *pmbk = NULL;
16843 BANK32 *pmbk32 = NULL;
16844 BANK32A *pmbk32a = NULL;
16845 char *pdata;
16846
16847 /* compose bank list */
16848 bklist[0] = 0;
16849 nbk = 0;
16850 do {
16851 /* scan all banks for bank name only */
16852 if (bk_is32a(event)) {
16853 bk_iterate32a(event, &pmbk32a, &pdata);
16854 if (pmbk32a == NULL)
16855 break;
16856 } else if (bk_is32(event)) {
16857 bk_iterate32(event, &pmbk32, &pdata);
16858 if (pmbk32 == NULL)
16859 break;
16860 } else {
16861 bk_iterate(event, &pmbk, &pdata);
16862 if (pmbk == NULL)
16863 break;
16864 }
16865 nbk++;
16866
16867 if (nbk > BANKLIST_MAX) {
16868 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
16869 return (nbk - 1);
16870 }
16871 if (bk_is32a(event))
16872 strncat(bklist, (char *) pmbk32a->name, 4);
16873 else if (bk_is32(event))
16874 strncat(bklist, (char *) pmbk32->name, 4);
16875 else
16876 strncat(bklist, (char *) pmbk->name, 4);
16877 } while (1);
16878 return (nbk);
16879}
16880
16881/********************************************************************/
16889INT bk_locate(const void *event, const char *name, void *pdata) {
16890 BANK *pbk;
16891 BANK32 *pbk32;
16892 BANK32A *pbk32a;
16893 DWORD dname;
16894
16895 if (bk_is32a(event)) {
16896 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16897 copy_bk_name((char *) &dname, name);
16898 while ((DWORD) ((char *) pbk32a - (char *) event) <
16899 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16900 if (*((DWORD *) pbk32a->name) == dname) {
16901 *((void **) pdata) = pbk32a + 1;
16902 if (tid_size[pbk32a->type & 0xFF] == 0)
16903 return pbk32a->data_size;
16904 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16905 }
16906 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16907 }
16908 } else if (bk_is32(event)) {
16909 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16910 copy_bk_name((char *) &dname, name);
16911 while ((DWORD) ((char *) pbk32 - (char *) event) <
16912 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16913 if (*((DWORD *) pbk32->name) == dname) {
16914 *((void **) pdata) = pbk32 + 1;
16915 if (tid_size[pbk32->type & 0xFF] == 0)
16916 return pbk32->data_size;
16917 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
16918 }
16919 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16920 }
16921 } else {
16922 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16923 copy_bk_name((char *) &dname, name);
16924 while ((DWORD) ((char *) pbk - (char *) event) <
16925 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16926 if (*((DWORD *) pbk->name) == dname) {
16927 *((void **) pdata) = pbk + 1;
16928 if (tid_size[pbk->type & 0xFF] == 0)
16929 return pbk->data_size;
16930 return pbk->data_size / tid_size[pbk->type & 0xFF];
16931 }
16932 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16933 }
16934
16935 }
16936
16937 /* bank not found */
16938 *((void **) pdata) = NULL;
16939 return 0;
16940}
16941
16942/********************************************************************/
16952INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
16953 DWORD dname;
16954
16955 if (bk_is32a(pbkh)) {
16956 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
16957 copy_bk_name((char *) &dname, name);
16958 do {
16959 if (*((DWORD *) pbk32a->name) == dname) {
16960 *((void **) pdata) = pbk32a + 1;
16961 if (tid_size[pbk32a->type & 0xFF] == 0)
16962 *bklen = pbk32a->data_size;
16963 else
16964 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16965
16966 *bktype = pbk32a->type;
16967 return 1;
16968 }
16969 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16970 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16971 } else if (bk_is32(pbkh)) {
16972 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
16973 copy_bk_name((char *) &dname, name);
16974 do {
16975 if (*((DWORD *) pbk32->name) == dname) {
16976 *((void **) pdata) = pbk32 + 1;
16977 if (tid_size[pbk32->type & 0xFF] == 0)
16978 *bklen = pbk32->data_size;
16979 else
16980 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
16981
16982 *bktype = pbk32->type;
16983 return 1;
16984 }
16985 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16986 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16987 } else {
16988 BANK *pbk = (BANK *) (pbkh + 1);
16989 copy_bk_name((char *) &dname, name);
16990 do {
16991 if (*((DWORD *) pbk->name) == dname) {
16992 *((void **) pdata) = pbk + 1;
16993 if (tid_size[pbk->type & 0xFF] == 0)
16994 *bklen = pbk->data_size;
16995 else
16996 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
16997
16998 *bktype = pbk->type;
16999 return 1;
17000 }
17001 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17002 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17003 }
17004
17005 /* bank not found */
17006 *((void **) pdata) = NULL;
17007 return 0;
17008}
17009
17010/********************************************************************/
17046INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
17047 if (*pbk == NULL)
17048 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
17049 else
17050 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17051
17052 *((void **) pdata) = (*pbk) + 1;
17053
17054 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17055 *pbk = *((BANK **) pdata) = NULL;
17056 return 0;
17057 }
17058
17059 return (*pbk)->data_size;
17060}
17061
17062
17064#ifndef DOXYGEN_SHOULD_SKIP_THIS
17065
17066/********************************************************************/
17067INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
17068/********************************************************************\
17069
17070 Routine: bk_iterate32
17071
17072 Purpose: Iterate through 32 bit MIDAS banks inside an event
17073
17074 Input:
17075 void *event pointer to the event
17076 BANK32 **pbk32 must be NULL for the first call to bk_iterate
17077
17078 Output:
17079 BANK32 **pbk32 pointer to the bank header
17080 void *pdata pointer to data area of the bank
17081
17082 Function value:
17083 INT size of the bank in bytes
17084
17085\********************************************************************/
17086{
17087 if (*pbk == NULL)
17088 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
17089 else
17090 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17091
17092 *((void **) pdata) = (*pbk) + 1;
17093
17094 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17095 *pbk = NULL;
17096 pdata = NULL;
17097 return 0;
17098 }
17099
17100 return (*pbk)->data_size;
17101}
17102
17103INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
17104/********************************************************************\
17105
17106 Routine: bk_iterate32a
17107
17108 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
17109
17110 Input:
17111 void *event pointer to the event
17112 BANK32A **pbk32a must be NULL for the first call to bk_iterate
17113
17114 Output:
17115 BANK32A **pbk32 pointer to the bank header
17116 void *pdata pointer to data area of the bank
17117
17118 Function value:
17119 INT size of the bank in bytes
17120
17121\********************************************************************/
17122{
17123 if (*pbk32a == NULL)
17124 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
17125 else
17126 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
17127
17128 *((void **) pdata) = (*pbk32a) + 1;
17129
17130 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17131 *pbk32a = NULL;
17132 pdata = NULL;
17133 return 0;
17134 }
17135
17136 return (*pbk32a)->data_size;
17137}
17138
17140#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17141
17142/********************************************************************/
17157INT bk_swap(void *event, BOOL force) {
17159 BANK *pbk;
17160 BANK32 *pbk32;
17161 BANK32A *pbk32a;
17162 void *pdata;
17163 WORD type;
17164
17165 pbh = (BANK_HEADER *) event;
17166
17167 /* only swap if flags in high 16-bit */
17168 if (pbh->flags < 0x10000 && !force)
17169 return 0;
17170
17171 /* swap bank header */
17172 DWORD_SWAP(&pbh->data_size);
17173 DWORD_SWAP(&pbh->flags);
17174
17175 pbk = (BANK *) (pbh + 1);
17176 pbk32 = (BANK32 *) pbk;
17177 pbk32a = (BANK32A *) pbk;
17178
17179 /* scan event */
17180 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
17181 /* swap bank header */
17182 if (bk_is32a(event)) {
17183 DWORD_SWAP(&pbk32a->type);
17184 DWORD_SWAP(&pbk32a->data_size);
17185 pdata = pbk32a + 1;
17186 type = (WORD) pbk32a->type;
17187 } else if (bk_is32(event)) {
17188 DWORD_SWAP(&pbk32->type);
17189 DWORD_SWAP(&pbk32->data_size);
17190 pdata = pbk32 + 1;
17191 type = (WORD) pbk32->type;
17192 } else {
17193 WORD_SWAP(&pbk->type);
17194 WORD_SWAP(&pbk->data_size);
17195 pdata = pbk + 1;
17196 type = pbk->type;
17197 }
17198
17199 /* pbk points to next bank */
17200 if (bk_is32a(event)) {
17201 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17202 pbk = (BANK *) pbk32a;
17203 } else if (bk_is32(event)) {
17204 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17205 pbk = (BANK *) pbk32;
17206 } else {
17207 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17208 pbk32 = (BANK32 *) pbk;
17209 }
17210
17211 switch (type) {
17212 case TID_UINT16:
17213 case TID_INT16:
17214 while ((char *) pdata < (char *) pbk) {
17216 pdata = (void *) (((WORD *) pdata) + 1);
17217 }
17218 break;
17219
17220 case TID_UINT32:
17221 case TID_INT32:
17222 case TID_BOOL:
17223 case TID_FLOAT:
17224 while ((char *) pdata < (char *) pbk) {
17226 pdata = (void *) (((DWORD *) pdata) + 1);
17227 }
17228 break;
17229
17230 case TID_DOUBLE:
17231 case TID_INT64:
17232 case TID_UINT64:
17233 while ((char *) pdata < (char *) pbk) {
17235 pdata = (void *) (((double *) pdata) + 1);
17236 }
17237 break;
17238 }
17239 }
17240
17241 return CM_SUCCESS;
17242}
17243
/* end of bkfunctionc */
17247
17248
17255#ifndef DOXYGEN_SHOULD_SKIP_THIS
17256/********************************************************************/
17257
17258/********************************************************************\
17259* *
17260* Ring buffer functions *
17261* *
17262* Provide an inter-thread buffer scheme for handling front-end *
17263* events. This code allows concurrent data acquisition, calibration *
17264* and network transfer on a multi-CPU machine. One thread reads *
17265* out the data, passes it vis the ring buffer functions *
17266* to another thread running on the other CPU, which can then *
17267* calibrate and/or send the data over the network. *
17268* *
17269\********************************************************************/
17270
17271typedef struct {
17272 unsigned char *buffer;
17273 unsigned int size;
17274 unsigned int max_event_size;
17275 unsigned char *rp;
17276 unsigned char *wp;
17277 unsigned char *ep;
17278} RING_BUFFER;
17279
17280#define MAX_RING_BUFFER 100
17281
17283
17284static volatile int _rb_nonblocking = 0;
17285
17287#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17288
17289/********************************************************************/
17296/********************************************************************\
17297
17298 Routine: rb_set_nonblocking
17299
17300 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17301 environments for stopping all theads without deadlock
17302
17303 Input:
17304 NONE
17305
17306 Output:
17307 NONE
17308
17309 Function value:
17310 DB_SUCCESS Successful completion
17311
17312\********************************************************************/
17313{
17314 _rb_nonblocking = 1;
17315
17316 return DB_SUCCESS;
17317}
17318
17319/********************************************************************/
17336int rb_create(int size, int max_event_size, int *handle)
17337/********************************************************************\
17338
17339 Routine: rb_create
17340
17341 Purpose: Create a ring buffer with a given size
17342
17343 Input:
17344 int size Size of ring buffer, must be larger than
17345 2*max_event_size
17346 int max_event_size Maximum event size to be placed into
17347 ring buffer
17348 Output:
17349 int *handle Handle to ring buffer
17350
17351 Function value:
17352 DB_SUCCESS Successful completion
17353 DB_NO_MEMORY Maximum number of ring buffers exceeded
17354 DB_INVALID_PARAM Invalid event size specified
17355
17356\********************************************************************/
17357{
17358 int i;
17359
17360 for (i = 0; i < MAX_RING_BUFFER; i++)
17361 if (rb[i].buffer == NULL)
17362 break;
17363
17364 if (i == MAX_RING_BUFFER)
17365 return DB_NO_MEMORY;
17366
17367 if (size < max_event_size * 2)
17368 return DB_INVALID_PARAM;
17369
17370 memset(&rb[i], 0, sizeof(RING_BUFFER));
17371 rb[i].buffer = (unsigned char *) M_MALLOC(size);
17372 assert(rb[i].buffer);
17373 rb[i].size = size;
17375 rb[i].rp = rb[i].buffer;
17376 rb[i].wp = rb[i].buffer;
17377 rb[i].ep = rb[i].buffer;
17378
17379 *handle = i + 1;
17380
17381 return DB_SUCCESS;
17382}
17383
17384/********************************************************************/
17390int rb_delete(int handle)
17391/********************************************************************\
17392
17393 Routine: rb_delete
17394
17395 Purpose: Delete a ring buffer
17396
17397 Input:
17398 none
17399 Output:
17400 int handle Handle to ring buffer
17401
17402 Function value:
17403 DB_SUCCESS Successful completion
17404
17405\********************************************************************/
17406{
17407 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17408 return DB_INVALID_HANDLE;
17409
17410 M_FREE(rb[handle - 1].buffer);
17411 rb[handle - 1].buffer = NULL;
17412 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
17413
17414 return DB_SUCCESS;
17415}
17416
17417/********************************************************************/
17427int rb_get_wp(int handle, void **p, int millisec)
17428/********************************************************************\
17429
17430Routine: rb_get_wp
17431
17432 Purpose: Retrieve write pointer where new data can be written
17433
17434 Input:
17435 int handle Ring buffer handle
17436 int millisec Optional timeout in milliseconds if
17437 buffer is full. Zero to not wait at
17438 all (non-blocking)
17439
17440 Output:
17441 char **p Write pointer
17442
17443 Function value:
17444 DB_SUCCESS Successful completion
17445
17446\********************************************************************/
17447{
17448 int h, i;
17449 unsigned char *rp;
17450
17451 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17452 return DB_INVALID_HANDLE;
17453
17454 h = handle - 1;
17455
17456 for (i = 0; i <= millisec / 10; i++) {
17457
17458 rp = rb[h].rp; // keep local copy for convenience
17459
17460 /* check if enough size for wp >= rp without wrap-around */
17461 if (rb[h].wp >= rp
17462 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17463 *p = rb[h].wp;
17464 return DB_SUCCESS;
17465 }
17466
17467 /* check if enough size for wp >= rp with wrap-around */
17468 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17469 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
17470 *p = rb[h].wp;
17471 return DB_SUCCESS;
17472 }
17473
17474 /* check if enough size for wp < rp */
17475 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17476 *p = rb[h].wp;
17477 return DB_SUCCESS;
17478 }
17479
17480 if (millisec == 0)
17481 return DB_TIMEOUT;
17482
17483 if (_rb_nonblocking)
17484 return DB_TIMEOUT;
17485
17486 /* wait one time slice */
17487 ss_sleep(10);
17488 }
17489
17490 return DB_TIMEOUT;
17491}
17492
17493/********************************************************************/
17502int rb_increment_wp(int handle, int size)
17503/********************************************************************\
17504
17505 Routine: rb_increment_wp
17506
17507 Purpose: Increment current write pointer, making the data at
17508 the write pointer available to the receiving thread
17509
17510 Input:
17511 int handle Ring buffer handle
17512 int size Number of bytes placed at the WP
17513
17514 Output:
17515 NONE
17516
17517 Function value:
17518 DB_SUCCESS Successful completion
17519 DB_INVALID_PARAM Event size too large or invalid handle
17520\********************************************************************/
17521{
17522 int h;
17523 unsigned char *new_wp;
17524
17525 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17526 return DB_INVALID_HANDLE;
17527
17528 h = handle - 1;
17529
17530 if ((DWORD) size > rb[h].max_event_size) {
17531 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
17532 size/1024/1024, rb[h].max_event_size/1024/1024);
17533 abort();
17534 }
17535
17536 new_wp = rb[h].wp + size;
17537
17538 /* wrap around wp if not enough space */
17539 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17540 rb[h].ep = new_wp;
17541 new_wp = rb[h].buffer;
17542 assert(rb[h].rp != rb[h].buffer);
17543 } else
17544 if (new_wp > rb[h].ep)
17545 rb[h].ep = new_wp;
17546
17547 rb[h].wp = new_wp;
17548
17549 return DB_SUCCESS;
17550}
17551
17552/********************************************************************/
17568int rb_get_rp(int handle, void **p, int millisec)
17569/********************************************************************\
17570
17571 Routine: rb_get_rp
17572
17573 Purpose: Obtain the current read pointer at which new data is
17574 available with optional timeout
17575
17576 Input:
17577 int handle Ring buffer handle
17578 int millisec Optional timeout in milliseconds if
17579 buffer is full. Zero to not wait at
17580 all (non-blocking)
17581
17582 Output:
17583 char **p Address of pointer pointing to newly
17584 available data. If p == NULL, only
17585 return status.
17586
17587 Function value:
17588 DB_SUCCESS Successful completion
17589
17590\********************************************************************/
17591{
17592 int i, h;
17593
17594 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17595 return DB_INVALID_HANDLE;
17596
17597 h = handle - 1;
17598
17599 for (i = 0; i <= millisec / 10; i++) {
17600
17601 if (rb[h].wp != rb[h].rp) {
17602 if (p != NULL)
17603 *p = rb[handle - 1].rp;
17604 return DB_SUCCESS;
17605 }
17606
17607 if (millisec == 0)
17608 return DB_TIMEOUT;
17609
17610 if (_rb_nonblocking)
17611 return DB_TIMEOUT;
17612
17613 /* wait one time slice */
17614 ss_sleep(10);
17615 }
17616
17617 return DB_TIMEOUT;
17618}
17619
17620/********************************************************************/
17630int rb_increment_rp(int handle, int size)
17631/********************************************************************\
17632
17633 Routine: rb_increment_rp
17634
17635 Purpose: Increment current read pointer, freeing up space for
17636 the writing thread.
17637
17638 Input:
17639 int handle Ring buffer handle
17640 int size Number of bytes to free up at current
17641 read pointer
17642
17643 Output:
17644 NONE
17645
17646 Function value:
17647 DB_SUCCESS Successful completion
17648 DB_INVALID_PARAM Event size too large or invalid handle
17649
17650\********************************************************************/
17651{
17652 int h;
17653
17654 unsigned char *new_rp;
17655 unsigned char *ep;
17656
17657 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17658 return DB_INVALID_HANDLE;
17659
17660 h = handle - 1;
17661
17662 if ((DWORD) size > rb[h].max_event_size)
17663 return DB_INVALID_PARAM;
17664
17665 new_rp = rb[h].rp + size;
17666 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
17667
17668 /* wrap around if end pointer reached */
17669 if (new_rp >= ep && rb[h].wp < ep)
17670 new_rp = rb[h].buffer;
17671
17672 rb[handle - 1].rp = new_rp;
17673
17674 return DB_SUCCESS;
17675}
17676
17677/********************************************************************/
17685int rb_get_buffer_level(int handle, int *n_bytes)
17686/********************************************************************\
17687
17688 Routine: rb_get_buffer_level
17689
17690 Purpose: Return number of bytes in a ring buffer
17691
17692 Input:
17693 int handle Handle of the buffer to get the info
17694
17695 Output:
17696 int *n_bytes Number of bytes in buffer
17697
17698 Function value:
17699 DB_SUCCESS Successful completion
17700 DB_INVALID_HANDLE Buffer handle is invalid
17701
17702\********************************************************************/
17703{
17704 int h;
17705
17706 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17707 return DB_INVALID_HANDLE;
17708
17709 h = handle - 1;
17710
17711 if (rb[h].wp >= rb[h].rp)
17712 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
17713 else
17714 *n_bytes =
17715 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
17716
17717 return DB_SUCCESS;
17718}
17719
/* end of rbfunctionc */
17721
17722
17724{
17725 if (format == FORMAT_FIXED) {
17726 int status;
17727 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
17728 if (status != DB_SUCCESS) {
17729 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
17730 return status;
17731 }
17732 return SUCCESS;
17733 } else if (format == FORMAT_MIDAS) {
17734 INT size, i, status, n_data;
17735 int n;
17736 char *pdata, *pdata0;
17737
17738 char name[5];
17740 BANK *pbk;
17741 BANK32 *pbk32;
17742 BANK32A *pbk32a;
17743 DWORD bkname;
17744 WORD bktype;
17746 KEY key;
17747
17748 pbh = (BANK_HEADER *) (pevent + 1);
17749 pbk = NULL;
17750 pbk32 = NULL;
17751 pbk32a = NULL;
17752
17753 /* count number of banks */
17754 for (n=0 ; ; n++) {
17755 if (bk_is32a(pbh)) {
17757 if (pbk32a == NULL)
17758 break;
17759 } else if (bk_is32(pbh)) {
17761 if (pbk32 == NULL)
17762 break;
17763 } else {
17764 bk_iterate(pbh, &pbk, &pdata);
17765 if (pbk == NULL)
17766 break;
17767 }
17768 }
17769
17770 /* build array of keys */
17771 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
17772
17773 pbk = NULL;
17774 pbk32 = NULL;
17775 n = 0;
17776 do {
17777 /* scan all banks */
17778 if (bk_is32a(pbh)) {
17779 size = bk_iterate32a(pbh, &pbk32a, &pdata);
17780 if (pbk32a == NULL)
17781 break;
17782 bkname = *((DWORD *) pbk32a->name);
17783 bktype = (WORD) pbk32a->type;
17784 } else if (bk_is32(pbh)) {
17785 size = bk_iterate32(pbh, &pbk32, &pdata);
17786 if (pbk32 == NULL)
17787 break;
17788 bkname = *((DWORD *) pbk32->name);
17789 bktype = (WORD) pbk32->type;
17790 } else {
17791 size = bk_iterate(pbh, &pbk, &pdata);
17792 if (pbk == NULL)
17793 break;
17794 bkname = *((DWORD *) pbk->name);
17795 bktype = (WORD) pbk->type;
17796 }
17797
17798 n_data = size;
17799 if (rpc_tid_size(bktype & 0xFF))
17800 n_data /= rpc_tid_size(bktype & 0xFF);
17801
17802 /* get bank key */
17803 *((DWORD *) name) = bkname;
17804 name[4] = 0;
17805 /* record the start of the data in case it is struct */
17806 pdata0 = pdata;
17807 if (bktype == TID_STRUCT) {
17809 if (status != DB_SUCCESS) {
17810 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
17811 continue;
17812 }
17813
17814 /* write structured bank */
17815 for (i = 0;; i++) {
17818 break;
17819
17820 db_get_key(hDB, hKeyl, &key);
17821
17822 /* adjust for alignment */
17823 if (key.type != TID_STRING && key.type != TID_LINK)
17825
17827 if (status != DB_SUCCESS) {
17828 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17829 continue;
17830 }
17831 hKeys[n++] = hKeyl;
17832
17833 /* shift data pointer to next item */
17835 }
17836 } else {
17837 /* write variable length bank */
17839 if (status != DB_SUCCESS) {
17841 if (status != DB_SUCCESS) {
17842 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);
17843 continue;
17844 }
17846 if (status != DB_SUCCESS) {
17847 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);
17848 continue;
17849 }
17850 }
17851 if (n_data > 0) {
17852 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
17853 if (status != DB_SUCCESS) {
17854 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17855 }
17856 hKeys[n++] = hKeyRoot;
17857 }
17858 }
17859 } while (1);
17860
17861 /* notify all hot-lined clients in one go */
17863
17864 free(hKeys);
17865
17866 return SUCCESS;
17867 } else {
17868 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
17869 return CM_DB_ERROR;
17870 }
17871}
17872
17873/* emacs
17874 * Local Variables:
17875 * tab-width: 8
17876 * c-basic-offset: 3
17877 * indent-tabs-mode: nil
17878 * End:
17879 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11435
std::atomic_bool connected
Definition midas.cxx:11434
std::string client_name
Definition midas.cxx:11439
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:66
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:16482
INT bk_close(void *event, void *pdata)
Definition midas.cxx:16780
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:17103
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:16499
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:17157
BOOL bk_is32a(const void *event)
Definition midas.cxx:16437
int bk_delete(void *event, const char *name)
Definition midas.cxx:16674
BOOL bk_is32(const void *event)
Definition midas.cxx:16415
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:17067
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:16889
void bk_init(void *event)
Definition midas.cxx:16406
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:16840
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:16604
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:17046
void bk_init32(void *event)
Definition midas.cxx:16469
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:16561
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:16952
INT bk_size(const void *event)
Definition midas.cxx:16495
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8787
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:10483
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6717
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6189
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9678
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:9987
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:6464
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:10833
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10063
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7280
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11272
INT bm_delete_request(INT request_id)
Definition midas.cxx:8584
INT bm_close_all_buffers(void)
Definition midas.cxx:7243
INT bm_poll_event()
Definition midas.cxx:11126
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:8314
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6586
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6223
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10809
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9603
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9589
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8966
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6066
INT bm_empty_buffers()
Definition midas.cxx:11240
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8720
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9069
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:8465
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5922
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8140
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6307
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10650
INT bm_check_buffers()
Definition midas.cxx:10954
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:8992
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:8292
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8518
static double _bm_mutex_timeout_sec
Definition midas.cxx:5920
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7096
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:9778
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8281
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:9504
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11281
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6256
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6015
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:8936
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8865
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6117
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8855
static DWORD _bm_max_event_size
Definition midas.cxx:5914
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6408
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10207
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8823
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8622
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:10264
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:9080
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7075
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9705
static int _bm_lock_timeout
Definition midas.cxx:5919
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:8891
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10731
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6391
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6035
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:10902
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:9392
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:3593
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7400
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4165
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:3353
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7323
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4082
INT cm_yield(INT millisec)
Definition midas.cxx:5642
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:3317
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11284
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:5999
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2586
INT cm_start_watchdog_thread()
Definition midas.cxx:7355
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:10918
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:3996
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3783
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:7904
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4029
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5286
INT cm_stop_watchdog_thread()
Definition midas.cxx:7370
std::string cm_asctime()
Definition midas.cxx:1412
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5790
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:7975
INT cm_periodic_tasks()
Definition midas.cxx:5579
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8037
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5385
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5238
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:3452
static BOOL _ctrlc_pressed
Definition midas.cxx:5439
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3404
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5456
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5723
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3336
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7610
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:6622
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2670
std::string cm_expand_env(const char *str)
Definition midas.cxx:7710
std::string cm_get_client_name()
Definition midas.cxx:2059
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7946
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5461
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1545
static void rpc_client_shutdown()
Definition midas.cxx:12632
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7322
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:6152
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11048
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:7349
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7742
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:3819
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5843
void cm_test_expand_env()
Definition midas.cxx:7757
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3837
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2166
static INT _requested_transition
Definition midas.cxx:3818
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:7925
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3669
INT cm_check_deferred_transition()
Definition midas.cxx:3889
std::string cm_get_experiment_name()
Definition midas.cxx:1580
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3723
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:3976
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1852
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7321
const char * cm_get_revision()
Definition midas.cxx:1484
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7329
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:2939
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5452
static INT tr_main_thread(void *param)
Definition midas.cxx:5254
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5441
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:5267
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:4400
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7520
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:4523
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:5706
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:7118
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:5556
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:5240
int ss_file_link_exist(const char *path)
Definition system.cxx:7154
std::string ss_getcwd()
Definition system.cxx:5770
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:7440
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:5393
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5346
int ss_dir_exist(const char *path)
Definition system.cxx:7186
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:7219
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:5448
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:7891
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:5225
char * ss_gets(char *string, int size)
Definition system.cxx:7770
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5629
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:5279
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:6713
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:13291
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:13934
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:13514
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13813
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:13552
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:13599
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:12623
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12291
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:12800
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14005
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:17568
#define MAX_RING_BUFFER
Definition midas.cxx:17280
int rb_delete(int handle)
Definition midas.cxx:17390
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:17427
int rb_increment_rp(int handle, int size)
Definition midas.cxx:17630
int rb_set_nonblocking()
Definition midas.cxx:17295
static volatile int _rb_nonblocking
Definition midas.cxx:17284
int rb_increment_wp(int handle, int size)
Definition midas.cxx:17502
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:17336
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:17685
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:17282
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:15235
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11706
#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:12012
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14538
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:14647
#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:14387
INT rpc_get_opt_tcp_size()
Definition midas.cxx:13872
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12676
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11576
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13472
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11827
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:15702
static std::mutex _client_connections_mutex
Definition midas.cxx:11495
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:12998
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
static std::mutex gAllowedHostsMutex
Definition midas.cxx:15207
INT recv_tcp_check(int sock)
Definition midas.cxx:14357
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15871
#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:13051
static RPC_SERVER_ACCEPTION * rpc_get_server_acception(int idx)
Definition midas.cxx:11505
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11513
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11583
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12381
std::string rpc_get_name()
Definition midas.cxx:13084
#define RPC_ID_WATCHDOG
Definition mrpc.h:133
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11518
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
bool rpc_is_remote(void)
Definition midas.cxx:12761
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13161
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11771
static int _tr_fifo_rp
Definition midas.cxx:14054
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14120
#define RPC_RC_TRANSITION
Definition mrpc.h:116
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13193
#define RPC_ID_EXIT
Definition mrpc.h:135
INT rpc_server_loop(void)
Definition midas.cxx:15843
INT rpc_clear_allowed_hosts()
Definition midas.cxx:15210
int rpc_test_rpc()
Definition midas.cxx:15046
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12805
INT rpc_get_hw_type()
Definition midas.cxx:12834
INT rpc_deregister_functions()
Definition midas.cxx:11870
bool rpc_is_connected(void)
Definition midas.cxx:12783
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13064
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11573
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:14646
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11496
#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:13231
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12612
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:15206
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:13663
static std::mutex rpc_list_mutex
Definition midas.cxx:11574
void rpc_client_check()
Definition midas.cxx:12270
const char * rpc_tid_name(INT id)
Definition midas.cxx:11764
INT rpc_flush_event()
Definition midas.cxx:14038
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11808
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11502
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:15313
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:16183
static int _tr_fifo_wp
Definition midas.cxx:14053
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:16136
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:13901
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14052
static std::string _mserver_path
Definition midas.cxx:13048
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:12973
INT rpc_server_disconnect()
Definition midas.cxx:12705
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13134
INT rpc_client_accept(int lsock)
Definition midas.cxx:15598
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11626
INT rpc_execute(INT sock, char *buffer, INT convert_flags)
Definition midas.cxx:14650
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:13864
#define RPC_BM_GET_BUFFER_LEVEL
Definition mrpc.h:40
int rpc_name_tid(const char *name)
Definition midas.cxx:11778
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:14579
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:15981
#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:15341
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14051
bool rpc_is_mserver(void)
Definition midas.cxx:12818
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11503
static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
Definition midas.cxx:14197
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11611
#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:13919
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11499
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13402
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:13925
INT rpc_set_name(const char *name)
Definition midas.cxx:13108
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:11897
#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:11945
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11498
INT rpc_check_channels(void)
Definition midas.cxx:16254
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14056
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:11931
#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:11661
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11642
#define RPC_CM_GET_WATCHDOG_INFO
Definition mrpc.h:24
INT rpc_get_convert_flags(void)
Definition midas.cxx:13030
INT rpc_tid_size(INT id)
Definition midas.cxx:11757
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:15264
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11681
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:7791
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:7838
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:17723
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8063
#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:1611
#define M_MALLOC(x)
Definition midas.h:1552
#define CINT(_i)
Definition midas.h:1622
#define RPC_OUT
Definition midas.h:1581
#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:1580
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1606
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1607
#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:1595
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1448
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:907
#define RPC_OUTGOING
Definition midas.h:1585
#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:1646
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1582
#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:1554
#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:1612
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1584
#define POINTER_T
Definition midas.h:166
#define BANK_FORMAT_64BIT_ALIGNED
Definition midas.h:1205
#define BANK_FORMAT_32BIT
Definition midas.h:1204
#define CF_VAX2IEEE
Definition midas.h:1613
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:1583
#define BANK_FORMAT_VERSION
Definition midas.h:1203
#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:1230
DWORD data_size
Definition midas.h:1224
Definition midas.h:1215
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:6427
int last_count_lock
Definition midas.cxx:6431
BOOL get_all_flag
Definition midas.cxx:6423
int count_lock
Definition midas.cxx:6426
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6440
int count_write_wait
Definition midas.cxx:6429
double bytes_read
Definition midas.cxx:6436
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6437
int wait_client_index
Definition midas.cxx:6433
DWORD time_write_wait
Definition midas.cxx:6430
int count_read
Definition midas.cxx:6435
double bytes_sent
Definition midas.cxx:6428
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6438
DWORD wait_start_time
Definition midas.cxx:6432
int max_requested_space
Definition midas.cxx:6434
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:11278
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:17276
unsigned char * buffer
Definition midas.cxx:17272
unsigned int max_event_size
Definition midas.cxx:17274
unsigned int size
Definition midas.cxx:17273
unsigned char * rp
Definition midas.cxx:17275
unsigned char * ep
Definition midas.cxx:17277
RPC_HANDLER * dispatch
Definition midas.h:1601
const char * name
Definition midas.h:1599
midas_thread_t thread_id
Definition midas.cxx:14641
int buffer_size
Definition midas.cxx:14642
char * buffer
Definition midas.cxx:14643
int transition
Definition midas.cxx:14045
time_t trans_time
Definition midas.cxx:14047
int run_number
Definition midas.cxx:14046
int sequence_number
Definition midas.cxx:14048
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:3945
int transition
Definition midas.cxx:3930
std::string host_name
Definition midas.cxx:3936
DWORD connect_end_time
Definition midas.cxx:3947
std::atomic_int status
Definition midas.cxx:3940
~TrClient()
Definition midas.cxx:3958
std::vector< int > wait_for_index
Definition midas.cxx:3935
DWORD init_time
Definition midas.cxx:3943
std::string key_name
Definition midas.cxx:3939
DWORD rpc_end_time
Definition midas.cxx:3950
std::string waiting_for_client
Definition midas.cxx:3944
DWORD rpc_timeout
Definition midas.cxx:3948
int async_flag
Definition midas.cxx:3932
int port
Definition midas.cxx:3938
DWORD rpc_start_time
Definition midas.cxx:3949
int sequence_number
Definition midas.cxx:3934
int debug_flag
Definition midas.cxx:3933
int run_number
Definition midas.cxx:3931
DWORD end_time
Definition midas.cxx:3951
DWORD connect_start_time
Definition midas.cxx:3946
std::string client_name
Definition midas.cxx:3937
std::string errorstr
Definition midas.cxx:3942
void Print() const
Definition midas.cxx:3964
std::thread * thread
Definition midas.cxx:3941
int transition
Definition midas.cxx:3983
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:3991
int async_flag
Definition midas.cxx:3985
DWORD end_time
Definition midas.cxx:3990
int run_number
Definition midas.cxx:3984
int status
Definition midas.cxx:3987
DWORD start_time
Definition midas.cxx:3989
std::string errorstr
Definition midas.cxx:3988
int debug_flag
Definition midas.cxx:3986
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