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;
215 dispatcher = NULL;
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
261static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
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
286static DBG_MEM_LOC *_mem_loc = NULL;
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
323 _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
324 }
325
326 assert(_mem_loc != NULL);
327
328 _mem_loc[i].adr = adr;
329 _mem_loc[i].size = size;
330 strcpy(_mem_loc[i].file, file);
331 _mem_loc[i].line = line;
332
333 f = fopen("mem.txt", "w");
334
335 assert(f != NULL);
336
337 for (i = 0; i < _n_mem; i++)
338 if (_mem_loc[i].adr)
339 fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size, _mem_loc[i].adr);
340
341 fclose(f);
342
343 return adr;
344}
345
346void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line) {
347 void *adr;
348
349 adr = dbg_malloc(size * count, file, line);
350 if (adr)
351 memset(adr, 0, size * count);
352
353 return adr;
354}
355
356void dbg_free(void *adr, char *file, int line) {
357 FILE *f;
358 int i;
359
360 free(adr);
361
362 for (i = 0; i < _n_mem; i++)
363 if (_mem_loc[i].adr == adr)
364 break;
365
366 if (i < _n_mem)
367 _mem_loc[i].adr = NULL;
368
369 f = fopen("mem.txt", "w");
370
371 assert(f != NULL);
372
373 for (i = 0; i < _n_mem; i++)
374 if (_mem_loc[i].adr)
375 fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
376 file, line, _mem_loc[i].size, _mem_loc[i].adr);
377
378 fclose(f);
379}
380
381static std::vector<std::string> split(const char* sep, const std::string& s)
382{
383 unsigned sep_len = strlen(sep);
384 std::vector<std::string> v;
385 std::string::size_type pos = 0;
386 while (1) {
387 std::string::size_type next = s.find(sep, pos);
388 if (next == std::string::npos) {
389 v.push_back(s.substr(pos));
390 break;
391 }
392 v.push_back(s.substr(pos, next-pos));
393 pos = next+sep_len;
394 }
395 return v;
396}
397
398static std::string join(const char* sep, const std::vector<std::string>& v)
399{
400 std::string s;
401
402 for (unsigned i=0; i<v.size(); i++) {
403 if (i>0) {
404 s += sep;
405 }
406 s += v[i];
407 }
408
409 return s;
410}
411
412bool ends_with_char(const std::string& s, char c)
413{
414 if (s.length() < 1)
415 return false;
416 return s[s.length()-1] == c;
417}
418
419std::string msprintf(const char *format, ...) {
420 assert(format != NULL);
421 va_list ap, ap1;
422 va_start(ap, format);
423 va_copy(ap1, ap);
424 int length = vsnprintf(nullptr, 0, format, ap1);
425 va_end(ap1);
426 if (length < 0) {
427 va_end(ap);
428 return "";
429 }
430 size_t size = (size_t) length + 1;
431 char *buffer = (char *)malloc(size);
432 if (!buffer) {
433 va_end(ap);
434 return "";
435 }
436 vsnprintf(buffer, size, format, ap);
437 va_end(ap);
438 std::string s(buffer);
439 free(buffer);
440 return s;
441}
442
443/********************************************************************\
444* *
445* Common message functions *
446* *
447\********************************************************************/
448
449typedef int (*MessagePrintCallback)(const char *);
450
451static std::atomic<MessagePrintCallback> _message_print{puts};
452
453static std::atomic_int _message_mask_system{MT_ALL};
454static std::atomic_int _message_mask_user{MT_ALL};
455
456
458#endif /* DOXYGEN_SHOULD_SKIP_THIS */
459
465/********************************************************************/
473std::string cm_get_error(INT code)
474{
475 for (int i = 0; _error_table[i].code; i++) {
476 if (_error_table[i].code == code) {
477 return _error_table[i].string;
478 }
479 }
480
481 return msprintf("unlisted status code %d", code);
482}
483
484/********************************************************************/
486
487 return CM_SUCCESS;
488}
489
490/********************************************************************/
491
493 //printf("cm_msg_open_buffer!\n");
494 if (_msg_buffer == 0) {
496 if (status != BM_SUCCESS && status != BM_CREATED) {
497 return status;
498 }
499 }
500 return CM_SUCCESS;
501}
502
503/********************************************************************/
504
506 //printf("cm_msg_close_buffer!\n");
507 if (_msg_buffer) {
509 _msg_buffer = 0;
510 }
511 return CM_SUCCESS;
512}
513
514/********************************************************************/
515
523 std::string path;
524
525 cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
526
527 /* extract directory name from full path name of midas.log */
528 size_t pos = path.rfind(DIR_SEPARATOR);
529 if (pos != std::string::npos) {
530 path.resize(pos);
531 } else {
532 path = "";
533 }
534
535 //printf("cm_msg_facilities: path [%s]\n", path.c_str());
536
537 STRING_LIST flist;
538
539 ss_file_find(path.c_str(), "*.log", &flist);
540
541 for (size_t i = 0; i < flist.size(); i++) {
542 const char *p = flist[i].c_str();
543 if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
544 size_t pos = flist[i].rfind('.');
545 if (pos != std::string::npos) {
546 flist[i].resize(pos);
547 }
548 list->push_back(flist[i]);
549 }
550 }
551
552 return SUCCESS;
553}
554
555/********************************************************************/
556
557void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
558 HNDLE hDB;
559 int status;
560
562
563 // check for call to cm_msg() before MIDAS is fully initialized
564 // or after MIDAS is partially shutdown.
565 if (status != CM_SUCCESS) {
566 if (filename)
567 *filename = std::string(fac) + ".log";
568 if (linkname)
569 *linkname = "";
570 if (linktarget)
571 *linktarget = "";
572 return;
573 }
574
575 if (filename)
576 *filename = "";
577 if (linkname)
578 *linkname = "";
579 if (linktarget)
580 *linktarget = "";
581
582 std::string facility;
583 if (fac && fac[0])
584 facility = fac;
585 else
586 facility = "midas";
587
588 std::string message_format;
589 db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
590 if (message_format.find('%') != std::string::npos) {
591 /* replace stings such as %y%m%d with current date */
592 struct tm tms;
593
594 ss_tzset();
595 if (t == 0)
596 time(&t);
597 localtime_r(&t, &tms);
598
599 char de[256];
600 de[0] = '_';
601 strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
602 message_format = de;
603 }
604
605 std::string message_dir;
606 db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
607 if (message_dir.empty()) {
608 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
609 if (message_dir.empty()) {
610 message_dir = cm_get_path();
611 if (message_dir.empty()) {
612 message_dir = ss_getcwd();
613 }
614 }
615 }
616
617 // prepend experiment directory
618 if (message_dir[0] != DIR_SEPARATOR)
619 message_dir = cm_get_path() + message_dir;
620
621 if (message_dir.back() != DIR_SEPARATOR)
622 message_dir.push_back(DIR_SEPARATOR);
623
624 if (filename)
625 *filename = message_dir + facility + message_format + ".log";
626 if (!message_format.empty()) {
627 if (linkname)
628 *linkname = message_dir + facility + ".log";
629 if (linktarget)
630 *linktarget = facility + message_format + ".log";
631 }
632}
633
634/********************************************************************/
665INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
666 _message_mask_system = system_mask;
667 _message_mask_user = user_mask;
668 _message_print = func;
669
670 return BM_SUCCESS;
671}
672
673/********************************************************************/
682INT cm_msg_log(INT message_type, const char *facility, const char *message) {
683 INT status;
684
685 if (rpc_is_remote()) {
686 if (rpc_is_connected()) {
687 status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
688 if (status != RPC_SUCCESS) {
689 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);
690 }
691 return status;
692 } else {
693 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
694 return RPC_NET_ERROR;
695 }
696 }
697
698 if (message_type != MT_DEBUG) {
699 std::string filename, linkname, linktarget;
700
701 cm_msg_get_logfile(facility, 0, &filename, &linkname, &linktarget);
702
703#ifdef OS_LINUX
704 if (!linkname.empty()) {
705 //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
706 // If filename does not exist, user just switched from non-date format to date format.
707 // In that case we must copy linkname to filename, otherwise messages might get lost.
708 if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
709 ss_file_copy(linkname.c_str(), filename.c_str(), true);
710 }
711
712 unlink(linkname.c_str());
713 status = symlink(linktarget.c_str(), linkname.c_str());
714 if (status != 0) {
715 fprintf(stderr,
716 "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
717 linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
718 }
719 }
720#endif
721
722 int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
723 if (fh < 0) {
724 fprintf(stderr,
725 "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
726 message, filename.c_str(), errno, strerror(errno));
727 } else {
728
729 struct timeval tv;
730 struct tm tms;
731
732 ss_tzset();
733 gettimeofday(&tv, NULL);
734 localtime_r(&tv.tv_sec, &tms);
735
736 char str[256];
737 strftime(str, sizeof(str), "%H:%M:%S", &tms);
738 sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
739 strftime(str + strlen(str), sizeof(str), "%Y/%m/%d", &tms);
740
741 std::string msg;
742 msg += str;
743 msg += " ";
744 msg += message;
745 msg += "\n";
746
747 /* avoid c++ complaint about comparison between
748 unsigned size_t returned by msg.length() and
749 signed ssize_t returned by write() */
750 ssize_t len = msg.length();
751
752 /* atomic write, no need to take a semaphore */
753 ssize_t wr = write(fh, msg.c_str(), len);
754
755 if (wr < 0) {
756 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
757 } else if (wr != len) {
758 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);
759 }
760
761 close(fh);
762 }
763 }
764
765 return CM_SUCCESS;
766}
767
768
769static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
770{
771 /* strip path */
772 const char* pc = filename + strlen(filename);
773 while (*pc != '\\' && *pc != '/' && pc != filename)
774 pc--;
775 if (pc != filename)
776 pc++;
777
778 /* convert type to string */
779 std::string type_str;
780 if (message_type & MT_ERROR)
781 type_str += MT_ERROR_STR;
782 if (message_type & MT_INFO)
783 type_str += MT_INFO_STR;
784 if (message_type & MT_DEBUG)
785 type_str += MT_DEBUG_STR;
786 if (message_type & MT_USER)
787 type_str += MT_USER_STR;
788 if (message_type & MT_LOG)
789 type_str += MT_LOG_STR;
790 if (message_type & MT_TALK)
791 type_str += MT_TALK_STR;
792
793 std::string message;
794
795 /* print client name into string */
796 if (message_type == MT_USER)
797 message = msprintf("[%s] ", routine);
798 else {
799 std::string name = rpc_get_name();
800 if (name.length() > 0)
801 message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
802 else
803 message = "";
804 }
805
806 /* preceed error messages with file and line info */
807 if (message_type == MT_ERROR) {
808 message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
809 } else if (message_type == MT_USER) {
810 message = msprintf("[%s,%s] ", routine, type_str.c_str());
811 }
812
813 int bufsize = 1024;
814 char* buf = (char*)malloc(bufsize);
815 assert(buf);
816
817 for (int i=0; i<10; i++) {
818 va_list ap;
819 va_copy(ap, *argptr);
820
821 /* print argument list into message */
822 int n = vsnprintf(buf, bufsize, format, ap);
823
824 //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
825
826 va_end(ap);
827
828 if (n < 0) {
829 buf[0] = 0;
830 break;
831 }
832
833 if (n < bufsize) {
834 break;
835 }
836
837 bufsize = n + 1;
838 buf = (char*)realloc(buf, bufsize);
839 assert(buf);
840 }
841
842 message += buf;
843 free(buf);
844
845 return message;
846}
847
848static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
849 //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
850
851 /* send event if not of type MLOG */
852 if (message_type != MT_LOG) {
853 if (_msg_buffer) {
854 /* copy message to event */
855 size_t len = strlen(send_message);
856 int event_length = sizeof(EVENT_HEADER) + len + 1;
857 char event[event_length];
858 EVENT_HEADER *pevent = (EVENT_HEADER *) event;
859
860 memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
861
862 /* setup the event header and send the message */
863 bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
864 if (ts)
865 pevent->time_stamp = ts;
866 //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)));
867 bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
868 }
869 }
870
871 return CM_SUCCESS;
872}
873
877 std::string message;
878};
879
880static std::deque<msg_buffer_entry> gMsgBuf;
881static std::mutex gMsgBufMutex;
882
883/********************************************************************/
890 int i;
891
892 //printf("cm_msg_flush_buffer!\n");
893
894 for (i = 0; i < 100; i++) {
896 {
897 std::lock_guard<std::mutex> lock(gMsgBufMutex);
898 if (gMsgBuf.empty())
899 break;
900 e = gMsgBuf.front();
901 gMsgBuf.pop_front();
902 // implicit unlock
903 }
904
905 /* log message */
906 cm_msg_log(e.message_type, "midas", e.message.c_str());
907
908 /* send message to SYSMSG */
909 int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
910 if (status != CM_SUCCESS)
911 return status;
912 }
913
914 return CM_SUCCESS;
915}
916
917/********************************************************************/
939INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
940{
941 DWORD ts = ss_time();
942
943 /* print argument list into message */
944 std::string message;
945 va_list argptr;
946 va_start(argptr, format);
947 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
948 va_end(argptr);
949
950 //printf("message [%s]\n", message.c_str());
951
952 /* call user function if set via cm_set_msg_print */
954 if (f != NULL && (message_type & _message_mask_user) != 0) {
955 if (message_type != MT_LOG) { // do not print MLOG messages
956 (*f)(message.c_str());
957 }
958 }
959
960 /* return if system mask is not set */
961 if ((message_type & _message_mask_system) == 0) {
962 return CM_SUCCESS;
963 }
964
965 gMsgBufMutex.lock();
966 gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
967 gMsgBufMutex.unlock();
968
969 return CM_SUCCESS;
970}
971
972/********************************************************************/
997INT cm_msg1(INT message_type, const char *filename, INT line,
998 const char *facility, const char *routine, const char *format, ...) {
999 va_list argptr;
1000 std::string message;
1001 static BOOL in_routine = FALSE;
1002
1003 /* avoid recursive calles */
1004 if (in_routine)
1005 return 0;
1006
1007 in_routine = TRUE;
1008
1009 /* print argument list into message */
1010 va_start(argptr, format);
1011 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
1012 va_end(argptr);
1013
1014 /* call user function if set via cm_set_msg_print */
1016 if (f != NULL && (message_type & _message_mask_user) != 0)
1017 (*f)(message.c_str());
1018
1019 /* return if system mask is not set */
1020 if ((message_type & _message_mask_system) == 0) {
1021 in_routine = FALSE;
1022 return CM_SUCCESS;
1023 }
1024
1025 /* send message to SYSMSG */
1026 cm_msg_send_event(0, message_type, message.c_str());
1027
1028 /* log message */
1029 cm_msg_log(message_type, facility, message.c_str());
1030
1031 in_routine = FALSE;
1032
1033 return CM_SUCCESS;
1034}
1035
1036/********************************************************************/
1076 INT status, id;
1077
1078 // we should only come here after the message buffer
1079 // was opened by cm_connect_experiment()
1080 assert(_msg_buffer);
1081
1082 _msg_dispatch = func;
1083
1085
1086 return status;
1087}
1088
1089static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1090 int new_message_length = strlen(new_message);
1091 int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1092 char buf[100];
1093 int buf_length;
1094
1095 //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);
1096
1097 if (*length + new_message_length + 100 > *allocated) {
1098 *messages = (char *) realloc(*messages, new_allocated);
1099 assert(*messages != NULL);
1100 *allocated = new_allocated;
1101 }
1102
1103 if (*length > 0)
1104 if ((*messages)[(*length) - 1] != '\n') {
1105 (*messages)[*length] = '\n'; // separator between messages
1106 (*length) += 1;
1107 }
1108
1109 sprintf(buf, "%ld ", tstamp);
1110 buf_length = strlen(buf);
1111 memcpy(&((*messages)[*length]), buf, buf_length);
1112 (*length) += buf_length;
1113
1114 memcpy(&((*messages)[*length]), new_message, new_message_length);
1115 (*length) += new_message_length;
1116 (*messages)[*length] = 0; // make sure string is NUL terminated
1117}
1118
1119/* Retrieve message from an individual file. Internal use only */
1120static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1121 int *num_messages) {
1122 BOOL stop;
1123 int fh;
1124 char *p, str[1000];
1125 struct stat stat_buf;
1126 time_t tstamp, tstamp_valid, tstamp_last;
1127
1128 ss_tzset(); // required by localtime_r()
1129
1130 *num_messages = 0;
1131
1132 fh = open(filename, O_RDONLY | O_TEXT, 0644);
1133 if (fh < 0) {
1134 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1135 strerror(errno));
1136 return SS_FILE_ERROR;
1137 }
1138
1139 /* read whole file into memory */
1140 if (fstat(fh, &stat_buf) != 0) {
1141 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot stat log file \"%s\", errno %d (%s)", filename, errno,
1142 strerror(errno));
1143 close(fh);
1144 return SS_FILE_ERROR;
1145 }
1146 ssize_t size = stat_buf.st_size;
1147
1148 /* if file is too big, only read tail of file */
1149 ssize_t maxsize = 10 * 1024 * 1024;
1150 if (size > maxsize) {
1151 lseek(fh, -maxsize, SEEK_END);
1152 //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1153 size = maxsize;
1154 }
1155
1156 char *buffer = (char *) malloc(size + 1);
1157
1158 if (buffer == NULL) {
1159 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1160 filename, errno, strerror(errno));
1161 close(fh);
1162 return SS_FILE_ERROR;
1163 }
1164
1165 ssize_t rd = read(fh, buffer, size);
1166
1167 if (rd != size) {
1168 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1169 (int) size, filename, (int) rd, errno, strerror(errno));
1170 free(buffer);
1171 close(fh);
1172 return SS_FILE_ERROR;
1173 }
1174
1175 buffer[size] = 0;
1176 close(fh);
1177
1178 tstamp_last = tstamp_valid = 0;
1179 stop = FALSE;
1180
1181 if (size == 0) {
1182 free(buffer);
1183 return CM_SUCCESS;
1184 }
1185
1186 p = buffer + size - 1;
1187
1188 while (p > buffer && (*p == '\n' || *p == '\r'))
1189 p--;
1190
1191 if (p == buffer && (*p == '\n' || *p == '\r')) {
1192 free(buffer);
1193 return CM_SUCCESS;
1194 }
1195
1196 int n;
1197 for (n = 0; !stop;) {
1198
1199 /* go to beginning of line */
1200 int i;
1201 for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1202 p--;
1203
1204 /* limit line length to sizeof(str) */
1205 if (i >= (int) sizeof(str))
1206 i = sizeof(str) - 1;
1207
1208 if (p == buffer) {
1209 i++;
1210 memcpy(str, p, i);
1211 } else
1212 memcpy(str, p + 1, i);
1213 str[i] = 0;
1214 if (strchr(str, '\n'))
1215 *strchr(str, '\n') = 0;
1216 if (strchr(str, '\r'))
1217 *strchr(str, '\r') = 0;
1218 mstrlcat(str, "\n", sizeof(str));
1219
1220 // extract time tag
1221 time_t now;
1222 time(&now);
1223
1224 struct tm tms;
1225 localtime_r(&now, &tms); // must call tzset() beforehand!
1226
1227 if (str[0] >= '0' && str[0] <= '9') {
1228 // new format
1229 tms.tm_hour = atoi(str);
1230 tms.tm_min = atoi(str + 3);
1231 tms.tm_sec = atoi(str + 6);
1232 tms.tm_year = atoi(str + 13) - 1900;
1233 tms.tm_mon = atoi(str + 18) - 1;
1234 tms.tm_mday = atoi(str + 21);
1235 } else {
1236 // old format
1237 tms.tm_hour = atoi(str + 11);
1238 tms.tm_min = atoi(str + 14);
1239 tms.tm_sec = atoi(str + 17);
1240 tms.tm_year = atoi(str + 20) - 1900;
1241 for (i = 0; i < 12; i++)
1242 if (strncmp(str + 4, mname[i], 3) == 0)
1243 break;
1244 tms.tm_mon = i;
1245 tms.tm_mday = atoi(str + 8);
1246 }
1247 tstamp = ss_mktime(&tms);
1248 if (tstamp != -1)
1249 tstamp_valid = tstamp;
1250
1251 // for new messages (n=0!), stop when t reached
1252 if (n_messages == 0) {
1253 if (tstamp_valid < t)
1254 break;
1255 }
1256
1257 // for old messages, stop when all messages belonging to tstamp_last are sent
1258 if (n_messages != 0) {
1259 if (tstamp_last > 0 && tstamp_valid < tstamp_last)
1260 break;
1261 }
1262
1263 if (t == 0 || tstamp == -1 ||
1264 (n_messages > 0 && tstamp <= t) ||
1265 (n_messages == 0 && tstamp >= t)) {
1266
1267 n++;
1268
1269 add_message(messages, length, allocated, tstamp, str);
1270 }
1271
1272 if (p == buffer)
1273 break;
1274
1275 while (p > buffer && (*p == '\n' || *p == '\r'))
1276 p--;
1277
1278 if (p == buffer && (*p == '\n' || *p == '\r'))
1279 break;
1280
1281 if (n_messages == 1)
1282 stop = TRUE;
1283 else if (n_messages > 1) {
1284 // continue collecting messages until time stamp differs from current one
1285 if (n == n_messages)
1286 tstamp_last = tstamp_valid;
1287
1288 // if all messages without time tags, just return after n
1289 if (n == n_messages && tstamp_valid == 0)
1290 break;
1291 }
1292 }
1293
1294 free(buffer);
1295
1296 *num_messages = n;
1297
1298 return CM_SUCCESS;
1299}
1300
1301/********************************************************************/
1311INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages) {
1312 std::string filename, linkname;
1313 INT n, i;
1314 time_t filedate;
1315 int length = 0;
1316 int allocated = 0;
1317
1318 time(&filedate);
1319 cm_msg_get_logfile(facility, filedate, &filename, &linkname, NULL);
1320
1321 //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1322
1323 // see if file exists, use linkname if not
1324 if (!linkname.empty()) {
1325 if (!ss_file_exist(filename.c_str()))
1326 filename = linkname;
1327 }
1328
1329 if (ss_file_exist(filename.c_str())) {
1330 cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1331 } else {
1332 n = 0;
1333 }
1334
1335 /* if there is no symlink, then there is no additional log files to read */
1336 if (linkname.empty()) {
1337 *num_messages = n;
1338 return CM_SUCCESS;
1339 }
1340
1341 //printf("read more messages %d %d!\n", n, n_message);
1342
1343 int missing = 0;
1344 while (n < n_message) {
1345 filedate -= 3600 * 24; // go one day back
1346
1347 cm_msg_get_logfile(facility, filedate, &filename, NULL, NULL);
1348
1349 //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1350
1351 if (ss_file_exist(filename.c_str())) {
1352 cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1353 n += i;
1354 missing = 0;
1355 } else {
1356 missing++;
1357 }
1358
1359 // stop if ten consecutive files are not found
1360 if (missing > 10)
1361 break;
1362 }
1363
1364 *num_messages = n;
1365
1366 return CM_SUCCESS;
1367}
1368
1369/********************************************************************/
1381INT cm_msg_retrieve(INT n_message, char *message, INT buf_size) {
1382 int status;
1383 char *messages = NULL;
1384 int num_messages = 0;
1385
1386 if (rpc_is_remote())
1387 return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1388
1389 if (buf_size > 0)
1390 message[0] = 0;
1391
1392 status = cm_msg_retrieve2("midas", 0, n_message, &messages, &num_messages);
1393
1394 if (messages) {
1395 mstrlcpy(message, messages, buf_size);
1396 int len = strlen(messages);
1397 if (len >= buf_size)
1399 free(messages);
1400 }
1401
1402 return status;
1403}
1404
/* end of msgfunctionc */
1407
1413/********************************************************************/
1420 INT sec, status;
1421
1422 /* if connected to server, get time from there */
1423 if (rpc_is_remote()) {
1425
1426 /* set local time */
1427 if (status == CM_SUCCESS)
1428 ss_settime(sec);
1429 }
1430
1431 /* return time to caller */
1432 if (seconds != NULL) {
1433 *seconds = ss_time();
1434 }
1435
1436 return CM_SUCCESS;
1437}
1438
1439/********************************************************************/
1446INT cm_asctime(char *str, INT buf_size) {
1447 /* if connected to server, get time from there */
1448 if (rpc_is_remote())
1449 return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1450
1451 /* return local time */
1452 mstrlcpy(str, ss_asctime().c_str(), buf_size);
1453
1454 return CM_SUCCESS;
1455}
1456
1457/********************************************************************/
1462std::string cm_asctime() {
1463 /* if connected to server, get time from there */
1464 if (rpc_is_remote()) {
1465 char buf[256];
1466 int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1467 if (status == CM_SUCCESS) {
1468 return buf;
1469 } else {
1470 return "";
1471 }
1472 }
1473
1474 /* return local time */
1475 return ss_asctime();
1476}
1477
1478/********************************************************************/
1485 /* if connected to server, get time from there */
1486 if (rpc_is_remote())
1487 return rpc_call(RPC_CM_TIME, t);
1488
1489 /* return local time */
1490 *t = ss_time();
1491
1492 return CM_SUCCESS;
1493}
1494
/* end of cmfunctionc */
1497
1498/********************************************************************\
1499* *
1500* cm_xxx - Common Functions to buffer & database *
1501* *
1502\********************************************************************/
1503
1504/* Globals */
1505
1506static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1507static HNDLE _hDB = 0; /* Database handle */
1508static std::string _experiment_name;
1509static std::string _client_name;
1510static std::string _path_name;
1515//INT _semaphore_msg = -1;
1516
1526const char *cm_get_version() {
1527 return MIDAS_VERSION;
1528}
1529
1534const char *cm_get_revision() {
1535 return GIT_REVISION;
1536}
1537
1538/********************************************************************/
1547INT cm_set_path(const char *path) {
1548 assert(path);
1549 assert(path[0] != 0);
1550
1551 _path_name = path;
1552
1553 if (_path_name.back() != DIR_SEPARATOR) {
1555 }
1556
1557 //printf("cm_set_path [%s]\n", _path_name.c_str());
1558
1559 return CM_SUCCESS;
1560}
1561
1562/********************************************************************/
1568INT cm_get_path(char *path, int path_size) {
1569 // check that we were not accidentally called
1570 // with the size of the pointer to a string
1571 // instead of the size of the string buffer
1572 assert(path_size != sizeof(char *));
1573 assert(path);
1574 assert(_path_name.length() > 0);
1575
1576 mstrlcpy(path, _path_name.c_str(), path_size);
1577
1578 return CM_SUCCESS;
1579}
1580
1581/********************************************************************/
1587std::string cm_get_path() {
1588 assert(_path_name.length() > 0);
1589 return _path_name;
1590}
1591
1592/********************************************************************/
1593/* C++ wrapper for cm_get_path */
1594
1595INT EXPRT cm_get_path_string(std::string *path) {
1596 assert(path != NULL);
1597 assert(_path_name.length() > 0);
1598 *path = _path_name;
1599 return CM_SUCCESS;
1600}
1601
1602/********************************************************************/
1610 return CM_SUCCESS;
1611}
1612
1613/********************************************************************/
1620INT cm_get_experiment_name(char *name, int name_length) {
1621 mstrlcpy(name, _experiment_name.c_str(), name_length);
1622 return CM_SUCCESS;
1623}
1624
1625/********************************************************************/
1631 return _experiment_name;
1632}
1633
/* end of cmfunctionc */
1636
1642#ifdef LOCAL_ROUTINES
1643
1645 std::string name;
1646 std::string directory;
1647 std::string user;
1648};
1649
1651 std::string filename;
1652 std::vector<exptab_entry> exptab;
1653};
1654
1655static exptab_struct _exptab; // contents of exptab file
1656
1665{
1666 exptab->exptab.clear();
1667
1668 /* MIDAS_DIR overrides exptab */
1669 if (getenv("MIDAS_DIR")) {
1670 exptab->filename = "MIDAS_DIR";
1671
1673
1674 if (getenv("MIDAS_EXPT_NAME")) {
1675 e.name = getenv("MIDAS_EXPT_NAME");
1676 } else {
1677 e.name = "Default";
1678 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());
1679 }
1680
1681 e.directory = getenv("MIDAS_DIR");
1682 e.user = "";
1683
1684 exptab->exptab.push_back(e);
1685
1686 return CM_SUCCESS;
1687 }
1688
1689 /* default directory for different OSes */
1690#if defined (OS_WINNT)
1691 std::string str;
1692 if (getenv("SystemRoot"))
1693 str = getenv("SystemRoot");
1694 else if (getenv("windir"))
1695 str = getenv("windir");
1696 else
1697 str = "";
1698
1699 std::string alt_str = str;
1700 str += "\\system32\\exptab";
1701 alt_str += "\\system\\exptab";
1702#elif defined (OS_UNIX)
1703 std::string str = "/etc/exptab";
1704 std::string alt_str = "/exptab";
1705#else
1706 std::strint str = "exptab";
1707 std::string alt_str = "exptab";
1708#endif
1709
1710 /* MIDAS_EXPTAB overrides default directory */
1711 if (getenv("MIDAS_EXPTAB")) {
1712 str = getenv("MIDAS_EXPTAB");
1713 alt_str = getenv("MIDAS_EXPTAB");
1714 }
1715
1716 exptab->filename = str;
1717
1718 /* read list of available experiments */
1719 FILE* f = fopen(str.c_str(), "r");
1720 if (f == NULL) {
1721 f = fopen(alt_str.c_str(), "r");
1722 if (f == NULL)
1723 return CM_UNDEF_ENVIRON;
1724 exptab->filename = alt_str;
1725 }
1726
1727 if (f != NULL) {
1728 do {
1729 char buf[256];
1730 memset(buf, 0, sizeof(buf));
1731 char* str = fgets(buf, sizeof(buf)-1, f);
1732 if (str == NULL)
1733 break;
1734 if (str[0] == 0) continue; // empty line
1735 if (str[0] == '#') continue; // comment line
1736
1738
1739 // following code emulates the function of this sprintf():
1740 //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1741
1742 // skip leading spaces
1743 while (*str && isspace(*str))
1744 str++;
1745
1746 char* p1 = str;
1747 char* p2 = str;
1748
1749 while (*p2 && !isspace(*p2))
1750 p2++;
1751
1752 ssize_t len = p2-p1;
1753
1754 if (len<1)
1755 continue;
1756
1757 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1758
1759 e.name = std::string(p1, len);
1760
1761 if (*p2 == 0)
1762 continue;
1763
1764 str = p2;
1765
1766 // skip leading spaces
1767 while (*str && isspace(*str))
1768 str++;
1769
1770 p1 = str;
1771 p2 = str;
1772
1773 while (*p2 && !isspace(*p2))
1774 p2++;
1775
1776 len = p2-p1;
1777
1778 if (len<1)
1779 continue;
1780
1781 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1782
1783 e.directory = std::string(p1, len);
1784
1785 if (*p2 == 0)
1786 continue;
1787
1788 str = p2;
1789
1790 // skip leading spaces
1791 while (*str && isspace(*str))
1792 str++;
1793
1794 p1 = str;
1795 p2 = str;
1796
1797 while (*p2 && !isspace(*p2))
1798 p2++;
1799
1800 len = p2-p1;
1801
1802 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1803
1804 e.user = std::string(p1, len);
1805
1806 /* check for trailing directory separator */
1807 if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1808 e.directory += DIR_SEPARATOR_STR;
1809 }
1810
1811 exptab->exptab.push_back(e);
1812 } while (!feof(f));
1813 fclose(f);
1814 }
1815
1816#if 0
1817 cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1818 for (unsigned j=0; j<exptab->exptab.size(); j++) {
1819 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());
1820 }
1821#endif
1822
1823 return CM_SUCCESS;
1824}
1825
1826/********************************************************************/
1833int cm_get_exptab_filename(char *s, int size) {
1834 mstrlcpy(s, _exptab.filename.c_str(), size);
1835 return CM_SUCCESS;
1836}
1837
1839 return _exptab.filename;
1840}
1841
1842/********************************************************************/
1849int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1850
1851 if (_exptab.exptab.size() == 0) {
1853 if (status != CM_SUCCESS)
1854 return status;
1855 }
1856
1857 for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1858 if (_exptab.exptab[i].name == expname) {
1859 if (dir)
1860 *dir = _exptab.exptab[i].directory;
1861 if (user)
1862 *user = _exptab.exptab[i].user;
1863 return CM_SUCCESS;
1864 }
1865 }
1866 if (dir)
1867 *dir = "";
1868 if (user)
1869 *user = "";
1870 return CM_UNDEF_EXP;
1871}
1872
1873/********************************************************************/
1880int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1881 std::string sdir, suser;
1882 int status = cm_get_exptab(expname, &sdir, &suser);
1883 if (status == CM_SUCCESS) {
1884 if (dir)
1885 mstrlcpy(dir, sdir.c_str(), dir_size);
1886 if (user)
1887 mstrlcpy(user, suser.c_str(), user_size);
1888 return CM_SUCCESS;
1889 }
1890 return CM_UNDEF_EXP;
1891}
1892
1893#endif // LOCAL_ROUTINES
1894
1895/********************************************************************/
1903 /* only do it if local */
1904 if (!rpc_is_remote()) {
1906 }
1907 return CM_SUCCESS;
1908}
1909
1910/********************************************************************/
1920 if (rpc_is_remote())
1921 return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
1922
1923#ifdef LOCAL_ROUTINES
1924 return db_check_client(hDB, hKeyClient);
1925#endif /*LOCAL_ROUTINES */
1926 return CM_SUCCESS;
1927}
1928
1929/********************************************************************/
1943INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name,
1944 const char *program_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1945 if (rpc_is_remote())
1946 return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
1947 host_name, program_name, hw_type, password, watchdog_timeout);
1948
1949#ifdef LOCAL_ROUTINES
1950 {
1951 INT status, data, i, idx, size;
1953 char pwd[NAME_LENGTH];
1954 BOOL call_watchdog, allow;
1955
1956 std::string start_command(255, '\0');
1957 std::string alarm_class(31, '\0');
1958 midas::odb program_info = {
1959 {"Required", false},
1960 {"Watchdog timeout", 10000},
1961 {"Check interval", (uint32_t)180000},
1962 {"Start command", start_command},
1963 {"Auto start", false},
1964 {"Auto stop", false},
1965 {"Auto restart", false},
1966 {"Alarm class", alarm_class},
1967 {"First failed", (uint32_t)0}
1968 };
1969
1970 if (strlen(program_name) >= NAME_LENGTH) {
1971 cm_msg(MINFO, "cm_set_client_info", "Client name \"%s\" length %zu is longer than NAME_LENGTH %d", program_name, strlen(program_name), NAME_LENGTH);
1972 return CM_SET_ERROR;
1973 }
1974
1975 /* check security if password is present */
1976 status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1977 if (hKey) {
1978 /* get password */
1979 size = sizeof(pwd);
1980 db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1981
1982 /* first check allowed hosts list */
1983 allow = FALSE;
1984 db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1986 allow = TRUE;
1987
1988 /* check allowed programs list */
1989 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1990 if (hKey && db_find_key(hDB, hKey, program_name, &hKey) == DB_SUCCESS)
1991 allow = TRUE;
1992
1993 /* now check password */
1994 if (!allow && strcmp(password, pwd) != 0) {
1995 if (password[0])
1996 cm_msg(MINFO, "cm_set_client_info", "Wrong password for host \"%s\"", host_name);
1997 return CM_WRONG_PASSWORD;
1998 }
1999 }
2000
2001 /* make following operation atomic by locking database */
2003
2004 /* check if entry with this pid exists already */
2005 pid_t pid = ss_getpid();
2006
2007 std::string str = msprintf("System/Clients/%0d", pid);
2008
2009 status = db_find_key(hDB, 0, str.c_str(), &hKey);
2010
2011 if (status == DB_SUCCESS) {
2013 db_delete(hDB, 0, str.c_str());
2014 hKey = 0; // invalid after db_delete(). K.O. Aug 2026
2015 }
2016
2017 std::string client_name = program_name;
2018
2019 /* check if client name already exists */
2020 status = db_find_key(hDB, 0, "System/Clients", &hKey);
2021
2022 for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
2023 for (i = 0;; i++) {
2026 break;
2027
2028 if (status == DB_SUCCESS) {
2029 status = db_get_value_string(hDB, hSubkey, "Name", 0, &str);
2030 if (status != DB_SUCCESS)
2031 continue;
2032 }
2033
2034 /* check if client is living */
2036 continue;
2037
2038 if (equal_ustring(str.c_str(), client_name.c_str())) {
2039 client_name = msprintf("%s%d", program_name, idx);
2040 break;
2041 }
2042 }
2043 }
2044
2045 /* set name */
2046 str = msprintf("System/Clients/%0d/Name", pid);
2047
2048 status = db_set_value(hDB, 0, str.c_str(), client_name.c_str(), NAME_LENGTH, 1, TID_STRING);
2049 if (status != DB_SUCCESS) {
2051 cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str.c_str(), status);
2052 return status;
2053 }
2054
2055 /* name is the new client name */
2056
2057 db_set_client_name(hDB, client_name.c_str());
2058
2059 /* set also as rpc name */
2060 rpc_set_name(client_name.c_str());
2061
2062 /* use /system/clients/PID as root */
2063 str = msprintf("System/Clients/%0d", pid);
2064 db_find_key(hDB, 0, str.c_str(), &hKey);
2065
2066 /* set program name */
2067 status = db_set_value(hDB, hKey, "Program", program_name, NAME_LENGTH, 1, TID_STRING);
2068 if (status != DB_SUCCESS) {
2070 return status;
2071 }
2072
2073 /* set host name */
2075 if (status != DB_SUCCESS) {
2077 return status;
2078 }
2079
2080 /* set computer id */
2081 status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2082 if (status != DB_SUCCESS) {
2084 return status;
2085 }
2086
2087 /* set server port */
2088 data = 0;
2089 status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2090 if (status != DB_SUCCESS) {
2092 return status;
2093 }
2094
2095 /* lock client entry */
2097
2098 /* get (set) default watchdog timeout */
2099 size = sizeof(watchdog_timeout);
2100 str = msprintf("/Programs/%s/Watchdog Timeout", program_name);
2101 db_get_value(hDB, 0, str.c_str(), &watchdog_timeout, &size, TID_INT32, TRUE);
2102
2103 /* save handle for ODB and client */
2105
2106 /* define /programs entry */
2107 str = msprintf("/Programs/%s", program_name);
2108 program_info.connect(str.c_str());
2109
2110 /* save watchdog timeout */
2111 cm_get_watchdog_params(&call_watchdog, NULL);
2112 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2113
2114 /* end of atomic operations */
2116
2117 /* touch notify key to inform others */
2118 data = 0;
2119 db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2120
2121 *hKeyClient = hKey;
2122 }
2123#endif /* LOCAL_ROUTINES */
2124
2125 return CM_SUCCESS;
2126}
2127
2128/********************************************************************/
2134{
2135 INT status;
2136 HNDLE hDB, hKey;
2137
2138 /* get root key of client */
2140 if (!hDB) {
2141 return "unknown";
2142 }
2143
2144 std::string name;
2145
2146 status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2147 if (status != DB_SUCCESS) {
2148 return "unknown";
2149 }
2150
2151 //printf("get client name: [%s]\n", name.c_str());
2152
2153 return name;
2154}
2155
2156/********************************************************************/
2208INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
2209 if (host_name)
2210 host_name[0] = 0;
2211 if (exp_name)
2212 exp_name[0] = 0;
2213
2214 if (host_name && getenv("MIDAS_SERVER_HOST"))
2215 mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2216
2217 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2218 mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2219
2220 return CM_SUCCESS;
2221}
2222
2223INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2224 if (host_name)
2225 *host_name = "";
2226 if (exp_name)
2227 *exp_name = "";
2228
2229 if (host_name && getenv("MIDAS_SERVER_HOST"))
2230 *host_name = getenv("MIDAS_SERVER_HOST");
2231
2232 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2233 *exp_name = getenv("MIDAS_EXPT_NAME");
2234
2235 return CM_SUCCESS;
2236}
2237
2238#ifdef LOCAL_ROUTINES
2239
2241{
2242 std::string exp_name1;
2243
2244 if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2245 exp_name1 = exp_name;
2246 } else {
2247 int status = cm_select_experiment_local(&exp_name1);
2248 if (status != CM_SUCCESS)
2249 return status;
2250 }
2251
2252 std::string expdir, expuser;
2253
2254 int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2255
2256 if (status != CM_SUCCESS) {
2257 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());
2258 return CM_UNDEF_EXP;
2259 }
2260
2261 if (!ss_dir_exist(expdir.c_str())) {
2262 cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2263 return CM_UNDEF_EXP;
2264 }
2265
2266 cm_set_experiment_name(exp_name1.c_str());
2267 cm_set_path(expdir.c_str());
2268
2269 return CM_SUCCESS;
2270}
2271
2272#endif // LOCAL_ROUTINES
2273
2274/********************************************************************/
2276 if (_hKeyClient) {
2277 cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2279 }
2280}
2281
2282/********************************************************************/
2352INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2353 INT status;
2354
2357 if (status != CM_SUCCESS) {
2358 std::string s = cm_get_error(status);
2359 puts(s.c_str());
2360 }
2361
2362 return status;
2363}
2364
2365/********************************************************************/
2371INT cm_connect_experiment1(const char *host_name, const char *default_exp_name,
2372 const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2373 INT status, size;
2374 char password[NAME_LENGTH], str[256];
2375 HNDLE hDB = 0, hKeyClient = 0;
2376 BOOL call_watchdog;
2377
2378 ss_tzset(); // required for localtime_r()
2379
2380 if (_hKeyClient)
2382
2384
2385 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2386 //cm_msg_flush_buffer();
2387
2388 rpc_set_name(client_name);
2389
2390 /* check for local host */
2391 if (equal_ustring(host_name, "local"))
2392 host_name = NULL;
2393
2394#ifdef OS_WINNT
2395 {
2396 WSADATA WSAData;
2397
2398 /* Start windows sockets */
2399 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2400 return RPC_NET_ERROR;
2401 }
2402#endif
2403
2404 std::string default_exp_name1;
2405 if (default_exp_name)
2406 default_exp_name1 = default_exp_name;
2407
2408 /* connect to MIDAS server */
2409 if (host_name && host_name[0]) {
2410 if (default_exp_name1.length() == 0) {
2411 status = cm_select_experiment_remote(host_name, &default_exp_name1);
2412 if (status != CM_SUCCESS)
2413 return status;
2414 }
2415
2416 cm_set_experiment_name(default_exp_name1.c_str());
2417
2418 status = rpc_server_connect(host_name, default_exp_name1.c_str());
2419 if (status != RPC_SUCCESS)
2420 return status;
2421
2422 /* register MIDAS library functions */
2424 if (status != RPC_SUCCESS)
2425 return status;
2426 } else {
2427 /* lookup path for *SHM files and save it */
2428
2429#ifdef LOCAL_ROUTINES
2430 status = cm_set_experiment_local(default_exp_name1.c_str());
2431 if (status != CM_SUCCESS)
2432 return status;
2433
2434 default_exp_name1 = cm_get_experiment_name();
2435
2437
2438 INT semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg;
2439
2440 /* create alarm and elog semaphores */
2441 status = ss_semaphore_create("ALARM", &semaphore_alarm);
2442 if (status != SS_CREATED && status != SS_SUCCESS) {
2443 cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2444 return status;
2445 }
2446 status = ss_semaphore_create("ELOG", &semaphore_elog);
2447 if (status != SS_CREATED && status != SS_SUCCESS) {
2448 cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2449 return status;
2450 }
2451 status = ss_semaphore_create("HISTORY", &semaphore_history);
2452 if (status != SS_CREATED && status != SS_SUCCESS) {
2453 cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2454 return status;
2455 }
2456 status = ss_semaphore_create("MSG", &semaphore_msg);
2457 if (status != SS_CREATED && status != SS_SUCCESS) {
2458 cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2459 return status;
2460 }
2461
2462 cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
2463#else
2464 return CM_UNDEF_EXP;
2465#endif
2466 }
2467
2468 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2469 //cm_msg_flush_buffer();
2470
2471 /* open ODB */
2472 if (odb_size == 0)
2474
2475 status = db_open_database("ODB", odb_size, &hDB, client_name);
2476 if (status != DB_SUCCESS && status != DB_CREATED) {
2477 cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2478 return status;
2479 }
2480
2481 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2482 //cm_msg_flush_buffer();
2483
2484 int odb_timeout = db_set_lock_timeout(hDB, 0);
2485 size = sizeof(odb_timeout);
2486 status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2487 if (status != DB_SUCCESS) {
2488 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2489 }
2490
2491 if (odb_timeout > 0) {
2492 db_set_lock_timeout(hDB, odb_timeout);
2493 }
2494
2495 BOOL protect_odb = FALSE;
2496 size = sizeof(protect_odb);
2497 status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2498 if (status != DB_SUCCESS) {
2499 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2500 }
2501
2502 if (protect_odb) {
2504 }
2505
2506 BOOL enable_core_dumps = FALSE;
2507 size = sizeof(enable_core_dumps);
2508 status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2509 if (status != DB_SUCCESS) {
2510 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2511 }
2512
2513 if (enable_core_dumps) {
2514#ifdef RLIMIT_CORE
2515 struct rlimit limit;
2516 limit.rlim_cur = RLIM_INFINITY;
2517 limit.rlim_max = RLIM_INFINITY;
2518 status = setrlimit(RLIMIT_CORE, &limit);
2519 if (status != 0) {
2520 cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2521 strerror(errno));
2522 }
2523#else
2524#warning setrlimit(RLIMIT_CORE) is not available
2525#endif
2526 }
2527
2528 size = sizeof(disable_bind_rpc_to_localhost);
2529 status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2530 TID_BOOL, TRUE);
2531 if (status != DB_SUCCESS) {
2532 cm_msg(MERROR, "cm_connect_experiment1",
2533 "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2534 }
2535
2536 std::string local_host_name;
2537
2538 /* now setup client info */
2540 local_host_name = "localhost";
2541 else
2542 local_host_name = ss_gethostname();
2543
2544 /* check watchdog timeout */
2545 if (watchdog_timeout == 0)
2546 watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2547
2548 password[0] = 0;
2549 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name, rpc_get_hw_type(), password, watchdog_timeout);
2550
2551 if (status == CM_WRONG_PASSWORD) {
2552 if (func == NULL)
2553 strcpy(str, ss_getpass("Password: "));
2554 else
2555 func(str);
2556
2557 strcpy(password, ss_crypt(str, "mi"));
2558 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name, rpc_get_hw_type(), password, watchdog_timeout);
2559 if (status != CM_SUCCESS) {
2560 /* disconnect */
2561 if (rpc_is_remote())
2564
2565 return status;
2566 }
2567 }
2568
2569 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2570 //cm_msg_flush_buffer();
2571
2572 /* tell the rest of MIDAS that ODB is open for business */
2573
2574 cm_set_experiment_database(hDB, hKeyClient);
2575
2576 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2577 //cm_msg_flush_buffer();
2578
2579 /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2580 * to get event buffer size, etc */
2581
2583 if (status != CM_SUCCESS) {
2584 cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2585 return status;
2586 }
2587
2588 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2589 //cm_msg_flush_buffer();
2590
2591 /* set experiment name in ODB if not present */
2592 std::string current_name;
2593 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
2594 if (current_name.length() == 0 || current_name == "Default") {
2595 db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2596 }
2597
2598 if (!rpc_is_remote()) {
2599 /* experiment path is only set for local connections */
2600 /* set data dir in ODB */
2601 std::string path = cm_get_path();
2602 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2603 }
2604
2605 /* register server to be able to be called by other clients */
2607 if (status != CM_SUCCESS) {
2608 cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2609 if (!equal_ustring(client_name, "odbedit")) {
2610 return status;
2611 }
2612 }
2613
2614 /* set watchdog timeout */
2615 cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2616 size = sizeof(watchdog_timeout);
2617 sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2618 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2619 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2620
2621 /* set command line */
2622 std::string cmdline = ss_get_cmdline();
2623 std::string path = "/Programs/" + std::string(client_name);
2624 midas::odb prog(path);
2625 if (!midas::odb::exists(path + "/Start command") ||
2626 prog["Start command"] == std::string(""))
2627 prog["Start command"].set_string_size(cmdline, 256);
2628
2629 /* get final client name */
2630 std::string xclient_name = rpc_get_name();
2631
2632 /* startup message is not displayed */
2633 cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2634
2635 /* enable system and user messages to stdout as default */
2637
2638 /* call cm_check_connect when exiting */
2639 atexit((void (*)(void)) cm_check_connect);
2640
2641 /* register ctrl-c handler */
2643
2644 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2645 //cm_msg_flush_buffer();
2646
2647 return CM_SUCCESS;
2648}
2649
2650#ifdef LOCAL_ROUTINES
2651/********************************************************************/
2659 assert(exp_names != NULL);
2660 exp_names->clear();
2661
2662 if (_exptab.exptab.size() == 0) {
2664 if (status != CM_SUCCESS)
2665 return status;
2666 }
2667
2668 for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2669 exp_names->push_back(_exptab.exptab[i].name);
2670 }
2671
2672 return CM_SUCCESS;
2673}
2674#endif // LOCAL_ROUTINES
2675
2676/********************************************************************/
2685 INT status;
2686 INT sock;
2687 int port = MIDAS_TCP_PORT;
2688 char hname[256];
2689 char *s;
2690
2691 assert(exp_names != NULL);
2692 exp_names->clear();
2693
2694 /* extract port number from host_name */
2695 mstrlcpy(hname, host_name, sizeof(hname));
2696 s = strchr(hname, ':');
2697 if (s) {
2698 *s = 0;
2699 port = strtoul(s + 1, NULL, 0);
2700 }
2701
2702 std::string errmsg;
2703
2704 status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2705
2706 if (status != SS_SUCCESS) {
2707 cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2708 return RPC_NET_ERROR;
2709 }
2710
2711 /* request experiment list */
2712 send(sock, "I", 2, 0);
2713
2714 while (1) {
2715 char str[256];
2716
2717 status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2718
2719 if (status < 0)
2720 return RPC_NET_ERROR;
2721
2722 if (status == 0)
2723 break;
2724
2725 exp_names->push_back(str);
2726 }
2727
2728 ss_socket_close(&sock);
2729
2730 return CM_SUCCESS;
2731}
2732
2733#ifdef LOCAL_ROUTINES
2734/********************************************************************/
2743 INT status;
2744 STRING_LIST expts;
2745
2746 assert(exp_name != NULL);
2747
2748 /* retrieve list of experiments and make selection */
2750 if (status != CM_SUCCESS)
2751 return status;
2752
2753 if (expts.size() == 1) {
2754 *exp_name = expts[0];
2755 } else if (expts.size() > 1) {
2756 printf("Available experiments on local computer:\n");
2757
2758 for (unsigned i = 0; i < expts.size(); i++) {
2759 printf("%d : %s\n", i, expts[i].c_str());
2760 }
2761
2762 while (1) {
2763 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2764 char str[32];
2765 ss_gets(str, 32);
2766 int isel = atoi(str);
2767 if (isel < 0)
2768 continue;
2769 if (isel >= (int)expts.size())
2770 continue;
2771 *exp_name = expts[isel];
2772 break;
2773 }
2774 } else {
2775 return CM_UNDEF_EXP;
2776 }
2777
2778 return CM_SUCCESS;
2779}
2780#endif // LOCAL_ROUTINES
2781
2782/********************************************************************/
2792 INT status;
2793 STRING_LIST expts;
2794
2795 assert(exp_name != NULL);
2796
2797 /* retrieve list of experiments and make selection */
2799 if (status != CM_SUCCESS)
2800 return status;
2801
2802 if (expts.size() > 1) {
2803 printf("Available experiments on server %s:\n", host_name);
2804
2805 for (unsigned i = 0; i < expts.size(); i++) {
2806 printf("%d : %s\n", i, expts[i].c_str());
2807 }
2808
2809 while (1) {
2810 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2811 char str[32];
2812 ss_gets(str, 32);
2813 int isel = atoi(str);
2814 if (isel < 0)
2815 continue;
2816 if (isel >= (int)expts.size())
2817 continue;
2818 *exp_name = expts[isel];
2819 break;
2820 }
2821 } else {
2822 *exp_name = expts[0];
2823 }
2824
2825 return CM_SUCCESS;
2826}
2827
2828/********************************************************************/
2838INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2839 HNDLE hDB, hKeyRoot, hSubkey, hKey;
2840 INT status, i, length, port;
2842
2843 /* find client entry in ODB */
2845
2846 status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2847 if (status != DB_SUCCESS)
2848 return status;
2849
2850 i = 0;
2851 do {
2852 /* search for client with specific name */
2853 status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
2855 return CM_NO_CLIENT;
2856
2857 status = db_find_key(hDB, hSubkey, "Name", &hKey);
2858 if (status != DB_SUCCESS)
2859 return status;
2860
2861 length = NAME_LENGTH;
2862 status = db_get_data(hDB, hKey, name, &length, TID_STRING);
2863 if (status != DB_SUCCESS)
2864 return status;
2865
2866 if (equal_ustring(name, client_name)) {
2867 status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2868 if (status != DB_SUCCESS)
2869 return status;
2870
2871 length = sizeof(INT);
2872 status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2873 if (status != DB_SUCCESS)
2874 return status;
2875
2876 status = db_find_key(hDB, hSubkey, "Host", &hKey);
2877 if (status != DB_SUCCESS)
2878 return status;
2879
2880 length = sizeof(host_name);
2882 if (status != DB_SUCCESS)
2883 return status;
2884
2885 /* client found -> connect to its server port */
2886 return rpc_client_connect(host_name, port, client_name, hConn);
2887 }
2888
2889
2890 } while (TRUE);
2891}
2892
2893static void rpc_client_shutdown();
2894
2895/********************************************************************/
2906 return rpc_client_disconnect(hConn, bShutdown);
2907}
2908
2909/********************************************************************/
2919 HNDLE hDB, hKey;
2920
2921 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2922 //cm_msg_flush_buffer();
2923
2924 /* wait on any transition thread */
2925 if (_trp.transition && !_trp.finished) {
2926 printf("Waiting for transition to finish...\n");
2927 do {
2928 ss_sleep(10);
2929 } while (!_trp.finished);
2930 }
2931
2932 /* stop the watchdog thread */
2934
2935 /* send shutdown notification */
2936 std::string client_name = rpc_get_name();
2937
2938 std::string local_host_name;
2939
2941 local_host_name = "localhost";
2942 else {
2943 local_host_name = ss_gethostname();
2944 //if (strchr(local_host_name, '.'))
2945 // *strchr(local_host_name, '.') = 0;
2946 }
2947
2948 /* disconnect message not displayed */
2949 cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2951
2952 if (rpc_is_remote()) {
2953 if (rpc_is_connected()) {
2954 /* close open records */
2956
2958 }
2959
2962
2964 } else {
2966
2967 /* delete client info */
2969
2970 if (hDB)
2972
2973 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2974 //cm_msg_flush_buffer();
2975
2979
2981
2982 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2983 //cm_msg_flush_buffer();
2984 }
2985
2986 if (!rpc_is_mserver())
2988
2989 /* free RPC list */
2991
2992 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2993 //cm_msg_flush_buffer();
2994
2995 /* last flush before we delete the message ring buffer */
2997
2998 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2999 //cm_msg_flush_buffer();
3000
3001 return CM_SUCCESS;
3002}
3003
3004/********************************************************************/
3012 //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
3013
3014 _hDB = hDB;
3015 _hKeyClient = hKeyClient;
3016
3017 //if (hDB == 0) {
3018 // rpc_set_server_option(RPC_ODB_HANDLE, 0);
3019 //}
3020
3021 return CM_SUCCESS;
3022}
3023
3024
3025
3027#ifndef DOXYGEN_SHOULD_SKIP_THIS
3028
3029/********************************************************************/
3030INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
3031/********************************************************************\
3032
3033 Routine: cm_set_experiment_semaphore
3034
3035 Purpose: Set the handle to the experiment wide semaphorees
3036
3037 Input:
3038 INT semaphore_alarm Alarm semaphore
3039 INT semaphore_elog Elog semaphore
3040 INT semaphore_history History semaphore
3041 INT semaphore_msg Message semaphore
3042
3043 Output:
3044 none
3045
3046 Function value:
3047 CM_SUCCESS Successful completion
3048
3049\********************************************************************/
3050{
3051 _semaphore_alarm = semaphore_alarm;
3052 _semaphore_elog = semaphore_elog;
3053 _semaphore_history = semaphore_history;
3054 //_semaphore_msg = semaphore_msg;
3055
3056 return CM_SUCCESS;
3057}
3058
3060#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3061
3062/********************************************************************/
3084 if (_hDB) {
3085 //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3086 if (hDB != NULL)
3087 *hDB = _hDB;
3088 if (hKeyClient != NULL)
3089 *hKeyClient = _hKeyClient;
3090 return CM_SUCCESS;
3091 } else {
3092 //printf("cm_get_experiment_database no init\n");
3093 if (hDB != NULL)
3094 *hDB = 0;
3095 if (hKeyClient != NULL)
3096 *hKeyClient = 0;
3097 return CM_DB_ERROR;
3098 }
3099}
3100
3102#ifndef DOXYGEN_SHOULD_SKIP_THIS
3103
3104/********************************************************************/
3105INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
3106/********************************************************************\
3107
3108 Routine: cm_get_experiment_semaphore
3109
3110 Purpose: Get the handle to the experiment wide semaphores
3111
3112 Input:
3113 none
3114
3115 Output:
3116 INT semaphore_alarm Alarm semaphore
3117 INT semaphore_elog Elog semaphore
3118 INT semaphore_history History semaphore
3119 INT semaphore_msg Message semaphore
3120
3121 Function value:
3122 CM_SUCCESS Successful completion
3123
3124\********************************************************************/
3125{
3126 if (semaphore_alarm)
3127 *semaphore_alarm = _semaphore_alarm;
3128 if (semaphore_elog)
3129 *semaphore_elog = _semaphore_elog;
3130 if (semaphore_history)
3131 *semaphore_history = _semaphore_history;
3132 //if (semaphore_msg)
3133 // *semaphore_msg = _semaphore_msg;
3134 if (semaphore_msg)
3135 *semaphore_msg = -1;
3136
3137 return CM_SUCCESS;
3138}
3139
3141#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3142
3143#ifdef LOCAL_ROUTINES
3144static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3145static int bm_lock_buffer_read_cache(BUFFER *pbuf);
3146static int bm_lock_buffer_write_cache(BUFFER *pbuf);
3147static int bm_lock_buffer_mutex(BUFFER *pbuf);
3148static int xbm_lock_buffer(BUFFER *pbuf);
3149static void xbm_unlock_buffer(BUFFER *pbuf);
3150
3152{
3153public:
3154 bool fDebug = false;
3155
3156public:
3157 bm_lock_buffer_guard(BUFFER* pbuf, bool do_not_lock=false) // ctor
3158 {
3159 assert(pbuf != NULL);
3160 fBuf = pbuf;
3161 if (do_not_lock) {
3162 if (fDebug)
3163 printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3164 return;
3165 }
3166 if (fDebug)
3167 printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3169 if (status != BM_SUCCESS) {
3170 fLocked = false;
3171 fError = true;
3172 fStatus = status;
3173 } else {
3174 fLocked = true;
3175 }
3176 }
3177
3179 {
3180 if (fInvalid) {
3181 if (fDebug)
3182 printf("lock_buffer_guard(invalid) dtor\n");
3183 } else {
3184 assert(fBuf != NULL);
3185 if (fDebug)
3186 printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3187 if (fLocked) {
3189 fLocked = false;
3190 fError = false;
3191 }
3192 fBuf = NULL;
3193 }
3194 }
3195
3196 // make object uncopyable
3199
3200 void unlock()
3201 {
3202 assert(fBuf != NULL);
3203 if (fDebug)
3204 printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3205 assert(fLocked);
3207 fLocked = false;
3208 fError = false;
3209 }
3210
3211 bool relock()
3212 {
3213 assert(fBuf != NULL);
3214 if (fDebug)
3215 printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3216 assert(!fLocked);
3218 if (status != BM_SUCCESS) {
3219 fLocked = false;
3220 fError = true;
3221 fStatus = status;
3222 } else {
3223 fLocked = true;
3224 }
3225 return fLocked;
3226 }
3227
3229 {
3230 assert(fBuf != NULL);
3231 if (fDebug)
3232 printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3233 assert(!fLocked);
3234 fInvalid = true;
3235 fBuf = NULL;
3236 }
3237
3238 bool is_locked() const
3239 {
3240 return fLocked;
3241 }
3242
3243 bool is_error() const
3244 {
3245 return fError;
3246 }
3247
3248 int get_status() const
3249 {
3250 return fStatus;
3251 }
3252
3254 {
3255 assert(!fInvalid); // pbuf was deleted
3256 assert(fBuf); // we do not return NULL
3257 return fBuf;
3258 }
3259
3260private:
3261 BUFFER* fBuf = NULL;
3262 bool fLocked = false;
3263 bool fError = false;
3264 bool fInvalid = false;
3265 int fStatus = 0;
3266};
3267
3269
3270#endif
3271
3272static INT bm_notify_client(const char *buffer_name, int s);
3273
3274static INT bm_push_event(const char *buffer_name);
3275
3276static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3277 EVENT_HEADER *pevent, void *pdata,
3278 EVENT_HANDLER *dispatcher);
3279
3280/********************************************************************/
3317{
3318#ifdef LOCAL_ROUTINES
3319 _watchdog_timeout = timeout;
3320
3321 std::vector<BUFFER*> mybuffers;
3322
3323 gBuffersMutex.lock();
3324 mybuffers = gBuffers;
3325 gBuffersMutex.unlock();
3326
3327 /* set watchdog timeout of all open buffers */
3328 for (BUFFER* pbuf : mybuffers) {
3329
3330 if (!pbuf || !pbuf->attached)
3331 continue;
3332
3333 bm_lock_buffer_guard pbuf_guard(pbuf);
3334
3335 if (!pbuf_guard.is_locked())
3336 continue;
3337
3338 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
3339
3340 /* clear entry from client structure in buffer header */
3341 pclient->watchdog_timeout = timeout;
3342
3343 /* show activity */
3344 pclient->last_activity = ss_millitime();
3345 }
3346
3347 /* set watchdog timeout for ODB */
3348 db_set_watchdog_params(timeout);
3349
3350#endif /* LOCAL_ROUTINES */
3351
3352 return CM_SUCCESS;
3353}
3354
3355INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
3356{
3357 /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3358 _watchdog_timeout = timeout;
3359
3360 if (rpc_is_remote()) { // we are connected remotely
3361
3362 return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
3363
3364 } else if (rpc_is_mserver()) { // we are the mserver
3365
3367 if (sa)
3368 sa->watchdog_timeout = timeout;
3369
3370 /* write timeout value to client entry in ODB */
3371 HNDLE hDB, hKey;
3373
3374 if (hDB) {
3376 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3378 }
3379
3380 /* set the watchdog for the local mserver program */
3381 return cm_set_watchdog_params_local(call_watchdog, timeout);
3382
3383 } else { // only running locally
3384
3385 return cm_set_watchdog_params_local(call_watchdog, timeout);
3386
3387 }
3388}
3389
3390/********************************************************************/
3397INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout) {
3398 if (call_watchdog)
3399 *call_watchdog = FALSE;
3400 if (timeout)
3401 *timeout = _watchdog_timeout;
3402
3403 return CM_SUCCESS;
3404}
3405
3406/********************************************************************/
3416INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3417 if (rpc_is_remote())
3418 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3419
3420#ifdef LOCAL_ROUTINES
3421 return db_get_watchdog_info(hDB, client_name, timeout, last);
3422#else /* LOCAL_ROUTINES */
3423 return CM_SUCCESS;
3424#endif /* LOCAL_ROUTINES */
3425}
3426
3427
3429#ifndef DOXYGEN_SHOULD_SKIP_THIS
3430
3431/********************************************************************/
3432
3433static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3434 int status;
3435 int i, last;
3436 KEY key;
3437 int max_size;
3438 char *str;
3439
3440// if (index != -99)
3441// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3442
3444
3445 if (status != DB_SUCCESS)
3446 return;
3447
3448 //printf("clear rpc hosts!\n");
3450
3451 max_size = key.item_size;
3452 str = (char *) malloc(max_size);
3453
3454 last = 0;
3455 for (i = 0; i < key.num_values; i++) {
3456 int size = max_size;
3458 if (status != DB_SUCCESS)
3459 break;
3460
3461 if (strlen(str) < 1) // skip emties
3462 continue;
3463
3464 if (str[0] == '#') // skip commented-out entries
3465 continue;
3466
3467 //printf("add rpc hosts %d [%s]\n", i, str);
3469 last = i;
3470 }
3471
3472 if (key.num_values - last < 10) {
3473 int new_size = last + 10;
3474 status = db_set_num_values(hDB, hKey, new_size);
3475 if (status != DB_SUCCESS) {
3476 cm_msg(MERROR, "load_rpc_hosts",
3477 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3478 }
3479 }
3480
3481 free(str);
3482}
3483
3485 int status;
3486 char buf[256];
3487 int size, i;
3488 HNDLE hKey;
3489
3490 strcpy(buf, "localhost");
3491 size = sizeof(buf);
3492
3493 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3494
3495 if (status != DB_SUCCESS) {
3496 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3497 status);
3498 return;
3499 }
3500
3501 size = sizeof(i);
3502 i = 0;
3503 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3504
3505 if (status != DB_SUCCESS) {
3506 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3507 return;
3508 }
3509
3510 if (i != 0) // RPC hosts check is disabled
3511 return;
3512
3513 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3514
3515 if (status != DB_SUCCESS || hKey == 0) {
3516 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3517 status);
3518 return;
3519 }
3520
3521 load_rpc_hosts(hDB, hKey, -99, NULL);
3522
3524
3525 if (status != DB_SUCCESS) {
3526 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3527 return;
3528 }
3529}
3530
3531/********************************************************************/
3533/********************************************************************\
3534
3535 Routine: cm_register_server
3536
3537 Purpose: Register a server which can be called from other clients
3538 of a specific experiment.
3539
3540 Input:
3541 none
3542
3543 Output:
3544 none
3545
3546 Function value:
3547 CM_SUCCESS Successful completion
3548
3549\********************************************************************/
3550{
3551 if (!_rpc_registered) {
3552 INT status;
3553 int size;
3554 HNDLE hDB, hKey;
3555 char name[NAME_LENGTH];
3556 char str[256];
3557 int port = 0;
3558
3560
3561 size = sizeof(name);
3562 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3563
3564 if (status != DB_SUCCESS) {
3565 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3566 return status;
3567 }
3568
3569 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3570 mstrlcat(str, name, sizeof(str));
3571
3572 size = sizeof(port);
3573 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3574
3575 if (status != DB_SUCCESS) {
3576 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3577 return status;
3578 }
3579
3580 int lport = 0; // actual port number assigned to us by the OS
3581
3583 if (status != RPC_SUCCESS) {
3584 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3585 return status;
3586 }
3587
3589
3590 /* register MIDAS library functions */
3592
3593 /* store port number in ODB */
3594
3595 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3596 if (status != DB_SUCCESS) {
3597 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3598 return status;
3599 }
3600
3601 /* unlock database */
3603
3604 /* set value */
3605 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3606 if (status != DB_SUCCESS) {
3607 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3608 return status;
3609 }
3610
3611 /* lock database */
3613
3615 }
3616
3617 return CM_SUCCESS;
3618}
3619
3621#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3622
3623/********************************************************************/
3673INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3674 INT status;
3675 HNDLE hDB, hKey, hKeyTrans;
3676 KEY key;
3677 char str[256];
3678
3679 /* check for valid transition */
3681 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3682 return CM_INVALID_TRANSITION;
3683 }
3684
3686
3688
3689 /* register new transition request */
3690
3691 {
3692 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3693
3694 for (size_t i = 0; i < _trans_table.size(); i++) {
3695 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3696 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3697 return CM_INVALID_TRANSITION;
3698 }
3699 }
3700
3701 bool found = false;
3702 for (size_t i = 0; i < _trans_table.size(); i++) {
3703 if (!_trans_table[i].transition) {
3704 _trans_table[i].transition = transition;
3705 _trans_table[i].sequence_number = sequence_number;
3706 _trans_table[i].func = func;
3707 found = true;
3708 break;
3709 }
3710 }
3711
3712 if (!found) {
3713 TRANS_TABLE tt;
3715 tt.sequence_number = sequence_number;
3716 tt.func = func;
3717 _trans_table.push_back(tt);
3718 }
3719
3720 // implicit unlock
3721 }
3722
3723 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3724
3725 /* unlock database */
3727
3728 /* set value */
3729 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3730 if (!hKeyTrans) {
3731 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3732 if (status != DB_SUCCESS)
3733 return status;
3734 } else {
3735 status = db_get_key(hDB, hKeyTrans, &key);
3736 if (status != DB_SUCCESS)
3737 return status;
3738 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3739 if (status != DB_SUCCESS)
3740 return status;
3741 }
3742
3743 /* re-lock database */
3745
3746 return CM_SUCCESS;
3747}
3748
3750 INT status;
3751 HNDLE hDB, hKey, hKeyTrans;
3752 char str[256];
3753
3754 /* check for valid transition */
3756 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3757 return CM_INVALID_TRANSITION;
3758 }
3759
3761
3762 {
3763 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3764
3765 /* remove existing transition request */
3766 for (size_t i = 0; i < _trans_table.size(); i++) {
3768 _trans_table[i].transition = 0;
3769 _trans_table[i].sequence_number = 0;
3770 _trans_table[i].func = NULL;
3771 }
3772 }
3773
3774 // implicit unlock
3775 }
3776
3777 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3778
3779 /* unlock database */
3781
3782 /* set value */
3783 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3784 if (hKeyTrans) {
3785 status = db_delete_key(hDB, hKeyTrans);
3786 if (status != DB_SUCCESS)
3787 return status;
3788 }
3789
3790 /* re-lock database */
3792
3793 return CM_SUCCESS;
3794}
3795
3796/********************************************************************/
3804 INT status;
3805 HNDLE hDB, hKey;
3806 char str[256];
3807
3808 /* check for valid transition */
3810 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3811 return CM_INVALID_TRANSITION;
3812 }
3813
3814 {
3815 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3816
3817 int count = 0;
3818 for (size_t i = 0; i < _trans_table.size(); i++) {
3820 _trans_table[i].sequence_number = sequence_number;
3821 count++;
3822 }
3823 }
3824
3825 if (count == 0) {
3826 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3827 return CM_INVALID_TRANSITION;
3828 } else if (count > 1) {
3829 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3830 return CM_INVALID_TRANSITION;
3831 }
3832
3833 /* Change local sequence number for this transition type */
3834
3835 for (size_t i = 0; i < _trans_table.size(); i++) {
3837 _trans_table[i].sequence_number = sequence_number;
3838 }
3839 }
3840
3841 // implicit unlock
3842 }
3843
3845
3846 /* unlock database */
3848
3849 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3850
3851 /* set value */
3852 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3853 if (status != DB_SUCCESS)
3854 return status;
3855
3856 /* re-lock database */
3858
3859 return CM_SUCCESS;
3860
3861}
3862
3864 INT status;
3865 HNDLE hDB, hKey;
3866 KEY key;
3867
3869
3870 /* check that hKey is still valid */
3872
3873 if (status != DB_SUCCESS) {
3874 cm_msg(MERROR, "cm_set_client_run_state",
3875 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3876 hKey);
3877 return status;
3878 }
3879
3880 /* unlock database */
3882
3883 /* set value */
3884 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3885 if (status != DB_SUCCESS)
3886 return status;
3887
3888 /* re-lock database */
3890
3891 return CM_SUCCESS;
3892
3893}
3894
3896#ifndef DOXYGEN_SHOULD_SKIP_THIS
3897
3900
3902#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3903
3904/********************************************************************/
3918 INT status, size;
3919 char tr_key_name[256];
3920 HNDLE hDB, hKey;
3921
3923
3924 for (int i = 0; _deferred_trans_table[i].transition; i++)
3926 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3927
3928 /* set new transition mask */
3930
3931 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3932
3933 /* unlock database */
3935
3936 /* set value */
3937 int i = 0;
3938 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3939 if (status != DB_SUCCESS)
3940 return status;
3941
3942 /* re-lock database */
3944
3945 /* hot link requested transition */
3946 size = sizeof(_requested_transition);
3947 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3948 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3949 status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
3950 if (status != DB_SUCCESS) {
3951 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3952 return status;
3953 }
3954
3955 return CM_SUCCESS;
3956}
3957
3958/********************************************************************/
3970 INT i, status;
3971 char str[256];
3972 static BOOL first;
3973
3974 if (_requested_transition == 0)
3975 first = TRUE;
3976
3978 for (i = 0; _deferred_trans_table[i].transition; i++)
3980 break;
3981
3983 if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func)(_requested_transition, first)) {
3985 if (status != CM_SUCCESS)
3986 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3987
3988 /* bypass hotlink and set _requested_transition directly to zero */
3990
3991 return status;
3992 }
3993 first = FALSE;
3994 }
3995 }
3996
3997 return SUCCESS;
3998}
3999
4000
4002#ifndef DOXYGEN_SHOULD_SKIP_THIS
4003
4004/********************************************************************/
4005
4007#endif /* DOXYGEN_SHOULD_SKIP_THIS */
4008
4009struct TrClient {
4010 int transition = 0;
4011 int run_number = 0;
4012 int async_flag = 0;
4013 int debug_flag = 0;
4015 std::vector<int> wait_for_index;
4016 std::string host_name;
4017 std::string client_name;
4018 int port = 0;
4019 std::string key_name; /* this client key name in /System/Clients */
4020 std::atomic_int status{0};
4021 std::thread* thread = NULL;
4022 std::string errorstr;
4023 DWORD init_time = 0; // time when tr_client created
4024 std::string waiting_for_client; // name of client we are waiting for
4026 DWORD connect_start_time = 0; // time when client rpc connection is started
4027 DWORD connect_end_time = 0; // time when client rpc connection is finished
4029 DWORD rpc_start_time = 0; // time client rpc call is started
4030 DWORD rpc_end_time = 0; // time client rpc call is finished
4031 DWORD end_time = 0; // time client thread is finished
4032
4033 TrClient() // ctor
4034 {
4035 // empty
4036 }
4037
4038 ~TrClient() // dtor
4039 {
4040 //printf("TrClient::dtor: client \"%s\"\n", client_name);
4041 assert(thread == NULL);
4042 }
4043
4044 void Print() const
4045 {
4046 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
4047 if (wait_for_index.size() > 0) {
4048 printf(", wait for:");
4049 for (size_t i=0; i<wait_for_index.size(); i++) {
4050 printf(" %d", wait_for_index[i]);
4051 }
4052 }
4053 }
4054};
4055
4056static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
4057 return arg1->sequence_number < arg2->sequence_number;
4058}
4059
4060/*------------------------------------------------------------------*/
4061
4062struct TrState {
4063 int transition = 0;
4064 int run_number = 0;
4065 int async_flag = 0;
4066 int debug_flag = 0;
4067 int status = 0;
4068 std::string errorstr;
4071 std::vector<std::unique_ptr<TrClient>> clients;
4072};
4073
4074/*------------------------------------------------------------------*/
4075
4076static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4077{
4078 DWORD end_time = ss_millitime();
4079
4080 if (transition != TR_STARTABORT) {
4081 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4082 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4083
4084 if (errorstr) {
4085 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4086 } else if (status == CM_SUCCESS) {
4087 const char *buf = "Success";
4088 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4089 } else {
4090 char buf[256];
4091 sprintf(buf, "status %d", status);
4092 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4093 }
4094 }
4095
4096 tr->status = status;
4097 tr->end_time = end_time;
4098 if (errorstr) {
4099 tr->errorstr = errorstr;
4100 } else {
4101 tr->errorstr = "(null)";
4102 }
4103
4104 return status;
4105}
4106
4107/*------------------------------------------------------------------*/
4108
4109static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client) {
4110 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4111
4112 int status;
4113 HNDLE hKey;
4114
4115 if (tr_client->transition == TR_STARTABORT) {
4116 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4117 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4118 if (status != DB_SUCCESS)
4119 return;
4120 } else {
4121 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4122 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4123 if (status != DB_SUCCESS)
4124 return;
4125 }
4126
4127 // same client_name can exist with different sequence numbers!
4128 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4129
4130 status = db_create_key(hDB, hKey, keyname.c_str(), TID_KEY);
4131 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4132 if (status != DB_SUCCESS)
4133 return;
4134
4135 DWORD now = ss_millitime();
4136
4137 //int transition;
4138 //int run_number;
4139 //int async_flag;
4140 //int debug_flag;
4141 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4142 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4143 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4144 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4145 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4146 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);
4147 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4148 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4149 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4150 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4151 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4152 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4153 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4154 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4155 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4156 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4157}
4158
4159/*------------------------------------------------------------------*/
4160
4161/* Perform a detached transition through the external "mtransition" program */
4162static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4163 HNDLE hDB;
4164 int status;
4165 const char *args[100];
4166 std::string path;
4167 char debug_arg[256];
4168 char start_arg[256];
4169 std::string expt_name;
4170 std::string mserver_hostname;
4171
4172 int iarg = 0;
4173
4175
4176 const char *midassys = getenv("MIDASSYS");
4177 if (midassys) {
4178 path += midassys;
4179 path += DIR_SEPARATOR_STR;
4180 path += "bin";
4181 path += DIR_SEPARATOR_STR;
4182 }
4183 path += "mtransition";
4184
4185 args[iarg++] = path.c_str();
4186
4187 if (rpc_is_remote()) {
4188 /* if connected to mserver, pass connection info to mtransition */
4189 mserver_hostname = rpc_get_mserver_hostname();
4190 args[iarg++] = "-h";
4191 args[iarg++] = mserver_hostname.c_str();
4192 }
4193
4194 /* get experiment name from ODB */
4195 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4196
4197 if (expt_name.length() > 0) {
4198 args[iarg++] = "-e";
4199 args[iarg++] = expt_name.c_str();
4200 }
4201
4202 if (debug_flag) {
4203 args[iarg++] = "-d";
4204
4205 sprintf(debug_arg, "%d", debug_flag);
4206 args[iarg++] = debug_arg;
4207 }
4208
4209 if (transition == TR_STOP)
4210 args[iarg++] = "STOP";
4211 else if (transition == TR_PAUSE)
4212 args[iarg++] = "PAUSE";
4213 else if (transition == TR_RESUME)
4214 args[iarg++] = "RESUME";
4215 else if (transition == TR_START) {
4216 args[iarg++] = "START";
4217
4218 sprintf(start_arg, "%d", run_number);
4219 args[iarg++] = start_arg;
4220 }
4221
4222 args[iarg++] = NULL;
4223
4224#if 0
4225 for (iarg = 0; args[iarg] != NULL; iarg++) {
4226 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4227 }
4228#endif
4229
4230 status = ss_spawnv(P_DETACH, args[0], args);
4231
4232 if (status != SS_SUCCESS) {
4233 if (errstr != NULL) {
4234 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4235 }
4236 return CM_SET_ERROR;
4237 }
4238
4239 return CM_SUCCESS;
4240}
4241
4242/*------------------------------------------------------------------*/
4243
4244/* contact a client via RPC and execute the remote transition */
4245static int cm_transition_call(TrState* s, int idx) {
4246 INT old_timeout, status, i, t1, t0, size;
4247 HNDLE hDB;
4248 HNDLE hConn = -1;
4249 int connect_timeout = 10000;
4250 int timeout = 120000;
4251
4253 assert(hDB);
4254
4255 TrClient *tr_client = s->clients[idx].get();
4256
4257 tr_client->errorstr = "";
4258 //tr_client->init_time = ss_millitime();
4259 tr_client->waiting_for_client = "";
4260 tr_client->connect_timeout = 0;
4261 tr_client->connect_start_time = 0;
4262 tr_client->connect_end_time = 0;
4263 tr_client->rpc_timeout = 0;
4264 tr_client->rpc_start_time = 0;
4265 tr_client->rpc_end_time = 0;
4266 tr_client->end_time = 0;
4267
4268 write_tr_client_to_odb(hDB, tr_client);
4269
4270 /* wait for predecessor if set */
4271 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4272 while (1) {
4273 TrClient* wait_for = NULL;
4274
4275 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4276 int wait_for_index = tr_client->wait_for_index[i];
4277
4278 assert(wait_for_index >= 0);
4279 assert(wait_for_index < (int)s->clients.size());
4280
4281 TrClient *t = s->clients[wait_for_index].get();
4282
4283 if (!t)
4284 continue;
4285
4286 if (t->status == 0) {
4287 wait_for = t;
4288 break;
4289 }
4290
4291 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4292 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));
4293 tr_client->status = -1;
4294 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4295 tr_client->end_time = ss_millitime();
4296 write_tr_client_to_odb(hDB, tr_client);
4297 return CM_SUCCESS;
4298 }
4299 }
4300
4301 if (wait_for == NULL)
4302 break;
4303
4304 tr_client->waiting_for_client = wait_for->client_name;
4305 write_tr_client_to_odb(hDB, tr_client);
4306
4307 if (tr_client->debug_flag == 1)
4308 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4309
4310 i = 0;
4311 size = sizeof(i);
4312 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4313
4314 if (status == DB_SUCCESS && i == 0) {
4315 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());
4316 tr_client->status = -1;
4317 tr_client->errorstr = "Canceled";
4318 tr_client->end_time = ss_millitime();
4319 write_tr_client_to_odb(hDB, tr_client);
4320 return CM_SUCCESS;
4321 }
4322
4323 ss_sleep(100);
4324 };
4325 }
4326
4327 tr_client->waiting_for_client[0] = 0;
4328
4329 /* contact client if transition mask set */
4330 if (tr_client->debug_flag == 1)
4331 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4332 if (tr_client->debug_flag == 2)
4333 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());
4334
4335 /* get transition timeout for rpc connect */
4336 size = sizeof(timeout);
4337 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4338
4339 if (connect_timeout < 1000)
4340 connect_timeout = 1000;
4341
4342 /* get transition timeout */
4343 size = sizeof(timeout);
4344 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4345
4346 if (timeout < 1000)
4347 timeout = 1000;
4348
4349 /* set our timeout for rpc_client_connect() */
4350 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4351 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4352
4353 tr_client->connect_timeout = connect_timeout;
4354 tr_client->connect_start_time = ss_millitime();
4355
4356 write_tr_client_to_odb(hDB, tr_client);
4357
4358 /* client found -> connect to its server port */
4359 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4360
4361 rpc_set_timeout(RPC_HNDLE_CONNECT, old_timeout);
4362
4363 tr_client->connect_end_time = ss_millitime();
4364 write_tr_client_to_odb(hDB, tr_client);
4365
4366 if (status != RPC_SUCCESS) {
4367 cm_msg(MERROR, "cm_transition_call",
4368 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4369 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4370 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4371
4372 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4373 cm_shutdown(tr_client->client_name.c_str());
4374 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4375
4376 if (tr_client->transition != TR_STOP) {
4377 /* indicate abort */
4378 i = 1;
4379 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4380 i = 0;
4381 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4382 }
4383
4384 tr_client->status = status;
4385 tr_client->end_time = ss_millitime();
4386
4387 write_tr_client_to_odb(hDB, tr_client);
4388 return status;
4389 }
4390
4391 if (tr_client->debug_flag == 1)
4392 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4393 if (tr_client->debug_flag == 2)
4394 cm_msg(MINFO, "cm_transition_call",
4395 "cm_transition: Connection established to client \"%s\" on host %s",
4396 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4397
4398 /* call RC_TRANSITION on remote client with increased timeout */
4399 //old_timeout = rpc_get_timeout(hConn);
4400 rpc_set_timeout(hConn, timeout, &old_timeout);
4401
4402 tr_client->rpc_timeout = timeout;
4403 tr_client->rpc_start_time = ss_millitime();
4404 write_tr_client_to_odb(hDB, tr_client);
4405
4406 if (tr_client->debug_flag == 1)
4407 printf("Executing RPC transition client \"%s\" on host %s...\n",
4408 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4409 if (tr_client->debug_flag == 2)
4410 cm_msg(MINFO, "cm_transition_call",
4411 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4412 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4413
4414 t0 = ss_millitime();
4415
4416 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4417 errorstr[0] = 0;
4418
4419 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4420
4421 tr_client->errorstr = errorstr;
4422
4423 t1 = ss_millitime();
4424
4425 tr_client->rpc_end_time = ss_millitime();
4426
4427 write_tr_client_to_odb(hDB, tr_client);
4428
4429 /* fix for clients returning 0 as error code */
4430 if (status == 0)
4431 status = FE_ERR_HW;
4432
4433 /* reset timeout */
4434 rpc_set_timeout(hConn, old_timeout);
4435
4436 //DWORD t2 = ss_millitime();
4437
4438 if (tr_client->debug_flag == 1)
4439 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4440 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4441 if (tr_client->debug_flag == 2)
4442 cm_msg(MINFO, "cm_transition_call",
4443 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4444 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4445
4446 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4447 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());
4448 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4449 cm_shutdown(tr_client->client_name.c_str());
4450 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4451 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4452 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());
4453 }
4454
4455 tr_client->status = status;
4456 tr_client->end_time = ss_millitime();
4457
4458 // write updated status and end_time to ODB
4459
4460 write_tr_client_to_odb(hDB, tr_client);
4461
4462#if 0
4463 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4464 hConn,
4465 tr_client->client_name.c_str(),
4466 tr_client->init_time - tr_client->init_time,
4467 tr_client->connect_start_time - tr_client->init_time,
4468 tr_client->connect_end_time - tr_client->init_time,
4469 tr_client->rpc_start_time - tr_client->init_time,
4470 tr_client->rpc_end_time - tr_client->init_time,
4471 t2 - tr_client->init_time,
4472 tr_client->end_time - tr_client->init_time);
4473#endif
4474
4475 return CM_SUCCESS;
4476}
4477
4478/*------------------------------------------------------------------*/
4479
4481{
4482 HNDLE hDB;
4483
4485
4486 DWORD now = ss_millitime();
4487
4488 tr_client->errorstr = "";
4489 //tr_client->init_time = now;
4490 tr_client->waiting_for_client = "";
4491 tr_client->connect_timeout = 0;
4492 tr_client->connect_start_time = now;
4493 tr_client->connect_end_time = now;
4494 tr_client->rpc_timeout = 0;
4495 tr_client->rpc_start_time = 0;
4496 tr_client->rpc_end_time = 0;
4497 tr_client->end_time = 0;
4498
4499 write_tr_client_to_odb(hDB, tr_client);
4500
4501 // find registered handler
4502 // NB: this code should match same code in rpc_transition_dispatch()
4503 // NB: only use the first handler, this is how MIDAS always worked
4504 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4505
4506 _trans_table_mutex.lock();
4507 size_t n = _trans_table.size();
4508 _trans_table_mutex.unlock();
4509
4510 for (size_t i = 0; i < n; i++) {
4511 _trans_table_mutex.lock();
4513 _trans_table_mutex.unlock();
4514 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4515 /* call registered function */
4516 if (tt.func) {
4517 if (tr_client->debug_flag == 1)
4518 printf("Calling local transition callback\n");
4519 if (tr_client->debug_flag == 2)
4520 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4521
4522 tr_client->rpc_start_time = ss_millitime();
4523
4524 write_tr_client_to_odb(hDB, tr_client);
4525
4526 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4527 errorstr[0] = 0;
4528
4529 tr_client->status = tt.func(tr_client->run_number, errorstr);
4530
4531 tr_client->errorstr = errorstr;
4532
4533 tr_client->rpc_end_time = ss_millitime();
4534
4535 if (tr_client->debug_flag == 1)
4536 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4537 if (tr_client->debug_flag == 2)
4538 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4539
4540 tr_client->end_time = ss_millitime();
4541
4542 // write status and end_time to ODB
4543
4544 write_tr_client_to_odb(hDB, tr_client);
4545
4546 return tr_client->status;
4547 }
4548 }
4549 }
4550
4551 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4552
4553 tr_client->status = CM_SUCCESS;
4554 tr_client->end_time = ss_millitime();
4555
4556 // write status and end_time to ODB
4557
4558 write_tr_client_to_odb(hDB, tr_client);
4559
4560 return CM_SUCCESS;
4561}
4562
4563/********************************************************************/
4603static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4604{
4605 INT i, status, size, sequence_number, port, state;
4606 HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hKeyTrans;
4607 DWORD seconds;
4608 char tr_key_name[256];
4609 KEY key;
4610 BOOL deferred;
4611 char xerrstr[TRANSITION_ERROR_STRING_LENGTH];
4612
4613 //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);
4614
4615 /* if needed, use internal error string */
4616 if (!errstr) {
4617 errstr = xerrstr;
4618 errstr_size = sizeof(xerrstr);
4619 }
4620
4621 /* erase error string */
4622 errstr[0] = 0;
4623
4624 /* get key of local client */
4625 cm_get_experiment_database(&hDB, &hKeylocal);
4626
4627 deferred = (transition & TR_DEFERRED) > 0;
4628 transition &= ~TR_DEFERRED;
4629
4630 /* check for valid transition */
4632 && transition != TR_STARTABORT) {
4633 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4634 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4635 return CM_INVALID_TRANSITION;
4636 }
4637
4638 /* check if transition in progress */
4639 if (!deferred) {
4640 i = 0;
4641 size = sizeof(i);
4642 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4643 if (i == 1) {
4644 if (errstr) {
4645 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4646 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4647 }
4648 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4650 }
4651 }
4652
4653 /* indicate transition in progress */
4654 i = transition;
4655 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4656
4657 /* clear run abort flag */
4658 i = 0;
4659 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4660
4661 /* construct new transition state */
4662
4663 TrState s;
4664
4667 s.async_flag = async_flag;
4668 s.debug_flag = debug_flag;
4669 s.status = 0;
4670 s.errorstr[0] = 0;
4672 s.end_time = 0;
4673
4674 /* construct the ODB tree /System/Transition */
4675
4676 status = db_delete(hDB, 0, "/System/Transition/TR_STARTABORT");
4677
4678 if (transition != TR_STARTABORT) {
4679 status = db_delete(hDB, 0, "/System/Transition/Clients");
4680 }
4681
4682 if (transition != TR_STARTABORT) {
4683 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4684 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4685 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4686 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4687 status = 0;
4688 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4689 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4690 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4691 }
4692
4693 /* check for alarms */
4694 i = 0;
4695 size = sizeof(i);
4696 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4697 if (i == TRUE && transition == TR_START) {
4698 al_check();
4699 std::string alarms;
4700 if (al_get_alarms(&alarms) > 0) {
4701 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4702 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4703 mstrlcat(errstr, alarms.c_str(), errstr_size);
4704 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4705 }
4706 }
4707
4708 /* check for required programs */
4709 i = 0;
4710 size = sizeof(i);
4711 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4712 if (i == TRUE && transition == TR_START) {
4713
4714 HNDLE hkeyroot, hkey;
4715
4716 /* check /programs alarms */
4717 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4718 if (hkeyroot) {
4719 for (i = 0;; i++) {
4720 BOOL program_info_required = FALSE;
4721 status = db_enum_key(hDB, hkeyroot, i, &hkey);
4723 break;
4724
4725 db_get_key(hDB, hkey, &key);
4726
4727 /* don't check "execute on xxx" */
4728 if (key.type != TID_KEY)
4729 continue;
4730
4731 size = sizeof(program_info_required);
4732 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4733 if (status != DB_SUCCESS) {
4734 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4735 continue;
4736 }
4737
4738 if (program_info_required) {
4739 std::string name = rpc_get_name();
4740 std::string str = name;
4741 str.resize(strlen(key.name));
4742 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4743 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4744 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4745 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4746 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4747 }
4748 }
4749 }
4750 }
4751 }
4752
4753 /* do detached transition via mtransition tool */
4754 if (async_flag & TR_DETACH) {
4755 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4756 return tr_finish(hDB, &s, transition, status, errstr);
4757 }
4758
4759 mstrlcpy(errstr, "Unknown error", errstr_size);
4760
4761 if (debug_flag == 0) {
4762 size = sizeof(i);
4763 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4764 }
4765
4766 /* if no run number is given, get it from ODB and increment it */
4767 if (run_number == 0) {
4768 size = sizeof(run_number);
4769 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4770 assert(status == SUCCESS);
4771 if (transition == TR_START) {
4772 run_number++;
4773 }
4775
4776 if (transition != TR_STARTABORT) {
4777 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4778 }
4779 }
4780
4781 if (run_number <= 0) {
4782 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4783 abort();
4784 }
4785
4786 /* Set new run number in ODB */
4787 if (transition == TR_START) {
4788 if (debug_flag == 1)
4789 printf("Setting run number %d in ODB\n", run_number);
4790 if (debug_flag == 2)
4791 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4792
4793 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4794 if (status != DB_SUCCESS) {
4795 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4796 abort();
4797 }
4798 }
4799
4800 if (deferred) {
4801 if (debug_flag == 1)
4802 printf("Clearing /Runinfo/Requested transition\n");
4803 if (debug_flag == 2)
4804 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4805
4806 /* remove transition request */
4807 i = 0;
4808 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4809 } else {
4810 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4811 if (status != DB_SUCCESS) {
4812 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4813 if (errstr)
4814 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4815 return tr_finish(hDB, &s, transition, status, errstr);
4816 }
4817
4818 /* check if deferred transition already in progress */
4819 size = sizeof(i);
4820 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4821 if (i) {
4822 if (errstr) {
4823 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4824 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4825 }
4826 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4827 }
4828
4829 std::string trname = cm_transition_name(transition);
4830
4831 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4832
4833 /* search database for clients with deferred transition request */
4834 for (i = 0, status = 0;; i++) {
4835 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4837 break;
4838
4839 if (status == DB_SUCCESS) {
4840 size = sizeof(sequence_number);
4841 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4842
4843 /* if registered for deferred transition, set flag in ODB and return */
4844 if (status == DB_SUCCESS) {
4845 char str[256];
4846 size = NAME_LENGTH;
4847 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4848
4849 if (debug_flag == 1)
4850 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4851 if (debug_flag == 2)
4852 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4853
4854 if (debug_flag == 1)
4855 printf("Setting /Runinfo/Requested transition\n");
4856 if (debug_flag == 2)
4857 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4858
4859 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4860 * will activate the deferred transition code in the frontend.
4861 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4862
4863 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4864
4865 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4866
4867 if (errstr)
4868 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4869
4870 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4871 }
4872 }
4873 }
4874 }
4875
4876 /* execute programs on start */
4877 if (transition == TR_START) {
4878 char str[256];
4879 str[0] = 0;
4880 size = sizeof(str);
4881 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4882 if (str[0])
4883 ss_system(str);
4884
4885 db_find_key(hDB, 0, "/Programs", &hRootKey);
4886 if (hRootKey) {
4887 for (i = 0;; i++) {
4888 BOOL program_info_auto_start = FALSE;
4889 status = db_enum_key(hDB, hRootKey, i, &hKey);
4891 break;
4892
4893 db_get_key(hDB, hKey, &key);
4894
4895 /* don't check "execute on xxx" */
4896 if (key.type != TID_KEY)
4897 continue;
4898
4899 size = sizeof(program_info_auto_start);
4900 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4901 if (status != DB_SUCCESS) {
4902 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4903 continue;
4904 }
4905
4906 if (program_info_auto_start) {
4907 char start_command[MAX_STRING_LENGTH];
4908 start_command[0] = 0;
4909
4910 size = sizeof(start_command);
4911 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4912 if (status != DB_SUCCESS) {
4913 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4914 continue;
4915 }
4916
4917 if (start_command[0]) {
4918 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4919 start_command);
4920 ss_system(start_command);
4921 }
4922 }
4923 }
4924 }
4925 }
4926
4927 /* execute programs on startabort */
4928 if (transition == TR_STARTABORT) {
4929 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4930 std::string cmd;
4931 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4932
4933 if (!cmd.empty())
4934 ss_system(cmd.c_str());
4935 }
4936
4937 /* set new start time in database */
4938 if (transition == TR_START) {
4939 /* ASCII format */
4940 std::string now = cm_asctime();
4941 now.reserve(32);
4942 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4943
4944 /* reset stop time */
4945 seconds = 0;
4946 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4947
4948 /* Seconds since 1.1.1970 */
4949 cm_time(&seconds);
4950 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4951 }
4952
4953 size = sizeof(state);
4954 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4955
4956 /* set stop time in database */
4957 if (transition == TR_STOP) {
4958 if (status != DB_SUCCESS)
4959 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4960
4961 if (state != STATE_STOPPED) {
4962 /* stop time binary */
4963 cm_time(&seconds);
4964 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4965 if (status != DB_SUCCESS)
4966 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4967
4968 /* stop time ascii */
4969 std::string now = cm_asctime();
4970 now.reserve(32);
4971 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4972 if (status != DB_SUCCESS)
4973 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4974 }
4975 }
4976
4977 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4978 if (status != DB_SUCCESS) {
4979 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4980 if (errstr)
4981 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4982 return tr_finish(hDB, &s, transition, status, errstr);
4983 }
4984
4985 std::string trname = cm_transition_name(transition);
4986
4987 /* check that all transition clients are alive */
4988 for (int i = 0;;) {
4989 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4990 if (status != DB_SUCCESS)
4991 break;
4992
4994
4995 if (status == DB_SUCCESS) {
4996 /* this client is alive. Check next one! */
4997 i++;
4998 continue;
4999 }
5000
5001 assert(status == CM_NO_CLIENT);
5002
5003 /* start from scratch: removing odb entries as we iterate over them
5004 * does strange things to db_enum_key() */
5005 i = 0;
5006 }
5007
5008 /* check for broken RPC connections */
5010
5011 if (debug_flag == 1)
5012 printf("---- Transition %s started ----\n", trname.c_str());
5013 if (debug_flag == 2)
5014 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
5015
5016 sprintf(tr_key_name, "Transition %s", trname.c_str());
5017
5018 /* search database for clients which registered for transition */
5019
5020 for (int i = 0, status = 0;; i++) {
5021 KEY subkey;
5022 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
5024 break;
5025
5026 status = db_get_key(hDB, hSubkey, &subkey);
5027 assert(status == DB_SUCCESS);
5028
5029 if (status == DB_SUCCESS) {
5030 status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
5031
5032 if (status == DB_SUCCESS) {
5033
5034 db_get_key(hDB, hKeyTrans, &key);
5035
5036 for (int j = 0; j < key.num_values; j++) {
5037 size = sizeof(sequence_number);
5038 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
5039 assert(status == DB_SUCCESS);
5040
5041 TrClient *c = new TrClient;
5042
5044 c->transition = transition;
5045 c->run_number = run_number;
5046 c->async_flag = async_flag;
5047 c->debug_flag = debug_flag;
5048 c->sequence_number = sequence_number;
5049 c->status = 0;
5050 c->key_name = subkey.name;
5051
5052 /* get client info */
5053 char client_name[NAME_LENGTH];
5054 size = sizeof(client_name);
5055 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
5056 c->client_name = client_name;
5057
5059 size = sizeof(host_name);
5060 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
5061 c->host_name = host_name;
5062
5063 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
5064
5065 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5066 /* remember own client */
5067 c->port = 0;
5068 } else {
5069 size = sizeof(port);
5070 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5071 c->port = port;
5072 }
5073
5074 /* check for duplicates */
5075
5076 bool found = false;
5077 for (size_t k=0; k<s.clients.size(); k++) {
5078 TrClient* cc = s.clients[k].get();
5079 if (cc->client_name == c->client_name)
5080 if (cc->host_name == c->host_name)
5081 if (cc->port == c->port)
5082 if (cc->sequence_number == c->sequence_number)
5083 found = true;
5084 }
5085
5086 if (!found) {
5087 s.clients.push_back(std::unique_ptr<TrClient>(c));
5088 c = NULL;
5089 } else {
5090 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);
5091 delete c;
5092 c = NULL;
5093 }
5094 }
5095 }
5096 }
5097 }
5098
5099 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5100
5101 /* set predecessor for multi-threaded transitions */
5102 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5103 if (s.clients[idx]->sequence_number == 0) {
5104 // sequence number 0 means "don't care"
5105 } else {
5106 /* find clients with smaller sequence number */
5107 if (idx > 0) {
5108 for (size_t i = idx - 1; ; i--) {
5109 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5110 if (s.clients[i]->sequence_number > 0) {
5111 s.clients[idx]->wait_for_index.push_back(i);
5112 }
5113 }
5114 if (i==0)
5115 break;
5116 }
5117 }
5118 }
5119 }
5120
5121 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5122 write_tr_client_to_odb(hDB, s.clients[idx].get());
5123 }
5124
5125#if 0
5126 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5127 printf("TrClient[%d]: ", int(idx));
5128 s.clients[idx]->Print();
5129 printf("\n");
5130 }
5131#endif
5132
5133 /* contact ordered clients for transition -----------------------*/
5135 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5136 if (debug_flag == 1)
5137 printf("\n==== Found client \"%s\" with sequence number %d\n",
5138 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5139 if (debug_flag == 2)
5140 cm_msg(MINFO, "cm_transition",
5141 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5142 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5143
5144 if (async_flag & TR_MTHREAD) {
5146 assert(s.clients[idx]->thread == NULL);
5147 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5148 } else {
5149 if (s.clients[idx]->port == 0) {
5150 /* if own client call transition callback directly */
5152 } else {
5153 /* if other client call transition via RPC layer */
5154 status = cm_transition_call(&s, idx);
5155 }
5156
5157 if (status == CM_SUCCESS && transition != TR_STOP)
5158 if (s.clients[idx]->status != SUCCESS) {
5159 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5160 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5161 break;
5162 }
5163 }
5164
5165 if (status != CM_SUCCESS)
5166 break;
5167 }
5168
5169 /* wait until all threads have finished */
5170 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5171 if (s.clients[idx]->thread) {
5172 // join() will wait forever until thread finishes
5173 s.clients[idx]->thread->join();
5174 delete s.clients[idx]->thread;
5175 s.clients[idx]->thread = NULL;
5176 }
5177 }
5178
5179 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5180
5181 i = 0;
5182 size = sizeof(i);
5183 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5184
5185 if (status == DB_SUCCESS && i == 0) {
5186 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5187
5188 if (errstr != NULL)
5189 mstrlcpy(errstr, "Canceled", errstr_size);
5190
5191 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5192 }
5193
5194 /* search for any error */
5195 for (size_t idx = 0; idx < s.clients.size(); idx++)
5196 if (s.clients[idx]->status != CM_SUCCESS) {
5197 status = s.clients[idx]->status;
5198 if (errstr)
5199 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5200 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5201 break;
5202 }
5203
5204 if (transition != TR_STOP && status != CM_SUCCESS) {
5205 /* indicate abort */
5206 i = 1;
5207 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5208 i = 0;
5209 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5210
5211 return tr_finish(hDB, &s, transition, status, errstr);
5212 }
5213
5214 if (debug_flag == 1)
5215 printf("\n---- Transition %s finished ----\n", trname.c_str());
5216 if (debug_flag == 2)
5217 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5218
5219 /* set new run state in database */
5222
5223 if (transition == TR_PAUSE)
5225
5226 if (transition == TR_STOP)
5228
5231
5232 size = sizeof(state);
5233 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5234 if (status != DB_SUCCESS)
5235 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5236
5237 /* send notification message */
5238 if (transition == TR_START)
5239 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5240 if (transition == TR_STOP)
5241 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5242 if (transition == TR_PAUSE)
5243 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5244 if (transition == TR_RESUME)
5245 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5247 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5248
5249 /* lock/unlock ODB values if present */
5250 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5251 if (hKey) {
5252 if (state == STATE_STOPPED)
5254 else
5256 }
5257
5258 /* flush online database */
5259 if (transition == TR_STOP)
5261
5262 /* execute/stop programs on stop */
5263 if (transition == TR_STOP) {
5264 std::string cmd;
5265 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5266 if (!cmd.empty())
5267 ss_system(cmd.c_str());
5268
5269 db_find_key(hDB, 0, "/Programs", &hRootKey);
5270 if (hRootKey) {
5271 for (i = 0;; i++) {
5272 BOOL program_info_auto_stop = FALSE;
5273 status = db_enum_key(hDB, hRootKey, i, &hKey);
5275 break;
5276
5277 db_get_key(hDB, hKey, &key);
5278
5279 /* don't check "execute on xxx" */
5280 if (key.type != TID_KEY)
5281 continue;
5282
5283 size = sizeof(program_info_auto_stop);
5284 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5285 if (status != DB_SUCCESS) {
5286 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5287 continue;
5288 }
5289
5290 if (program_info_auto_stop) {
5291 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5293 }
5294 }
5295 }
5296 }
5297
5298
5299 /* indicate success */
5300 i = 0;
5301 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5302
5303 if (errstr != NULL)
5304 mstrlcpy(errstr, "Success", errstr_size);
5305
5306 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5307}
5308
5309/*------------------------------------------------------------------*/
5310
5311/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5312static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5313 int status;
5314
5315 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5316
5317 if (transition == TR_START && status != CM_SUCCESS) {
5318 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5319 errstr);
5320 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5321 }
5322
5323 return status;
5324}
5325
5326/*------------------------------------------------------------------*/
5327
5328static INT tr_main_thread(void *param) {
5329 INT status;
5330 TR_PARAM *trp;
5331
5332 trp = (TR_PARAM *) param;
5333 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5334
5335 trp->status = status;
5336 trp->finished = TRUE;
5337
5338 return 0;
5339}
5340
5342{
5343 if (_trp.thread && !_trp.finished) {
5344 //printf("main transition thread did not finish yet!\n");
5346 }
5347
5348 std::thread* t = _trp.thread.exchange(NULL);
5349
5350 if (t) {
5351 t->join();
5352 delete t;
5353 t = NULL;
5354 }
5355
5356 return CM_SUCCESS;
5357}
5358
5359/* wrapper around cm_transition1() for detached multi-threaded transitions */
5360INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5361 int mflag = async_flag & TR_MTHREAD;
5362 int sflag = async_flag & TR_SYNC;
5363
5365
5366 if (status != CM_SUCCESS) {
5367 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5369 }
5370
5371 /* get key of local client */
5372 HNDLE hDB;
5374
5375 bool deferred = (transition & TR_DEFERRED) > 0;
5376 INT trans_raw = (transition & ~TR_DEFERRED);
5377
5378 /* check for valid transition */
5379 if (trans_raw != TR_START && trans_raw != TR_STOP && trans_raw != TR_PAUSE && trans_raw != TR_RESUME && trans_raw != TR_STARTABORT) {
5380 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5381 if (errstr) {
5382 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5383 }
5384 return CM_INVALID_TRANSITION;
5385 }
5386
5387 /* check if transition in progress */
5388 if (!deferred) {
5389 int i = 0;
5390 int size = sizeof(i);
5391 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5392 if (i == 1) {
5393 if (errstr) {
5394 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5395 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5396 }
5397 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5399 }
5400 }
5401
5402 if (mflag) {
5405 if (sflag) {
5406 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5407 _trp.errstr = errstr;
5408 _trp.errstr_size = errstr_size;
5409 } else {
5410 /* in normal MTHREAD mode, we return right away and
5411 * if errstr is a local variable in the caller and they return too,
5412 * errstr becomes a stale reference and writing into it will corrupt the stack
5413 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5414 * and we definitely corrupt mlogger memory with out this: */
5415 _trp.errstr = NULL;
5416 _trp.errstr_size = 0;
5417 }
5418 _trp.async_flag = async_flag;
5419 _trp.debug_flag = debug_flag;
5420 _trp.status = 0;
5422
5423 if (errstr)
5424 *errstr = 0; // null error string
5425
5426 //ss_thread_create(tr_main_thread, &_trp);
5427
5428 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5429
5430 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5431
5432 if (sflag) {
5433
5434 /* wait until main thread has finished */
5435 do {
5436 ss_sleep(10);
5437 } while (!_trp.finished);
5438
5439 std::thread* t = _trp.thread.exchange(NULL);
5440
5441 if (t) {
5442 t->join();
5443 delete t;
5444 t = NULL;
5445 }
5446
5447 return _trp.status;
5448 }
5449 } else
5450 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5451
5452 return CM_SUCCESS;
5453}
5454
5456#ifndef DOXYGEN_SHOULD_SKIP_THIS
5457
5458/********************************************************************/
5459INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
5460/********************************************************************\
5461
5462 Routine: cm_dispatch_ipc
5463
5464 Purpose: Called from ss_suspend if an IPC message arrives
5465
5466 Input:
5467 INT msg IPC message we got, MSG_ODB/MSG_BM
5468 INT p1, p2 Optional parameters
5469 int s Optional server socket
5470
5471 Output:
5472 none
5473
5474 Function value:
5475 CM_SUCCESS Successful completion
5476
5477\********************************************************************/
5478{
5479 if (message[0] == 'O') {
5480 HNDLE hDB, hKey, hKeyRoot;
5481 INT index;
5482 index = 0;
5483 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5484 if (client_socket) {
5485 return db_update_record_mserver(hDB, hKeyRoot, hKey, index, client_socket);
5486 } else {
5487 return db_update_record_local(hDB, hKeyRoot, hKey, index);
5488 }
5489 }
5490
5491 /* message == "B" means "resume event sender" */
5492 if (message[0] == 'B' && message[2] != ' ') {
5493 char str[NAME_LENGTH];
5494
5495 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5496
5497 mstrlcpy(str, message + 2, sizeof(str));
5498 if (strchr(str, ' '))
5499 *strchr(str, ' ') = 0;
5500
5501 if (client_socket)
5502 return bm_notify_client(str, client_socket);
5503 else
5504 return bm_push_event(str);
5505 }
5506
5507 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5508
5509 return CM_SUCCESS;
5510}
5511
5512/********************************************************************/
5514
5515void cm_ctrlc_handler(int sig) {
5516 if (_ctrlc_pressed) {
5517 printf("Received 2nd Ctrl-C, hard abort\n");
5518 exit(0);
5519 }
5520 printf("Received Ctrl-C, aborting...\n");
5522
5524}
5525
5529
5533
5534/********************************************************************/
5535int cm_exec_script(const char *odb_path_to_script)
5536/********************************************************************\
5537
5538 Routine: cm_exec_script
5539
5540 Purpose: Execute script from /Script tree
5541
5542 exec_script is enabled by the tree /Script
5543 The /Script struct is composed of list of keys
5544 from which the name of the key is the button name
5545 and the sub-structure is a record as follow:
5546
5547 /Script/<button_name> = <script command> (TID_STRING)
5548
5549 The "Script command", containing possible arguements,
5550 is directly executed.
5551
5552 /Script/<button_name>/<script command>
5553 <soft link1>|<arg1>
5554 <soft link2>|<arg2>
5555 ...
5556
5557 The arguments for the script are derived from the
5558 subtree below <button_name>, where <button_name> must be
5559 TID_KEY. The subtree may then contain arguments or links
5560 to other values in the ODB, like run number etc.
5561
5562\********************************************************************/
5563{
5564 HNDLE hDB, hkey;
5565 KEY key;
5566 int status;
5567
5569 if (status != DB_SUCCESS)
5570 return status;
5571
5572 status = db_find_key(hDB, 0, odb_path_to_script, &hkey);
5573 if (status != DB_SUCCESS)
5574 return status;
5575
5576 status = db_get_key(hDB, hkey, &key);
5577 if (status != DB_SUCCESS)
5578 return status;
5579
5580 std::string command;
5581
5582 if (key.type == TID_STRING) {
5583 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5584 if (status != DB_SUCCESS) {
5585 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5586 odb_path_to_script, status);
5587 return status;
5588 }
5589 } else if (key.type == TID_KEY) {
5590 for (int i = 0;; i++) {
5591 HNDLE hsubkey;
5592 KEY subkey;
5593 db_enum_key(hDB, hkey, i, &hsubkey);
5594 if (!hsubkey)
5595 break;
5596 db_get_key(hDB, hsubkey, &subkey);
5597
5598 if (i > 0)
5599 command += " ";
5600
5601 if (subkey.type == TID_KEY) {
5602 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5603 subkey.name);
5604 return DB_TYPE_MISMATCH;
5605 } else {
5606 int size = subkey.item_size;
5607 char *buf = (char *) malloc(size);
5608 assert(buf != NULL);
5609 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5610 if (status != DB_SUCCESS) {
5611 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5612 odb_path_to_script, subkey.name, subkey.type, status);
5613 free(buf);
5614 return status;
5615 }
5616 if (subkey.type == TID_STRING) {
5617 command += buf;
5618 } else {
5619 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5620 }
5621 free(buf);
5622 }
5623 }
5624 } else {
5625 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5626 odb_path_to_script, key.type);
5627 return DB_TYPE_MISMATCH;
5628 }
5629
5630 // printf("exec_script: %s\n", command.c_str());
5631
5632 if (command.length() > 0) {
5633 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5634 ss_system(command.c_str());
5635 }
5636
5637 return SUCCESS;
5638}
5639
5641#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5642
5643static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5644
5645/********************************************************************/
5654 static DWORD alarm_last_checked_sec = 0;
5655 DWORD now_sec = ss_time();
5656
5657 DWORD now_millitime = ss_millitime();
5658 static DWORD last_millitime = 0;
5659 DWORD tdiff_millitime = now_millitime - last_millitime;
5660 const DWORD kPeriod = 1000;
5661 if (last_millitime == 0) {
5662 last_millitime = now_millitime;
5663 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5664 }
5665
5666 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5667
5668 //if (now_millitime < last_millitime) {
5669 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5670 //}
5671
5672 /* check alarms once every 10 seconds */
5673 if (now_sec - alarm_last_checked_sec > 10) {
5674 al_check();
5675 alarm_last_checked_sec = now_sec;
5676 }
5677
5678 /* run periodic checks previously done by cm_watchdog */
5679
5680 if (tdiff_millitime >= kPeriod) {
5681 BOOL wrong_interval = FALSE;
5682 if (tdiff_millitime > 60000)
5683 wrong_interval = TRUE;
5684
5685 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5686
5687 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5688 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5689
5691
5692 last_millitime = now_millitime;
5693 }
5694
5695 /* reap transition thread */
5696
5698
5699 return CM_SUCCESS;
5700}
5701
5702/********************************************************************/
5716INT cm_yield(INT millisec) {
5717 INT status;
5718 INT bMore;
5719 //static DWORD last_yield = 0;
5720 //static DWORD last_yield_time = 0;
5721 //DWORD start_yield = ss_millitime();
5722
5723 /* check for ctrl-c */
5724 if (_ctrlc_pressed)
5725 return RPC_SHUTDOWN;
5726
5727 /* flush the cm_msg buffer */
5729
5730 if (!rpc_is_remote()) {
5731 /* flush the ODB to its binary file */
5732 /* for remote clients, ODB is flushed by the mserver */
5733 HNDLE hDB;
5736 }
5737
5738 /* check for available events */
5739 if (rpc_is_remote()) {
5740 //printf("cm_yield() calling bm_poll_event()\n");
5742
5743 if (status == SS_ABORT) {
5744 return status;
5745 }
5746
5747 if (status == BM_SUCCESS) {
5748 /* one or more events received by bm_poll_event() */
5749 status = ss_suspend(0, 0);
5750 } else {
5751 status = ss_suspend(millisec, 0);
5752 }
5753
5754 return status;
5755 }
5756
5758
5759 if (status != CM_SUCCESS)
5760 return status;
5761
5762 //DWORD start_check = ss_millitime();
5763
5764 bMore = bm_check_buffers();
5765
5766 //DWORD end_check = ss_millitime();
5767 //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);
5768 //fflush(stdout);
5769
5770 if (bMore == BM_CORRUPTED) {
5771 status = SS_ABORT;
5772 } else if (bMore) {
5773 /* if events available, quickly check other IPC channels */
5774 status = ss_suspend(0, 0);
5775 } else {
5776 status = ss_suspend(millisec, 0);
5777 }
5778
5779 /* flush the cm_msg buffer */
5781
5782 //DWORD end_yield = ss_millitime();
5783 //last_yield_time = end_yield - start_yield;
5784 //last_yield = start_yield;
5785
5786 return status;
5787}
5788
5789/********************************************************************/
5797INT cm_execute(const char *command, char *result, INT bufsize) {
5798 INT n;
5799 int fh;
5800 int status = 0;
5801 static int check_cm_execute = 1;
5802 static int enable_cm_execute = 0;
5803
5804 if (rpc_is_remote())
5805 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5806
5807 if (check_cm_execute) {
5808 int status;
5809 int size;
5810 HNDLE hDB;
5811 check_cm_execute = 0;
5812
5814 assert(status == DB_SUCCESS);
5815
5816 size = sizeof(enable_cm_execute);
5817 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5818 assert(status == DB_SUCCESS);
5819
5820 //printf("enable_cm_execute %d\n", enable_cm_execute);
5821 }
5822
5823 if (!enable_cm_execute) {
5824 char buf[32];
5825 mstrlcpy(buf, command, sizeof(buf));
5826 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5827 return CM_WRONG_PASSWORD;
5828 }
5829
5830 if (bufsize > 0) {
5831 std::string filename = msprintf("%d.tmp", ss_getpid());
5832 std::string str = msprintf("%s > %s", command, filename.c_str());
5833
5834 status = system(str.c_str());
5835
5836 fh = open(filename.c_str(), O_RDONLY, 0644);
5837 result[0] = 0;
5838 if (fh >= 0) {
5839 n = read(fh, result, bufsize - 1);
5840 result[MAX(0, n)] = 0;
5841 close(fh);
5842 }
5843 remove(filename.c_str());
5844 } else {
5845 status = system(command);
5846 }
5847
5848 if (status < 0) {
5849 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5850 return CM_SET_ERROR;
5851 }
5852
5853 return CM_SUCCESS;
5854}
5855
5856
5857
5859#ifndef DOXYGEN_SHOULD_SKIP_THIS
5860
5861/********************************************************************/
5862INT cm_register_function(INT id, INT(*func)(INT, void **))
5863/********************************************************************\
5864
5865 Routine: cm_register_function
5866
5867 Purpose: Call rpc_register_function and publish the registered
5868 function under system/clients/<pid>/RPC
5869
5870 Input:
5871 INT id RPC ID
5872 INT *func New dispatch function
5873
5874 Output:
5875 <implicit: func gets copied to rpc_list>
5876
5877 Function value:
5878 CM_SUCCESS Successful completion
5879 RPC_INVALID_ID RPC ID not found
5880
5881\********************************************************************/
5882{
5883 HNDLE hDB, hKey;
5884 INT status;
5885 char str[80];
5886
5887 status = rpc_register_function(id, func);
5888 if (status != RPC_SUCCESS)
5889 return status;
5890
5892
5893 /* create new key for this id */
5894 status = 1;
5895 sprintf(str, "RPC/%d", id);
5896
5898 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5900
5901 if (status != DB_SUCCESS)
5902 return status;
5903
5904 return CM_SUCCESS;
5905}
5906
5907
5909#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5910
5911//
5912// Return "/"-terminated file path for given history channel
5913//
5914
5915std::string cm_get_history_path(const char* history_channel)
5916{
5917 int status;
5918 HNDLE hDB;
5919 std::string path;
5920
5922
5923 if (history_channel && (strlen(history_channel) > 0)) {
5924 std::string p;
5925 p += "/Logger/History/";
5926 p += history_channel;
5927 p += "/History dir";
5928
5929 // NB: be careful to avoid creating odb entries under /logger
5930 // for whatever values of "history_channel" we get called with!
5931 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5932 if (status == DB_SUCCESS && path.length() > 0) {
5933 // if not absolute path, prepend with experiment directory
5934 if (path[0] != DIR_SEPARATOR)
5935 path = cm_get_path() + path;
5936 // append directory separator
5937 if (path.back() != DIR_SEPARATOR)
5938 path += DIR_SEPARATOR_STR;
5939 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5940 return path;
5941 }
5942 }
5943
5944 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5945 if (status == DB_SUCCESS && path.length() > 0) {
5946 // if not absolute path, prepend with experiment directory
5947 if (path[0] != DIR_SEPARATOR)
5948 path = cm_get_path() + path;
5949 // append directory separator
5950 if (path.back() != DIR_SEPARATOR)
5951 path += DIR_SEPARATOR_STR;
5952 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5953 return path;
5954 }
5955
5956 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5957 if (status == DB_SUCCESS && path.length() > 0) {
5958 // if not absolute path, prepend with experiment directory
5959 if (path[0] != DIR_SEPARATOR)
5960 path = cm_get_path() + path;
5961 // append directory separator
5962 if (path.back() != DIR_SEPARATOR)
5963 path += DIR_SEPARATOR_STR;
5964 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5965 return path;
5966 }
5967
5968 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5969 return cm_get_path();
5970}
5971
/* end of cmfunctionc */
5974
5980/********************************************************************\
5981* *
5982* bm_xxx - Buffer Manager Functions *
5983* *
5984\********************************************************************/
5985
5987
5988#ifdef LOCAL_ROUTINES
5989
5990// see locking code in xbm_lock_buffer()
5991static int _bm_lock_timeout = 5 * 60 * 1000;
5992static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5993
5995{
5996 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5997
5998 bool badindex = false;
5999 bool badclient = false;
6000
6001 int idx = pbuf->client_index;
6002
6003 if (idx < 0) {
6004 badindex = true;
6005 } else if (idx > pbuf->buffer_header->max_client_index) {
6006 badindex = true;
6007 } else {
6008 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
6009 if (pclient->name[0] == 0)
6010 badclient = true;
6011 else if (pclient->pid != ss_getpid())
6012 badclient = true;
6013
6014 //if (strcmp(pclient->name,"mdump")==0) {
6015 // for (int i=0; i<15; i++) {
6016 // printf("sleep %d\n", i);
6017 // ::sleep(1);
6018 // }
6019 //}
6020 }
6021
6022#if 0
6023 if (badindex) {
6024 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
6025 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
6026 badindex, ss_getpid());
6027 } else if (badclient) {
6028 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",
6029 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
6030 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
6031 ss_getpid(), badclient);
6032 } else {
6033 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",
6034 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
6035 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
6036 ss_getpid());
6037 }
6038#endif
6039
6040 if (badindex || badclient) {
6041 static int prevent_recursion = 1;
6042
6043 if (prevent_recursion) {
6044 prevent_recursion = 0;
6045
6046 if (badindex) {
6047 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());
6048 } else {
6049 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());
6050 }
6051
6052 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
6053 }
6054
6055 if (badindex) {
6056 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());
6057 } else {
6058 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());
6059 }
6060
6061 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
6062
6063 pbuf_guard.unlock();
6064
6065 abort();
6066 }
6067
6068 return idx;
6069}
6070
6072 int my_client_index = bm_validate_client_index_locked(pbuf_guard);
6073 return pbuf_guard.get_pbuf()->buffer_header->client + my_client_index;
6074}
6075
6076#endif // LOCAL_ROUTINES
6077
6078/********************************************************************/
6087INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6088 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6089 // because of mismatch in sign-extension between signed 16-bit event_id and
6090 // unsigned 16-bit constants. K.O.
6091
6092 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)))
6093 /* fragmented event */
6094 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6095 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6096
6097 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6098 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6099}
6100
6101#ifdef LOCAL_ROUTINES
6102
6103/********************************************************************/
6108 int k, nc;
6109 BUFFER_CLIENT *pbctmp;
6110
6111 /* clear entry from client structure in buffer header */
6112 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6113
6114 /* calculate new max_client_index entry */
6115 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6116 if (pheader->client[k].pid != 0)
6117 break;
6118 pheader->max_client_index = k + 1;
6119
6120 /* count new number of clients */
6121 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6122 if (pheader->client[k].pid != 0)
6123 nc++;
6124 pheader->num_clients = nc;
6125
6126 /* check if anyone is waiting and wake him up */
6127 pbctmp = pheader->client;
6128
6129 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6130 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6131 ss_resume(pbctmp->port, "B ");
6132}
6133
6134/********************************************************************/
6138static void bm_cleanup_buffer_locked(BUFFER* pbuf, const char *who, DWORD actual_time) {
6139 BUFFER_HEADER *pheader;
6140 BUFFER_CLIENT *pbclient;
6141 int j;
6142
6143 pheader = pbuf->buffer_header;
6144 pbclient = pheader->client;
6145
6146 /* now check other clients */
6147 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6148 if (pbclient->pid) {
6149 if (!ss_pid_exists(pbclient->pid)) {
6150 cm_msg(MINFO, "bm_cleanup",
6151 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6152 pheader->name, who, pbclient->pid);
6153
6154 bm_remove_client_locked(pheader, j);
6155 continue;
6156 }
6157 }
6158
6159 /* If client process has no activity, clear its buffer entry. */
6160 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6161 DWORD tdiff = actual_time - pbclient->last_activity;
6162#if 0
6163 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6164 pheader->name,
6165 pbclient->name,
6166 pbclient->last_activity,
6168 tdiff,
6169 tdiff,
6170 pbclient->watchdog_timeout);
6171#endif
6172 if (actual_time > pbclient->last_activity &&
6173 tdiff > pbclient->watchdog_timeout) {
6174
6175 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6176 pbclient->name, pheader->name, who,
6177 tdiff / 1000.0,
6178 pbclient->watchdog_timeout / 1000.0);
6179
6180 bm_remove_client_locked(pheader, j);
6181 }
6182 }
6183 }
6184}
6185
6189static void bm_update_last_activity(DWORD millitime) {
6190 int pid = ss_getpid();
6191
6192 std::vector<BUFFER*> mybuffers;
6193
6194 gBuffersMutex.lock();
6195 mybuffers = gBuffers;
6196 gBuffersMutex.unlock();
6197
6198 for (BUFFER* pbuf : mybuffers) {
6199 if (!pbuf)
6200 continue;
6201 if (pbuf->attached) {
6202
6203 bm_lock_buffer_guard pbuf_guard(pbuf);
6204
6205 if (!pbuf_guard.is_locked())
6206 continue;
6207
6208 BUFFER_HEADER *pheader = pbuf->buffer_header;
6209 for (int j = 0; j < pheader->max_client_index; j++) {
6210 BUFFER_CLIENT *pclient = pheader->client + j;
6211 if (pclient->pid == pid) {
6212 pclient->last_activity = millitime;
6213 }
6214 }
6215 }
6216 }
6217}
6218
6219#endif // LOCAL_ROUTINES
6220
6224static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
6225{
6226#ifdef LOCAL_ROUTINES
6227
6228 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6229
6230 std::vector<BUFFER*> mybuffers;
6231
6232 gBuffersMutex.lock();
6233 mybuffers = gBuffers;
6234 gBuffersMutex.unlock();
6235
6236 /* check buffers */
6237 for (BUFFER* pbuf : mybuffers) {
6238 if (!pbuf)
6239 continue;
6240 if (pbuf->attached) {
6241 /* update the last_activity entry to show that we are alive */
6242
6243 bm_lock_buffer_guard pbuf_guard(pbuf);
6244
6245 if (!pbuf_guard.is_locked())
6246 continue;
6247
6248 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
6249 pclient->last_activity = actual_time;
6250
6251 /* don't check other clients if interval is strange */
6252 if (!wrong_interval)
6254 }
6255 }
6256#endif // LOCAL_ROUTINES
6257}
6258
6259#ifdef LOCAL_ROUTINES
6260
6261static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6262 if (rp < 0 || rp > pheader->size) {
6263 cm_msg(MERROR, "bm_validate_rp",
6264 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6265 pheader->name,
6266 rp,
6267 pheader->read_pointer,
6268 pheader->write_pointer,
6269 pheader->size,
6270 who);
6271 return FALSE;
6272 }
6273
6274 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6275 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6276 cm_msg(MERROR, "bm_validate_rp",
6277 "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",
6278 pheader->name,
6279 rp,
6280 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6281 pheader->read_pointer,
6282 pheader->write_pointer,
6283 pheader->size,
6284 who);
6285 return FALSE;
6286 }
6287
6288 return TRUE;
6289}
6290
6291#if 0
6292static FILE* gRpLog = NULL;
6293#endif
6294
6295static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6296{
6297#if 0
6298 if (gRpLog == NULL) {
6299 gRpLog = fopen("rp.log", "a");
6300 }
6301 if (gRpLog && (total_size < 16)) {
6302 const char *pdata = (const char *) (pheader + 1);
6303 const DWORD *pevent = (const DWORD*) (pdata + rp);
6304 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,
6305 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6306 }
6307#endif
6308
6309 // these checks are already done before we come here.
6310 // but we check again as last-ressort protection. K.O.
6311 assert(total_size > 0);
6312 assert(total_size >= (int)sizeof(EVENT_HEADER));
6313
6314 rp += total_size;
6315 if (rp >= pheader->size) {
6316 rp -= pheader->size;
6317 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6318 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6319 // if at the end of the buffer, the remaining free space is exactly
6320 // equal to the size of an event header, the event header
6321 // is written there, the pointer is wrapped and the event data
6322 // is written to the beginning of the buffer.
6323 rp = 0;
6324 }
6325 return rp;
6326}
6327
6328static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6329 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6330 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6331 int total_size = ALIGN8(event_size);
6332
6333 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6334 cm_msg(MERROR, "bm_next_rp",
6335 "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",
6336 pheader->name,
6337 rp,
6338 pevent->data_size,
6339 event_size,
6340 total_size,
6341 pheader->read_pointer,
6342 pheader->write_pointer,
6343 pheader->size,
6344 who);
6345 return -1;
6346 }
6347
6348 int remaining = 0;
6349 if (rp < pheader->write_pointer) {
6350 remaining = pheader->write_pointer - rp;
6351 } else {
6352 remaining = pheader->size - rp;
6353 remaining += pheader->write_pointer;
6354 }
6355
6356 //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);
6357
6358 if (total_size > remaining) {
6359 cm_msg(MERROR, "bm_next_rp",
6360 "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",
6361 pheader->name,
6362 rp,
6363 pevent->data_size,
6364 event_size,
6365 total_size,
6366 pheader->read_pointer,
6367 pheader->write_pointer,
6368 pheader->size,
6369 remaining,
6370 who);
6371 return -1;
6372 }
6373
6374 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6375
6376 return rp;
6377}
6378
6379static int bm_validate_buffer_locked(const BUFFER *pbuf) {
6380 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6381 const char *pdata = (const char *) (pheader + 1);
6382
6383 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6384
6385 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6386
6387 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6388
6389 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6390 cm_msg(MERROR, "bm_validate_buffer",
6391 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6392 pheader->read_pointer, pheader->size, pheader->write_pointer);
6393 return BM_CORRUPTED;
6394 }
6395
6396 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6397 cm_msg(MERROR, "bm_validate_buffer",
6398 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6399 pheader->write_pointer, pheader->size, pheader->read_pointer);
6400 return BM_CORRUPTED;
6401 }
6402
6403 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6404 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6405 pheader->read_pointer);
6406 return BM_CORRUPTED;
6407 }
6408
6409 int rp = pheader->read_pointer;
6410 int rp0 = -1;
6411 while (rp != pheader->write_pointer) {
6412 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6413 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6414 pheader->name, rp, rp0);
6415 return BM_CORRUPTED;
6416 }
6417 //bm_print_event(pdata, rp);
6418 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6419 if (rp1 < 0) {
6420 cm_msg(MERROR, "bm_validate_buffer",
6421 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6422 return BM_CORRUPTED;
6423 }
6424 rp0 = rp;
6425 rp = rp1;
6426 }
6427
6428 int i;
6429 for (i = 0; i < MAX_CLIENTS; i++) {
6430 const BUFFER_CLIENT *c = &pheader->client[i];
6431 if (c->pid == 0)
6432 continue;
6433 BOOL get_all = FALSE;
6434 int j;
6435 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6436 const EVENT_REQUEST *r = &c->event_request[j];
6437 if (!r->valid)
6438 continue;
6439 BOOL xget_all = r->sampling_type == GET_ALL;
6440 get_all = (get_all || xget_all);
6441 //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);
6442 }
6443
6444 int rp = c->read_pointer;
6445 int rp0 = -1;
6446 while (rp != pheader->write_pointer) {
6447 //bm_print_event(pdata, rp);
6448 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6449 if (rp1 < 0) {
6450 cm_msg(MERROR, "bm_validate_buffer",
6451 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6452 pheader->name, c->name, c->read_pointer, rp, rp0);
6453 return BM_CORRUPTED;
6454 }
6455 rp0 = rp;
6456 rp = rp1;
6457 }
6458 }
6459
6460 return BM_SUCCESS;
6461}
6462
6463static void bm_reset_buffer_locked(BUFFER *pbuf) {
6464 BUFFER_HEADER *pheader = pbuf->buffer_header;
6465
6466 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6467
6468 pheader->read_pointer = 0;
6469 pheader->write_pointer = 0;
6470
6471 int i;
6472 for (i = 0; i < pheader->max_client_index; i++) {
6473 BUFFER_CLIENT *pc = pheader->client + i;
6474 if (pc->pid) {
6475 pc->read_pointer = 0;
6476 }
6477 }
6478}
6479
6481 std::string str = msprintf("/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6482 //printf("delete [%s]\n", str);
6483 db_delete(hDB, 0, str.c_str());
6484}
6485
6528
6529static 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)
6530{
6531 int status;
6532
6533 DWORD now = ss_millitime();
6534
6535 HNDLE hKey;
6536 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6537 if (status != DB_SUCCESS) {
6538 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6539 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6540 if (status != DB_SUCCESS)
6541 return;
6542 }
6543
6544 HNDLE hKeyBuffer;
6545 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6546 if (status != DB_SUCCESS) {
6548 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6549 if (status != DB_SUCCESS)
6550 return;
6551 }
6552
6553 double buf_size = pheader->size;
6554 double buf_rptr = pheader->read_pointer;
6555 double buf_wptr = pheader->write_pointer;
6556
6557 double buf_fill = 0;
6558 double buf_cptr = 0;
6559 double buf_cused = 0;
6560 double buf_cused_pct = 0;
6561
6562 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6563 buf_cptr = pheader->client[client_index].read_pointer;
6564
6565 if (buf_wptr == buf_cptr) {
6566 buf_cused = 0;
6567 } else if (buf_wptr > buf_cptr) {
6568 buf_cused = buf_wptr - buf_cptr;
6569 } else {
6570 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6571 }
6572
6573 buf_cused_pct = buf_cused / buf_size * 100.0;
6574
6575 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6576 // because some other GET_ALL client may have different buf_cused & etc,
6577 // so they must be written into the per-client statistics
6578 // and the web page should look at all the GET_ALL clients and used
6579 // the biggest buf_cused as the whole-buffer "bytes used" value.
6580 }
6581
6582 if (buf_wptr == buf_rptr) {
6583 buf_fill = 0;
6584 } else if (buf_wptr > buf_rptr) {
6585 buf_fill = buf_wptr - buf_rptr;
6586 } else {
6587 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6588 }
6589
6590 double buf_fill_pct = buf_fill / buf_size * 100.0;
6591
6592 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6593 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6594 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6595 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6596 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6597
6598 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6599 if (status != DB_SUCCESS) {
6600 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6601 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6602 if (status != DB_SUCCESS)
6603 return;
6604 }
6605
6606 HNDLE hKeyClient;
6607 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6608 if (status != DB_SUCCESS) {
6609 db_create_key(hDB, hKey, client_name, TID_KEY);
6610 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6611 if (status != DB_SUCCESS)
6612 return;
6613 }
6614
6615 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6616 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6617 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6618 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6619 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6620 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6621 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6622 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6623 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6624 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6625 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6626 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6627
6628 for (int i = 0; i < MAX_CLIENTS; i++) {
6629 if (!pbuf->client_count_write_wait[i])
6630 continue;
6631
6632 if (pheader->client[i].pid == 0)
6633 continue;
6634
6635 if (pheader->client[i].name[0] == 0)
6636 continue;
6637
6638 char str[100 + NAME_LENGTH];
6639
6640 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6641 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6642
6643 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6644 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6645 }
6646
6647 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6648 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6649}
6650
6652{
6653 //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);
6654
6655 bm_lock_buffer_guard pbuf_guard(pbuf);
6656
6657 if (!pbuf_guard.is_locked())
6658 return;
6659
6660 if (!force) {
6661 if (pbuf->count_lock == pbuf->last_count_lock) {
6662 return;
6663 }
6664 }
6665
6666 std::string buffer_name = pbuf->buffer_name;
6667 std::string client_name = pbuf->client_name;
6668
6669 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6670 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6671 pbuf_guard.unlock(); // unlock before cm_msg()
6672 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());
6673 return;
6674 }
6675
6676 pbuf->last_count_lock = pbuf->count_lock;
6677
6678 BUFFER_INFO xbuf(pbuf);
6679 BUFFER_HEADER xheader = *pbuf->buffer_header;
6680 int client_index = pbuf->client_index;
6681
6682 pbuf_guard.unlock();
6683
6684 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6685}
6686
6687static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6688{
6689 size_t sbuffer_handle = buffer_handle;
6690
6691 size_t nbuf = 0;
6692 BUFFER* pbuf = NULL;
6693
6694 gBuffersMutex.lock();
6695
6696 nbuf = gBuffers.size();
6697 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6698 pbuf = gBuffers[buffer_handle-1];
6699 }
6700
6701 gBuffersMutex.unlock();
6702
6703 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6704 if (who)
6705 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6706 if (pstatus)
6707 *pstatus = BM_INVALID_HANDLE;
6708 return NULL;
6709 }
6710
6711 if (!pbuf) {
6712 if (who)
6713 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6714 if (pstatus)
6715 *pstatus = BM_INVALID_HANDLE;
6716 return NULL;
6717 }
6718
6719 if (!pbuf->attached) {
6720 if (who)
6721 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6722 if (pstatus)
6723 *pstatus = BM_INVALID_HANDLE;
6724 return NULL;
6725 }
6726
6727 if (pstatus)
6728 *pstatus = BM_SUCCESS;
6729
6730 return pbuf;
6731}
6732
6733#endif // LOCAL_ROUTINES
6734
6735/********************************************************************/
6782INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6783 INT status;
6784
6785 if (rpc_is_remote()) {
6786 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6787
6788 HNDLE hDB;
6790 if (status != SUCCESS || hDB == 0) {
6791 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6792 return BM_NO_SHM;
6793 }
6794
6796
6797 int size = sizeof(INT);
6798 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6799
6800 if (status != DB_SUCCESS) {
6801 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6802 status);
6803 return status;
6804 }
6805
6806 return status;
6807 }
6808#ifdef LOCAL_ROUTINES
6809 {
6810 HNDLE shm_handle;
6811 size_t shm_size;
6812 HNDLE hDB;
6813 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6814
6815 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6816
6817 if (!buffer_name || !buffer_name[0]) {
6818 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6819 return BM_INVALID_PARAM;
6820 }
6821
6822 if (strlen(buffer_name) >= NAME_LENGTH) {
6823 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6824 return BM_INVALID_PARAM;
6825 }
6826
6828
6829 if (status != SUCCESS || hDB == 0) {
6830 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6831 return BM_NO_SHM;
6832 }
6833
6834 /* get buffer size from ODB, user parameter as default if not present in ODB */
6835 std::string odb_path;
6836 odb_path += "/Experiment/Buffer sizes/";
6837 odb_path += buffer_name;
6838
6839 int size = sizeof(INT);
6840 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6841
6842 if (buffer_size <= 0 || buffer_size > max_buffer_size) {
6843 cm_msg(MERROR, "bm_open_buffer",
6844 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6845 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6846
6847 // special kludge for "SYSMSG": if ODB has wrong data for the buffer size, odbedit cannot start and we cannot cannot fix it. K.O. 17 Jul 2026
6848
6849 if (strcmp(buffer_name, MESSAGE_BUFFER_NAME) != 0)
6850 return BM_INVALID_PARAM;
6851
6852 cm_msg(MERROR, "bm_open_buffer",
6853 "Will use default SYSMSG buffer size %d to open buffer \"%s\"",
6855 buffer_size = MESSAGE_BUFFER_SIZE;
6856 }
6857
6859
6860 size = sizeof(INT);
6861 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6862
6863 if (status != DB_SUCCESS) {
6864 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6865 status);
6866 return status;
6867 }
6868
6869 /* check if buffer already is open */
6870 gBuffersMutex.lock();
6871 for (size_t i = 0; i < gBuffers.size(); i++) {
6872 BUFFER* pbuf = gBuffers[i];
6873 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6874 *buffer_handle = i + 1;
6875 gBuffersMutex.unlock();
6876 return BM_SUCCESS;
6877 }
6878 }
6879 gBuffersMutex.unlock();
6880
6881 // only one thread at a time should create new buffers
6882
6883 static std::mutex gNewBufferMutex;
6884 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6885
6886 // if we had a race against another thread
6887 // and while we were waiting for gNewBufferMutex
6888 // the other thread created this buffer, we return it.
6889
6890 gBuffersMutex.lock();
6891 for (size_t i = 0; i < gBuffers.size(); i++) {
6892 BUFFER* pbuf = gBuffers[i];
6893 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6894 *buffer_handle = i + 1;
6895 gBuffersMutex.unlock();
6896 return BM_SUCCESS;
6897 }
6898 }
6899 gBuffersMutex.unlock();
6900
6901 /* allocate new BUFFER object */
6902
6903 BUFFER* pbuf = new BUFFER;
6904
6905 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6906
6907 for (int i=0; i<MAX_CLIENTS; i++) {
6908 pbuf->client_count_write_wait[i] = 0;
6909 pbuf->client_time_write_wait[i] = 0;
6910 }
6911
6912 /* create buffer semaphore */
6913
6915
6916 if (status != SS_CREATED && status != SS_SUCCESS) {
6917 *buffer_handle = 0;
6918 delete pbuf;
6919 return BM_NO_SEMAPHORE;
6920 }
6921
6922 std::string client_name = cm_get_client_name();
6923
6924 /* store client name */
6925 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6926
6927 /* store buffer name */
6928 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6929
6930 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6931
6932 pbuf->attached = true; // required by bm_lock_buffer()
6933
6934 bm_lock_buffer_guard pbuf_guard(pbuf);
6935
6936 if (!pbuf_guard.is_locked()) {
6937 // cannot happen, no other thread can see this pbuf
6938 abort();
6939 return BM_NO_SEMAPHORE;
6940 }
6941
6942 /* open shared memory */
6943
6944 void *p = NULL;
6945 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6946
6947 if (status != SS_SUCCESS && status != SS_CREATED) {
6948 *buffer_handle = 0;
6949 pbuf_guard.unlock();
6950 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6951 delete pbuf;
6952 return BM_NO_SHM;
6953 }
6954
6955 pbuf->buffer_header = (BUFFER_HEADER *) p;
6956
6957 BUFFER_HEADER *pheader = pbuf->buffer_header;
6958
6959 bool shm_created = (status == SS_CREATED);
6960
6961 if (shm_created) {
6962 /* initialize newly created shared memory */
6963
6964 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6965
6966 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6967 pheader->size = buffer_size;
6968
6969 } else {
6970 /* validate existing shared memory */
6971
6972 if (!equal_ustring(pheader->name, buffer_name)) {
6973 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6974 pbuf_guard.unlock();
6975 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6976 cm_msg(MERROR, "bm_open_buffer",
6977 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6978 pheader->name);
6979 *buffer_handle = 0;
6980 delete pbuf;
6981 return BM_CORRUPTED;
6982 }
6983
6984 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6985 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6986 pbuf_guard.unlock();
6987 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6988 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6990 *buffer_handle = 0;
6991 delete pbuf;
6992 return BM_CORRUPTED;
6993 }
6994
6995 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6996 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6997 pbuf_guard.unlock();
6998 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6999 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
7001 *buffer_handle = 0;
7002 delete pbuf;
7003 return BM_CORRUPTED;
7004 }
7005
7006 /* check if buffer size is identical */
7007 if (pheader->size != buffer_size) {
7008 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
7009 buffer_name, buffer_size, pheader->size);
7010
7011 buffer_size = pheader->size;
7012
7013 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
7014
7015 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
7016
7017 if (status != SS_SUCCESS) {
7018 *buffer_handle = 0;
7019 pbuf_guard.unlock();
7020 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
7021 delete pbuf;
7022 return BM_NO_SHM;
7023 }
7024
7025 pbuf->buffer_header = (BUFFER_HEADER *) p;
7026 pheader = pbuf->buffer_header;
7027 }
7028 }
7029
7030 /* shared memory is good from here down */
7031
7032 pbuf->attached = true;
7033
7034 pbuf->shm_handle = shm_handle;
7035 pbuf->shm_size = shm_size;
7036 pbuf->callback = FALSE;
7037
7038 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
7039
7041 if (status != BM_SUCCESS) {
7042 cm_msg(MERROR, "bm_open_buffer",
7043 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
7044 status);
7046 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
7047 }
7048
7049 /* add our client BUFFER_HEADER */
7050
7051 int iclient = 0;
7052 for (; iclient < MAX_CLIENTS; iclient++)
7053 if (pheader->client[iclient].pid == 0)
7054 break;
7055
7056 if (iclient == MAX_CLIENTS) {
7057 *buffer_handle = 0;
7058 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
7059 pbuf_guard.unlock();
7060 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
7061 delete pbuf;
7062 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
7063 return BM_NO_SLOT;
7064 }
7065
7066 /* store slot index in _buffer structure */
7067 pbuf->client_index = iclient;
7068
7069 /*
7070 Save the index of the last client of that buffer so that later only
7071 the clients 0..max_client_index-1 have to be searched through.
7072 */
7073 pheader->num_clients++;
7074 if (iclient + 1 > pheader->max_client_index)
7075 pheader->max_client_index = iclient + 1;
7076
7077 /* setup buffer header and client structure */
7078 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7079
7080 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7081
7082 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7083
7084 pclient->pid = ss_getpid();
7085
7087
7088 pclient->read_pointer = pheader->write_pointer;
7089 pclient->last_activity = ss_millitime();
7090
7092
7093 pbuf_guard.unlock();
7094
7095 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7096
7097 pheader = NULL;
7098
7099 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7100
7103
7104 /* add pbuf to buffer list */
7105
7106 gBuffersMutex.lock();
7107
7108 bool added = false;
7109 for (size_t i=0; i<gBuffers.size(); i++) {
7110 if (gBuffers[i] == NULL) {
7111 gBuffers[i] = pbuf;
7112 added = true;
7113 *buffer_handle = i+1;
7114 break;
7115 }
7116 }
7117 if (!added) {
7118 *buffer_handle = gBuffers.size() + 1;
7119 gBuffers.push_back(pbuf);
7120 }
7121
7122 /* from here down we should not touch pbuf without locking it */
7123
7124 pbuf = NULL;
7125
7126 gBuffersMutex.unlock();
7127
7128 /* new buffer is now ready for use */
7129
7130 /* initialize buffer counters */
7131 bm_init_buffer_counters(*buffer_handle);
7132
7133 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7134
7135 if (shm_created)
7136 return BM_CREATED;
7137 }
7138#endif /* LOCAL_ROUTINES */
7139
7140 return BM_SUCCESS;
7141}
7142
7143/********************************************************************/
7149INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7150{
7151 gBuffersMutex.lock();
7152 for (size_t i = 0; i < gBuffers.size(); i++) {
7153 BUFFER* pbuf = gBuffers[i];
7154 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7155 *buffer_handle = i + 1;
7156 gBuffersMutex.unlock();
7157 return BM_SUCCESS;
7158 }
7159 }
7160 gBuffersMutex.unlock();
7161 return BM_NOT_FOUND;
7162}
7163
7164/********************************************************************/
7170INT bm_close_buffer(INT buffer_handle) {
7171 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7172
7173 if (rpc_is_remote())
7174 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7175
7176#ifdef LOCAL_ROUTINES
7177 {
7178 int status = 0;
7179
7180 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7181
7182 if (!pbuf)
7183 return status;
7184
7185 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7186
7187 int i;
7188
7189 { /* delete all requests for this buffer */
7190 _request_list_mutex.lock();
7191 std::vector<EventRequest> request_list_copy = _request_list;
7192 _request_list_mutex.unlock();
7193 for (size_t i = 0; i < request_list_copy.size(); i++) {
7194 if (request_list_copy[i].buffer_handle == buffer_handle) {
7196 }
7197 }
7198 }
7199
7200 HNDLE hDB;
7202
7203 if (hDB) {
7204 /* write statistics to odb */
7206 }
7207
7208 /* lock buffer in correct order */
7209
7211
7212 if (status != BM_SUCCESS) {
7213 return status;
7214 }
7215
7217
7218 if (status != BM_SUCCESS) {
7219 pbuf->read_cache_mutex.unlock();
7220 return status;
7221 }
7222
7223 bm_lock_buffer_guard pbuf_guard(pbuf);
7224
7225 if (!pbuf_guard.is_locked()) {
7226 pbuf->write_cache_mutex.unlock();
7227 pbuf->read_cache_mutex.unlock();
7228 return pbuf_guard.get_status();
7229 }
7230
7231 BUFFER_HEADER *pheader = pbuf->buffer_header;
7232
7233 /* mark entry in _buffer as empty */
7234 pbuf->attached = false;
7235
7236 BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
7237
7238 if (pclient) {
7239 /* clear entry from client structure in buffer header */
7240 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7241 }
7242
7243 /* calculate new max_client_index entry */
7244 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7245 if (pheader->client[i].pid != 0)
7246 break;
7247 pheader->max_client_index = i + 1;
7248
7249 /* count new number of clients */
7250 int j = 0;
7251 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7252 if (pheader->client[i].pid != 0)
7253 j++;
7254 pheader->num_clients = j;
7255
7256 int destroy_flag = (pheader->num_clients == 0);
7257
7258 // we hold the locks on the read cache and the write cache.
7259
7260 /* free cache */
7261 if (pbuf->read_cache_size > 0) {
7262 free(pbuf->read_cache);
7263 pbuf->read_cache = NULL;
7264 pbuf->read_cache_size = 0;
7265 pbuf->read_cache_rp = 0;
7266 pbuf->read_cache_wp = 0;
7267 }
7268
7269 if (pbuf->write_cache_size > 0) {
7270 free(pbuf->write_cache);
7271 pbuf->write_cache = NULL;
7272 pbuf->write_cache_size = 0;
7273 pbuf->write_cache_rp = 0;
7274 pbuf->write_cache_wp = 0;
7275 }
7276
7277 /* check if anyone is waiting and wake him up */
7278
7279 for (int i = 0; i < pheader->max_client_index; i++) {
7280 BUFFER_CLIENT *pclient = pheader->client + i;
7281 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7282 ss_resume(pclient->port, "B ");
7283 }
7284
7285 /* unmap shared memory, delete it if we are the last */
7286
7287 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7288
7289 /* after ss_shm_close() these are invalid: */
7290
7291 pheader = NULL;
7292 pbuf->buffer_header = NULL;
7293 pbuf->shm_size = 0;
7294 pbuf->shm_handle = 0;
7295
7296 /* unlock buffer in correct order */
7297
7298 pbuf_guard.unlock();
7299
7300 pbuf->write_cache_mutex.unlock();
7301 pbuf->read_cache_mutex.unlock();
7302
7303 /* delete semaphore */
7304
7305 ss_semaphore_delete(pbuf->semaphore, destroy_flag);
7306 }
7307#endif /* LOCAL_ROUTINES */
7308
7309 return BM_SUCCESS;
7310}
7311
7312/********************************************************************/
7318 if (rpc_is_remote())
7320
7321#ifdef LOCAL_ROUTINES
7322 {
7324
7325 gBuffersMutex.lock();
7326 size_t nbuf = gBuffers.size();
7327 gBuffersMutex.unlock();
7328
7329 for (size_t i = nbuf; i > 0; i--) {
7331 }
7332
7333 gBuffersMutex.lock();
7334 for (size_t i=0; i< gBuffers.size(); i++) {
7335 BUFFER* pbuf = gBuffers[i];
7336 if (!pbuf)
7337 continue;
7338 delete pbuf;
7339 pbuf = NULL;
7340 gBuffers[i] = NULL;
7341 }
7342 gBuffersMutex.unlock();
7343 }
7344#endif /* LOCAL_ROUTINES */
7345
7346 return BM_SUCCESS;
7347}
7348
7349/********************************************************************/
7355#ifdef LOCAL_ROUTINES
7356 {
7357 int status;
7358 HNDLE hDB;
7359
7361
7362 if (status != CM_SUCCESS) {
7363 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7364 return BM_SUCCESS;
7365 }
7366
7367 std::vector<BUFFER*> mybuffers;
7368
7369 gBuffersMutex.lock();
7370 mybuffers = gBuffers;
7371 gBuffersMutex.unlock();
7372
7373 for (BUFFER* pbuf : mybuffers) {
7374 if (!pbuf || !pbuf->attached)
7375 continue;
7377 }
7378 }
7379#endif /* LOCAL_ROUTINES */
7380
7381 return BM_SUCCESS;
7382}
7383
/* end of bmfunctionc */
7386
7392/*-- Watchdog routines ---------------------------------------------*/
7393#ifdef LOCAL_ROUTINES
7394
7395static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7396static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7397static std::atomic<std::thread*> _watchdog_thread{NULL};
7398
7399/********************************************************************/
7405 //printf("cm_watchdog_thread started!\n");
7406 while (_watchdog_thread_run) {
7407 //printf("cm_watchdog_thread runs!\n");
7408 DWORD now = ss_millitime();
7411 int i;
7412 for (i = 0; i < 20; i++) {
7413 ss_sleep(100);
7415 break;
7416 }
7417 }
7418 //printf("cm_watchdog_thread stopped!\n");
7420 return 0;
7421}
7422
7423static void xcm_watchdog_thread() {
7424 cm_watchdog_thread(NULL);
7425}
7426
7427#endif
7428
7430 /* watchdog does not run inside remote clients.
7431 * watchdog timeout timers are maintained by the mserver */
7432 if (rpc_is_remote())
7433 return CM_SUCCESS;
7434#ifdef LOCAL_ROUTINES
7435 /* only start once */
7436 if (_watchdog_thread)
7437 return CM_SUCCESS;
7438 _watchdog_thread_run = true;
7439 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7440#endif
7441 return CM_SUCCESS;
7442}
7443
7445 /* watchdog does not run inside remote clients.
7446 * watchdog timeout timers are maintained by the mserver */
7447 if (rpc_is_remote())
7448 return CM_SUCCESS;
7449#ifdef LOCAL_ROUTINES
7450 _watchdog_thread_run = false;
7452 //printf("waiting for watchdog thread to shut down\n");
7453 ss_sleep(10);
7454 }
7455 if (_watchdog_thread != NULL) {
7456 _watchdog_thread.load()->join();
7457 delete static_cast<std::thread *>(_watchdog_thread);
7458 _watchdog_thread = NULL;
7459 }
7460#endif
7461 return CM_SUCCESS;
7462}
7463
7464/********************************************************************/
7474INT cm_shutdown(const char *name, BOOL bUnique) {
7475 return cm_shutdown(name);
7476}
7477
7478INT cm_shutdown(const char *name) {
7479 INT status, return_status, size;
7480 HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
7481 DWORD start_time;
7482 DWORD timeout;
7483 DWORD last;
7484
7485 cm_get_experiment_database(&hDB, &hKeyClient);
7486
7487 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7488 if (status != DB_SUCCESS)
7489 return DB_NO_KEY;
7490
7491 return_status = CM_NO_CLIENT;
7492
7493 /* loop over all clients */
7494 for (int i = 0;; i++) {
7497 break;
7498
7499 /* don't shutdown ourselves */
7500 if (hSubkey == hKeyClient)
7501 continue;
7502
7503 if (status == DB_SUCCESS) {
7504 std::string client_name;
7505 std::string remote_host;
7506 KEY key;
7507 INT port = 0;
7508
7510
7511 /* contact client */
7512 size = sizeof(client_name);
7513 status = db_get_value_string(hDB, hSubkey, "Name", 0, &client_name);
7514 if (status != DB_SUCCESS)
7515 continue;
7516
7517 /* check if individual client */
7518 if (!equal_ustring("all", name) && !equal_ustring(client_name.c_str(), name))
7519 continue;
7520
7521 size = sizeof(port);
7522 status = db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, FALSE);
7523 if (status != DB_SUCCESS)
7524 continue;
7525
7526 status = db_get_value_string(hDB, hSubkey, "Host", 0, &remote_host);
7527 if (status != DB_SUCCESS)
7528 continue;
7529
7530 cm_get_watchdog_info(hDB, name, &timeout, &last);
7531 if (timeout == 0)
7532 timeout = 5000;
7533
7534 /* client found -> connect to its server port */
7535 status = rpc_client_connect(remote_host.c_str(), port, client_name.c_str(), &hConn);
7536 if (status != RPC_SUCCESS) {
7537 int client_pid = atoi(key.name);
7538 return_status = CM_NO_CLIENT;
7539 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d", client_name.c_str(), remote_host.c_str(), port);
7540#ifdef SIGKILL
7541 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name.c_str(), client_pid);
7542 kill(client_pid, SIGKILL);
7543 return_status = CM_SUCCESS;
7544 status = cm_delete_client_info(hDB, client_pid);
7545 if (status != CM_SUCCESS)
7546 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid, status);
7547#endif
7548 } else {
7549 /* call disconnect with shutdown=TRUE */
7551
7552 /* wait until client has shut down */
7553 start_time = ss_millitime();
7554 do {
7555 ss_sleep(100);
7556 status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
7557 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7558
7559 if (status == DB_SUCCESS) {
7560 int client_pid = atoi(key.name);
7561 return_status = CM_NO_CLIENT;
7562 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name.c_str());
7563#ifdef SIGKILL
7564 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name.c_str(), client_pid);
7565 kill(client_pid, SIGKILL);
7566 status = cm_delete_client_info(hDB, client_pid);
7567 if (status != CM_SUCCESS)
7568 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid, status);
7569#endif
7570 return_status = CM_NO_CLIENT;
7571 } else {
7572 return_status = CM_SUCCESS;
7573 i--;
7574 }
7575 }
7576 }
7577
7578 /* display any message created during each shutdown */
7580 }
7581
7582 return return_status;
7583}
7584
7585/********************************************************************/
7594INT cm_exist(const char *name, BOOL bClientName) {
7595 INT status;
7596 HNDLE hDB, hKeyClient, hKey, hSubkey;
7597
7598 if (rpc_is_remote())
7599 return rpc_call(RPC_CM_EXIST, name, bClientName);
7600
7601 //printf("cm_exist(\"%s\",%d)\n", name, bClientName);
7602
7603 cm_get_experiment_database(&hDB, &hKeyClient);
7604
7605 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7606 if (status != DB_SUCCESS)
7607 return DB_NO_KEY;
7608
7610
7611 /* loop over all clients */
7612 for (int i = 0;; i++) {
7615 break;
7616
7617 if (hSubkey == hKeyClient)
7618 continue;
7619
7620 if (status == DB_SUCCESS) {
7621 std::string client_name;
7622
7623 /* get client name */
7624 status = db_get_value_string(hDB, hSubkey, "Name", 0, &client_name);
7625
7626 if (status != DB_SUCCESS) { // invalid /System/Clients/pid entry
7627 //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);
7628 continue;
7629 }
7630
7631 if (bClientName) { // name is client name
7632 if (equal_ustring(client_name.c_str(), name)) {
7633 //printf("cm_exist: exact client name match \"%s\" with \"%s\"\n", client_name.c_str(), name);
7635 return CM_SUCCESS;
7636 }
7637 continue; // go to next client, will not check the program name
7638 }
7639
7640 // name is program name
7641
7642 if (equal_ustring(client_name.c_str(), name)) { // exact match
7643 //printf("cm_exist: exact client name and program name match \"%s\" with \"%s\"\n", client_name.c_str(), name);
7645 return CM_SUCCESS;
7646 }
7647
7648 std::string program_name;
7649
7650 /* get program name */
7651 status = db_get_value_string(hDB, hSubkey, "Program", 0, &program_name);
7652
7653 if (status == DB_SUCCESS) {
7654 if (equal_ustring(program_name.c_str(), name)) { // exact match
7655 //printf("cm_exist: exact program name match \"%s\" with \"%s\", client name \"%s\"\n", program_name.c_str(), name, client_name.c_str());
7657 return CM_SUCCESS;
7658 }
7659 continue;
7660 }
7661
7662 // no program name in ODB, must be an old client
7663
7664 size_t name_len = strlen(name);
7665
7666 if (client_name.length() < name_len) { // client name too short, not us
7667 //printf("cm_exist: client name too short \"%s\" against \"%s\"\n", client_name.c_str(), name);
7668 continue;
7669 }
7670
7671 std::string truncated_client_name = client_name;
7672
7673 truncated_client_name.resize(name_len); /* strip number */
7674
7675 if (equal_ustring(truncated_client_name.c_str(), name)) {
7676 //printf("cm_exist: truncated client name and program name match \"%s\" with \"%s\", client_name \"%s\"\n", truncated_client_name.c_str(), name, client_name.c_str());
7678 return CM_SUCCESS;
7679 }
7680 }
7681 }
7682
7684
7685 return CM_NO_CLIENT;
7686}
7687
7688/********************************************************************/
7723INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7724 if (rpc_is_remote())
7725 return rpc_call(RPC_CM_CLEANUP, client_name);
7726
7727#ifdef LOCAL_ROUTINES
7728 {
7729 DWORD interval;
7730 DWORD now = ss_millitime();
7731
7732 std::vector<BUFFER*> mybuffers;
7733
7734 gBuffersMutex.lock();
7735 mybuffers = gBuffers;
7736 gBuffersMutex.unlock();
7737
7738 /* check buffers */
7739 for (BUFFER* pbuf : mybuffers) {
7740 if (!pbuf)
7741 continue;
7742 if (pbuf->attached) {
7743 std::string msg;
7744
7745 bm_lock_buffer_guard pbuf_guard(pbuf);
7746
7747 if (!pbuf_guard.is_locked())
7748 continue;
7749
7750 /* update the last_activity entry to show that we are alive */
7751 BUFFER_HEADER *pheader = pbuf->buffer_header;
7752 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7753 pclient->last_activity = ss_millitime();
7754
7755 /* now check other clients */
7756 for (int j = 0; j < pheader->max_client_index; j++) {
7757 BUFFER_CLIENT *pbclient = &pheader->client[j];
7758 if (j != pbuf->client_index && pbclient->pid &&
7759 (client_name == NULL || client_name[0] == 0
7760 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7761 if (ignore_timeout)
7762 interval = 2 * WATCHDOG_INTERVAL;
7763 else
7764 interval = pbclient->watchdog_timeout;
7765
7766 /* If client process has no activity, clear its buffer entry. */
7767 if (interval > 0
7768 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7769
7770 /* now make again the check with the buffer locked */
7771 if (interval > 0
7772 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7773 msg = msprintf(
7774 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7775 pbclient->name, pheader->name,
7776 (ss_millitime() - pbclient->last_activity) / 1000.0,
7777 interval / 1000.0);
7778
7779 bm_remove_client_locked(pheader, j);
7780 }
7781
7782 /* go again through whole list */
7783 j = 0;
7784 }
7785 }
7786 }
7787
7788 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7789 pbuf_guard.unlock();
7790
7791 /* display info message after unlocking buffer */
7792 if (!msg.empty())
7793 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7794 }
7795 }
7796
7797 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7798 }
7799#endif /* LOCAL_ROUTINES */
7800
7801 return CM_SUCCESS;
7802}
7803
7804/********************************************************************/
7823std::string cm_expand_env(const char *str) {
7824 const char *s = str;
7825 std::string r;
7826 for (; *s;) {
7827 if (*s == '$') {
7828 s++;
7829 std::string envname;
7830 for (; *s;) {
7831 if (*s == DIR_SEPARATOR)
7832 break;
7833 envname += *s;
7834 s++;
7835 }
7836 const char *e = getenv(envname.c_str());
7837 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7838 if (!e) {
7839 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7840 r += '$';
7841 r += envname;
7842 } else {
7843 r += e;
7844 //if (r[r.length()-1] != DIR_SEPARATOR)
7845 //r += DIR_SEPARATOR_STR;
7846 }
7847 } else {
7848 r += *s;
7849 s++;
7850 }
7851 }
7852 return r;
7853}
7854
7855static bool test_cm_expand_env1(const char *str, const char *expected) {
7856 std::string s = cm_expand_env(str);
7857 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7858 str,
7859 s.c_str(),
7860 expected);
7861 if (s != expected) {
7862 printf(", MISMATCH!\n");
7863 return false;
7864 }
7865
7866 printf("\n");
7867 return true;
7868}
7869
7871 printf("Test expand_end()\n");
7872 setenv("FOO", "foo", 1);
7873 setenv("BAR", "bar", 1);
7874 setenv("EMPTY", "", 1);
7875 unsetenv("UNDEF");
7876
7877 bool ok = true;
7878
7879 ok &= test_cm_expand_env1("aaa", "aaa");
7880 ok &= test_cm_expand_env1("$FOO", "foo");
7881 ok &= test_cm_expand_env1("/$FOO", "/foo");
7882 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7883 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7884 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7885 ok &= test_cm_expand_env1("1$FOO", "1foo");
7886 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7887 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7888
7889 if (ok) {
7890 printf("test_expand_env: all tests passed!\n");
7891 } else {
7892 printf("test_expand_env: test FAILED!\n");
7893 }
7894}
7895
/* end of cmfunctionc */
7899
7901#ifndef DOXYGEN_SHOULD_SKIP_THIS
7902
7903/********************************************************************/
7904INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7905/********************************************************************\
7906
7907 Routine: bm_buffer_info
7908
7909 Purpose: Copies the current buffer header referenced by buffer_handle
7910 into the *buffer_header structure which must be supplied
7911 by the calling routine.
7912
7913 Input:
7914 INT buffer_handle Handle of the buffer to get the header from
7915
7916 Output:
7917 BUFFER_HEADER *buffer_header Destination address which gets a copy
7918 of the buffer header structure.
7919
7920 Function value:
7921 BM_SUCCESS Successful completion
7922 BM_INVALID_HANDLE Buffer handle is invalid
7923 RPC_NET_ERROR Network error
7924
7925\********************************************************************/
7926{
7927 if (rpc_is_remote())
7928 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7929
7930#ifdef LOCAL_ROUTINES
7931
7932 int status = 0;
7933 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7934
7935 if (!pbuf)
7936 return status;
7937
7938 bm_lock_buffer_guard pbuf_guard(pbuf);
7939
7940 if (!pbuf_guard.is_locked())
7941 return pbuf_guard.get_status();
7942
7943 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7944
7945#endif /* LOCAL_ROUTINES */
7946
7947 return BM_SUCCESS;
7948}
7949
7950/********************************************************************/
7951INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7952/********************************************************************\
7953
7954 Routine: bm_get_buffer_level
7955
7956 Purpose: Return number of bytes in buffer or in cache
7957
7958 Input:
7959 INT buffer_handle Handle of the buffer to get the info
7960
7961 Output:
7962 INT *n_bytes Number of bytes in buffer
7963
7964 Function value:
7965 BM_SUCCESS Successful completion
7966 BM_INVALID_HANDLE Buffer handle is invalid
7967 RPC_NET_ERROR Network error
7968
7969\********************************************************************/
7970{
7971 if (rpc_is_remote())
7972 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7973
7974#ifdef LOCAL_ROUTINES
7975 {
7976 int status = 0;
7977
7978 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7979
7980 if (!pbuf)
7981 return status;
7982
7983 bm_lock_buffer_guard pbuf_guard(pbuf);
7984
7985 if (!pbuf_guard.is_locked())
7986 return pbuf_guard.get_status();
7987
7988 BUFFER_HEADER *pheader = pbuf->buffer_header;
7989
7990 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7991
7992 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7993 if (*n_bytes < 0)
7994 *n_bytes += pheader->size;
7995
7996 pbuf_guard.unlock();
7997
7998 if (pbuf->read_cache_size) {
8000 if (status == BM_SUCCESS) {
8001 /* add bytes in cache */
8002 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
8003 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
8004 pbuf->read_cache_mutex.unlock();
8005 }
8006 }
8007 }
8008#endif /* LOCAL_ROUTINES */
8009
8010 return BM_SUCCESS;
8011}
8012
8013
8014#ifdef LOCAL_ROUTINES
8015
8016/********************************************************************/
8018{
8019 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
8020
8021 if (!locked) {
8022 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);
8023 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);
8024 abort();
8025 /* DOES NOT RETURN */
8026 }
8027
8028 if (!pbuf->attached) {
8029 pbuf->read_cache_mutex.unlock();
8030 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);
8031 return BM_INVALID_HANDLE;
8032 }
8033
8034 return BM_SUCCESS;
8035}
8036
8037/********************************************************************/
8039{
8040 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
8041
8042 if (!locked) {
8043 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);
8044 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);
8045 abort();
8046 /* DOES NOT RETURN */
8047 }
8048
8049 if (!pbuf->attached) {
8050 pbuf->write_cache_mutex.unlock();
8051 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);
8052 return BM_INVALID_HANDLE;
8053 }
8054
8055 return BM_SUCCESS;
8056}
8057
8058/********************************************************************/
8060{
8061 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
8062
8063 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
8064
8065 if (!locked) {
8066 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
8067 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
8068 abort();
8069 /* DOES NOT RETURN */
8070 }
8071
8072 if (!pbuf->attached) {
8073 pbuf->buffer_mutex.unlock();
8074 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);
8075 return BM_INVALID_HANDLE;
8076 }
8077
8078 //static int counter = 0;
8079 //counter++;
8080 //printf("locked %d!\n", counter);
8081 //if (counter > 50)
8082 // ::sleep(3);
8083
8084 return BM_SUCCESS;
8085}
8086
8087/********************************************************************/
8088static int xbm_lock_buffer(BUFFER *pbuf)
8089{
8090 int status;
8091
8092 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8093
8094 //if (pbuf->locked) {
8095 // fprintf(stderr, "double lock, abort!\n");
8096 // abort();
8097 //}
8098
8100
8101 if (status != BM_SUCCESS)
8102 return status;
8103
8104 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8105
8106 if (status != SS_SUCCESS) {
8107 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8108
8109 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8110
8111 if (status != SS_SUCCESS) {
8112 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);
8113
8114 if (pbuf->buffer_header) {
8115 for (int i=0; i<MAX_CLIENTS; i++) {
8116 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);
8117 }
8118 }
8119
8121
8122 if (status != SS_SUCCESS) {
8123 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8124 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8125 abort();
8126 /* DOES NOT RETURN */
8127 }
8128 }
8129 }
8130
8131 // protect against double lock
8132 assert(!pbuf->locked);
8133 pbuf->locked = TRUE;
8134
8135#if 0
8136 int x = MAX_CLIENTS - 1;
8137 if (pbuf->buffer_header->client[x].unused1 != 0) {
8138 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8139 }
8140 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8141 pbuf->buffer_header->client[x].unused1 = getpid();
8142#endif
8143
8144 pbuf->count_lock++;
8145
8146 return BM_SUCCESS;
8147}
8148
8149/********************************************************************/
8150static void xbm_unlock_buffer(BUFFER *pbuf) {
8151 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8152
8153#if 0
8154 int x = MAX_CLIENTS-1;
8155 if (pbuf->attached) {
8156 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8157 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8158 }
8159 pbuf->buffer_header->client[x].unused1 = 0;
8160 } else {
8161 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8162 }
8163#endif
8164
8165 // protect against double unlock
8166 assert(pbuf->locked);
8167 pbuf->locked = FALSE;
8168
8170 pbuf->buffer_mutex.unlock();
8171}
8172
8173#endif /* LOCAL_ROUTINES */
8174
8175/********************************************************************/
8177/********************************************************************\
8178
8179 Routine: bm_init_event_counters
8180
8181 Purpose: Initialize counters for a specific buffer. This routine
8182 should be called at the beginning of a run.
8183
8184 Input:
8185 INT buffer_handle Handle to the buffer to be
8186 initialized.
8187 Output:
8188 none
8189
8190 Function value:
8191 BM_SUCCESS Successful completion
8192 BM_INVALID_HANDLE Buffer handle is invalid
8193
8194\********************************************************************/
8195{
8196 if (rpc_is_remote())
8197 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8198
8199#ifdef LOCAL_ROUTINES
8200
8201 int status = 0;
8202
8203 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8204
8205 if (!pbuf)
8206 return status;
8207
8208 bm_lock_buffer_guard pbuf_guard(pbuf);
8209
8210 if (!pbuf_guard.is_locked())
8211 return pbuf_guard.get_status();
8212
8213 pbuf->buffer_header->num_in_events = 0;
8214 pbuf->buffer_header->num_out_events = 0;
8215
8216#endif /* LOCAL_ROUTINES */
8217
8218 return BM_SUCCESS;
8219}
8220
8222#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8223
8229/********************************************************************/
8253INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8254/*------------------------------------------------------------------*/
8255{
8256 if (rpc_is_remote())
8257 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8258
8259#ifdef LOCAL_ROUTINES
8260 {
8261 int status = 0;
8262
8263 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8264
8265 if (!pbuf)
8266 return status;
8267
8268 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8269
8271
8272 if (status != BM_SUCCESS)
8273 return status;
8274
8275 if (write_size < 0)
8276 write_size = 0;
8277
8278 if (write_size > 0) {
8279 if (write_size < MIN_WRITE_CACHE_SIZE) {
8280 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);
8281 write_size = MIN_WRITE_CACHE_SIZE;
8282 }
8283 }
8284
8285 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8286
8287 if (write_size > max_write_size) {
8288 size_t new_write_size = max_write_size;
8289 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);
8290 write_size = new_write_size;
8291 }
8292
8293 pbuf->buffer_mutex.unlock();
8294
8295 /* resize read cache */
8296
8298
8299 if (status != BM_SUCCESS) {
8300 return status;
8301 }
8302
8303 if (pbuf->read_cache_size > 0) {
8304 free(pbuf->read_cache);
8305 pbuf->read_cache = NULL;
8306 }
8307
8308 if (read_size > 0) {
8309 pbuf->read_cache = (char *) malloc(read_size);
8310 if (pbuf->read_cache == NULL) {
8311 pbuf->read_cache_size = 0;
8312 pbuf->read_cache_rp = 0;
8313 pbuf->read_cache_wp = 0;
8314 pbuf->read_cache_mutex.unlock();
8315 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);
8316 return BM_NO_MEMORY;
8317 }
8318 }
8319
8320 pbuf->read_cache_size = read_size;
8321 pbuf->read_cache_rp = 0;
8322 pbuf->read_cache_wp = 0;
8323
8324 pbuf->read_cache_mutex.unlock();
8325
8326 /* resize the write cache */
8327
8329
8330 if (status != BM_SUCCESS)
8331 return status;
8332
8333 // FIXME: should flush the write cache!
8334 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8335 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8336 }
8337
8338 /* manage write cache */
8339 if (pbuf->write_cache_size > 0) {
8340 free(pbuf->write_cache);
8341 pbuf->write_cache = NULL;
8342 }
8343
8344 if (write_size > 0) {
8345 pbuf->write_cache = (char *) M_MALLOC(write_size);
8346 if (pbuf->write_cache == NULL) {
8347 pbuf->write_cache_size = 0;
8348 pbuf->write_cache_rp = 0;
8349 pbuf->write_cache_wp = 0;
8350 pbuf->write_cache_mutex.unlock();
8351 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);
8352 return BM_NO_MEMORY;
8353 }
8354 }
8355
8356 pbuf->write_cache_size = write_size;
8357 pbuf->write_cache_rp = 0;
8358 pbuf->write_cache_wp = 0;
8359
8360 pbuf->write_cache_mutex.unlock();
8361 }
8362#endif /* LOCAL_ROUTINES */
8363
8364 return BM_SUCCESS;
8365}
8366
8367/********************************************************************/
8394INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
8395{
8396 event_header->event_id = event_id;
8397 event_header->trigger_mask = trigger_mask;
8398 event_header->data_size = data_size;
8399 event_header->time_stamp = ss_time();
8400 event_header->serial_number = serial;
8401
8402 return BM_SUCCESS;
8403}
8404
8405INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
8406{
8407 static std::mutex mutex;
8408
8409 event_header->event_id = event_id;
8410 event_header->trigger_mask = trigger_mask;
8411 event_header->data_size = data_size;
8412 event_header->time_stamp = ss_time();
8413 {
8414 std::lock_guard<std::mutex> lock(mutex);
8415 event_header->serial_number = *serial;
8416 *serial = *serial + 1;
8417 // implicit unlock
8418 }
8419
8420 return BM_SUCCESS;
8421}
8422
8424#ifndef DOXYGEN_SHOULD_SKIP_THIS
8425
8426/********************************************************************/
8427INT bm_add_event_request(INT buffer_handle, short int event_id,
8428 short int trigger_mask,
8429 INT sampling_type,
8430 EVENT_HANDLER *func,
8431 INT request_id)
8432/********************************************************************\
8433
8434 Routine: bm_add_event_request
8435
8436 Purpose: Place a request for a specific event type in the client
8437 structure of the buffer refereced by buffer_handle.
8438
8439 Input:
8440 INT buffer_handle Handle to the buffer where the re-
8441 quest should be placed in
8442
8443 short int event_id Event ID \
8444 short int trigger_mask Trigger mask / Event specification
8445
8446 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8447
8448
8449 Note: to request all types of events, use
8450 event_id = 0 (all others should be !=0 !)
8451 trigger_mask = TRIGGER_ALL
8452 sampling_typ = GET_ALL
8453
8454
8455 void *func Callback function
8456 INT request_id Request id (unique number assigned
8457 by bm_request_event)
8458
8459 Output:
8460 none
8461
8462 Function value:
8463 BM_SUCCESS Successful completion
8464 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8465 MIDAS.H should be increased.
8466 BM_INVALID_HANDLE Buffer handle is invalid
8467 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8468 RPC_NET_ERROR Network error
8469
8470\********************************************************************/
8471{
8472 if (rpc_is_remote())
8473 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8474 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8475
8476#ifdef LOCAL_ROUTINES
8477 {
8478 int status = 0;
8479
8480 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8481
8482 if (!pbuf)
8483 return status;
8484
8485 /* lock buffer */
8486 bm_lock_buffer_guard pbuf_guard(pbuf);
8487
8488 if (!pbuf_guard.is_locked())
8489 return pbuf_guard.get_status();
8490
8491 /* avoid callback/non callback requests */
8492 if (func == NULL && pbuf->callback) {
8493 pbuf_guard.unlock(); // unlock before cm_msg()
8494 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8495 return BM_INVALID_MIXING;
8496 }
8497
8498 /* do not allow GET_RECENT with nonzero cache size */
8499 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8500 pbuf_guard.unlock(); // unlock before cm_msg()
8501 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8502 return BM_INVALID_PARAM;
8503 }
8504
8505 /* get a pointer to the proper client structure */
8506 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8507
8508 /* look for a empty request entry */
8509 int i;
8510 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8511 if (!pclient->event_request[i].valid)
8512 break;
8513
8514 if (i == MAX_EVENT_REQUESTS) {
8515 // implicit unlock
8516 return BM_NO_MEMORY;
8517 }
8518
8519 /* setup event_request structure */
8520 pclient->event_request[i].id = request_id;
8521 pclient->event_request[i].valid = TRUE;
8522 pclient->event_request[i].event_id = event_id;
8524 pclient->event_request[i].sampling_type = sampling_type;
8525
8526 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8527
8528 pbuf->get_all_flag = pclient->all_flag;
8529
8530 /* set callback flag in buffer structure */
8531 if (func != NULL)
8532 pbuf->callback = TRUE;
8533
8534 /*
8535 Save the index of the last request in the list so that later only the
8536 requests 0..max_request_index-1 have to be searched through.
8537 */
8538
8539 if (i + 1 > pclient->max_request_index)
8540 pclient->max_request_index = i + 1;
8541 }
8542#endif /* LOCAL_ROUTINES */
8543
8544 return BM_SUCCESS;
8545}
8546
8548#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8549
8550/********************************************************************/
8578INT bm_request_event(HNDLE buffer_handle, short int event_id,
8579 short int trigger_mask,
8580 INT sampling_type, HNDLE *request_id,
8581 EVENT_HANDLER *func)
8582{
8583 assert(request_id != NULL);
8584
8585 EventRequest r;
8586 r.buffer_handle = buffer_handle;
8587 r.event_id = event_id;
8589 r.dispatcher = func;
8590
8591 {
8592 std::lock_guard<std::mutex> guard(_request_list_mutex);
8593
8594 bool found = false;
8595
8596 // find deleted entry
8597 for (size_t i = 0; i < _request_list.size(); i++) {
8598 if (_request_list[i].buffer_handle == 0) {
8599 _request_list[i] = r;
8600 *request_id = i;
8601 found = true;
8602 break;
8603 }
8604 }
8605
8606 if (!found) { // not found
8607 *request_id = _request_list.size();
8608 _request_list.push_back(r);
8609 }
8610
8611 // implicit unlock()
8612 }
8613
8614 /* add request in buffer structure */
8615 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8616 if (status != BM_SUCCESS)
8617 return status;
8618
8619 return BM_SUCCESS;
8620}
8621
8622/********************************************************************/
8631INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8632 if (rpc_is_remote())
8633 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8634
8635#ifdef LOCAL_ROUTINES
8636 {
8637 int status = 0;
8638
8639 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8640
8641 if (!pbuf)
8642 return status;
8643
8644 /* lock buffer */
8645 bm_lock_buffer_guard pbuf_guard(pbuf);
8646
8647 if (!pbuf_guard.is_locked())
8648 return pbuf_guard.get_status();
8649
8650 INT i, deleted;
8651
8652 /* get a pointer to the proper client structure */
8653 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8654
8655 /* check all requests and set to zero if matching */
8656 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8657 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8658 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8659 deleted++;
8660 }
8661
8662 /* calculate new max_request_index entry */
8663 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8664 if (pclient->event_request[i].valid)
8665 break;
8666
8667 pclient->max_request_index = i + 1;
8668
8669 /* calculate new all_flag */
8670 pclient->all_flag = FALSE;
8671
8672 for (i = 0; i < pclient->max_request_index; i++)
8673 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8674 pclient->all_flag = TRUE;
8675 break;
8676 }
8677
8678 pbuf->get_all_flag = pclient->all_flag;
8679
8680 if (!deleted)
8681 return BM_NOT_FOUND;
8682 }
8683#endif /* LOCAL_ROUTINES */
8684
8685 return BM_SUCCESS;
8686}
8687
8688/********************************************************************/
8698{
8699 _request_list_mutex.lock();
8700
8701 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8702 _request_list_mutex.unlock();
8703 return BM_INVALID_HANDLE;
8704 }
8705
8706 int buffer_handle = _request_list[request_id].buffer_handle;
8707
8708 _request_list[request_id].clear();
8709
8710 _request_list_mutex.unlock();
8711
8712 /* remove request entry from buffer */
8713 return bm_remove_event_request(buffer_handle, request_id);
8714}
8715
8716#if 0 // currently not used
8717static void bm_show_pointers(const BUFFER_HEADER * pheader)
8718{
8719 int i;
8720 const BUFFER_CLIENT *pclient;
8721
8722 pclient = pheader->client;
8723
8724 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8725 pheader->write_pointer, pheader->size);
8726 for (i = 0; i < pheader->max_client_index; i++)
8727 if (pclient[i].pid) {
8728 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8729 }
8730
8731 printf("done\n");
8732}
8733#endif
8734
8736 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8737 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8738
8739 if (pheader->read_pointer <= pheader->write_pointer) {
8740
8741 if (pclient->read_pointer < pheader->read_pointer) {
8742 cm_msg(MINFO, "bm_validate_client_pointers",
8743 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8744 pclient->name,
8745 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8746
8747 pclient->read_pointer = pheader->read_pointer;
8748 }
8749
8750 if (pclient->read_pointer > pheader->write_pointer) {
8751 cm_msg(MINFO, "bm_validate_client_pointers",
8752 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8753 pclient->name,
8754 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8755
8756 pclient->read_pointer = pheader->write_pointer;
8757 }
8758
8759 } else {
8760
8761 if (pclient->read_pointer < 0) {
8762 cm_msg(MINFO, "bm_validate_client_pointers",
8763 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8764 pclient->name,
8765 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8766
8767 pclient->read_pointer = pheader->read_pointer;
8768 }
8769
8770 if (pclient->read_pointer >= pheader->size) {
8771 cm_msg(MINFO, "bm_validate_client_pointers",
8772 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8773 pclient->name,
8774 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8775
8776 pclient->read_pointer = pheader->read_pointer;
8777 }
8778
8779 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8780 cm_msg(MINFO, "bm_validate_client_pointers",
8781 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8782 pclient->name,
8783 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8784
8785 pclient->read_pointer = pheader->read_pointer;
8786 }
8787 }
8788}
8789
8790#if 0 // currently not used
8791static void bm_validate_pointers(BUFFER_HEADER * pheader)
8792{
8793 BUFFER_CLIENT *pclient = pheader->client;
8794 int i;
8795
8796 for (i = 0; i < pheader->max_client_index; i++)
8797 if (pclient[i].pid) {
8798 bm_validate_client_pointers(pheader, &pclient[i]);
8799 }
8800}
8801#endif
8802
8803//
8804// Buffer pointers
8805//
8806// normal:
8807//
8808// zero -->
8809// ... free space
8810// read_pointer -->
8811// client1 rp -->
8812// client2 rp -->
8813// ... buffered data
8814// write_pointer -->
8815// ... free space
8816// pheader->size -->
8817//
8818// inverted:
8819//
8820// zero -->
8821// client3 rp -->
8822// ... buffered data
8823// client4 rp -->
8824// write_pointer -->
8825// ... free space
8826// read_pointer -->
8827// client1 rp -->
8828// client2 rp -->
8829// ... buffered data
8830// pheader->size -->
8831//
8832
8833static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
8834 assert(caller_name);
8835
8836 /* calculate global read pointer as "minimum" of client read pointers */
8837 int min_rp = pheader->write_pointer;
8838
8839 int i;
8840 for (i = 0; i < pheader->max_client_index; i++) {
8841 BUFFER_CLIENT *pc = pheader->client + i;
8842 if (pc->pid) {
8844
8845#if 0
8846 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8847 pheader->name,
8848 pheader->read_pointer,
8849 pheader->write_pointer,
8850 pheader->size,
8851 min_rp,
8852 pc->name,
8853 pc->read_pointer);
8854#endif
8855
8856 if (pheader->read_pointer <= pheader->write_pointer) {
8857 // normal pointers
8858 if (pc->read_pointer < min_rp)
8859 min_rp = pc->read_pointer;
8860 } else {
8861 // inverted pointers
8862 if (pc->read_pointer <= pheader->write_pointer) {
8863 // clients 3 and 4
8864 if (pc->read_pointer < min_rp)
8865 min_rp = pc->read_pointer;
8866 } else {
8867 // clients 1 and 2
8868 int xptr = pc->read_pointer - pheader->size;
8869 if (xptr < min_rp)
8870 min_rp = xptr;
8871 }
8872 }
8873 }
8874 }
8875
8876 if (min_rp < 0)
8877 min_rp += pheader->size;
8878
8879 assert(min_rp >= 0);
8880 assert(min_rp < pheader->size);
8881
8882 if (min_rp == pheader->read_pointer) {
8883 return FALSE;
8884 }
8885
8886#if 0
8887 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8888 pheader->name,
8889 pheader->read_pointer,
8890 pheader->write_pointer,
8891 pheader->size,
8892 min_rp);
8893#endif
8894
8895 pheader->read_pointer = min_rp;
8896
8897 return TRUE;
8898}
8899
8900static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8901 int i;
8902 int have_get_all_requests = 0;
8903
8904 for (i = 0; i < pc->max_request_index; i++)
8905 if (pc->event_request[i].valid)
8906 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8907
8908 /* only GET_ALL requests actually free space in the event buffer */
8909 if (!have_get_all_requests)
8910 return;
8911
8912 /*
8913 If read pointer has been changed, it may have freed up some space
8914 for waiting producers. So check if free space is now more than 50%
8915 of the buffer size and wake waiting producers.
8916 */
8917
8918 int free_space = pc->read_pointer - pheader->write_pointer;
8919 if (free_space <= 0)
8920 free_space += pheader->size;
8921
8922 if (free_space >= pheader->size * 0.5) {
8923 for (i = 0; i < pheader->max_client_index; i++) {
8924 const BUFFER_CLIENT *pc = pheader->client + i;
8925 if (pc->pid && pc->write_wait) {
8926 BOOL send_wakeup = (pc->write_wait < free_space);
8927 //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);
8928 if (send_wakeup) {
8929 ss_resume(pc->port, "B ");
8930 }
8931 }
8932 }
8933 }
8934}
8935
8936static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8937{
8938 _request_list_mutex.lock();
8939 bool locked = true;
8940 size_t n = _request_list.size();
8941 /* call dispatcher */
8942 for (size_t i = 0; i < n; i++) {
8943 if (!locked) {
8944 _request_list_mutex.lock();
8945 locked = true;
8946 }
8948 if (r.buffer_handle != buffer_handle)
8949 continue;
8950 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8951 continue;
8952 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8953 _request_list_mutex.unlock();
8954 locked = false;
8955 /* if event is fragmented, call defragmenter */
8956 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))) {
8957 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8958 } else {
8959 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8960 }
8961 }
8962 if (locked)
8963 _request_list_mutex.unlock();
8964}
8965
8966#ifdef LOCAL_ROUTINES
8967
8968static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8969 /* increment read cache read pointer */
8970 pbuf->read_cache_rp += total_size;
8971
8972 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8973 pbuf->read_cache_rp = 0;
8974 pbuf->read_cache_wp = 0;
8975 }
8976}
8977
8978static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8979{
8980 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8981 return FALSE;
8982
8983 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8984 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8985 int total_size = ALIGN8(event_size);
8986
8987 if (ppevent)
8988 *ppevent = pevent;
8989 if (pevent_size)
8990 *pevent_size = event_size;
8991 if (ptotal_size)
8992 *ptotal_size = total_size;
8993
8994 return TRUE;
8995}
8996
8997//
8998// return values:
8999// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
9000// BM_ASYNC_RETURN - buffer is empty
9001// BM_CORRUPTED - buffer is corrupted
9002//
9003
9004static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
9005{
9006 if (pc->read_pointer == pheader->write_pointer) {
9007 /* no more events buffered for this client */
9008 if (!pc->read_wait) {
9009 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
9010 pc->read_wait = TRUE;
9011 }
9012 return BM_ASYNC_RETURN;
9013 }
9014
9015 if (pc->read_wait) {
9016 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
9017 pc->read_wait = FALSE;
9018 }
9019
9020 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
9021 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);
9022 return BM_CORRUPTED;
9023 }
9024
9025 char *pdata = (char *) (pheader + 1);
9026
9027 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
9028 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9029 int total_size = ALIGN8(event_size);
9030
9031 if ((total_size <= 0) || (total_size > pheader->size)) {
9032 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);
9033 return BM_CORRUPTED;
9034 }
9035
9036 assert(total_size > 0);
9037 assert(total_size <= pheader->size);
9038
9039 if (ppevent)
9040 *ppevent = pevent;
9041 if (pevent_size)
9042 *pevent_size = event_size;
9043 if (ptotal_size)
9044 *ptotal_size = total_size;
9045
9046 return BM_SUCCESS;
9047}
9048
9049static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
9050{
9051 const char *pdata = (const char *) (pheader + 1);
9052
9053 if (rp + event_size <= pheader->size) {
9054 /* copy event to cache */
9055 memcpy(buf, pdata + rp, event_size);
9056 } else {
9057 /* event is splitted */
9058 int size = pheader->size - rp;
9059 memcpy(buf, pdata + rp, size);
9060 memcpy(buf + size, pdata, event_size - size);
9061 }
9062}
9063
9064static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
9065{
9066 const char *pdata = (const char *) (pheader + 1);
9067
9068 if (rp + event_size <= pheader->size) {
9069 /* copy event to cache */
9070 vecptr->assign(pdata + rp, pdata + rp + event_size);
9071 } else {
9072 /* event is splitted */
9073 int size = pheader->size - rp;
9074 vecptr->assign(pdata + rp, pdata + rp + size);
9075 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
9076 }
9077}
9078
9079static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
9080
9081 BOOL is_requested = FALSE;
9082 int i;
9083 for (i = 0; i < pc->max_request_index; i++) {
9084 const EVENT_REQUEST *prequest = pc->event_request + i;
9085 if (prequest->valid) {
9086 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9087 /* check if this is a recent event */
9088 if (prequest->sampling_type == GET_RECENT) {
9089 if (ss_time() - pevent->time_stamp > 1) {
9090 /* skip that event */
9091 continue;
9092 }
9093 }
9094
9095 is_requested = TRUE;
9096 break;
9097 }
9098 }
9099 }
9100 return is_requested;
9101}
9102
9103static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
9104
9105static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
9106{
9107 BUFFER* pbuf = pbuf_guard.get_pbuf();
9108 BUFFER_HEADER* pheader = pbuf->buffer_header;
9109 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9110 BOOL need_wakeup = FALSE;
9111
9112 //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);
9113
9114 /* loop over all events in the buffer */
9115
9116 while (1) {
9117 EVENT_HEADER *pevent = NULL;
9118 int event_size = 3; // poison value
9119 int total_size = 3; // poison value
9120
9121 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9122 if (status == BM_CORRUPTED) {
9123 return status;
9124 } else if (status != BM_SUCCESS) {
9125 /* event buffer is empty */
9126 if (timeout_msec == BM_NO_WAIT) {
9127 if (need_wakeup)
9128 bm_wakeup_producers_locked(pheader, pc);
9129 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9130 // read cache is empty
9131 return BM_ASYNC_RETURN;
9132 }
9133 return BM_SUCCESS;
9134 }
9135
9136 int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
9137
9138 if (status != BM_SUCCESS) {
9139 // we only come here with SS_ABORT & co
9140 return status;
9141 }
9142
9143 // make sure we wait for new event only once
9144 timeout_msec = BM_NO_WAIT;
9145 // go back to bm_peek_buffer_locked
9146 continue;
9147 }
9148
9149 /* loop over all requests: if this event matches a request,
9150 * copy it to the read cache */
9151
9152 BOOL is_requested = bm_check_requests(pc, pevent);
9153
9154 if (is_requested) {
9155 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9156 /* read cache is full */
9157 if (need_wakeup)
9158 bm_wakeup_producers_locked(pheader, pc);
9159 return BM_SUCCESS;
9160 }
9161
9163
9164 pbuf->read_cache_wp += total_size;
9165
9166 /* update statistics */
9167 pheader->num_out_events++;
9168 pbuf->count_read++;
9169 pbuf->bytes_read += event_size;
9170 }
9171
9172 /* shift read pointer */
9173
9174 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9175 pc->read_pointer = new_read_pointer;
9176
9177 need_wakeup = TRUE;
9178 }
9179 /* NOT REACHED */
9180}
9181
9182static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9183 /* now convert event header */
9184 if (convert_flags) {
9185 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9186 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9187 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9188 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9189 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9190 }
9191}
9192
9193static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
9194{
9195 // return values:
9196 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9197 // BM_CORRUPTED - shared memory is corrupted
9198 // BM_NO_MEMORY - asked for more than buffer size
9199 // BM_ASYNC_RETURN - timeout waiting for free space
9200 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9201 // SS_ABORT - we are told to shutdown (locks releases)
9202
9203 int status;
9204 BUFFER* pbuf = pbuf_guard.get_pbuf();
9205 BUFFER_HEADER *pheader = pbuf->buffer_header;
9206 char *pdata = (char *) (pheader + 1);
9207
9208 /* make sure the buffer never completely full:
9209 * read pointer and write pointer would coincide
9210 * and the code cannot tell if it means the
9211 * buffer is 100% full or 100% empty. It will explode
9212 * or lose events */
9213 requested_space += 100;
9214
9215 if (requested_space >= pheader->size)
9216 return BM_NO_MEMORY;
9217
9218 DWORD time_start = ss_millitime();
9219 DWORD time_end = time_start + timeout_msec;
9220
9221 //DWORD blocking_time = 0;
9222 //int blocking_loops = 0;
9223 int blocking_client_index = -1;
9224 char blocking_client_name[NAME_LENGTH];
9225 blocking_client_name[0] = 0;
9226
9227 while (1) {
9228 while (1) {
9229 /* check if enough space in buffer */
9230
9231 int free = pheader->read_pointer - pheader->write_pointer;
9232 if (free <= 0)
9233 free += pheader->size;
9234
9235 //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);
9236
9237 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9238 //if (blocking_loops) {
9239 // DWORD wait_time = ss_millitime() - blocking_time;
9240 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9241 //}
9242
9243 if (pbuf->wait_start_time != 0) {
9244 DWORD now = ss_millitime();
9245 DWORD wait_time = now - pbuf->wait_start_time;
9246 pbuf->time_write_wait += wait_time;
9247 pbuf->wait_start_time = 0;
9248 int iclient = pbuf->wait_client_index;
9249 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9250 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9251 pbuf->client_count_write_wait[iclient] += 1;
9252 pbuf->client_time_write_wait[iclient] += wait_time;
9253 }
9254 }
9255
9256 //if (blocking_loops > 0) {
9257 // 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);
9258 //}
9259
9260 return BM_SUCCESS;
9261 }
9262
9263 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9264 cm_msg(MERROR, "bm_wait_for_free_space",
9265 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9266 pheader->name,
9267 pheader->read_pointer,
9268 pheader->write_pointer,
9269 pheader->size,
9270 free,
9271 requested_space);
9272 return BM_CORRUPTED;
9273 }
9274
9275 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9276 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9277 int total_size = ALIGN8(event_size);
9278
9279#if 0
9280 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);
9281#endif
9282
9283 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9284 cm_msg(MERROR, "bm_wait_for_free_space",
9285 "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",
9286 pheader->name,
9287 pheader->read_pointer,
9288 pheader->write_pointer,
9289 pheader->size,
9290 free,
9291 requested_space,
9292 pevent->data_size,
9293 event_size,
9294 total_size);
9295 return BM_CORRUPTED;
9296 }
9297
9298 int blocking_client = -1;
9299
9300 int i;
9301 for (i = 0; i < pheader->max_client_index; i++) {
9302 BUFFER_CLIENT *pc = pheader->client + i;
9303 if (pc->pid) {
9304 if (pc->read_pointer == pheader->read_pointer) {
9305 /*
9306 First assume that the client with the "minimum" read pointer
9307 is not really blocking due to a GET_ALL request.
9308 */
9309 BOOL blocking = FALSE;
9310 //int blocking_request_id = -1;
9311
9312 int j;
9313 for (j = 0; j < pc->max_request_index; j++) {
9314 const EVENT_REQUEST *prequest = pc->event_request + j;
9315 if (prequest->valid
9316 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9317 if (prequest->sampling_type & GET_ALL) {
9318 blocking = TRUE;
9319 //blocking_request_id = prequest->id;
9320 break;
9321 }
9322 }
9323 }
9324
9325 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9326
9327 if (blocking) {
9328 blocking_client = i;
9329 break;
9330 }
9331
9332 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9333 }
9334 }
9335 } /* client loop */
9336
9337 if (blocking_client >= 0) {
9338 blocking_client_index = blocking_client;
9339 mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
9340 //if (!blocking_time) {
9341 // blocking_time = ss_millitime();
9342 //}
9343
9344 //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);
9345
9346 // from this "break" we go into timeout check and sleep/wait.
9347 break;
9348 }
9349
9350 /* no blocking clients. move the read pointer and again check for free space */
9351
9352 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9353
9354 if (!moved) {
9355 cm_msg(MERROR, "bm_wait_for_free_space",
9356 "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",
9357 pheader->name,
9358 pheader->read_pointer,
9359 pheader->write_pointer,
9360 pheader->size,
9361 free,
9362 requested_space);
9363 return BM_CORRUPTED;
9364 }
9365
9366 /* we freed one event, loop back to the check for free space */
9367 }
9368
9369 //blocking_loops++;
9370
9371 /* at least one client is blocking */
9372
9373 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9374 pc->write_wait = requested_space;
9375
9376 if (pbuf->wait_start_time == 0) {
9377 pbuf->wait_start_time = ss_millitime();
9378 pbuf->count_write_wait++;
9379 if (requested_space > pbuf->max_requested_space)
9380 pbuf->max_requested_space = requested_space;
9381 pbuf->wait_client_index = blocking_client_index;
9382 }
9383
9384 DWORD now = ss_millitime();
9385
9386 //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);
9387
9388 int sleep_time_msec = 1000;
9389
9390 if (timeout_msec == BM_WAIT) {
9391 // wait forever
9392 } else if (timeout_msec == BM_NO_WAIT) {
9393 // no wait
9394 return BM_ASYNC_RETURN;
9395 } else {
9396 // check timeout
9397 if (now >= time_end) {
9398 // timeout!
9399 return BM_ASYNC_RETURN;
9400 }
9401
9402 sleep_time_msec = time_end - now;
9403
9404 if (sleep_time_msec <= 0) {
9405 sleep_time_msec = 10;
9406 } else if (sleep_time_msec > 1000) {
9407 sleep_time_msec = 1000;
9408 }
9409 }
9410
9412
9413 /* before waiting, unlock everything in the correct order */
9414
9415 pbuf_guard.unlock();
9416
9417 if (unlock_write_cache)
9418 pbuf->write_cache_mutex.unlock();
9419
9420 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9421
9422#ifdef DEBUG_MSG
9423 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9424#endif
9425
9427 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9428 //if (idx >= 0)
9429 // pheader->client[idx].write_wait = requested_space;
9430
9431 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9432
9433 status = ss_suspend(sleep_time_msec, MSG_BM);
9434
9435 /* we are told to shutdown */
9436 if (status == SS_ABORT) {
9437 // NB: buffer is locked!
9438 return SS_ABORT;
9439 }
9440
9441 /* make sure we do sleep in this loop:
9442 * if we are the mserver receiving data on the event
9443 * socket and the data buffer is full, ss_suspend() will
9444 * never sleep: it will detect data on the event channel,
9445 * call rpc_server_receive() (recursively, we already *are* in
9446 * rpc_server_receive()) and return without sleeping. Result
9447 * is a busy loop waiting for free space in data buffer */
9448
9449 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9450 * the event socket, and should sleep now, so this sleep below
9451 * maybe is not needed now. but for safety, I keep it. K.O. */
9452
9453 if (status != SS_TIMEOUT) {
9454 //printf("ss_suspend: status %d\n", status);
9455 ss_sleep(1);
9456 }
9457
9458 /* we may be stuck in this loop for an arbitrary long time,
9459 * depending on how other buffer clients read the accumulated data
9460 * so we should update all the timeouts & etc. K.O. */
9461
9463
9464 /* lock things again in the correct order */
9465
9466 if (unlock_write_cache) {
9468
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_write_cache) {
9477 pbuf->write_cache_mutex.unlock();
9478 }
9479
9480 // bail out with all locks released
9481 return pbuf_guard.get_status();
9482 }
9483
9484 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9485 pc = bm_get_my_client_locked(pbuf_guard);
9486
9487 pc->write_wait = 0;
9488
9490 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9491 //if (idx >= 0)
9492 // pheader->client[idx].write_wait = 0;
9493 //else {
9494 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9495 // status = SS_ABORT;
9496 //}
9497
9498#ifdef DEBUG_MSG
9499 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);
9500#endif
9501
9502 }
9503}
9504
9505static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
9506{
9507 BUFFER* pbuf = pbuf_guard.get_pbuf();
9508 BUFFER_HEADER* pheader = pbuf->buffer_header;
9509
9510 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9511
9512 if (pc->read_pointer != pheader->write_pointer) {
9513 // buffer has data
9514 return BM_SUCCESS;
9515 }
9516
9517 if (timeout_msec == BM_NO_WAIT) {
9518 /* event buffer is empty and we are told to not wait */
9519 if (!pc->read_wait) {
9520 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9521 pc->read_wait = TRUE;
9522 }
9523 return BM_ASYNC_RETURN;
9524 }
9525
9526 DWORD time_start = ss_millitime();
9527 DWORD time_wait = time_start + timeout_msec;
9528 DWORD sleep_time = 1000;
9529 if (timeout_msec == BM_NO_WAIT) {
9530 // default sleep time
9531 } else if (timeout_msec == BM_WAIT) {
9532 // default sleep time
9533 } else {
9534 if (sleep_time > (DWORD)timeout_msec)
9535 sleep_time = timeout_msec;
9536 }
9537
9538 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9539
9540 while (pc->read_pointer == pheader->write_pointer) {
9541 /* wait until there is data in the buffer (write pointer moves) */
9542
9543 if (!pc->read_wait) {
9544 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9545 pc->read_wait = TRUE;
9546 }
9547
9549
9551
9552 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9553
9554 pbuf_guard.unlock();
9555
9556 if (unlock_read_cache)
9557 pbuf->read_cache_mutex.unlock();
9558
9559 int status = ss_suspend(sleep_time, MSG_BM);
9560
9561 if (timeout_msec == BM_NO_WAIT) {
9562 // return immediately
9563 } else if (timeout_msec == BM_WAIT) {
9564 // wait forever
9565 } else {
9566 DWORD now = ss_millitime();
9567 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9568 if (now >= time_wait) {
9569 timeout_msec = BM_NO_WAIT; // cause immediate return
9570 } else {
9571 sleep_time = time_wait - now;
9572 if (sleep_time > 1000)
9573 sleep_time = 1000;
9574 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9575 }
9576 }
9577
9578 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9579
9580 if (unlock_read_cache) {
9582 if (status != BM_SUCCESS) {
9583 // bail out with all locks released
9584 return status;
9585 }
9586 }
9587
9588 if (!pbuf_guard.relock()) {
9589 if (unlock_read_cache) {
9590 pbuf->read_cache_mutex.unlock();
9591 }
9592 // bail out with all locks released
9593 return pbuf_guard.get_status();
9594 }
9595
9596 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9597 * because we may have been removed from the buffer by bm_cleanup() & co
9598 * due to a timeout or whatever. */
9599 pc = bm_get_my_client_locked(pbuf_guard);
9600
9601 /* return if TCP connection broken */
9602 if (status == SS_ABORT)
9603 return SS_ABORT;
9604
9605 if (timeout_msec == BM_NO_WAIT)
9606 return BM_ASYNC_RETURN;
9607 }
9608
9609 if (pc->read_wait) {
9610 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9611 pc->read_wait = FALSE;
9612 }
9613
9614 return BM_SUCCESS;
9615}
9616
9617static 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)
9618{
9619 char *pdata = (char *) (pheader + 1);
9620
9621 //int old_write_pointer = pheader->write_pointer;
9622
9623 /* new event fits into the remaining space? */
9624 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9625 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9626 char* wptr = pdata + pheader->write_pointer;
9627 for (int i=0; i<sg_n; i++) {
9628 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9629 memcpy(wptr, sg_ptr[i], sg_len[i]);
9630 wptr += sg_len[i];
9631 }
9632 pheader->write_pointer = pheader->write_pointer + total_size;
9633 assert(pheader->write_pointer <= pheader->size);
9634 /* remaining space is smaller than size of an event header? */
9635 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9636 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9637 // equal to the event header size, we will write the next event header here,
9638 // then wrap the pointer and write the event data at the beginning of the buffer.
9639 //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);
9640 pheader->write_pointer = 0;
9641 }
9642 } else {
9643 /* split event */
9644 size_t size = pheader->size - pheader->write_pointer;
9645
9646 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9647
9648 //memcpy(pdata + pheader->write_pointer, pevent, size);
9649 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9650
9651 char* wptr = pdata + pheader->write_pointer;
9652 size_t count = 0;
9653
9654 // copy first part
9655
9656 int i = 0;
9657 for (; i<sg_n; i++) {
9658 if (count + sg_len[i] > size)
9659 break;
9660 memcpy(wptr, sg_ptr[i], sg_len[i]);
9661 wptr += sg_len[i];
9662 count += sg_len[i];
9663 }
9664
9665 //printf("wptr %d, count %d\n", wptr-pdata, count);
9666
9667 // split segment
9668
9669 size_t first = size - count;
9670 size_t second = sg_len[i] - first;
9671 assert(first + second == sg_len[i]);
9672 assert(count + first == size);
9673
9674 //printf("first %d, second %d\n", first, second);
9675
9676 memcpy(wptr, sg_ptr[i], first);
9677 wptr = pdata + 0;
9678 count += first;
9679 memcpy(wptr, sg_ptr[i] + first, second);
9680 wptr += second;
9681 count += second;
9682 i++;
9683
9684 // copy remaining
9685
9686 for (; i<sg_n; i++) {
9687 memcpy(wptr, sg_ptr[i], sg_len[i]);
9688 wptr += sg_len[i];
9689 count += sg_len[i];
9690 }
9691
9692 //printf("wptr %d, count %d\n", wptr-pdata, count);
9693
9694 //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);
9695
9696 pheader->write_pointer = total_size - size;
9697 }
9698
9699 //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);
9700}
9701
9703 if (pc->pid) {
9704 int j;
9705 for (j = 0; j < pc->max_request_index; j++) {
9706 const EVENT_REQUEST *prequest = pc->event_request + j;
9707 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9708 return prequest->id;
9709 }
9710 }
9711 }
9712
9713 return -1;
9714}
9715
9716static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9717 if (request_id >= 0) {
9718 /* if that client has a request and is suspended, wake it up */
9719 if (pc->read_wait) {
9720 char str[80];
9721 sprintf(str, "B %s %d", pheader->name, request_id);
9722 ss_resume(pc->port, str);
9723 //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);
9724 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9725 pc->read_wait = FALSE;
9726 }
9727 }
9728}
9729
9730#endif // LOCAL_ROUTINES
9731
9732#if 0
9733INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9734{
9735 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9736
9737 DWORD time_start = ss_millitime();
9738 DWORD time_end = time_start + timeout_msec;
9739
9740 int xtimeout_msec = timeout_msec;
9741
9742 while (1) {
9743 if (timeout_msec == BM_WAIT) {
9744 xtimeout_msec = 1000;
9745 } else if (timeout_msec == BM_NO_WAIT) {
9746 xtimeout_msec = BM_NO_WAIT;
9747 } else {
9748 if (xtimeout_msec > 1000) {
9749 xtimeout_msec = 1000;
9750 }
9751 }
9752
9753 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9754
9755 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9756
9757 if (status == BM_ASYNC_RETURN) {
9758 if (timeout_msec == BM_WAIT) {
9759 // BM_WAIT means wait forever
9760 continue;
9761 } else if (timeout_msec == BM_NO_WAIT) {
9762 // BM_NO_WAIT means do not wait
9763 return status;
9764 } else {
9765 DWORD now = ss_millitime();
9766 if (now >= time_end) {
9767 // timeout, return BM_ASYNC_RETURN
9768 return status;
9769 }
9770
9771 DWORD remain = time_end - now;
9772
9773 if (remain < xtimeout_msec) {
9774 xtimeout_msec = remain;
9775 }
9776
9777 // keep asking for event...
9778 continue;
9779 }
9780 } else if (status == BM_SUCCESS) {
9781 // success, return BM_SUCCESS
9782 return status;
9783 } else {
9784 // error
9785 return status;
9786 }
9787 }
9788}
9789#endif
9790
9791INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9792{
9793 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9794 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9795
9796 if (data_size == 0) {
9797 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9798 return BM_INVALID_SIZE;
9799 }
9800
9801 if (data_size > MAX_DATA_SIZE) {
9802 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);
9803 return BM_INVALID_SIZE;
9804 }
9805
9806 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9807
9808 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9809
9810 if (rpc_is_remote()) {
9811 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9812 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9813 } else {
9814 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9815 }
9816}
9817
9818int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9819{
9820 const char* cptr = event.data();
9821 size_t clen = event.size();
9822 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9823}
9824
9825int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9826{
9827 int sg_n = event.size();
9828 const char* sg_ptr[sg_n];
9829 size_t sg_len[sg_n];
9830 for (int i=0; i<sg_n; i++) {
9831 sg_ptr[i] = event[i].data();
9832 sg_len[i] = event[i].size();
9833 }
9834 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9835}
9836
9837#ifdef LOCAL_ROUTINES
9838static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
9839#endif
9840
9841/********************************************************************/
9891int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9892{
9893 if (rpc_is_remote())
9894 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9895
9896 if (sg_n < 1) {
9897 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9898 return BM_INVALID_SIZE;
9899 }
9900
9901 if (sg_ptr[0] == NULL) {
9902 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9903 return BM_INVALID_SIZE;
9904 }
9905
9906 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9907 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));
9908 return BM_INVALID_SIZE;
9909 }
9910
9911 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9912
9913 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9914 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9915
9916 if (data_size == 0) {
9917 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9918 return BM_INVALID_SIZE;
9919 }
9920
9921 if (data_size > MAX_DATA_SIZE) {
9922 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);
9923 return BM_INVALID_SIZE;
9924 }
9925
9926 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9927
9928 size_t count = 0;
9929 for (int i=0; i<sg_n; i++) {
9930 count += sg_len[i];
9931 }
9932
9933 if (count != event_size) {
9934 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);
9935 return BM_INVALID_SIZE;
9936 }
9937
9938 //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);
9939
9940#ifdef LOCAL_ROUTINES
9941 {
9942 int status = 0;
9943 const size_t total_size = ALIGN8(event_size);
9944
9945 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9946
9947 if (!pbuf)
9948 return status;
9949
9950 /* round up total_size to next DWORD boundary */
9951 //int total_size = ALIGN8(event_size);
9952
9953 /* check if write cache is enabled */
9954 if (pbuf->write_cache_size) {
9956
9957 if (status != BM_SUCCESS)
9958 return status;
9959
9960 /* check if write cache is enabled */
9961 if (pbuf->write_cache_size) {
9963 bool too_big = event_size > max_event_size;
9964
9965 //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);
9966
9967 /* if this event does not fit into the write cache, flush the write cache */
9968 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9969 //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);
9970
9971 bm_lock_buffer_guard pbuf_guard(pbuf);
9972
9973 if (!pbuf_guard.is_locked()) {
9974 pbuf->write_cache_mutex.unlock();
9975 return pbuf_guard.get_status();
9976 }
9977
9978 int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
9979
9980 if (pbuf_guard.is_locked()) {
9981 // check if bm_wait_for_free_space() failed to relock the buffer
9982 pbuf_guard.unlock();
9983 }
9984
9985 if (status != BM_SUCCESS) {
9986 pbuf->write_cache_mutex.unlock();
9987 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9988 if (status == BM_NO_MEMORY)
9989 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9990 return status;
9991 }
9992
9993 // write cache must be empty here
9994 assert(pbuf->write_cache_wp == 0);
9995 }
9996
9997 /* write this event into the write cache, if it is not too big and if it fits */
9998 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9999 //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);
10000
10001 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
10002
10003 for (int i=0; i<sg_n; i++) {
10004 memcpy(wptr, sg_ptr[i], sg_len[i]);
10005 wptr += sg_len[i];
10006 }
10007
10008 pbuf->write_cache_wp += total_size;
10009
10010 pbuf->write_cache_mutex.unlock();
10011 return BM_SUCCESS;
10012 }
10013 }
10014
10015 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
10016 pbuf->write_cache_mutex.unlock();
10017 }
10018
10019 /* we come here only for events that are too big to fit into the cache */
10020
10021 /* lock the buffer */
10022 bm_lock_buffer_guard pbuf_guard(pbuf);
10023
10024 if (!pbuf_guard.is_locked()) {
10025 return pbuf_guard.get_status();
10026 }
10027
10028 /* calculate some shorthands */
10029 BUFFER_HEADER *pheader = pbuf->buffer_header;
10030
10031#if 0
10033 if (status != BM_SUCCESS) {
10034 printf("bm_send_event: corrupted 111!\n");
10035 abort();
10036 }
10037#endif
10038
10039 /* check if buffer is large enough */
10040 if (total_size >= (size_t)pheader->size) {
10041 pbuf_guard.unlock(); // unlock before cm_msg()
10042 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
10043 return BM_NO_MEMORY;
10044 }
10045
10046 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
10047
10048 if (status != BM_SUCCESS) {
10049 // implicit unlock
10050 return status;
10051 }
10052
10053#if 0
10055 if (status != BM_SUCCESS) {
10056 printf("bm_send_event: corrupted 222!\n");
10057 abort();
10058 }
10059#endif
10060
10061 int old_write_pointer = pheader->write_pointer;
10062
10063 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
10064
10065 /* write pointer was incremented, but there should
10066 * always be some free space in the buffer and the
10067 * write pointer should never cacth up to the read pointer:
10068 * the rest of the code gets confused this happens (buffer 100% full)
10069 * as it is write_pointer == read_pointer can be either
10070 * 100% full or 100% empty. My solution: never fill
10071 * the buffer to 100% */
10072 assert(pheader->write_pointer != pheader->read_pointer);
10073
10074 /* send wake up messages to all clients that want this event */
10075 int i;
10076 for (i = 0; i < pheader->max_client_index; i++) {
10077 BUFFER_CLIENT *pc = pheader->client + i;
10078 int request_id = bm_find_first_request_locked(pc, pevent);
10079 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
10080 }
10081
10082#if 0
10084 if (status != BM_SUCCESS) {
10085 printf("bm_send_event: corrupted 333!\n");
10086 abort();
10087 }
10088#endif
10089
10090 /* update statistics */
10091 pheader->num_in_events++;
10092 pbuf->count_sent += 1;
10093 pbuf->bytes_sent += total_size;
10094 }
10095#endif /* LOCAL_ROUTINES */
10096
10097 return BM_SUCCESS;
10098}
10099
10100static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
10101{
10102 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10103
10104 DWORD time_start = ss_millitime();
10105 DWORD time_end = time_start + timeout_msec;
10106 DWORD time_bombout = time_end;
10107
10108 if (timeout_msec < 10000)
10109 time_bombout = time_start + 10000; // 10 seconds
10110
10111 int xtimeout_msec = timeout_msec;
10112
10113 while (1) {
10114 if (timeout_msec == BM_WAIT) {
10115 xtimeout_msec = 1000;
10116 } else if (timeout_msec == BM_NO_WAIT) {
10117 xtimeout_msec = BM_NO_WAIT;
10118 } else {
10119 if (xtimeout_msec > 1000) {
10120 xtimeout_msec = 1000;
10121 }
10122 }
10123
10124 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10125
10126 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10127
10128 if (status == BM_ASYNC_RETURN) {
10129 if (timeout_msec == BM_WAIT) {
10130 DWORD now = ss_millitime();
10131 if (now >= time_bombout) {
10132 // timeout
10133 return BM_TIMEOUT;
10134 }
10135
10136 // BM_WAIT means wait forever
10137 continue;
10138 } else if (timeout_msec == BM_NO_WAIT) {
10139 // BM_NO_WAIT means do not wait
10140 return status;
10141 } else {
10142 DWORD now = ss_millitime();
10143 if (now >= time_end) {
10144 // timeout, return BM_ASYNC_RETURN
10145 return status;
10146 }
10147
10148 DWORD remain = time_end - now;
10149
10150 if (remain < (DWORD)xtimeout_msec) {
10151 xtimeout_msec = remain;
10152 }
10153
10154 if (now >= time_bombout) {
10155 // timeout
10156 return BM_TIMEOUT;
10157 }
10158
10159 // keep asking for event...
10160 continue;
10161 }
10162 } else if (status == BM_SUCCESS) {
10163 // success, return BM_SUCCESS
10164 return status;
10165 } else {
10166 // error
10167 return status;
10168 }
10169 }
10170}
10171
10172/********************************************************************/
10190#ifdef LOCAL_ROUTINES
10191static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
10192{
10193 // NB we come here with write cache locked and buffer locked.
10194
10195 {
10196 INT status = 0;
10197
10198 //printf("bm_flush_cache_locked!\n");
10199
10200 BUFFER* pbuf = pbuf_guard.get_pbuf();
10201 BUFFER_HEADER* pheader = pbuf->buffer_header;
10202
10203 //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);
10204
10205 int old_write_pointer = pheader->write_pointer;
10206
10207 int request_id[MAX_CLIENTS];
10208 for (int i = 0; i < pheader->max_client_index; i++) {
10209 request_id[i] = -1;
10210 }
10211
10212 size_t ask_rp = pbuf->write_cache_rp;
10213 size_t ask_wp = pbuf->write_cache_wp;
10214
10215 if (ask_wp == 0) { // nothing to do
10216 return BM_SUCCESS;
10217 }
10218
10219 if (ask_rp == ask_wp) { // nothing to do
10220 return BM_SUCCESS;
10221 }
10222
10223 assert(ask_rp < ask_wp);
10224
10225 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10226
10227 if (ask_free == 0) { // nothing to do
10228 return BM_SUCCESS;
10229 }
10230
10231#if 0
10233 if (status != BM_SUCCESS) {
10234 printf("bm_flush_cache: corrupted 111!\n");
10235 abort();
10236 }
10237#endif
10238
10239 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
10240
10241 if (status != BM_SUCCESS) {
10242 return status;
10243 }
10244
10245 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10246 //
10247 // wait_for_free_space() will sleep with all locks released,
10248 // during this time, another thread may call bm_send_event() that will
10249 // add one or more events to the write cache and after wait_for_free_space()
10250 // returns, size of data in cache will be bigger than the amount
10251 // of free space we requested. so we need to keep track of how
10252 // much data we write to the buffer and ask for more data
10253 // if we run short. This is the reason for the big loop
10254 // around wait_for_free_space(). We ask for slightly too little free
10255 // space to make sure all this code is always used and does work. K.O.
10256
10257 if (pbuf->write_cache_wp == 0) {
10258 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10259 return BM_SUCCESS;
10260 }
10261
10262 //size_t written = 0;
10263 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10264 /* loop over all events in cache */
10265
10266 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10267 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10268 size_t total_size = ALIGN8(event_size);
10269
10270#if 0
10271 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",
10272 int(pbuf->write_cache_size),
10273 int(pbuf->write_cache_wp),
10274 int(pbuf->write_cache_rp),
10275 int(pevent->data_size),
10276 int(event_size),
10277 int(total_size),
10278 int(ask_free),
10279 int(written));
10280#endif
10281
10282 // check for crazy event size
10283 assert(total_size >= sizeof(EVENT_HEADER));
10284 assert(total_size <= (size_t)pheader->size);
10285
10286 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10287
10288 /* update statistics */
10289 pheader->num_in_events++;
10290 pbuf->count_sent += 1;
10291 pbuf->bytes_sent += total_size;
10292
10293 /* see comment for the same code in bm_send_event().
10294 * We make sure the buffer is never 100% full */
10295 assert(pheader->write_pointer != pheader->read_pointer);
10296
10297 /* check if anybody has a request for this event */
10298 for (int i = 0; i < pheader->max_client_index; i++) {
10299 BUFFER_CLIENT *pc = pheader->client + i;
10300 int r = bm_find_first_request_locked(pc, pevent);
10301 if (r >= 0) {
10302 request_id[i] = r;
10303 }
10304 }
10305
10306 /* this loop does not loop forever because rp
10307 * is monotonously incremented here. write_cache_wp does
10308 * not change */
10309
10310 pbuf->write_cache_rp += total_size;
10311 //written += total_size;
10312
10313 assert(pbuf->write_cache_rp > 0);
10314 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10315 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10316 }
10317
10318 /* the write cache is now empty */
10319 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10320 pbuf->write_cache_wp = 0;
10321 pbuf->write_cache_rp = 0;
10322
10323 /* check which clients are waiting */
10324 for (int i = 0; i < pheader->max_client_index; i++) {
10325 BUFFER_CLIENT *pc = pheader->client + i;
10326 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10327 }
10328 }
10329
10330 return BM_SUCCESS;
10331}
10332
10333#endif /* LOCAL_ROUTINES */
10334
10335INT bm_flush_cache(int buffer_handle, int timeout_msec)
10336{
10337 if (rpc_is_remote()) {
10338 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10339 }
10340
10341#ifdef LOCAL_ROUTINES
10342 {
10343 INT status = 0;
10344
10345 //printf("bm_flush_cache!\n");
10346
10347 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10348
10349 if (!pbuf)
10350 return status;
10351
10352 if (pbuf->write_cache_size == 0)
10353 return BM_SUCCESS;
10354
10356
10357 if (status != BM_SUCCESS)
10358 return status;
10359
10360 /* check if anything needs to be flushed */
10361 if (pbuf->write_cache_wp == 0) {
10362 pbuf->write_cache_mutex.unlock();
10363 return BM_SUCCESS;
10364 }
10365
10366 /* lock the buffer */
10367 bm_lock_buffer_guard pbuf_guard(pbuf);
10368
10369 if (!pbuf_guard.is_locked())
10370 return pbuf_guard.get_status();
10371
10372 status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
10373
10374 /* unlock in correct order */
10375
10376 if (pbuf_guard.is_locked()) {
10377 // check if bm_wait_for_free_space() failed to relock the buffer
10378 pbuf_guard.unlock();
10379 }
10380
10381 pbuf->write_cache_mutex.unlock();
10382
10383 return status;
10384 }
10385#endif /* LOCAL_ROUTINES */
10386
10387 return BM_SUCCESS;
10388}
10389
10390#ifdef LOCAL_ROUTINES
10391
10392static 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) {
10394
10395 int max_size = 0;
10396 if (buf_size) {
10397 max_size = *buf_size;
10398 *buf_size = 0;
10399 }
10400
10401 //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);
10402
10403 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10404
10405 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10406
10407 /* look if there is anything in the cache */
10408 if (pbuf->read_cache_size > 0) {
10409
10411
10412 if (status != BM_SUCCESS)
10413 return status;
10414
10415 if (pbuf->read_cache_wp == 0) {
10416
10417 // lock buffer for the first time
10418
10419 if (!pbuf_guard.relock()) {
10420 pbuf->read_cache_mutex.unlock();
10421 return pbuf_guard.get_status();
10422 }
10423
10424 status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
10425 if (status != BM_SUCCESS) {
10426 // unlock in correct order
10427 if (pbuf_guard.is_locked()) {
10428 // check if bm_wait_for_more_events() failed to relock the buffer
10429 pbuf_guard.unlock();
10430 }
10431 pbuf->read_cache_mutex.unlock();
10432 return status;
10433 }
10434
10435 // buffer remains locked here
10436 }
10437 EVENT_HEADER *pevent;
10438 int event_size;
10439 int total_size;
10440 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10441 if (pbuf_guard.is_locked()) {
10442 // do not need to keep the event buffer locked
10443 // when reading from the read cache
10444 pbuf_guard.unlock();
10445 }
10446 //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);
10448 if (buf) {
10449 if (event_size > max_size) {
10450 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);
10451 event_size = max_size;
10453 }
10454
10455 memcpy(buf, pevent, event_size);
10456
10457 if (buf_size) {
10458 *buf_size = event_size;
10459 }
10460 if (convert_flags) {
10461 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10462 }
10463 } else if (bufptr) {
10464 *bufptr = malloc(event_size);
10465 memcpy(*bufptr, pevent, event_size);
10467 } else if (vecptr) {
10468 vecptr->resize(0);
10469 char* cptr = (char*)pevent;
10470 vecptr->assign(cptr, cptr+event_size);
10471 }
10472 bm_incr_read_cache_locked(pbuf, total_size);
10473 pbuf->read_cache_mutex.unlock();
10474 if (dispatch) {
10475 // FIXME need to protect currently dispatched event against
10476 // another thread overwriting it by refilling the read cache
10477 bm_dispatch_event(buffer_handle, pevent);
10478 return BM_MORE_EVENTS;
10479 }
10480 // buffer is unlocked here
10481 return status;
10482 }
10483 pbuf->read_cache_mutex.unlock();
10484 }
10485
10486 /* we come here if the read cache is disabled */
10487 /* we come here if the next event is too big to fit into the read cache */
10488
10489 if (!pbuf_guard.is_locked()) {
10490 if (!pbuf_guard.relock())
10491 return pbuf_guard.get_status();
10492 }
10493
10494 EVENT_HEADER *event_buffer = NULL;
10495
10496 BUFFER_HEADER *pheader = pbuf->buffer_header;
10497
10498 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
10499
10500 while (1) {
10501 /* loop over events in the event buffer */
10502
10503 status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
10504
10505 if (status != BM_SUCCESS) {
10506 // implicit unlock
10507 return status;
10508 }
10509
10510 /* check if event at current read pointer matches a request */
10511
10512 EVENT_HEADER *pevent;
10513 int event_size;
10514 int total_size;
10515
10516 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10517 if (status == BM_CORRUPTED) {
10518 // implicit unlock
10519 return status;
10520 } else if (status != BM_SUCCESS) {
10521 /* event buffer is empty */
10522 break;
10523 }
10524
10525 BOOL is_requested = bm_check_requests(pc, pevent);
10526
10527 if (is_requested) {
10528 //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);
10529
10531
10532 if (buf) {
10533 if (event_size > max_size) {
10534 cm_msg(MERROR, "bm_read_buffer",
10535 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10536 event_size, pheader->name);
10537 event_size = max_size;
10539 }
10540
10541 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10542
10543 if (buf_size) {
10544 *buf_size = event_size;
10545 }
10546
10547 if (convert_flags) {
10548 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10549 }
10550
10551 pbuf->count_read++;
10552 pbuf->bytes_read += event_size;
10553 } else if (dispatch || bufptr) {
10554 assert(event_buffer == NULL); // make sure we only come here once
10555 event_buffer = (EVENT_HEADER *) malloc(event_size);
10557 pbuf->count_read++;
10558 pbuf->bytes_read += event_size;
10559 } else if (vecptr) {
10560 bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
10561 pbuf->count_read++;
10562 pbuf->bytes_read += event_size;
10563 }
10564
10565 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10566 pc->read_pointer = new_read_pointer;
10567
10568 pheader->num_out_events++;
10569 /* exit loop over events */
10570 break;
10571 }
10572
10573 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10574 pc->read_pointer = new_read_pointer;
10575 pheader->num_out_events++;
10576 }
10577
10578 /*
10579 If read pointer has been changed, it may have freed up some space
10580 for waiting producers. So check if free space is now more than 50%
10581 of the buffer size and wake waiting producers.
10582 */
10583
10584 bm_wakeup_producers_locked(pheader, pc);
10585
10586 pbuf_guard.unlock();
10587
10588 if (dispatch && event_buffer) {
10589 bm_dispatch_event(buffer_handle, event_buffer);
10590 free(event_buffer);
10591 event_buffer = NULL;
10592 return BM_MORE_EVENTS;
10593 }
10594
10595 if (bufptr && event_buffer) {
10596 *bufptr = event_buffer;
10597 event_buffer = NULL;
10599 }
10600
10601 if (event_buffer) {
10602 free(event_buffer);
10603 event_buffer = NULL;
10604 }
10605
10606 return status;
10607}
10608
10609#endif
10610
10611static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10612{
10613 //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);
10614
10615 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10616
10617 void *xbuf = NULL;
10618 int xbuf_size = 0;
10619
10620 if (buf) {
10621 xbuf = buf;
10622 xbuf_size = *buf_size;
10623 } else if (ppevent) {
10624 *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
10625 xbuf_size = _bm_max_event_size;
10626 } else if (pvec) {
10627 pvec->resize(_bm_max_event_size);
10628 xbuf = pvec->data();
10629 xbuf_size = pvec->size();
10630 } else {
10631 assert(!"incorrect call to bm_receivent_event_rpc()");
10632 }
10633
10634 int status;
10635 DWORD time_start = ss_millitime();
10636 DWORD time_end = time_start + timeout_msec;
10637
10638 int xtimeout_msec = timeout_msec;
10639
10640 int zbuf_size = xbuf_size;
10641
10642 while (1) {
10643 if (timeout_msec == BM_WAIT) {
10644 xtimeout_msec = 1000;
10645 } else if (timeout_msec == BM_NO_WAIT) {
10646 xtimeout_msec = BM_NO_WAIT;
10647 } else {
10648 if (xtimeout_msec > 1000) {
10649 xtimeout_msec = 1000;
10650 }
10651 }
10652
10653 zbuf_size = xbuf_size;
10654
10655 status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
10656
10657 //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);
10658
10659 if (status == BM_ASYNC_RETURN) {
10660 if (timeout_msec == BM_WAIT) {
10661 // BM_WAIT means wait forever
10662 continue;
10663 } else if (timeout_msec == BM_NO_WAIT) {
10664 // BM_NO_WAIT means do not wait
10665 break;
10666 } else {
10667 DWORD now = ss_millitime();
10668 if (now >= time_end) {
10669 // timeout, return BM_ASYNC_RETURN
10670 break;
10671 }
10672
10673 DWORD remain = time_end - now;
10674
10675 if (remain < (DWORD)xtimeout_msec) {
10676 xtimeout_msec = remain;
10677 }
10678
10679 // keep asking for event...
10680 continue;
10681 }
10682 } else if (status == BM_SUCCESS) {
10683 // success, return BM_SUCCESS
10684 break;
10685 }
10686
10687 // RPC error
10688
10689 if (buf) {
10690 *buf_size = 0;
10691 } else if (ppevent) {
10692 free(*ppevent);
10693 *ppevent = NULL;
10694 } else if (pvec) {
10695 pvec->resize(0);
10696 } else {
10697 assert(!"incorrect call to bm_receivent_event_rpc()");
10698 }
10699
10700 return status;
10701 }
10702
10703 // status is BM_SUCCESS or BM_ASYNC_RETURN
10704
10705 if (buf) {
10706 *buf_size = zbuf_size;
10707 } else if (ppevent) {
10708 // nothing to do
10709 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10710 } else if (pvec) {
10711 pvec->resize(zbuf_size);
10712 } else {
10713 assert(!"incorrect call to bm_receivent_event_rpc()");
10714 }
10715
10716 return status;
10717}
10718
10719static INT bm_receive_event_rpc_cxx(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10720{
10721 //printf("bm_receive_event_rpc_cxx: 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);
10722
10723 std::vector<char> *pv;
10724
10725 if (pvec == NULL)
10726 pv = new std::vector<char>;
10727 else
10728 pv = pvec;
10729
10730 pv->clear();
10731
10732 int status;
10733 DWORD time_start = ss_millitime();
10734 DWORD time_end = time_start + timeout_msec;
10735
10736 int xtimeout_msec = timeout_msec;
10737
10738 while (1) {
10739 if (timeout_msec == BM_WAIT) {
10740 xtimeout_msec = 1000;
10741 } else if (timeout_msec == BM_NO_WAIT) {
10742 xtimeout_msec = BM_NO_WAIT;
10743 } else {
10744 if (xtimeout_msec > 1000) {
10745 xtimeout_msec = 1000;
10746 }
10747 }
10748
10749 status = rpc_call(RPC_BM_RECEIVE_EVENT_CXX, buffer_handle, pv, xtimeout_msec);
10750
10751 printf("bm_receive_event_rpc_cxx: handle %d, timeout %d, status %d, size %zu, via RPC_BM_RECEIVE_EVENT_CXX\n", buffer_handle, xtimeout_msec, status, pv->size());
10752
10753 if (status == BM_ASYNC_RETURN) {
10754 if (timeout_msec == BM_WAIT) {
10755 // BM_WAIT means wait forever
10756 continue;
10757 } else if (timeout_msec == BM_NO_WAIT) {
10758 // BM_NO_WAIT means do not wait
10759 break;
10760 } else {
10761 DWORD now = ss_millitime();
10762 if (now >= time_end) {
10763 // timeout, return BM_ASYNC_RETURN
10764 break;
10765 }
10766
10767 DWORD remain = time_end - now;
10768
10769 if (remain < (DWORD)xtimeout_msec) {
10770 xtimeout_msec = remain;
10771 }
10772
10773 // keep asking for event...
10774 continue;
10775 }
10776 } else if (status == BM_SUCCESS) {
10777 // success, return BM_SUCCESS
10778 break;
10779 }
10780
10781 // RPC error
10782
10783 if (buf) {
10784 *buf_size = 0;
10785 } else if (ppevent) {
10786 free(*ppevent);
10787 *ppevent = NULL;
10788 } else if (pvec) {
10789 pvec->clear();
10790 } else {
10791 assert(!"incorrect call to bm_receivent_event_rpc_cxx()");
10792 }
10793
10794 if (pvec == NULL)
10795 delete pv;
10796
10797 return status;
10798 }
10799
10800 // status is BM_SUCCESS or BM_ASYNC_RETURN
10801
10802 if (buf) {
10803 if (pv->size() > (size_t)*buf_size) {
10805 memcpy(buf, pv->data(), *buf_size);
10806 } else {
10807 *buf_size = pv->size();
10808 memcpy(buf, pv->data(), *buf_size);
10809 }
10810 } else if (ppevent) {
10811 if (*ppevent == NULL) {
10812 *ppevent = (EVENT_HEADER*)malloc(pv->size());
10813 assert(*ppevent != NULL);
10814 memcpy(*ppevent, pv->data(), pv->size());
10815 } else {
10816 *ppevent = (EVENT_HEADER*)realloc(*ppevent, pv->size()); // shrink memory allocation
10817 assert(*ppevent != NULL);
10818 memcpy(*ppevent, pv->data(), pv->size());
10819 }
10820 } else if (pvec) {
10821 // nothing to do
10822 } else {
10823 assert(!"incorrect call to bm_receivent_event_rpc()");
10824 }
10825
10826 if (!pvec)
10827 delete pv;
10828
10829 return status;
10830}
10831
10832/********************************************************************/
10891INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10892 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10893 if (rpc_is_remote()) {
10894 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10895 }
10896#ifdef LOCAL_ROUTINES
10897 {
10899
10900 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10901
10902 if (!pbuf)
10903 return status;
10904
10905 int convert_flags = rpc_get_convert_flags();
10906
10907 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10908 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10909 return status;
10910 }
10911#else /* LOCAL_ROUTINES */
10912
10913 return BM_SUCCESS;
10914#endif
10915}
10916
10917/********************************************************************/
10972INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
10973 if (rpc_is_remote()) {
10974 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10975 }
10976#ifdef LOCAL_ROUTINES
10977 {
10979
10980 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10981
10982 if (!pbuf)
10983 return status;
10984
10985 int convert_flags = rpc_get_convert_flags();
10986
10987 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10988 }
10989#else /* LOCAL_ROUTINES */
10990
10991 return BM_SUCCESS;
10992#endif
10993}
10994
10995/********************************************************************/
11050INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
11051 if (rpc_is_remote()) {
11052 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
11053 }
11054#ifdef LOCAL_ROUTINES
11055 {
11057
11058 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
11059
11060 if (!pbuf)
11061 return status;
11062
11063 int convert_flags = rpc_get_convert_flags();
11064
11065 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
11066 }
11067#else /* LOCAL_ROUTINES */
11068 return BM_SUCCESS;
11069#endif
11070}
11071
11072#ifdef LOCAL_ROUTINES
11073
11074static int bm_skip_event(BUFFER* pbuf)
11075{
11076 /* clear read cache */
11077 if (pbuf->read_cache_size > 0) {
11078
11080
11081 if (status != BM_SUCCESS)
11082 return status;
11083
11084 pbuf->read_cache_rp = 0;
11085 pbuf->read_cache_wp = 0;
11086
11087 pbuf->read_cache_mutex.unlock();
11088 }
11089
11090 bm_lock_buffer_guard pbuf_guard(pbuf);
11091
11092 if (!pbuf_guard.is_locked())
11093 return pbuf_guard.get_status();
11094
11095 BUFFER_HEADER *pheader = pbuf->buffer_header;
11096
11097 /* forward read pointer to global write pointer */
11098 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
11099 pclient->read_pointer = pheader->write_pointer;
11100
11101 return BM_SUCCESS;
11102}
11103
11104#endif /* LOCAL_ROUTINES */
11105
11106/********************************************************************/
11115INT bm_skip_event(INT buffer_handle) {
11116 if (rpc_is_remote())
11117 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
11118
11119#ifdef LOCAL_ROUTINES
11120 {
11121 int status = 0;
11122
11123 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
11124
11125 if (!pbuf)
11126 return status;
11127
11128 return bm_skip_event(pbuf);
11129 }
11130#endif
11131
11132 return BM_SUCCESS;
11133}
11134
11135#ifdef LOCAL_ROUTINES
11136/********************************************************************/
11143static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
11144 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
11145
11146 /* return immediately if no callback routine is defined */
11147 if (!pbuf->callback)
11148 return BM_SUCCESS;
11149
11150 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
11151}
11152
11153/********************************************************************/
11159static INT bm_push_event(const char *buffer_name)
11160{
11161 std::vector<BUFFER*> mybuffers;
11162
11163 gBuffersMutex.lock();
11164 mybuffers = gBuffers;
11165 gBuffersMutex.unlock();
11166
11167 for (size_t i = 0; i < mybuffers.size(); i++) {
11168 BUFFER *pbuf = mybuffers[i];
11169 if (!pbuf || !pbuf->attached)
11170 continue;
11171 // FIXME: unlocked read access to pbuf->buffer_name!
11172 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
11173 return bm_push_buffer(pbuf, i + 1);
11174 }
11175 }
11176
11177 return BM_INVALID_HANDLE;
11178}
11179
11180#else
11181
11182static INT bm_push_event(const char *buffer_name)
11183{
11184 return BM_SUCCESS;
11185}
11186
11187#endif /* LOCAL_ROUTINES */
11188
11189/********************************************************************/
11196#ifdef LOCAL_ROUTINES
11197 {
11198 INT status = 0;
11199 BOOL bMore;
11200 DWORD start_time;
11201 //static DWORD last_time = 0;
11202
11203 /* if running as a server, buffer checking is done by client
11204 via ASYNC bm_receive_event */
11205 if (rpc_is_mserver()) {
11206 return FALSE;
11207 }
11208
11209 bMore = FALSE;
11210 start_time = ss_millitime();
11211
11212 std::vector<BUFFER*> mybuffers;
11213
11214 gBuffersMutex.lock();
11215 mybuffers = gBuffers;
11216 gBuffersMutex.unlock();
11217
11218 /* go through all buffers */
11219 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11220 BUFFER* pbuf = mybuffers[idx];
11221
11222 if (!pbuf || !pbuf->attached)
11223 continue;
11224
11225 //int count_loops = 0;
11226 while (1) {
11227 if (pbuf->attached) {
11228 /* one bm_push_event could cause a run stop and a buffer close, which
11229 * would crash the next call to bm_push_event(). So check for valid
11230 * buffer on each call */
11231
11232 /* this is what happens:
11233 * bm_push_buffer() may call a user callback function
11234 * user callback function may indirectly call bm_close() of this buffer,
11235 * i.e. if it stops the run,
11236 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11237 * here we will see pbuf->attched is false and quit this loop
11238 */
11239
11240 status = bm_push_buffer(pbuf, idx + 1);
11241
11242 if (status == BM_CORRUPTED) {
11243 return status;
11244 }
11245
11246 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11247
11248 if (status != BM_MORE_EVENTS) {
11249 //DWORD t = ss_millitime() - start_time;
11250 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11251 break;
11252 }
11253
11254 // count_loops++;
11255 }
11256
11257 // NB: this code has a logic error: if 2 buffers always have data,
11258 // this timeout will cause us to exit reading the 1st buffer
11259 // after 1000 msec, then we read the 2nd buffer exactly once,
11260 // and exit the loop because the timeout is still active -
11261 // we did not reset "start_time" when we started reading
11262 // from the 2nd buffer. Result is that we always read all
11263 // the data in a loop from the 1st buffer, but read just
11264 // one event from the 2nd buffer, resulting in severe unfairness.
11265
11266 /* stop after one second */
11267 DWORD t = ss_millitime() - start_time;
11268 if (t > 1000) {
11269 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11270 bMore = TRUE;
11271 break;
11272 }
11273 }
11274 }
11275
11276 //last_time = start_time;
11277
11278 return bMore;
11279
11280 }
11281#else /* LOCAL_ROUTINES */
11282
11283 return FALSE;
11284
11285#endif
11286}
11287
11288/********************************************************************/
11289static INT bm_notify_client(const char *buffer_name, int client_socket)
11290/********************************************************************\
11291
11292 Routine: bm_notify_client
11293
11294 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11295 the connected client. Used by mserver to relay the BM_MSG
11296 buffer message from local UDP socket to the remote connected client.
11297
11298 Input:
11299 char *buffer_name Name of buffer
11300 int client_socket Network socket to client
11301
11302 Output:
11303 none
11304
11305 Function value:
11306 BM_SUCCESS Successful completion
11307
11308\********************************************************************/
11309{
11310 static DWORD last_time = 0;
11311 DWORD now = ss_millitime();
11312
11313 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11314
11315 BUFFER* fbuf = NULL;
11316
11317 gBuffersMutex.lock();
11318
11319 for (size_t i = 0; i < gBuffers.size(); i++) {
11320 BUFFER* pbuf = gBuffers[i];
11321 if (!pbuf || !pbuf->attached)
11322 continue;
11323 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11324 fbuf = pbuf;
11325 break;
11326 }
11327 }
11328
11329 gBuffersMutex.unlock();
11330
11331 if (!fbuf)
11332 return BM_INVALID_HANDLE;
11333
11334 /* don't send notification if client has no callback defined
11335 to receive events -> client calls bm_receive_event manually */
11336 if (!fbuf->callback)
11337 return DB_SUCCESS;
11338
11339 int convert_flags = rpc_get_convert_flags();
11340
11341 /* only send notification once each 500ms */
11342 if (now - last_time < 500)
11343 return DB_SUCCESS;
11344
11345 last_time = now;
11346
11347 char buffer[32];
11348 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11349
11350 nc->header.routine_id = MSG_BM;
11351 nc->header.param_size = 0;
11352
11353 if (convert_flags) {
11356 }
11357
11358 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11359
11360 /* send the update notification to the client */
11361 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11362
11363 return BM_SUCCESS;
11364}
11365
11366/********************************************************************/
11368/********************************************************************\
11369
11370 Routine: bm_poll_event
11371
11372 Purpose: Poll an event from a remote server. Gets called by
11373 rpc_client_dispatch() and by cm_yield()
11374
11375 Function value:
11376 BM_SUCCESS At least one event was received and dispatched
11377 BM_ASYNC_RETURN No events received
11378 SS_ABORT Network connection broken
11379
11380\********************************************************************/
11381{
11382 BOOL dispatched_something = FALSE;
11383
11384 //printf("bm_poll_event!\n");
11385
11386 DWORD start_time = ss_millitime();
11387
11388 std::vector<char> vec;
11389
11390 /* loop over all requests */
11391 _request_list_mutex.lock();
11392 bool locked = true;
11393 size_t n = _request_list.size();
11394 for (size_t i = 0; i < n; i++) {
11395 if (!locked) {
11396 _request_list_mutex.lock();
11397 locked = true;
11398 }
11399 /* continue if no dispatcher set (manual bm_receive_event) */
11400 if (_request_list[i].dispatcher == NULL)
11401 continue;
11402
11403 int buffer_handle = _request_list[i].buffer_handle;
11404
11405 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11406 _request_list_mutex.unlock();
11407 locked = false;
11408
11409 do {
11410 /* receive event */
11411 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11412
11413 //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());
11414
11415 /* call user function if successful */
11416 if (status == BM_SUCCESS) {
11417 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11418 dispatched_something = TRUE;
11419 }
11420
11421 /* break if no more events */
11422 if (status == BM_ASYNC_RETURN)
11423 break;
11424
11425 /* break if corrupted event buffer */
11426 if (status == BM_TRUNCATED) {
11427 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());
11428 }
11429
11430 /* break if corrupted event buffer */
11431 if (status == BM_CORRUPTED)
11432 return SS_ABORT;
11433
11434 /* break if server died */
11435 if (status == RPC_NET_ERROR) {
11436 return SS_ABORT;
11437 }
11438
11439 /* stop after one second */
11440 if (ss_millitime() - start_time > 1000) {
11441 break;
11442 }
11443
11444 } while (TRUE);
11445 }
11446
11447 if (locked)
11448 _request_list_mutex.unlock();
11449
11450 if (dispatched_something)
11451 return BM_SUCCESS;
11452 else
11453 return BM_ASYNC_RETURN;
11454}
11455
11456/********************************************************************/
11482 if (rpc_is_remote())
11484
11485#ifdef LOCAL_ROUTINES
11486 {
11487 std::vector<BUFFER*> mybuffers;
11488
11489 gBuffersMutex.lock();
11490 mybuffers = gBuffers;
11491 gBuffersMutex.unlock();
11492
11493 /* go through all buffers */
11494 for (BUFFER* pbuf : mybuffers) {
11495 if (!pbuf)
11496 continue;
11497 if (!pbuf->attached)
11498 continue;
11499
11500 int status = bm_skip_event(pbuf);
11501 if (status != BM_SUCCESS)
11502 return status;
11503 }
11504 }
11505#endif /* LOCAL_ROUTINES */
11506
11507 return BM_SUCCESS;
11508}
11509
11511#ifndef DOXYGEN_SHOULD_SKIP_THIS
11512
11513#define MAX_DEFRAG_EVENTS 10
11514
11521
11523
11524/********************************************************************/
11525static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11526 EVENT_HEADER *pevent, void *pdata,
11527 EVENT_HANDLER *dispatcher)
11528/********************************************************************\
11529
11530 Routine: bm_defragment_event
11531
11532 Purpose: Called internally from the event receiving routines
11533 bm_push_event and bm_poll_event to recombine event
11534 fragments and call the user callback routine upon
11535 completion.
11536
11537 Input:
11538 HNDLE buffer_handle Handle for the buffer containing event
11539 HNDLE request_id Handle for event request
11540 EVENT_HEADER *pevent Pointer to event header
11541 void *pata Pointer to event data
11542 dispatcher() User callback routine
11543
11544 Output:
11545 <calls dispatcher() after successfull recombination of event>
11546
11547 Function value:
11548 void
11549
11550\********************************************************************/
11551{
11552 INT i;
11553
11554 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11555 /*---- start new event ----*/
11556
11557 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11558
11559 /* check if fragments already stored */
11560 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11561 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11562 break;
11563
11564 if (i < MAX_DEFRAG_EVENTS) {
11565 free(defrag_buffer[i].pevent);
11566 defrag_buffer[i].pevent = NULL;
11567 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11568 cm_msg(MERROR, "bm_defragement_event",
11569 "Received new event with ID %d while old fragments were not completed",
11570 (pevent->event_id & 0x0FFF));
11571 }
11572
11573 /* search new slot */
11574 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11575 if (defrag_buffer[i].event_id == 0)
11576 break;
11577
11578 if (i == MAX_DEFRAG_EVENTS) {
11579 cm_msg(MERROR, "bm_defragment_event",
11580 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11581 return;
11582 }
11583
11584 /* check event size */
11585 if (pevent->data_size != sizeof(DWORD)) {
11586 cm_msg(MERROR, "bm_defragment_event",
11587 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11588 pevent->data_size, (int) sizeof(DWORD));
11589 return;
11590 }
11591
11592 /* setup defragment buffer */
11593 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11594 defrag_buffer[i].data_size = *(DWORD *) pdata;
11597
11598 if (defrag_buffer[i].pevent == NULL) {
11599 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11600 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11601 return;
11602 }
11603
11604 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11607
11608 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11609 // pevent->serial_number, defrag_buffer[i].data_size);
11610
11611 return;
11612 }
11613
11614 /* search buffer for that event */
11615 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11616 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11617 break;
11618
11619 if (i == MAX_DEFRAG_EVENTS) {
11620 /* no buffer available -> no first fragment received */
11621 cm_msg(MERROR, "bm_defragement_event",
11622 "Received fragment without first fragment (ID %d) Ser#:%d",
11623 pevent->event_id & 0x0FFF, pevent->serial_number);
11624 return;
11625 }
11626
11627 /* add fragment to buffer */
11629 free(defrag_buffer[i].pevent);
11630 defrag_buffer[i].pevent = NULL;
11631 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11632 cm_msg(MERROR, "bm_defragement_event",
11633 "Received fragments with more data (%d) than event size (%d)",
11634 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11635 return;
11636 }
11637
11638 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11639 defrag_buffer[i].received, pdata, pevent->data_size);
11640
11641 defrag_buffer[i].received += pevent->data_size;
11642
11643 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11644 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11645
11646 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11647 /* event complete */
11648 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11649 free(defrag_buffer[i].pevent);
11650 defrag_buffer[i].pevent = NULL;
11651 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11652 }
11653}
11654
11656#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11657
/* end of bmfunctionc */
11660
11666/********************************************************************\
11667* *
11668* RPC functions *
11669* *
11670\********************************************************************/
11671
11673{
11674public:
11675 std::atomic_bool connected{false}; /* socket is connected */
11676 std::string host_name; /* server name */
11677 int port = 0; /* server port */
11678 std::mutex mutex; /* connection lock */
11679 int index = 0; /* index in the connection array */
11680 std::string client_name; /* name of remote client */
11681 int send_sock = 0; /* tcp socket */
11682 int remote_hw_type = 0; /* remote hardware type */
11683 int rpc_timeout = 0; /* timeout in milliseconds */
11684
11685 void print() {
11686 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11687 index,
11688 client_name.c_str(),
11689 host_name.c_str(),
11690 port,
11691 send_sock,
11692 int(connected),
11693 rpc_timeout);
11694 }
11695
11697 if (send_sock > 0) {
11699 }
11700 connected = false;
11701 }
11702};
11703
11704/* globals */
11705
11706//
11707// locking rules for client connections:
11708//
11709// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11710// 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
11711// lock individual connection, check c->connected, work on the connection, unlock connection
11712//
11713// ok to access without locking client connection:
11714//
11715// - c->connected (std::atomic, but must recheck it after taking the lock)
11716// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11717//
11718// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11719// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11720//
11721// lifetime of client connections:
11722//
11723// - client connection slots are allocated by rpc_client_connect()
11724// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11725// - client slots marked NULL are free and will be reused by rpc_client_connect()
11726// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11727// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11728// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11729// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11730// client call will see an empty slot and return an error
11731// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11732// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11733// only when one is sure no other threads are running concurrent rpc client calls.
11734//
11735
11737static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11738
11739static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11740static bool _rpc_is_remote = false;
11741
11742//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11743static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11744static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11745
11750
11752{
11753 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11754 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11755 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11756 return _server_acceptions[idx];
11757 }
11758 }
11759
11761
11762 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11763 if (_server_acceptions[idx] == NULL) {
11764 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11765 _server_acceptions[idx] = sa;
11766 return _server_acceptions[idx];
11767 }
11768 }
11769
11770 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11771 _server_acceptions.push_back(sa);
11772
11773 return sa;
11774}
11775
11777{
11778 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11779
11780 if (is_mserver) {
11781 assert(_mserver_acception == this);
11782 _mserver_acception = NULL;
11783 is_mserver = false;
11784 }
11785
11786 /* close server connection */
11787 if (recv_sock)
11789 if (send_sock)
11791 if (event_sock)
11793
11794 /* free TCP cache */
11795 if (net_buffer) {
11796 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11797 free(net_buffer);
11798 net_buffer = NULL;
11799 net_buffer_size = 0;
11800 }
11801
11802 /* mark this entry as invalid */
11803 clear();
11804}
11805
11806static std::vector<RPC_LIST> rpc_list;
11807static std::mutex rpc_list_mutex;
11808
11810
11811
11812/********************************************************************\
11813* conversion functions *
11814\********************************************************************/
11815
11816void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11817 *convert_flags = 0;
11818
11819 /* big/little endian conversion */
11820 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11821 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11822 && (hw_type & DRI_BIG_ENDIAN)))
11823 *convert_flags |= CF_ENDIAN;
11824
11825 /* float conversion between IEEE and VAX G */
11826 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11827 *convert_flags |= CF_VAX2IEEE;
11828
11829 /* float conversion between VAX G and IEEE */
11830 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11831 *convert_flags |= CF_IEEE2VAX;
11832
11834 //if (remote_hw_type & DR_ASCII)
11835 // *convert_flags |= CF_ASCII;
11836}
11837
11838/********************************************************************/
11842
11843/********************************************************************/
11844void rpc_ieee2vax_float(float *var) {
11845 unsigned short int lo, hi;
11846
11847 /* swap hi and lo word */
11848 lo = *((short int *) (var) + 1);
11849 hi = *((short int *) (var));
11850
11851 /* correct exponent */
11852 if (lo != 0)
11853 lo += 0x100;
11854
11855 *((short int *) (var) + 1) = hi;
11856 *((short int *) (var)) = lo;
11857}
11858
11859void rpc_vax2ieee_float(float *var) {
11860 unsigned short int lo, hi;
11861
11862 /* swap hi and lo word */
11863 lo = *((short int *) (var) + 1);
11864 hi = *((short int *) (var));
11865
11866 /* correct exponent */
11867 if (hi != 0)
11868 hi -= 0x100;
11869
11870 *((short int *) (var) + 1) = hi;
11871 *((short int *) (var)) = lo;
11872
11873}
11874
11875void rpc_vax2ieee_double(double *var) {
11876 unsigned short int i1, i2, i3, i4;
11877
11878 /* swap words */
11879 i1 = *((short int *) (var) + 3);
11880 i2 = *((short int *) (var) + 2);
11881 i3 = *((short int *) (var) + 1);
11882 i4 = *((short int *) (var));
11883
11884 /* correct exponent */
11885 if (i4 != 0)
11886 i4 -= 0x20;
11887
11888 *((short int *) (var) + 3) = i4;
11889 *((short int *) (var) + 2) = i3;
11890 *((short int *) (var) + 1) = i2;
11891 *((short int *) (var)) = i1;
11892}
11893
11894void rpc_ieee2vax_double(double *var) {
11895 unsigned short int i1, i2, i3, i4;
11896
11897 /* swap words */
11898 i1 = *((short int *) (var) + 3);
11899 i2 = *((short int *) (var) + 2);
11900 i3 = *((short int *) (var) + 1);
11901 i4 = *((short int *) (var));
11902
11903 /* correct exponent */
11904 if (i1 != 0)
11905 i1 += 0x20;
11906
11907 *((short int *) (var) + 3) = i4;
11908 *((short int *) (var) + 2) = i3;
11909 *((short int *) (var) + 1) = i2;
11910 *((short int *) (var)) = i1;
11911}
11912
11913/********************************************************************/
11914void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11915
11916 if (convert_flags & CF_ENDIAN) {
11917 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11918 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11919 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11920 }
11921
11922 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11923 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11924 if (tid == TID_FLOAT)
11925 rpc_ieee2vax_float((float *) data);
11926 if (tid == TID_DOUBLE)
11927 rpc_ieee2vax_double((double *) data);
11928 }
11929
11930 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11931 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11932 if (tid == TID_FLOAT)
11933 rpc_vax2ieee_float((float *) data);
11934 if (tid == TID_DOUBLE)
11935 rpc_vax2ieee_double((double *) data);
11936 }
11937}
11938
11939void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11940/********************************************************************\
11941
11942 Routine: rpc_convert_data
11943
11944 Purpose: Convert data format between differenct computers
11945
11946 Input:
11947 void *data Pointer to data
11948 INT tid Type ID of data, one of TID_xxx
11949 INT flags Combination of following flags:
11950 RPC_IN: data is input parameter
11951 RPC_OUT: data is output variable
11952 RPC_FIXARRAY, RPC_VARARRAY: data is array
11953 of "size" bytes (see next param.)
11954 RPC_OUTGOING: data is outgoing
11955 INT total_size Size of bytes of data. Used for variable
11956 length arrays.
11957 INT convert_flags Flags for data conversion
11958
11959 Output:
11960 void *data Is converted according to _convert_flag
11961 value
11962
11963 Function value:
11964 RPC_SUCCESS Successful completion
11965
11966\********************************************************************/
11967{
11968 /* convert array */
11969 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11970 int single_size = rpc_tid_size(tid);
11971 /* don't convert TID_ARRAY & TID_STRUCT */
11972 if (single_size == 0)
11973 return;
11974
11975 int n = total_size / single_size;
11976
11977 for (int i = 0; i < n; i++) {
11978 char* p = (char *) data + (i * single_size);
11979 rpc_convert_single(p, tid, flags, convert_flags);
11980 }
11981 } else {
11982 rpc_convert_single(data, tid, flags, convert_flags);
11983 }
11984}
11985
11986/********************************************************************\
11987* type ID functions *
11988\********************************************************************/
11989
11991 if (id >= 0 && id < TID_LAST)
11992 return tid_size[id];
11993
11994 return 0;
11995}
11996
11997const char *rpc_tid_name(INT id) {
11998 if (id >= 0 && id < TID_LAST)
11999 return tid_name[id];
12000 else
12001 return "<unknown>";
12002}
12003
12004const char *rpc_tid_name_old(INT id) {
12005 if (id >= 0 && id < TID_LAST)
12006 return tid_name_old[id];
12007 else
12008 return "<unknown>";
12009}
12010
12011int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
12012{
12013 for (int i=0; i<TID_LAST; i++) {
12014 if (strcmp(name, tid_name[i]) == 0)
12015 return i;
12016 }
12017
12018 for (int i=0; i<TID_LAST; i++) {
12019 if (strcmp(name, tid_name_old[i]) == 0)
12020 return i;
12021 }
12022
12023 return 0;
12024}
12025
12026/********************************************************************\
12027* client functions *
12028\********************************************************************/
12029
12030/********************************************************************/
12045
12046 return RPC_SUCCESS;
12047}
12048
12049/********************************************************************/
12061{
12062 for (int i = 0; new_list[i].id != 0; i++) {
12063 /* check valid ID for user functions */
12064 if (new_list != rpc_get_internal_list(0) &&
12065 new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
12066 || new_list[i].id > RPC_MAX_ID)) {
12067 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
12068 }
12069 }
12070
12071 std::lock_guard<std::mutex> guard(rpc_list_mutex);
12072
12073 /* check double defined functions */
12074 for (int i = 0; new_list[i].id != 0; i++) {
12075 for (size_t j = 0; j < rpc_list.size(); j++) {
12076 if (rpc_list[j].id == new_list[i].id) {
12077 return RPC_DOUBLE_DEFINED;
12078 }
12079 }
12080 }
12081
12082 /* append new functions */
12083 for (int i = 0; new_list[i].id != 0; i++) {
12084 RPC_LIST e = new_list[i];
12085
12086 /* set default dispatcher */
12087 if (e.dispatch == NULL) {
12088 e.dispatch = func;
12089 }
12090
12091 rpc_list.push_back(e);
12092 }
12093
12094 return RPC_SUCCESS;
12095}
12096
12097
12098
12100#ifndef DOXYGEN_SHOULD_SKIP_THIS
12101
12102/********************************************************************/
12104/********************************************************************\
12105
12106 Routine: rpc_deregister_functions
12107
12108 Purpose: Free memory of previously registered functions
12109
12110 Input:
12111 none
12112
12113 Output:
12114 none
12115
12116 Function value:
12117 RPC_SUCCESS Successful completion
12118
12119\********************************************************************/
12120{
12121 rpc_list_mutex.lock();
12122 rpc_list.clear();
12123 rpc_list_mutex.unlock();
12124
12125 return RPC_SUCCESS;
12126}
12127
12128
12129/********************************************************************/
12130INT rpc_register_function(INT id, INT(*func)(INT, void **))
12131/********************************************************************\
12132
12133 Routine: rpc_register_function
12134
12135 Purpose: Replace a dispatch function for a specific rpc routine
12136
12137 Input:
12138 INT id RPC ID
12139 INT *func New dispatch function
12140
12141 Output:
12142 <implicit: func gets copied to rpc_list>
12143
12144 Function value:
12145 RPC_SUCCESS Successful completion
12146 RPC_INVALID_ID RPC ID not found
12147
12148\********************************************************************/
12149{
12150 std::lock_guard<std::mutex> guard(rpc_list_mutex);
12151
12152 for (size_t i = 0; i < rpc_list.size(); i++) {
12153 if (rpc_list[i].id == id) {
12154 rpc_list[i].dispatch = func;
12155 return RPC_SUCCESS;
12156 }
12157 }
12158
12159 return RPC_INVALID_ID;
12160}
12161
12162/********************************************************************/
12163
12164static int handle_msg_odb(int n, const NET_COMMAND *nc) {
12165 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
12166 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
12167 /* update a changed record */
12168 HNDLE hDB = *((INT *) nc->param);
12169 HNDLE hKeyRoot = *((INT *) nc->param + 1);
12170 HNDLE hKey = *((INT *) nc->param + 2);
12171 int index = *((INT *) nc->param + 3);
12172 return db_update_record_local(hDB, hKeyRoot, hKey, index);
12173 }
12174 return CM_VERSION_MISMATCH;
12175}
12176
12177/********************************************************************/
12179/********************************************************************\
12180
12181 Routine: rpc_client_dispatch
12182
12183 Purpose: Receive data from the mserver: watchdog and buffer notification messages
12184
12185\********************************************************************/
12186{
12187 INT status = 0;
12188 char net_buffer[256];
12189
12190 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12191 if (n <= 0)
12192 return SS_ABORT;
12193
12194 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
12195
12196 if (nc->header.routine_id == MSG_ODB) {
12197 status = handle_msg_odb(n, nc);
12198 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12199 nc->header.routine_id = 1;
12200 nc->header.param_size = 0;
12201 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12203 } else if (nc->header.routine_id == MSG_BM) {
12204 fd_set readfds;
12205 struct timeval timeout;
12206
12207 //printf("rpc_client_dispatch: received MSG_BM!\n");
12208
12209 /* receive further messages to empty TCP queue */
12210 do {
12211 FD_ZERO(&readfds);
12212 FD_SET(sock, &readfds);
12213
12214 timeout.tv_sec = 0;
12215 timeout.tv_usec = 0;
12216
12217 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12218
12219 if (FD_ISSET(sock, &readfds)) {
12220 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12221 if (n <= 0)
12222 return SS_ABORT;
12223
12224 if (nc->header.routine_id == MSG_ODB) {
12225 status = handle_msg_odb(n, nc);
12226 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12227 nc->header.routine_id = 1;
12228 nc->header.param_size = 0;
12229 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12231 }
12232 }
12233
12234 } while (FD_ISSET(sock, &readfds));
12235
12236 /* poll event from server */
12238 }
12239
12240 return status;
12241}
12242
12243
12244/********************************************************************/
12245INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12246/********************************************************************\
12247
12248 Routine: rpc_client_connect
12249
12250 Purpose: Establish a network connection to a remote client
12251
12252 Input:
12253 char *host_name IP address of host to connect to.
12254 INT port TPC port to connect to.
12255 char *clinet_name Client program name
12256
12257 Output:
12258 HNDLE *hConnection Handle for new connection which can be used
12259 in future rpc_call(hConnection....) calls
12260
12261 Function value:
12262 RPC_SUCCESS Successful completion
12263 RPC_NET_ERROR Error in socket call
12264 RPC_NO_CONNECTION Maximum number of connections reached
12265 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12266
12267\********************************************************************/
12268{
12269 INT i, status;
12270 bool debug = false;
12271
12272 /* check if cm_connect_experiment was called */
12273 if (_client_name.length() == 0) {
12274 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12275 return RPC_NOT_REGISTERED;
12276 }
12277
12278 /* refuse connection to port 0 */
12279 if (port == 0) {
12280 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12281 return RPC_NET_ERROR;
12282 }
12283
12284 RPC_CLIENT_CONNECTION* c = NULL;
12285
12286 static std::mutex gHostnameMutex;
12287
12288 {
12289 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12290
12291 if (debug) {
12292 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12293 for (size_t i = 0; i < _client_connections.size(); i++) {
12294 if (_client_connections[i]) {
12295 printf("client connection %d: ", (int)i);
12296 _client_connections[i]->print();
12297 printf("\n");
12298 }
12299 }
12300 }
12301
12302 // slot with index 0 is not used, fill it with a NULL
12303
12304 if (_client_connections.empty()) {
12305 _client_connections.push_back(NULL);
12306 }
12307
12308 bool hostname_locked = false;
12309
12310 /* check if connection already exists */
12311 for (size_t i = 1; i < _client_connections.size(); i++) {
12313 if (c && c->connected) {
12314
12315 if (!hostname_locked) {
12316 gHostnameMutex.lock();
12317 hostname_locked = true;
12318 }
12319
12320 if ((c->host_name == host_name) && (c->port == port)) {
12321 // NB: we must release the hostname lock before taking
12322 // c->mutex to avoid a locking order inversion deadlock:
12323 // later on we lock the hostname mutex while holding the c->mutex
12324 gHostnameMutex.unlock();
12325 hostname_locked = false;
12326 std::lock_guard<std::mutex> cguard(c->mutex);
12327 // check if socket is still connected
12328 if (c->connected) {
12329 // found connection slot with matching hostname and port number
12330 status = ss_socket_wait(c->send_sock, 0);
12331 if (status == SS_TIMEOUT) { // yes, still connected and empty
12332 // so reuse it connection
12333 *hConnection = c->index;
12334 if (debug) {
12335 printf("already connected: ");
12336 c->print();
12337 printf("\n");
12338 }
12339 // implicit unlock of c->mutex
12340 // gHostnameLock is not locked here
12341 return RPC_SUCCESS;
12342 }
12343 //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);
12344 c->close_locked();
12345 }
12346 // implicit unlock of c->mutex
12347 }
12348 }
12349 }
12350
12351 if (hostname_locked) {
12352 gHostnameMutex.unlock();
12353 hostname_locked = false;
12354 }
12355
12356 // only start reusing connections once we have
12357 // a good number of slots allocated.
12358 if (_client_connections.size() > 10) {
12359 static int last_reused = 0;
12360
12361 int size = _client_connections.size();
12362 for (int j = 1; j < size; j++) {
12363 int i = (last_reused + j) % size;
12364 if (_client_connections[i] && !_client_connections[i]->connected) {
12366 if (debug) {
12367 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12368 c->print();
12369 printf("\n");
12370 }
12371 last_reused = i;
12372 break;
12373 }
12374 }
12375 }
12376
12377 // no slots to reuse, allocate a new slot.
12378 if (!c) {
12380
12381 // if empty slot not found, add to end of array
12382 c->index = _client_connections.size();
12383 _client_connections.push_back(c);
12384
12385 if (debug) {
12386 printf("new connection appended to array: ");
12387 c->print();
12388 printf("\n");
12389 }
12390 }
12391
12392 c->mutex.lock();
12393 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12394
12395 // done with the array of connections
12396 // implicit unlock of _client_connections_mutex
12397 }
12398
12399 // locked connection slot for new connection
12400 assert(c != NULL);
12401
12402 std::string errmsg;
12403
12404 /* create a new socket for connecting to remote server */
12405 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12406 if (status != SS_SUCCESS) {
12407 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12408 c->mutex.unlock();
12409 return RPC_NET_ERROR;
12410 }
12411
12412 gHostnameMutex.lock();
12413
12414 c->host_name = host_name;
12415 c->port = port;
12416
12417 gHostnameMutex.unlock();
12418
12419 c->client_name = client_name;
12420 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12421
12422 /* set TCP_NODELAY option for better performance */
12423 i = 1;
12424 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12425
12426 /* send local computer info */
12427 std::string local_prog_name = rpc_get_name();
12428 std::string local_host_name = ss_gethostname();
12429
12430 int hw_type = rpc_get_hw_type();
12431
12432 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12433
12434 int size = cstr.length() + 1;
12435 i = send(c->send_sock, cstr.c_str(), size, 0);
12436 if (i < 0 || i != size) {
12437 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12438 c->mutex.unlock();
12439 return RPC_NET_ERROR;
12440 }
12441
12442 bool restore_watchdog_timeout = false;
12443 BOOL watchdog_call;
12444 DWORD watchdog_timeout;
12445 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12446
12447 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12448
12449 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12450 restore_watchdog_timeout = true;
12451 cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
12452 }
12453
12454 char str[256];
12455
12456 /* receive remote computer info */
12457 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12458
12459 if (restore_watchdog_timeout) {
12460 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12461 }
12462
12463 if (i <= 0) {
12464 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12465 c->close_locked();
12466 c->mutex.unlock();
12467 return RPC_NET_ERROR;
12468 }
12469
12470 int remote_hw_type = 0;
12471 char remote_version[32];
12472 remote_version[0] = 0;
12473 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12474
12475 c->remote_hw_type = remote_hw_type;
12476
12477 /* print warning if version patch level doesn't agree */
12478 char v1[32];
12479 mstrlcpy(v1, remote_version, sizeof(v1));
12480 if (strchr(v1, '.'))
12481 if (strchr(strchr(v1, '.') + 1, '.'))
12482 *strchr(strchr(v1, '.') + 1, '.') = 0;
12483
12484 mstrlcpy(str, cm_get_version(), sizeof(str));
12485 if (strchr(str, '.'))
12486 if (strchr(strchr(str, '.') + 1, '.'))
12487 *strchr(strchr(str, '.') + 1, '.') = 0;
12488
12489 if (strcmp(v1, str) != 0) {
12490 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12491 }
12492
12493 c->connected = true;
12494
12495 *hConnection = c->index;
12496
12497 c->mutex.unlock();
12498
12499 return RPC_SUCCESS;
12500}
12501
12502/********************************************************************/
12504/********************************************************************\
12505
12506 Routine: rpc_client_check
12507
12508 Purpose: Check all client connections if remote client closed link
12509
12510\********************************************************************/
12511{
12512#if 0
12513 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12514 if (_client_connection[i].send_sock != 0)
12515 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);
12516#endif
12517
12518 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12519
12520 /* check for broken connections */
12521 for (unsigned i = 0; i < _client_connections.size(); i++) {
12523 if (c && c->connected) {
12524 std::lock_guard<std::mutex> cguard(c->mutex);
12525
12526 if (!c->connected) {
12527 // implicit unlock
12528 continue;
12529 }
12530
12531 //printf("rpc_client_check: connection %d: ", i);
12532 //c->print();
12533 //printf("\n");
12534
12535 int ok = 0;
12536
12537 fd_set readfds;
12538 FD_ZERO(&readfds);
12539 FD_SET(c->send_sock, &readfds);
12540
12541 struct timeval timeout;
12542 timeout.tv_sec = 0;
12543 timeout.tv_usec = 0;
12544
12545 int status;
12546
12547#ifdef OS_WINNT
12548 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12549#else
12550 do {
12551 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12552 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12553#endif
12554
12555 if (!FD_ISSET(c->send_sock, &readfds)) {
12556 // implicit unlock
12557 continue;
12558 }
12559
12560 char buffer[64];
12561
12562 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12563 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12564
12565 if (status < 0) {
12566#ifndef OS_WINNT
12567 if (errno == EAGAIN) { // still connected
12568 ok = 1;
12569 } else
12570#endif
12571 {
12572 // connection error
12573 cm_msg(MERROR, "rpc_client_check",
12574 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12575 c->client_name.c_str(),
12576 c->host_name.c_str(),
12577 errno, strerror(errno));
12578 ok = 0;
12579 }
12580 } else if (status == 0) {
12581 // connection closed by remote end without sending an EXIT message
12582 // this can happen if the remote end has crashed, so this message
12583 // is still necessary as a useful diagnostic for unexpected crashes
12584 // of midas programs. K.O.
12585 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());
12586 ok = 0;
12587 } else {
12588 // read some data
12589 ok = 1;
12590 if (equal_ustring(buffer, "EXIT")) {
12591 /* normal exit */
12592 ok = 0;
12593 }
12594 }
12595
12596 if (!ok) {
12597 //printf("rpc_client_check: closing connection %d: ", i);
12598 //c->print();
12599 //printf("\n");
12600
12601 // connection lost, close the socket
12602 c->close_locked();
12603 }
12604
12605 // implicit unlock
12606 }
12607 }
12608
12609 // implicit unlock of _client_connections_mutex
12610}
12611
12612
12613/********************************************************************/
12614INT rpc_server_connect(const char *host_name, const char *exp_name)
12615/********************************************************************\
12616
12617 Routine: rpc_server_connect
12618
12619 Purpose: Extablish a network connection to a remote MIDAS
12620 server using a callback scheme.
12621
12622 Input:
12623 char *host_name IP address of host to connect to.
12624
12625 INT port TPC port to connect to.
12626
12627 char *exp_name Name of experiment to connect to. By using
12628 this name, several experiments (e.g. online
12629 DAQ and offline analysis) can run simultan-
12630 eously on the same host.
12631
12632 Output:
12633 none
12634
12635 Function value:
12636 RPC_SUCCESS Successful completion
12637 RPC_NET_ERROR Error in socket call
12638 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12639 CM_UNDEF_EXP Undefined experiment on server
12640
12641\********************************************************************/
12642{
12643 INT i, status;
12644 INT remote_hw_type, hw_type;
12645 char str[200], version[32], v1[32];
12646 fd_set readfds;
12647 struct timeval timeout;
12648 int port = MIDAS_TCP_PORT;
12649 char *s;
12650
12651#ifdef OS_WINNT
12652 {
12653 WSADATA WSAData;
12654
12655 /* Start windows sockets */
12656 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12657 return RPC_NET_ERROR;
12658 }
12659#endif
12660
12661 /* check if local connection */
12662 if (host_name[0] == 0)
12663 return RPC_SUCCESS;
12664
12665 /* register system functions */
12667
12668 /* check if cm_connect_experiment was called */
12669 if (_client_name.length() == 0) {
12670 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12671 return RPC_NOT_REGISTERED;
12672 }
12673
12674 /* check if connection already exists */
12676 return RPC_SUCCESS;
12677
12681
12682 bool listen_localhost = false;
12683
12684 if (strcmp(host_name, "localhost") == 0)
12685 listen_localhost = true;
12686
12687 int lsock1, lport1;
12688 int lsock2, lport2;
12689 int lsock3, lport3;
12690
12691 std::string errmsg;
12692
12693 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
12694
12695 if (status != SS_SUCCESS) {
12696 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12697 return RPC_NET_ERROR;
12698 }
12699
12700 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
12701
12702 if (status != SS_SUCCESS) {
12703 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12704 return RPC_NET_ERROR;
12705 }
12706
12707 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
12708
12709 if (status != SS_SUCCESS) {
12710 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12711 return RPC_NET_ERROR;
12712 }
12713
12714 /* extract port number from host_name */
12715 mstrlcpy(str, host_name, sizeof(str));
12716 s = strchr(str, ':');
12717 if (s) {
12718 *s = 0;
12719 port = strtoul(s + 1, NULL, 0);
12720 }
12721
12722 int sock;
12723
12724 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12725
12726 if (status != SS_SUCCESS) {
12727 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12728 return RPC_NET_ERROR;
12729 }
12730
12731 /* connect to experiment */
12732 if (exp_name[0] == 0)
12733 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12734 else
12735 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12736
12737 send(sock, str, strlen(str) + 1, 0);
12738 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12739 ss_socket_close(&sock);
12740 if (i <= 0) {
12741 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12742 return RPC_NET_ERROR;
12743 }
12744
12745 status = version[0] = 0;
12746 sscanf(str, "%d %s", &status, version);
12747
12748 if (status == 2) {
12749/* message "undefined experiment" should be displayed by application */
12750 return CM_UNDEF_EXP;
12751 }
12752
12753 /* print warning if version patch level doesn't agree */
12754 strcpy(v1, version);
12755 if (strchr(v1, '.'))
12756 if (strchr(strchr(v1, '.') + 1, '.'))
12757 *strchr(strchr(v1, '.') + 1, '.') = 0;
12758
12759 strcpy(str, cm_get_version());
12760 if (strchr(str, '.'))
12761 if (strchr(strchr(str, '.') + 1, '.'))
12762 *strchr(strchr(str, '.') + 1, '.') = 0;
12763
12764 if (strcmp(v1, str) != 0) {
12765 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12766 cm_get_version());
12767 }
12768
12769 /* wait for callback on send and recv socket with timeout */
12770 FD_ZERO(&readfds);
12771 FD_SET(lsock1, &readfds);
12772 FD_SET(lsock2, &readfds);
12773 FD_SET(lsock3, &readfds);
12774
12775 timeout.tv_sec = _rpc_connect_timeout / 1000;
12776 timeout.tv_usec = 0;
12777
12778 do {
12779 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12780
12781 /* if an alarm signal was cought, restart select with reduced timeout */
12782 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12783 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12784
12785 } while (status == -1); /* dont return if an alarm signal was cought */
12786
12787 if (!FD_ISSET(lsock1, &readfds)) {
12788 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12789 ss_socket_close(&lsock1);
12790 ss_socket_close(&lsock2);
12791 ss_socket_close(&lsock3);
12792 return RPC_NET_ERROR;
12793 }
12794
12795 _server_connection.send_sock = accept(lsock1, NULL, NULL);
12796 _server_connection.recv_sock = accept(lsock2, NULL, NULL);
12797 _server_connection.event_sock = accept(lsock3, NULL, NULL);
12798
12800 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12801 return RPC_NET_ERROR;
12802 }
12803
12804 ss_socket_close(&lsock1);
12805 ss_socket_close(&lsock2);
12806 ss_socket_close(&lsock3);
12807
12808 /* set TCP_NODELAY option for better performance */
12809 int flag = 1;
12810 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12811 setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12812
12813 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12814 flag = 2 * 1024 * 1024;
12815 status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
12816 if (status != 0)
12817 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12818
12819 /* send local computer info */
12820 std::string local_prog_name = rpc_get_name();
12821 hw_type = rpc_get_hw_type();
12822 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12823
12824 send(_server_connection.send_sock, str, strlen(str) + 1, 0);
12825
12826 /* receive remote computer info */
12828 if (i <= 0) {
12829 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12830 return RPC_NET_ERROR;
12831 }
12832
12833 sscanf(str, "%d", &remote_hw_type);
12834 _server_connection.remote_hw_type = remote_hw_type;
12835
12837
12838 _rpc_is_remote = true;
12839
12840 return RPC_SUCCESS;
12841}
12842
12843/********************************************************************/
12844
12846{
12848 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12850 if (c && c->connected) {
12852 c->mutex.lock();
12853 if (!c->connected) {
12854 // disconnected while we were waiting for the lock
12855 c->mutex.unlock();
12856 return NULL;
12857 }
12858 return c;
12859 }
12860 }
12862 return NULL;
12863}
12864
12866{
12867 /* close all open connections */
12868
12870
12871 for (unsigned i = 0; i < _client_connections.size(); i++) {
12873 if (c && c->connected) {
12874 int index = c->index;
12875 // must unlock the array, otherwise we hang -
12876 // rpc_client_disconnect() will do rpc_call_client()
12877 // which needs to lock the array to convert handle
12878 // to connection pointer. Ouch! K.O. Dec 2020.
12882 }
12883 }
12884
12885 for (unsigned i = 0; i < _client_connections.size(); i++) {
12887 //printf("client connection %d %p\n", i, c);
12888 if (c) {
12889 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12890 if (!c->connected) {
12891 delete c;
12892 _client_connections[i] = NULL;
12893 }
12894 }
12895 }
12896
12898
12899 /* close server connection from other clients */
12900 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12901 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12902 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12903 _server_acceptions[i]->close();
12904 }
12905 }
12906}
12907
12908/********************************************************************/
12910/********************************************************************\
12911
12912 Routine: rpc_client_disconnect
12913
12914 Purpose: Close a rpc connection to a MIDAS client
12915
12916 Input:
12917 HNDLE hConn Handle of connection
12918 BOOL bShutdown Shut down remote server if TRUE
12919
12920 Output:
12921 none
12922
12923 Function value:
12924 RPC_SUCCESS Successful completion
12925
12926\********************************************************************/
12927{
12928 /* notify server about exit */
12929
12930 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12931
12933
12934 return RPC_SUCCESS;
12935}
12936
12937/********************************************************************/
12939/********************************************************************\
12940
12941 Routine: rpc_server_disconnect
12942
12943 Purpose: Close a rpc connection to a MIDAS server and close all
12944 server connections from other clients
12945
12946 Input:
12947 none
12948
12949 Output:
12950 none
12951
12952 Function value:
12953 RPC_SUCCESS Successful completion
12954 RPC_NET_ERROR Error in socket call
12955 RPC_NO_CONNECTION Maximum number of connections reached
12956
12957\********************************************************************/
12958{
12959 static int rpc_server_disconnect_recursion_level = 0;
12960
12961 if (rpc_server_disconnect_recursion_level)
12962 return RPC_SUCCESS;
12963
12964 rpc_server_disconnect_recursion_level = 1;
12965
12966 /* flush remaining events */
12968
12969 /* notify server about exit */
12970 if (rpc_is_connected()) {
12972 }
12973
12974 /* close sockets */
12981
12983
12984 /* remove semaphore */
12985 if (_mutex_rpc)
12987 _mutex_rpc = NULL;
12988
12989 rpc_server_disconnect_recursion_level = 0;
12990 return RPC_SUCCESS;
12991}
12992
12993/********************************************************************/
12995/********************************************************************\
12996
12997 Routine: rpc_is_remote
12998
12999 Purpose: Return true if program is connected to a remote server
13000
13001 Input:
13002 none
13003
13004 Output:
13005 none
13006
13007 Function value:
13008 INT true is remote client connected to mserver, false if local connection
13009
13010\********************************************************************/
13011{
13012 return _rpc_is_remote;
13013}
13014
13015/********************************************************************/
13017/********************************************************************\
13018
13019 Routine: rpc_is_connected
13020
13021 Purpose: Return true if connection to mserver is still open
13022
13023 Input:
13024 none
13025
13026 Output:
13027 none
13028
13029 Function value:
13030 INT true if connection to mserver is still open, false if connection to mserver is already closed
13031
13032\********************************************************************/
13033{
13034 return _server_connection.send_sock != 0;
13035}
13036
13037/********************************************************************/
13039/********************************************************************\
13040
13041 Routine: rpc_get_mserver_hostname
13042
13043 Purpose: Return the hostname of the mserver connection (host:port format)
13044
13045\********************************************************************/
13046{
13048}
13049
13050/********************************************************************/
13052/********************************************************************\
13053
13054 Routine: rpc_is_mserver
13055
13056 Purpose: Return true if we are the mserver
13057
13058 Function value:
13059 INT "true" if we are the mserver
13060
13061\********************************************************************/
13062{
13063 return _mserver_acception != NULL;
13064}
13065
13066/********************************************************************/
13068/********************************************************************\
13069
13070 Routine: rpc_get_hw_type
13071
13072 Purpose: get hardware information
13073
13074 Function value:
13075 INT combination of DRI_xxx bits
13076
13077\********************************************************************/
13078{
13079 {
13080 {
13081 INT tmp_type, size;
13082 DWORD dummy;
13083 unsigned char *p;
13084 float f;
13085 double d;
13086
13087 tmp_type = 0;
13088
13089 /* test pointer size */
13090 size = sizeof(p);
13091 if (size == 2)
13092 tmp_type |= DRI_16;
13093 if (size == 4)
13094 tmp_type |= DRI_32;
13095 if (size == 8)
13096 tmp_type |= DRI_64;
13097
13098 /* test if little or big endian machine */
13099 dummy = 0x12345678;
13100 p = (unsigned char *) &dummy;
13101 if (*p == 0x78)
13102 tmp_type |= DRI_LITTLE_ENDIAN;
13103 else if (*p == 0x12)
13104 tmp_type |= DRI_BIG_ENDIAN;
13105 else
13106 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
13107
13108 /* floating point format */
13109 f = (float) 1.2345;
13110 dummy = 0;
13111 memcpy(&dummy, &f, sizeof(f));
13112 if ((dummy & 0xFF) == 0x19 &&
13113 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
13114 && ((dummy >> 24) & 0xFF) == 0x3F)
13115 tmp_type |= DRF_IEEE;
13116 else if ((dummy & 0xFF) == 0x9E &&
13117 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
13118 && ((dummy >> 24) & 0xFF) == 0x04)
13119 tmp_type |= DRF_G_FLOAT;
13120 else
13121 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13122
13123 d = (double) 1.2345;
13124 dummy = 0;
13125 memcpy(&dummy, &d, sizeof(f));
13126 if ((dummy & 0xFF) == 0x8D && /* little endian */
13127 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
13128 && ((dummy >> 24) & 0xFF) == 0x12)
13129 tmp_type |= DRF_IEEE;
13130 else if ((dummy & 0xFF) == 0x83 && /* big endian */
13131 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
13132 && ((dummy >> 24) & 0xFF) == 0x3F)
13133 tmp_type |= DRF_IEEE;
13134 else if ((dummy & 0xFF) == 0x13 &&
13135 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
13136 && ((dummy >> 24) & 0xFF) == 0xC0)
13137 tmp_type |= DRF_G_FLOAT;
13138 else if ((dummy & 0xFF) == 0x9E &&
13139 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
13140 && ((dummy >> 24) & 0xFF) == 0x04)
13141 cm_msg(MERROR, "rpc_get_option",
13142 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
13143 else
13144 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13145
13146 return tmp_type;
13147 }
13148 }
13149}
13150
13152#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13153
13154/********************************************************************/
13162#if 0
13163INT rpc_set_option(HNDLE hConn, INT item, INT value) {
13164 switch (item) {
13165 case RPC_OTIMEOUT:
13166 if (hConn == -1)
13168 else if (hConn == -2)
13170 else {
13172 if (c) {
13173 c->rpc_timeout = value;
13174 c->mutex.unlock();
13175 }
13176 }
13177 break;
13178
13179 case RPC_NODELAY:
13180 if (hConn == -1) {
13181 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13182 } else {
13184 if (c) {
13185 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13186 c->mutex.unlock();
13187 }
13188 }
13189 break;
13190
13191 default:
13192 cm_msg(MERROR, "rpc_set_option", "invalid argument");
13193 break;
13194 }
13195
13196 return 0;
13197}
13198#endif
13199
13200/********************************************************************/
13207{
13208 if (hConn == RPC_HNDLE_MSERVER) {
13210 } else if (hConn == RPC_HNDLE_CONNECT) {
13211 return _rpc_connect_timeout;
13212 } else {
13214 if (c) {
13215 int timeout = c->rpc_timeout;
13216 c->mutex.unlock();
13217 return timeout;
13218 }
13219 }
13220 return 0;
13221}
13222
13223/********************************************************************/
13231INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
13232{
13233 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13234
13235 if (hConn == RPC_HNDLE_MSERVER) {
13236 if (old_timeout_msec)
13237 *old_timeout_msec = _server_connection.rpc_timeout;
13238 _server_connection.rpc_timeout = timeout_msec;
13239 } else if (hConn == RPC_HNDLE_CONNECT) {
13240 if (old_timeout_msec)
13241 *old_timeout_msec = _rpc_connect_timeout;
13242 _rpc_connect_timeout = timeout_msec;
13243 } else {
13245 if (c) {
13246 if (old_timeout_msec)
13247 *old_timeout_msec = c->rpc_timeout;
13248 c->rpc_timeout = timeout_msec;
13249 c->mutex.unlock();
13250 } else {
13251 if (old_timeout_msec)
13252 *old_timeout_msec = 0;
13253 }
13254 }
13255 return RPC_SUCCESS;
13256}
13257
13258
13260#ifndef DOXYGEN_SHOULD_SKIP_THIS
13261
13262/********************************************************************/
13264/********************************************************************\
13265
13266 Routine: rpc_get_convert_flags
13267
13268 Purpose: Get RPC convert_flags for the mserver connection
13269
13270 Function value:
13271 INT Actual option
13272
13273\********************************************************************/
13274{
13277 else
13278 return 0;
13279}
13280
13281static std::string _mserver_path;
13282
13283/********************************************************************/
13285/********************************************************************\
13286
13287 Routine: rpc_get_mserver_path()
13288
13289 Purpose: Get path of the mserver executable
13290
13291\********************************************************************/
13292{
13293 return _mserver_path.c_str();
13294}
13295
13296/********************************************************************/
13297INT rpc_set_mserver_path(const char *path)
13298/********************************************************************\
13299
13300 Routine: rpc_set_mserver_path
13301
13302 Purpose: Remember the path of the mserver executable
13303
13304 Input:
13305 char *path Full path of the mserver executable
13306
13307 Function value:
13308 RPC_SUCCESS Successful completion
13309
13310\********************************************************************/
13311{
13312 _mserver_path = path;
13313 return RPC_SUCCESS;
13314}
13315
13316/********************************************************************/
13317std::string rpc_get_name()
13318/********************************************************************\
13319
13320 Routine: rpc_get_name
13321
13322 Purpose: Get name set by rpc_set_name
13323
13324 Input:
13325 none
13326
13327 Output:
13328 char* name The location pointed by *name receives a
13329 copy of the _prog_name
13330
13331 Function value:
13332 RPC_SUCCESS Successful completion
13333
13334\********************************************************************/
13335{
13336 return _client_name;
13337}
13338
13339
13340/********************************************************************/
13342/********************************************************************\
13343
13344 Routine: rpc_set_name
13345
13346 Purpose: Set name of actual program for further rpc connections
13347
13348 Input:
13349 char *name Program name, up to NAME_LENGTH chars,
13350 no blanks
13351
13352 Output:
13353 none
13354
13355 Function value:
13356 RPC_SUCCESS Successful completion
13357
13358\********************************************************************/
13359{
13361
13362 return RPC_SUCCESS;
13363}
13364
13365
13366/********************************************************************/
13367INT rpc_set_debug(void (*func)(const char *), INT mode)
13368/********************************************************************\
13369
13370 Routine: rpc_set_debug
13371
13372 Purpose: Set a function which is called on every RPC call to
13373 display the function name and parameters of the RPC
13374 call.
13375
13376 Input:
13377 void *func(char*) Pointer to function.
13378 INT mode Debug mode
13379
13380 Output:
13381 none
13382
13383 Function value:
13384 RPC_SUCCESS Successful completion
13385
13386\********************************************************************/
13387{
13388 _debug_print = func;
13389 _debug_mode = mode;
13390 return RPC_SUCCESS;
13391}
13392
13393/********************************************************************/
13394void rpc_debug_printf(const char *format, ...)
13395/********************************************************************\
13396
13397 Routine: rpc_debug_print
13398
13399 Purpose: Calls function set via rpc_set_debug to output a string.
13400
13401 Input:
13402 char *str Debug string
13403
13404 Output:
13405 none
13406
13407\********************************************************************/
13408{
13409 va_list argptr;
13410 char str[1000];
13411
13412 if (_debug_mode) {
13413 va_start(argptr, format);
13414 vsprintf(str, (char *) format, argptr);
13415 va_end(argptr);
13416
13417 if (_debug_print) {
13418 strcat(str, "\n");
13420 } else
13421 puts(str);
13422 }
13423}
13424
13425/********************************************************************/
13426void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
13427 switch (arg_type) {
13428 /* On the stack, the minimum parameter size is sizeof(int).
13429 To avoid problems on little endian systems, treat all
13430 smaller parameters as int's */
13431 case TID_UINT8:
13432 case TID_INT8:
13433 case TID_CHAR:
13434 case TID_UINT16:
13435 case TID_INT16:
13436 *((int *) arg) = va_arg(*arg_ptr, int);
13437 break;
13438
13439 case TID_INT32:
13440 case TID_BOOL:
13441 *((INT *) arg) = va_arg(*arg_ptr, INT);
13442 break;
13443
13444 case TID_UINT32:
13445 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13446 break;
13447
13448 /* float variables are passed as double by the compiler */
13449 case TID_FLOAT:
13450 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13451 break;
13452
13453 case TID_DOUBLE:
13454 *((double *) arg) = va_arg(*arg_ptr, double);
13455 break;
13456
13457 case TID_ARRAY:
13458 *((char **) arg) = va_arg(*arg_ptr, char *);
13459 break;
13460 }
13461}
13462
13463/********************************************************************/
13464static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13465{
13466 //printf("rpc_call_encode!\n");
13467
13468 bool debug = false;
13469
13470 if (debug) {
13471 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13472 for (int i=0; rl.param[i].tid != 0; i++) {
13473 int tid = rl.param[i].tid;
13474 int flags = rl.param[i].flags;
13475 int n = rl.param[i].n;
13476 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13477 }
13478 }
13479
13480 char args[MAX_RPC_PARAMS][8];
13481
13482 for (int i=0; rl.param[i].tid != 0; i++) {
13483 int tid = rl.param[i].tid;
13484 int flags = rl.param[i].flags;
13485 int arg_type = 0;
13486
13487 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13488 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13489 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13490
13491 if (bpointer)
13492 arg_type = TID_ARRAY;
13493 else
13494 arg_type = tid;
13495
13496 /* floats are passed as doubles, at least under NT */
13497 if (tid == TID_FLOAT && !bpointer)
13498 arg_type = TID_DOUBLE;
13499
13500 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13501
13502 rpc_va_arg(&ap, arg_type, args[i]);
13503 }
13504
13505 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13506 char* buf = (char *)malloc(buf_size);
13507 assert(buf);
13508
13509 (*nc) = (NET_COMMAND*) buf;
13510
13511 /* find out if we are on a big endian system */
13512 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13513
13514 char* param_ptr = (*nc)->param;
13515
13516 for (int i=0; rl.param[i].tid != 0; i++) {
13517 int tid = rl.param[i].tid;
13518 int flags = rl.param[i].flags;
13519 int arg_type = 0;
13520
13521 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13522 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13523 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13524
13525 if (bpointer)
13526 arg_type = TID_ARRAY;
13527 else
13528 arg_type = tid;
13529
13530 /* floats are passed as doubles, at least under NT */
13531 if (tid == TID_FLOAT && !bpointer)
13532 arg_type = TID_DOUBLE;
13533
13534 /* get pointer to argument */
13535 //char arg[8];
13536 //rpc_va_arg(&ap, arg_type, arg);
13537
13538 char* arg = args[i];
13539
13540 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13541 if (bbig) {
13542 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13543 arg[0] = arg[3];
13544 }
13545 if (tid == TID_UINT16 || tid == TID_INT16) {
13546 arg[0] = arg[2];
13547 arg[1] = arg[3];
13548 }
13549 }
13550
13551 if (flags & RPC_IN) {
13552 int arg_size = 0;
13553
13554 if (bpointer)
13555 arg_size = rpc_tid_size(tid);
13556 else
13557 arg_size = rpc_tid_size(arg_type);
13558
13559 /* for strings, the argument size depends on the string length */
13560 if (tid == TID_STRING || tid == TID_LINK) {
13561 arg_size = 1 + strlen((char *) *((char **) arg));
13562 }
13563
13564 /* for varibale length arrays, the size is given by
13565 the next parameter on the stack */
13566 if (flags & RPC_VARARRAY) {
13567 //va_list aptmp;
13569 //va_copy(aptmp, ap);
13570
13571 //char arg_tmp[8];
13572 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13573
13574 const char* arg_tmp = args[i+1];
13575
13576 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13577 if (flags & RPC_OUT)
13578 arg_size = *((INT *) *((void **) arg_tmp));
13579 else
13580 arg_size = *((INT *) arg_tmp);
13581
13582 *((INT *) param_ptr) = ALIGN8(arg_size);
13583 param_ptr += ALIGN8(sizeof(INT));
13584
13585 //va_end(aptmp);
13586 }
13587
13588 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13589 arg_size = rl.param[i].n;
13590
13591 /* always align parameter size */
13592 int param_size = ALIGN8(arg_size);
13593
13594 {
13595 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13596
13597 if (param_offset + param_size + 16 > buf_size) {
13598 size_t new_size = param_offset + param_size + 1024;
13599 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13600 buf = (char *) realloc(buf, new_size);
13601 assert(buf);
13602 buf_size = new_size;
13603 (*nc) = (NET_COMMAND*) buf;
13604 param_ptr = buf + param_offset;
13605 }
13606 }
13607
13608 if (bpointer) {
13609 if (debug) {
13610 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);
13611 }
13612 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13613 } else if (tid == TID_FLOAT) {
13614 if (debug) {
13615 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);
13616 }
13617 /* floats are passed as doubles on most systems */
13618 *((float *) param_ptr) = (float) *((double *) arg);
13619 } else {
13620 if (debug) {
13621 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);
13622 }
13623 memcpy(param_ptr, arg, arg_size);
13624 }
13625
13626 param_ptr += param_size;
13627 }
13628 }
13629
13630 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13631
13632 if (debug)
13633 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13634}
13635
13636/********************************************************************/
13637static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13638{
13639 //printf("rpc_call_decode!\n");
13640
13641 bool debug = false;
13642
13643 if (debug)
13644 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13645
13646 /* extract result variables and place it to argument list */
13647
13648 const char* param_ptr = buf;
13649
13650 for (int i = 0; rl.param[i].tid != 0; i++) {
13651 int tid = rl.param[i].tid;
13652 int flags = rl.param[i].flags;
13653 int arg_type = 0;
13654
13655 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13656 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13657 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13658
13659 if (bpointer)
13660 arg_type = TID_ARRAY;
13661 else
13662 arg_type = rl.param[i].tid;
13663
13664 if (tid == TID_FLOAT && !bpointer)
13665 arg_type = TID_DOUBLE;
13666
13667 char arg[8];
13668 rpc_va_arg(&ap, arg_type, arg);
13669
13670 if (rl.param[i].flags & RPC_OUT) {
13671
13672 if (param_ptr == NULL) {
13673 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);
13674 return RPC_NET_ERROR;
13675 }
13676
13677 tid = rl.param[i].tid;
13678 int arg_size = rpc_tid_size(tid);
13679
13680 if (tid == TID_STRING || tid == TID_LINK)
13681 arg_size = strlen((char *) (param_ptr)) + 1;
13682
13683 if (flags & RPC_VARARRAY) {
13684 arg_size = *((INT *) param_ptr);
13685 param_ptr += ALIGN8(sizeof(INT));
13686 }
13687
13688 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13689 arg_size = rl.param[i].n;
13690
13691 /* parameter size is always aligned */
13692 int param_size = ALIGN8(arg_size);
13693
13694 /* return parameters are always pointers */
13695 if (*((char **) arg)) {
13696 if (debug)
13697 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);
13698 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13699 }
13700
13701 param_ptr += param_size;
13702 }
13703 }
13704
13705 return RPC_SUCCESS;
13706}
13707
13708/********************************************************************/
13709static void rpc_call_encode_cxx(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13710{
13711 //printf("rpc_call_encode_cxx!\n");
13712
13713 bool debug = false;
13714
13715 //if (rl.id == RPC_TEST2)
13716 // debug = true;
13717
13718 //if (rl.id == RPC_TEST2_CXX)
13719 // debug = true;
13720
13721 if (debug) {
13722 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13723 for (int i=0; rl.param[i].tid != 0; i++) {
13724 int tid = rl.param[i].tid;
13725 int flags = rl.param[i].flags;
13726 int n = rl.param[i].n;
13727 printf("param %2d, tid %2d, flags 0x%02x, n %3d\n", i, tid, flags, n);
13728 }
13729 }
13730
13731 char args[MAX_RPC_PARAMS][sizeof(char*)];
13732
13733 for (int i=0; rl.param[i].tid != 0; i++) {
13734 int tid = rl.param[i].tid;
13735 int flags = rl.param[i].flags;
13736 int arg_type = 0;
13737
13738 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13739 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13740 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13741
13742 if (bpointer)
13743 arg_type = TID_ARRAY;
13744 else
13745 arg_type = tid;
13746
13747 /* floats are passed as doubles, at least under NT */
13748 if (tid == TID_FLOAT && !bpointer)
13749 arg_type = TID_DOUBLE;
13750
13751 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13752
13753 rpc_va_arg(&ap, arg_type, args[i]);
13754 }
13755
13756 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13757 char* buf = (char *)malloc(buf_size);
13758 assert(buf);
13759
13760 (*nc) = (NET_COMMAND*) buf;
13761
13762 /* find out if we are on a big endian system */
13763 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13764
13765 char* param_ptr = (*nc)->param;
13766
13767 for (int i=0; rl.param[i].tid != 0; i++) {
13768 int tid = rl.param[i].tid;
13769 int flags = rl.param[i].flags;
13770 int arg_type = 0;
13771
13772 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13773 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13774 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13775
13776 if (bpointer)
13777 arg_type = TID_ARRAY;
13778 else
13779 arg_type = tid;
13780
13781 /* floats are passed as doubles, at least under NT */
13782 if (tid == TID_FLOAT && !bpointer)
13783 arg_type = TID_DOUBLE;
13784
13785 /* get pointer to argument */
13786 //char arg[8];
13787 //rpc_va_arg(&ap, arg_type, arg);
13788
13789 char* arg = args[i];
13790
13791 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13792 if (bbig) {
13793 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13794 arg[0] = arg[3];
13795 }
13796 if (tid == TID_UINT16 || tid == TID_INT16) {
13797 arg[0] = arg[2];
13798 arg[1] = arg[3];
13799 }
13800 }
13801
13802 if (flags & RPC_IN) {
13803 int arg_size = 0;
13804
13805 if (bpointer)
13806 arg_size = rpc_tid_size(tid);
13807 else
13808 arg_size = rpc_tid_size(arg_type);
13809
13810 void* parg = (void *) *((void **) arg);
13811
13812 /* for strings, the argument size depends on the string length */
13813 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13814 std::string* s = (std::string*)parg;
13815 arg_size = 1 + s->length();
13816 parg = (void*)s->c_str();
13817 bpointer = TRUE;
13818 //printf("STRING %p, len %zu, [%s]\n", s, s->length(), s->c_str());
13819 } else if (tid == TID_STRING || tid == TID_LINK) {
13820 arg_size = 1 + strlen((char *) *((char **) arg));
13821 }
13822
13823 /* for varibale length arrays, the size is given by
13824 the next parameter on the stack */
13825 if (flags & RPC_VARARRAY) {
13826 //va_list aptmp;
13828 //va_copy(aptmp, ap);
13829
13830 //char arg_tmp[8];
13831 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13832
13833 if (flags & RPC_CXX) {
13834 std::vector<char>* pv = (std::vector<char>*)parg;
13835 arg_size = pv->size();
13836 parg = (void*)pv->data();
13837 bpointer = TRUE;
13838 //printf("VECTOR %p, size %zu\n", pv, pv->size());
13839
13840 *((INT *) param_ptr) = arg_size; // NB: for std:vector<char> data, pass true data size. it is safe because decoder always aligns it themselves.
13841
13842 } else {
13843 const char* arg_tmp = args[i+1];
13844
13845 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13846 if (flags & RPC_OUT) {
13847 arg_size = *((INT *) *((void **) arg_tmp));
13848 } else {
13849 arg_size = *((INT *) arg_tmp);
13850 }
13851
13852 *((INT *) param_ptr) = ALIGN8(arg_size);
13853 }
13854
13855 param_ptr += ALIGN8(sizeof(INT));
13856
13857 //va_end(aptmp);
13858 }
13859
13860 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13861 arg_size = rl.param[i].n;
13862
13863 /* always align parameter size */
13864 int param_size = ALIGN8(arg_size);
13865
13866 {
13867 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13868
13869 if (param_offset + param_size + 16 > buf_size) {
13870 size_t new_size = param_offset + param_size + 1024;
13871 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13872 buf = (char *) realloc(buf, new_size);
13873 assert(buf);
13874 buf_size = new_size;
13875 (*nc) = (NET_COMMAND*) buf;
13876 param_ptr = buf + param_offset;
13877 }
13878 }
13879
13880 if (bpointer) {
13881 if (debug) {
13882 printf("encode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, memcpy pointer %3d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
13883 }
13884 memcpy(param_ptr, parg, arg_size);
13885 } else if (tid == TID_FLOAT) {
13886 if (debug) {
13887 printf("encode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, double->float\n", i, flags, tid, arg_type, arg_size, param_size);
13888 }
13889 /* floats are passed as doubles on most systems */
13890 *((float *) param_ptr) = (float) *((double *) arg);
13891 } else {
13892 if (debug) {
13893 printf("encode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, memcpy %3d\n", i, flags, tid, arg_type, arg_size, param_size, arg_size);
13894 }
13895 memcpy(param_ptr, arg, arg_size);
13896 }
13897
13898 param_ptr += param_size;
13899 }
13900 }
13901
13902 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13903
13904 if (debug)
13905 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13906}
13907
13908/********************************************************************/
13909static int rpc_call_decode_cxx(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13910{
13911 //printf("rpc_call_decode_cxx!\n");
13912
13913 bool debug = false;
13914
13915 //if (rl.id == RPC_TEST2)
13916 // debug = true;
13917
13918 //if (rl.id == RPC_TEST2_CXX)
13919 // debug = true;
13920
13921 if (debug)
13922 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13923
13924 /* extract result variables and place it to argument list */
13925
13926 const char* param_ptr = buf;
13927
13928 for (int i = 0; rl.param[i].tid != 0; i++) {
13929 const int tid = rl.param[i].tid;
13930 const int flags = rl.param[i].flags;
13931 int arg_type = 0;
13932
13933 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13934 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13935 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13936
13937 if (bpointer)
13938 arg_type = TID_ARRAY;
13939 else
13940 arg_type = tid;
13941
13942 if (tid == TID_FLOAT && !bpointer)
13943 arg_type = TID_DOUBLE;
13944
13945 char arg[sizeof(double)+sizeof(uint64_t)+sizeof(char*)];
13946 rpc_va_arg(&ap, arg_type, arg);
13947
13948 if (flags & RPC_OUT) {
13949
13950 if (param_ptr == NULL) {
13951 cm_msg(MERROR, "rpc_call_decode_cxx", "routine \"%s\": no data in RPC reply, needed to decode an RPC_OUT parameter. param_ptr is NULL", rl.name);
13952 return RPC_NET_ERROR;
13953 }
13954
13955 int arg_size = rpc_tid_size(tid);
13956
13957 if (tid == TID_STRING || tid == TID_LINK) {
13958 arg_size = strlen((char *) (param_ptr)) + 1;
13959
13960 if (debug)
13961 printf("decode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, string [%s]\n", i, flags, tid, arg_type, arg_size, (char *) (param_ptr));
13962 }
13963
13964 if (flags & RPC_VARARRAY) {
13965 arg_size = *((INT *) param_ptr);
13966 param_ptr += ALIGN8(sizeof(INT));
13967 }
13968
13969 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13970 arg_size = rl.param[i].n;
13971
13972 /* parameter size is always aligned */
13973 int param_size = ALIGN8(arg_size);
13974
13975 /* return parameters are always pointers */
13976 void* parg = *(char**) arg;
13977 if (parg) {
13978 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13979 if (debug)
13980 printf("decode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, assign %3d to std::string at %p, offset %zu, string [%s]\n", i, flags, tid, arg_type, arg_size, param_size, arg_size, parg, param_ptr - buf, param_ptr);
13981 *(std::string*)parg = param_ptr;
13982 } else if ((tid == TID_ARRAY) && (flags & RPC_CXX)) {
13983 if (debug)
13984 printf("decode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, assign %3d to std::vector at %p, offset %zu\n", i, flags, tid, arg_type, arg_size, param_size, arg_size, parg, param_ptr - buf);
13985 std::vector<char>* pvec = (std::vector<char>*)parg;
13986 pvec->clear();
13987 pvec->insert(pvec->end(), param_ptr, param_ptr + arg_size);
13988 } else {
13989 if (debug)
13990 printf("decode param %2d, flags 0x%02x, tid %2d, arg_type %2d, arg_size %3d, param_size %3d, memcpy %3d to %p, offset %zu\n", i, flags, tid, arg_type, arg_size, param_size, arg_size, parg, param_ptr - buf);
13991 memcpy(parg, param_ptr, arg_size);
13992 }
13993 }
13994
13995 param_ptr += param_size;
13996 }
13997 }
13998
13999 return RPC_SUCCESS;
14000}
14001
14002static int rpc_find_rpc(int routine_id, RPC_LIST* pentry, bool* prpc_cxx)
14003{
14004 rpc_list_mutex.lock();
14005
14006 for (size_t i = 0; i < rpc_list.size(); i++) {
14007 if (rpc_list[i].id == routine_id) {
14008 *pentry = rpc_list[i];
14009
14010 rpc_list_mutex.unlock();
14011
14012 *prpc_cxx = false;
14013
14014 for (int j=0; j<MAX_RPC_PARAMS; j++)
14015 if (pentry->param[j].flags & RPC_CXX)
14016 *prpc_cxx = true;
14017
14018 return RPC_SUCCESS;
14019 }
14020 }
14021
14022 rpc_list_mutex.unlock();
14023
14024 return RPC_INVALID_ID;
14025}
14026
14027/********************************************************************/
14028INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
14029/********************************************************************\
14030
14031 Routine: rpc_client_call
14032
14033 Purpose: Call a function on a MIDAS client
14034
14035 Input:
14036 INT hConn Client connection
14037 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
14038
14039 ... variable argument list
14040
14041 Output:
14042 (depends on argument list)
14043
14044 Function value:
14045 RPC_SUCCESS Successful completion
14046 RPC_NET_ERROR Error in socket call
14047 RPC_NO_CONNECTION No active connection
14048 RPC_TIMEOUT Timeout in RPC call
14049 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
14050 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
14051
14052\********************************************************************/
14053{
14055
14056 if (!c) {
14057 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
14058 return RPC_NO_CONNECTION;
14059 }
14060
14061 //printf("rpc_client_call: handle %d, connection: ", hConn);
14062 //c->print();
14063 //printf("\n");
14064
14065 INT i, status;
14066
14067 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
14068 routine_id &= ~RPC_NO_REPLY;
14069
14070 //if (rpc_no_reply)
14071 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
14072
14073 // make local copy of the client name just in case _client_connection is erased by another thread
14074
14075 /* find rpc_index */
14076
14077 RPC_LIST rpc_entry;
14078 bool rpc_cxx = false;
14079
14080 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
14081
14082 if (status != RPC_SUCCESS) {
14083 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);
14084 c->mutex.unlock();
14085 return RPC_INVALID_ID;
14086 }
14087
14088 const char *rpc_name = rpc_entry.name;
14089
14090 NET_COMMAND *nc = NULL;
14091
14092 /* examine variable argument list and convert it to parameter array */
14093 va_list ap;
14094 va_start(ap, routine_id);
14095
14096 if (rpc_cxx)
14097 rpc_call_encode_cxx(ap, rpc_entry, &nc);
14098 else
14099 rpc_call_encode(ap, rpc_entry, &nc);
14100
14101 va_end(ap);
14102
14103 nc->header.routine_id = routine_id;
14104
14105 if (rpc_no_reply)
14107
14108 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14109
14110 /* in FAST TCP mode, only send call and return immediately */
14111 if (rpc_no_reply) {
14112 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14113
14114 if (i != send_size) {
14115 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);
14116 free(nc);
14117 c->mutex.unlock();
14118 return RPC_NET_ERROR;
14119 }
14120
14121 free(nc);
14122
14123 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
14124 //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);
14125 //c->print();
14126 //printf("\n");
14127 c->close_locked();
14128 }
14129
14130 c->mutex.unlock();
14131 return RPC_SUCCESS;
14132 }
14133
14134 /* in TCP mode, send and wait for reply on send socket */
14135 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14136 if (i != send_size) {
14137 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);
14138 c->mutex.unlock();
14139 return RPC_NET_ERROR;
14140 }
14141
14142 free(nc);
14143 nc = NULL;
14144
14145 bool restore_watchdog_timeout = false;
14146 BOOL watchdog_call;
14147 DWORD watchdog_timeout;
14148 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14149
14150 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
14151
14152 if (c->rpc_timeout >= (int) watchdog_timeout) {
14153 restore_watchdog_timeout = true;
14154 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
14155 }
14156
14157 DWORD rpc_status = 0;
14158 DWORD buf_size = 0;
14159 char* buf = NULL;
14160
14161 /* receive result on send socket */
14162 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
14163
14164 if (restore_watchdog_timeout) {
14165 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
14166 }
14167
14168 if (status == SS_TIMEOUT) {
14169 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);
14170 if (buf)
14171 free(buf);
14172 c->mutex.unlock();
14173 return RPC_TIMEOUT;
14174 }
14175
14176 if (status != SS_SUCCESS) {
14177 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);
14178 if (buf)
14179 free(buf);
14180 c->mutex.unlock();
14181 return RPC_NET_ERROR;
14182 }
14183
14184 c->mutex.unlock();
14185
14186 if (rpc_status == RPC_INVALID_ID) {
14187 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);
14188 if (buf)
14189 free(buf);
14190 return rpc_status;
14191 }
14192
14193 /* extract result variables and place it to argument list */
14194
14195 va_start(ap, routine_id);
14196
14197 if (rpc_cxx)
14198 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14199 else
14200 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14201
14202 if (status != RPC_SUCCESS) {
14203 rpc_status = status;
14204 }
14205
14206 va_end(ap);
14207
14208 if (buf)
14209 free(buf);
14210 buf = NULL;
14211 buf_size = 0;
14212
14213 return rpc_status;
14214}
14215
14216/********************************************************************/
14217INT rpc_call(DWORD routine_id, ...)
14218/********************************************************************\
14219
14220 Routine: rpc_call
14221
14222 Purpose: Call a function on a MIDAS server
14223
14224 Input:
14225 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
14226
14227 ... variable argument list
14228
14229 Output:
14230 (depends on argument list)
14231
14232 Function value:
14233 RPC_SUCCESS Successful completion
14234 RPC_NET_ERROR Error in socket call
14235 RPC_NO_CONNECTION No active connection
14236 RPC_TIMEOUT Timeout in RPC call
14237 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
14238 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
14239
14240\********************************************************************/
14241{
14242 va_list ap;
14243 INT i, status;
14244
14245 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
14246 routine_id &= ~RPC_NO_REPLY;
14247
14248 //if (rpc_no_reply)
14249 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
14250
14251 int send_sock = _server_connection.send_sock;
14252 int rpc_timeout = _server_connection.rpc_timeout;
14253
14254 if (!send_sock) {
14255 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
14256 return RPC_NET_ERROR;
14257 }
14258
14259 if (!_mutex_rpc) {
14260 /* create a local mutex for multi-threaded applications */
14262 }
14263
14264 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
14265 if (status != SS_SUCCESS) {
14266 cm_msg(MERROR, "rpc_call", "Mutex timeout");
14267 return RPC_MUTEX_TIMEOUT;
14268 }
14269
14270 /* find rpc definition */
14271
14272 RPC_LIST rpc_entry;
14273 bool rpc_cxx = false;
14274
14275 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
14276
14277 if (status != RPC_SUCCESS) {
14279 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
14280 return RPC_INVALID_ID;
14281 }
14282
14283 const char* rpc_name = rpc_entry.name;
14284
14285 /* prepare output buffer */
14286
14287 NET_COMMAND* nc = NULL;
14288
14289 /* examine variable argument list and convert it to parameter array */
14290 va_start(ap, routine_id);
14291
14292 if (rpc_cxx)
14293 rpc_call_encode_cxx(ap, rpc_entry, &nc);
14294 else
14295 rpc_call_encode(ap, rpc_entry, &nc);
14296
14297 va_end(ap);
14298
14299 nc->header.routine_id = routine_id;
14300
14301 if (rpc_no_reply)
14303
14304 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14305
14306 /* do not wait for reply if requested RPC_NO_REPLY */
14307 if (rpc_no_reply) {
14308 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14309
14310 if (i != send_size) {
14312 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14313 free(nc);
14314 return RPC_NET_ERROR;
14315 }
14316
14318 free(nc);
14319 return RPC_SUCCESS;
14320 }
14321
14322 /* in TCP mode, send and wait for reply on send socket */
14323 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14324 if (i != send_size) {
14326 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14327 free(nc);
14328 return RPC_NET_ERROR;
14329 }
14330
14331 free(nc);
14332 nc = NULL;
14333
14334 bool restore_watchdog_timeout = false;
14335 BOOL watchdog_call;
14336 DWORD watchdog_timeout;
14337 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14338
14339 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
14340
14341 if (!rpc_is_remote()) {
14342 // if RPC is remote, we are connected to an mserver,
14343 // the mserver takes care of watchdog timeouts.
14344 // otherwise we should make sure the watchdog timeout
14345 // is longer than the RPC timeout. K.O.
14346 if (rpc_timeout >= (int) watchdog_timeout) {
14347 restore_watchdog_timeout = true;
14348 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
14349 }
14350 }
14351
14352 DWORD rpc_status = 0;
14353 DWORD buf_size = 0;
14354 char* buf = NULL;
14355
14356 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
14357
14358 if (restore_watchdog_timeout) {
14359 cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
14360 }
14361
14362 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
14363
14365
14366 /* check for reply errors */
14367
14368 if (status == SS_TIMEOUT) {
14369 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
14370 if (buf)
14371 free(buf);
14372 abort(); // cannot continue - our mserver is not talking to us!
14373 return RPC_TIMEOUT;
14374 }
14375
14376 if (status != SS_SUCCESS) {
14377 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
14378 if (buf)
14379 free(buf);
14380 abort(); // cannot continue - something is wrong with our mserver connection
14381 return RPC_NET_ERROR;
14382 }
14383
14384 if (rpc_status == RPC_INVALID_ID) {
14385 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
14386 if (buf)
14387 free(buf);
14388 return rpc_status;
14389 }
14390
14391 /* extract result variables and place it to argument list */
14392
14393 va_start(ap, routine_id);
14394
14395 if (rpc_cxx)
14396 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14397 else
14398 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14399
14400 if (status != RPC_SUCCESS) {
14401 rpc_status = status;
14402 }
14403
14404 va_end(ap);
14405
14406 if (buf)
14407 free(buf);
14408
14409 return rpc_status;
14410}
14411
14412
14413/********************************************************************/
14415 INT old;
14416
14417 old = _opt_tcp_size;
14418 _opt_tcp_size = tcp_size;
14419 return old;
14420}
14421
14423 return _opt_tcp_size;
14424}
14425
14427#endif /* DOXYGEN_SHOULD_SKIP_THIS */
14428
14429/********************************************************************/
14451INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
14452{
14453 if (rpc_is_remote()) {
14454 return rpc_send_event1(buffer_handle, pevent);
14455 } else {
14456 return bm_send_event(buffer_handle, pevent, unused, async_flag);
14457 }
14458}
14459
14460/********************************************************************/
14469INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
14470{
14471 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
14472 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
14473}
14474
14475INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
14476{
14477 if (sg_n < 1) {
14478 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
14479 return BM_INVALID_SIZE;
14480 }
14481
14482 if (sg_ptr[0] == NULL) {
14483 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
14484 return BM_INVALID_SIZE;
14485 }
14486
14487 if (sg_len[0] < sizeof(EVENT_HEADER)) {
14488 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));
14489 return BM_INVALID_SIZE;
14490 }
14491
14492 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
14493
14494 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
14495 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
14496
14497 if (data_size == 0) {
14498 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
14499 return BM_INVALID_SIZE;
14500 }
14501
14502 if (data_size > MAX_DATA_SIZE) {
14503 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);
14504 return BM_INVALID_SIZE;
14505 }
14506
14507 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
14508 const size_t total_size = ALIGN8(event_size);
14509
14510 size_t count = 0;
14511 for (int i=0; i<sg_n; i++) {
14512 count += sg_len[i];
14513 }
14514
14515 if (count != event_size) {
14516 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);
14517 return BM_INVALID_SIZE;
14518 }
14519
14520 // protect non-atomic access to _server_connection.event_sock. K.O.
14521
14522 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
14523
14524 //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);
14525
14526 if (_server_connection.event_sock == 0) {
14527 return RPC_NO_CONNECTION;
14528 }
14529
14530 //
14531 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14532 //
14533 // 4 bytes of buffer handle
14534 // 16 bytes of event header, includes data_size
14535 // ALIGN8(data_size) bytes of event data
14536 //
14537
14538 int status;
14539
14540 /* send buffer handle */
14541
14542 assert(sizeof(DWORD) == 4);
14543 DWORD bh_buf = buffer_handle;
14544
14545 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14546 if (status != SS_SUCCESS) {
14548 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14549 return RPC_NET_ERROR;
14550 }
14551
14552 /* send data */
14553
14554 for (int i=0; i<sg_n; i++) {
14555 status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
14556 if (status != SS_SUCCESS) {
14558 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14559 return RPC_NET_ERROR;
14560 }
14561 }
14562
14563 /* send padding */
14564
14565 if (count < total_size) {
14566 char padding[8] = { 0,0,0,0,0,0,0,0 };
14567 size_t padlen = total_size - count;
14568 assert(padlen < 8);
14570 if (status != SS_SUCCESS) {
14572 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14573 return RPC_NET_ERROR;
14574 }
14575 }
14576
14577 return RPC_SUCCESS;
14578}
14579
14580/********************************************************************/
14589 return RPC_SUCCESS;
14590}
14591
14592/********************************************************************/
14593
14594struct TR_FIFO {
14595 int transition = 0;
14596 int run_number = 0;
14597 time_t trans_time = 0;
14599};
14600
14601static std::mutex _tr_fifo_mutex;
14603static int _tr_fifo_wp = 0;
14604static int _tr_fifo_rp = 0;
14605
14606static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
14607/********************************************************************\
14608
14609 Routine: rpc_transition_dispatch
14610
14611 Purpose: Gets called when a transition function was registered and
14612 a transition occured. Internal use only.
14613
14614 Input:
14615 INT idx RPC function ID
14616 void *prpc_param RPC parameters
14617
14618 Output:
14619 none
14620
14621 Function value:
14622 INT return value from called user routine
14623
14624\********************************************************************/
14625{
14626 /* erase error string */
14627 *(CSTRING(2)) = 0;
14628
14629 if (idx == RPC_RC_TRANSITION) {
14630 // find registered handler
14631 // NB: this code should match same code in cm_transition_call_direct()
14632 // NB: only use the first handler, this is how MIDAS always worked
14633 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14634 _trans_table_mutex.lock();
14635 size_t n = _trans_table.size();
14636 _trans_table_mutex.unlock();
14637
14638 for (size_t i = 0; i < n; i++) {
14639 _trans_table_mutex.lock();
14641 _trans_table_mutex.unlock();
14642
14643 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14644 if (tt.func) {
14645 /* execute callback if defined */
14646 return tt.func(CINT(1), CSTRING(2));
14647 } else {
14648 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14649 /* store transition in FIFO */
14652 _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
14654 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14655 // implicit unlock
14656 return RPC_SUCCESS;
14657 }
14658 }
14659 }
14660 // no handler for this transition
14661 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14662 return CM_SUCCESS;
14663 } else {
14664 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14665 return RPC_INVALID_ID;
14666 }
14667}
14668
14669/********************************************************************/
14670int cm_query_transition(int *transition, int *run_number, int *trans_time)
14671/********************************************************************\
14672
14673 Routine: cm_query_transition
14674
14675 Purpose: Query system if transition has occured. Normally, one
14676 registers callbacks for transitions via
14677 cm_register_transition. In some environments however,
14678 callbacks are not possible. In that case one spciefies
14679 a NULL pointer as the callback routine and can query
14680 transitions "manually" by calling this functions. A small
14681 FIFO takes care that no transition is lost if this functions
14682 did not get called between some transitions.
14683
14684 Output:
14685 INT *transition Type of transition, one of TR_xxx
14686 INT *run_nuber Run number for transition
14687 time_t *trans_time Time (in UNIX time) of transition
14688
14689 Function value:
14690 FALSE No transition occured since last call
14691 TRUE Transition occured
14692
14693\********************************************************************/
14694{
14695 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14696
14697 if (_tr_fifo_wp == _tr_fifo_rp)
14698 return FALSE;
14699
14700 if (transition)
14702
14703 if (run_number)
14705
14706 if (trans_time)
14707 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14708
14709 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14710
14711 // implicit unlock
14712 return TRUE;
14713}
14714
14715/********************************************************************\
14716* server functions *
14717\********************************************************************/
14718
14719#if 0
14720void debug_dump(unsigned char *p, int size)
14721{
14722 int i, j;
14723 unsigned char c;
14724
14725 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14726 printf("%p ", p + i * 16);
14727 for (j = 0; j < 16; j++)
14728 if (i * 16 + j < size)
14729 printf("%02X ", p[i * 16 + j]);
14730 else
14731 printf(" ");
14732 printf(" ");
14733
14734 for (j = 0; j < 16; j++) {
14735 c = p[i * 16 + j];
14736 if (i * 16 + j < size)
14737 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14738 }
14739 printf("\n");
14740 }
14741
14742 printf("\n");
14743}
14744#endif
14745
14746/********************************************************************/
14747static int recv_net_command_realloc(RPC_SERVER_ACCEPTION* sa, char **pbuf, int *pbufsize, INT *remaining)
14748/********************************************************************\
14749
14750 Routine: recv_net_command
14751
14752 Purpose: TCP receive routine with local cache. To speed up network
14753 performance, a 64k buffer is read in at once and split into
14754 several RPC command on successive calls to recv_net_command.
14755 Therefore, the number of recv() calls is minimized.
14756
14757 This routine is ment to be called by the server process.
14758 Clients should call recv_tcp instead.
14759
14760 Input:
14761 INT idx Index of server connection
14762 DWORD buffer_size Size of the buffer in bytes.
14763 INT flags Flags passed to recv()
14764 INT convert_flags Convert flags needed for big/little
14765 endian conversion
14766
14767 Output:
14768 char *buffer Network receive buffer.
14769 INT *remaining Remaining data in cache
14770
14771 Function value:
14772 INT Same as recv()
14773
14774\********************************************************************/
14775{
14776 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14777
14778 int sock = sa->recv_sock;
14779
14780 if (!sa->net_buffer) {
14781 if (sa->is_mserver)
14783 else
14785
14786 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14787 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14788 sa->write_ptr = 0;
14789 sa->read_ptr = 0;
14790 sa->misalign = 0;
14791 }
14792 if (!sa->net_buffer) {
14793 cm_msg(MERROR, "recv_net_command_realloc", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14794 return -1;
14795 }
14796
14797 int copied = 0;
14798 int param_size = -1;
14799
14800 int write_ptr = sa->write_ptr;
14801 int read_ptr = sa->read_ptr;
14802 int misalign = sa->misalign;
14803 char *net_buffer = sa->net_buffer;
14804
14805 do {
14806 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14807 if (param_size == -1) {
14808 if (copied > 0) {
14809 /* assemble split header */
14810 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14811 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14812 param_size = (INT) nc->header.param_size;
14813 } else {
14814 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14815 param_size = (INT) nc->header.param_size;
14816 }
14817
14818 if (sa->convert_flags)
14819 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14820 }
14821
14822 //printf("recv_net_command_realloc: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14823
14824 /* check if parameters fit in buffer */
14825 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14826 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14827 char *p = (char *) realloc(*pbuf, new_size);
14828 //printf("recv_net_command_realloc: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14829 if (p == NULL) {
14830 cm_msg(MERROR, "recv_net_command_realloc", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14831 sa->read_ptr = 0;
14832 sa->write_ptr = 0;
14833 return -1;
14834 }
14835 *pbuf = p;
14836 *pbufsize = new_size;
14837 }
14838
14839 buffer = *pbuf;
14840
14841 /* check if we have all parameters in buffer */
14842 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14843 break;
14844 }
14845
14846 /* not enough data, so copy partially and get new */
14847 int size = write_ptr - read_ptr;
14848
14849 if (size > 0) {
14850 memcpy(buffer + copied, net_buffer + read_ptr, size);
14851 copied += size;
14852 read_ptr = write_ptr;
14853 }
14854#ifdef OS_UNIX
14855 do {
14856 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14857
14858 /* don't return if an alarm signal was cought */
14859 } while (write_ptr == -1 && errno == EINTR);
14860#else
14861 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14862#endif
14863
14864 /* abort if connection broken */
14865 if (write_ptr <= 0) {
14866 if (write_ptr == 0)
14867 cm_msg(MERROR, "recv_net_command_realloc", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14868 else
14869 cm_msg(MERROR, "recv_net_command_realloc", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14870
14871 if (remaining)
14872 *remaining = 0;
14873
14874 return write_ptr;
14875 }
14876
14877 read_ptr = misalign;
14878 write_ptr += misalign;
14879
14880 misalign = write_ptr % 8;
14881 } while (TRUE);
14882
14883 /* copy rest of parameters */
14884 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14885 memcpy(buffer + copied, net_buffer + read_ptr, size);
14886 read_ptr += size;
14887
14888 if (remaining) {
14889 /* don't keep rpc_server_receive in an infinite loop */
14890 if (write_ptr - read_ptr < param_size)
14891 *remaining = 0;
14892 else
14893 *remaining = write_ptr - read_ptr;
14894 }
14895
14896 sa->write_ptr = write_ptr;
14897 sa->read_ptr = read_ptr;
14898 sa->misalign = misalign;
14899
14900 return size + copied;
14901}
14902
14903
14904/********************************************************************/
14906/********************************************************************\
14907
14908 Routine: recv_tcp_check
14909
14910 Purpose: Check if in TCP receive buffer associated with sock is
14911 some data. Called by ss_suspend.
14912
14913 Input:
14914 INT sock TCP receive socket
14915
14916 Output:
14917 none
14918
14919 Function value:
14920 INT count Number of bytes remaining in TCP buffer
14921
14922\********************************************************************/
14923{
14924 /* figure out to which connection socket belongs */
14925 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14926 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14927 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14928 }
14929
14930 return 0;
14931}
14932
14933
14934/********************************************************************/
14935static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
14936/********************************************************************\
14937
14938 Routine: recv_event_server_realloc
14939
14940 Purpose: receive events sent by rpc_send_event()
14941
14942 Input:
14943 INT idx Index of server connection
14944 DWORD buffer_size Size of the buffer in bytes.
14945 INT flags Flags passed to recv()
14946 INT convert_flags Convert flags needed for big/little
14947 endian conversion
14948
14949 Output:
14950 char *buffer Network receive buffer.
14951 INT *remaining Remaining data in cache
14952
14953 Function value:
14954 INT Same as recv()
14955
14956\********************************************************************/
14957{
14958 int sock = psa->event_sock;
14959
14960 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14961
14962 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14963
14964 char header_buf[header_size];
14965
14966 // First read the header.
14967 //
14968 // Data format is:
14969 // INT buffer handle (4 bytes)
14970 // EVENT_HEADER (16 bytes)
14971 // event data
14972 // ALIGN8() padding
14973 // ...next event
14974
14975 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14976
14977 if (hrd == 0) {
14978 // timeout waiting for data
14979 return 0;
14980 }
14981
14982 /* abort if connection broken */
14983 if (hrd < 0) {
14984 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14985 return -1;
14986 }
14987
14988 if (hrd < (int) header_size) {
14989 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14990
14991 /* abort if connection broken */
14992 if (hrd1 <= 0) {
14993 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14994 return -1;
14995 }
14996
14997 hrd += hrd1;
14998 }
14999
15000 /* abort if connection broken */
15001 if (hrd != (int) header_size) {
15002 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
15003 return -1;
15004 }
15005
15006 INT *pbh = (INT *) header_buf;
15007 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
15008
15009 /* convert header little endian/big endian */
15010 if (psa->convert_flags) {
15017 }
15018
15019 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
15020 int total_size = ALIGN8(event_size);
15021
15022 //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);
15023
15024 if (pevent->data_size == 0) {
15025 for (int i=0; i<5; i++) {
15026 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
15027 }
15028 abort();
15029 }
15030
15031 /* check for sane event size */
15032 if (event_size <= 0 || total_size <= 0) {
15033 cm_msg(MERROR, "recv_event_server",
15034 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
15035 event_size, total_size);
15036 return -1;
15037 }
15038
15039 //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);
15040
15041
15042 int bufsize = sizeof(INT) + total_size;
15043
15044 // Second, check that output buffer is big enough
15045
15046 /* check if data part fits in buffer */
15047 if (*pbuffer_size < bufsize) {
15048 int newsize = 1024 + ALIGN8(bufsize);
15049
15050 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
15051
15052 char *newbuf = (char *) realloc(*pbuffer, newsize);
15053 if (newbuf == NULL) {
15054 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
15055 newsize);
15056 return -1;
15057 }
15058 *pbuffer = newbuf;
15059 *pbuffer_size = newsize;
15060 }
15061
15062 // Third, copy header into output buffer
15063
15064 memcpy(*pbuffer, header_buf, header_size);
15065
15066 // Forth, read the event data
15067
15068 int to_read = sizeof(INT) + total_size - header_size;
15069 int rptr = header_size;
15070
15071 if (to_read > 0) {
15072 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
15073
15074 /* abort if connection broken */
15075 if (drd <= 0) {
15076 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
15077 return -1;
15078 }
15079 }
15080
15081 return bufsize;
15082}
15083
15084
15085/********************************************************************/
15086INT rpc_register_server(int port, int *plsock, int *pport)
15087/********************************************************************\
15088
15089 Routine: rpc_register_listener
15090
15091 Purpose: Register the calling process as a MIDAS RPC server. Note
15092 that cm_connnect_experiment must be called prior to any call of
15093 rpc_register_server.
15094
15095 Input:
15096 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
15097
15098 Output:
15099 int *plsock Listener socket, can be NULL
15100 int *pport Port under which server is listening, can be NULL
15101
15102 Function value:
15103 RPC_SUCCESS Successful completion
15104 RPC_NET_ERROR Error in socket call
15105 RPC_NOT_REGISTERED cm_connect_experiment was not called
15106
15107\********************************************************************/
15108{
15109 int status;
15110 int lsock;
15111
15112 status = rpc_register_listener(port, NULL, &lsock, pport);
15113 if (status != RPC_SUCCESS)
15114 return status;
15115
15117 if (status != SS_SUCCESS)
15118 return status;
15119
15120 if (plsock)
15121 *plsock = lsock;
15122
15123 return RPC_SUCCESS;
15124}
15125
15126/********************************************************************/
15127INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
15128/********************************************************************\
15129
15130 Routine: rpc_register_listener
15131
15132 Purpose: Register the calling process as a MIDAS RPC server. Note
15133 that cm_connnect_experiment must be called prior to any call of
15134 rpc_register_listener.
15135
15136 Input:
15137 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
15138 INT *func Default dispatch function
15139
15140 Output:
15141 int *plsock Listener socket, should not be NULL
15142 int *pport Port under which server is listening, can be NULL
15143
15144 Function value:
15145 RPC_SUCCESS Successful completion
15146 RPC_NET_ERROR Error in socket call
15147 RPC_NOT_REGISTERED cm_connect_experiment was not called
15148
15149\********************************************************************/
15150{
15151 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
15153
15154 /* create a socket for listening */
15155 int lsock = 0;
15156 int lport = 0;
15157 std::string errmsg;
15158
15159 int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
15160
15161 if (status != SS_SUCCESS) {
15162 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
15163 return RPC_NET_ERROR;
15164 }
15165
15166 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
15167#if defined(F_SETFD) && defined(FD_CLOEXEC)
15168 status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
15169 if (status < 0) {
15170 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
15171 return RPC_NET_ERROR;
15172 }
15173#endif
15174
15175 /* return port wich OS has choosen */
15176 if (pport) {
15177 *pport = lport;
15178 }
15179
15180 if (plsock)
15181 *plsock = lsock;
15182
15183 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
15184
15185 return RPC_SUCCESS;
15186}
15187
15193
15195static int tls_size = 0;
15196
15197/********************************************************************/
15198static INT rpc_execute_old(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15199/********************************************************************\
15200
15201 Routine: rpc_execute
15202
15203 Purpose: Execute a RPC command received over the network
15204
15205 Input:
15206 INT sock TCP socket to which the result should be
15207 send back
15208
15209 char *buffer Command buffer
15210 INT convert_flags Flags for data conversion
15211
15212 Output:
15213 none
15214
15215 Function value:
15216 RPC_SUCCESS Successful completion
15217 RPC_INVALID_ID Invalid routine_id received
15218 RPC_NET_ERROR Error in socket call
15219 RPC_EXCEED_BUFFER Not enough memory for network buffer
15220 RPC_SHUTDOWN Shutdown requested
15221 SS_ABORT TCP connection broken
15222 SS_EXIT TCP connection closed
15223
15224\********************************************************************/
15225{
15226 INT i, routine_id, status;
15227 char *in_param_ptr, *out_param_ptr, *last_param_ptr;
15228 INT tid, flags;
15229 NET_COMMAND *nc_in, *nc_out;
15230 INT param_size, max_size;
15231 void *prpc_param[20];
15232 char debug_line[1024], *return_buffer;
15233 int return_buffer_size;
15234 int return_buffer_tls;
15235#ifdef FIXED_BUFFER
15236 int initial_buffer_size = NET_BUFFER_SIZE;
15237#else
15238 int initial_buffer_size = 1024;
15239#endif
15240
15241 /* return buffer must must use thread local storage multi-thread servers */
15242 if (!tls_size) {
15243 tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
15245 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15246 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15247 tls_size = 1;
15248 }
15249 for (i = 0; i < tls_size; i++)
15250 if (tls_buffer[i].thread_id == ss_gettid())
15251 break;
15252 if (i == tls_size) {
15253 /* new thread -> allocate new buffer */
15254 tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
15256 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15257 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15258 tls_size++;
15259 }
15260
15261 return_buffer_tls = i;
15262 return_buffer_size = tls_buffer[i].buffer_size;
15263 return_buffer = tls_buffer[i].buffer;
15264 assert(return_buffer);
15265
15266 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
15267 // and valgrind complains that we transmit uninitialized data
15268 //memset(return_buffer, 0, return_buffer_size);
15269
15270 /* extract pointer array to parameters */
15271 nc_in = (NET_COMMAND *) buffer;
15272
15273 /* convert header format (byte swapping) */
15274 if (convert_flags) {
15275 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15276 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15277 }
15278
15279 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15280 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15281 //}
15282
15283 /* no result return as requested */
15284 if (nc_in->header.routine_id & RPC_NO_REPLY)
15285 sock = 0;
15286
15287 /* find entry in rpc_list */
15288 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15289
15290 assert(xroutine_id == routine_id);
15291
15292 again:
15293
15294 in_param_ptr = nc_in->param;
15295
15296 nc_out = (NET_COMMAND *) return_buffer;
15297 out_param_ptr = nc_out->param;
15298
15299 sprintf(debug_line, "%s(", rl.name);
15300
15301 for (i = 0; rl.param[i].tid != 0; i++) {
15302 tid = rl.param[i].tid;
15303 flags = rl.param[i].flags;
15304
15305 if (flags & RPC_IN) {
15306 param_size = ALIGN8(rpc_tid_size(tid));
15307
15308 if (tid == TID_STRING || tid == TID_LINK)
15309 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
15310
15311 if (flags & RPC_VARARRAY) {
15312 /* for arrays, the size is stored as a INT in front of the array */
15313 param_size = *((INT *) in_param_ptr);
15314 if (convert_flags)
15315 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
15316 param_size = ALIGN8(param_size);
15317
15318 in_param_ptr += ALIGN8(sizeof(INT));
15319 }
15320
15321 if (tid == TID_STRUCT)
15322 param_size = ALIGN8(rl.param[i].n);
15323
15324 prpc_param[i] = in_param_ptr;
15325
15326 /* convert data format */
15327 if (convert_flags) {
15328 if (flags & RPC_VARARRAY)
15329 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15330 else
15331 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
15332 convert_flags);
15333 }
15334
15335 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
15336 if (rl.param[i].tid == TID_STRING) {
15337 /* check for long strings (db_create_record...) */
15338 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
15339 strcat(debug_line, "\"");
15340 strcat(debug_line, str.c_str());
15341 strcat(debug_line, "\"");
15342 } else
15343 strcat(debug_line, "...");
15344 } else
15345 strcat(debug_line, str.c_str());
15346
15347 in_param_ptr += param_size;
15348 }
15349
15350 if (flags & RPC_OUT) {
15351 param_size = ALIGN8(rpc_tid_size(tid));
15352
15353 if (flags & RPC_VARARRAY || tid == TID_STRING) {
15354
15355 /* save maximum array length from the value of the next argument.
15356 * this means RPC_OUT arrays and strings should always be passed like this:
15357 * rpc_call(..., array_ptr, array_max_size, ...); */
15358
15359 max_size = *((INT *) in_param_ptr);
15360
15361 if (convert_flags)
15362 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15363 max_size = ALIGN8(max_size);
15364
15365 *((INT *) out_param_ptr) = max_size;
15366
15367 /* save space for return array length */
15368 out_param_ptr += ALIGN8(sizeof(INT));
15369
15370 /* use maximum array length from input */
15371 param_size = max_size;
15372 }
15373
15374 if (rl.param[i].tid == TID_STRUCT)
15375 param_size = ALIGN8(rl.param[i].n);
15376
15377 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
15378#ifdef FIXED_BUFFER
15379 cm_msg(MERROR, "rpc_execute",
15380 "return parameters (%d) too large for network buffer (%d)",
15381 (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
15382
15383 return RPC_EXCEED_BUFFER;
15384#else
15385 int itls;
15386 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
15387
15388#if 0
15389 cm_msg(MINFO, "rpc_execute",
15390 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
15391 (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
15392#endif
15393
15394 itls = return_buffer_tls;
15395
15396 tls_buffer[itls].buffer_size = new_size;
15397 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
15398
15399 if (!tls_buffer[itls].buffer) {
15400 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
15401 return RPC_EXCEED_BUFFER;
15402 }
15403
15404 return_buffer_size = tls_buffer[itls].buffer_size;
15405 return_buffer = tls_buffer[itls].buffer;
15406 assert(return_buffer);
15407
15408 goto again;
15409#endif
15410 }
15411
15412 /* if parameter goes both directions, copy input to output */
15413 if (rl.param[i].flags & RPC_IN)
15414 memcpy(out_param_ptr, prpc_param[i], param_size);
15415
15416 if (_debug_print && !(flags & RPC_IN))
15417 strcat(debug_line, "-");
15418
15419 prpc_param[i] = out_param_ptr;
15420 out_param_ptr += param_size;
15421 }
15422
15423 if (rl.param[i + 1].tid)
15424 strcat(debug_line, ", ");
15425 }
15426
15427 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
15428
15429 strcat(debug_line, ")");
15430 rpc_debug_printf(debug_line);
15431
15432 last_param_ptr = out_param_ptr;
15433
15434 /*********************************\
15435 * call dispatch function *
15436 \*********************************/
15437 if (rl.dispatch)
15438 status = rl.dispatch(routine_id, prpc_param);
15439 else
15441
15442 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15444
15445 /* return immediately for closed down client connections */
15446 if (!sock && routine_id == RPC_ID_EXIT)
15447 return SS_EXIT;
15448
15449 if (!sock && routine_id == RPC_ID_SHUTDOWN)
15450 return RPC_SHUTDOWN;
15451
15452 /* Return if TCP connection broken */
15453 if (status == SS_ABORT)
15454 return SS_ABORT;
15455
15456 /* if sock == 0, we are in FTCP mode and may not sent results */
15457 if (!sock)
15458 return RPC_SUCCESS;
15459
15460 /* compress variable length arrays */
15461 out_param_ptr = nc_out->param;
15462 for (i = 0; rl.param[i].tid != 0; i++)
15463 if (rl.param[i].flags & RPC_OUT) {
15464 tid = rl.param[i].tid;
15465 flags = rl.param[i].flags;
15466 param_size = ALIGN8(rpc_tid_size(tid));
15467
15468 if (tid == TID_STRING) {
15469 max_size = *((INT *) out_param_ptr);
15470 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15471 // and prpc_param() is now pointing to the wrong place. here we know our string data
15472 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
15473 //const char* param_ptr = (char *) prpc_param[i];
15474 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
15475 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
15476 param_size = strlen(param_ptr) + 1;
15477 param_size = ALIGN8(param_size);
15478
15479 /* move string ALIGN8(sizeof(INT)) left */
15480 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
15481
15482 /* move remaining parameters to end of string */
15483 memmove(out_param_ptr + param_size,
15484 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
15485 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
15486 }
15487
15488 if (flags & RPC_VARARRAY) {
15489 /* store array length at current out_param_ptr */
15490 max_size = *((INT *) out_param_ptr);
15491 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15492 // and prpc_param() is now pointing to the wrong place. instead, compute location
15493 // of next parameter using max_size. K.O.
15494 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
15495 if (rl.param[i+1].flags & RPC_OUT)
15496 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
15497 else
15498 param_size = *((INT *) prpc_param[i + 1]);
15499 *((INT *) out_param_ptr) = param_size; // store new array size
15500 if (convert_flags)
15501 rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
15502
15503 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
15504
15505 param_size = ALIGN8(param_size);
15506
15507 /* move remaining parameters to end of array */
15508 memmove(out_param_ptr + param_size,
15509 out_param_ptr + max_size,
15510 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
15511 }
15512
15513 if (tid == TID_STRUCT)
15514 param_size = ALIGN8(rl.param[i].n);
15515
15516 /* convert data format */
15517 if (convert_flags) {
15518 if (flags & RPC_VARARRAY)
15519 rpc_convert_data(out_param_ptr, tid,
15520 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15521 else
15522 rpc_convert_data(out_param_ptr, tid,
15523 rl.param[i].flags | RPC_OUTGOING,
15524 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15525 }
15526
15527 out_param_ptr += param_size;
15528 }
15529
15530 /* send return parameters */
15531 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15532 nc_out->header.routine_id = status;
15533 nc_out->header.param_size = param_size;
15534
15535 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15536
15537 /* convert header format (byte swapping) if necessary */
15538 if (convert_flags) {
15539 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15540 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15541 }
15542
15543 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15544 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15545
15546 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15547
15548 if (status < 0) {
15549 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15550 return RPC_NET_ERROR;
15551 }
15552
15553 /* print return buffer */
15554/*
15555 printf("Return buffer, ID %d:\n", routine_id);
15556 for (i=0; i<param_size ; i++)
15557 {
15558 status = (char) nc_out->param[i];
15559 printf("%02X ", status);
15560 if (i%8 == 7)
15561 printf("\n");
15562 }
15563*/
15564 /* return SS_EXIT if RPC_EXIT is called */
15565 if (routine_id == RPC_ID_EXIT)
15566 return SS_EXIT;
15567
15568 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15569 if (routine_id == RPC_ID_SHUTDOWN)
15570 return RPC_SHUTDOWN;
15571
15572 return RPC_SUCCESS;
15573}
15574
15575
15576
15577class RPE
15578{
15579public:
15580 size_t offset = 0;
15581 size_t arg_size = 0;
15582 size_t param_size = 0;
15584 size_t out_max_size = 0;
15585 std::string* ps = NULL;
15586 std::vector<char>* pv = NULL;
15587 void* pparam = NULL;
15588
15589 RPE() // ctor
15590 {
15591 }
15592 ~RPE() // dtor
15593 {
15594 if (ps)
15595 delete ps;
15596 if (pv)
15597 delete pv;
15598 ps = NULL; // poison the pointer
15599 pv = NULL; // poison the pointer
15600 pparam = NULL; // poison the pointer
15601 }
15602};
15603
15604/********************************************************************/
15605static INT rpc_execute_cxx(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15606/********************************************************************\
15607
15608 Routine: rpc_execute
15609
15610 Purpose: Execute a RPC command received over the network
15611
15612 Input:
15613 INT sock TCP socket to which the result should be
15614 send back
15615
15616 char *buffer Command buffer
15617 INT convert_flags Flags for data conversion
15618
15619 Output:
15620 none
15621
15622 Function value:
15623 RPC_SUCCESS Successful completion
15624 RPC_INVALID_ID Invalid routine_id received
15625 RPC_NET_ERROR Error in socket call
15626 RPC_EXCEED_BUFFER Not enough memory for network buffer
15627 RPC_SHUTDOWN Shutdown requested
15628 SS_ABORT TCP connection broken
15629 SS_EXIT TCP connection closed
15630
15631\********************************************************************/
15632{
15633 INT status;
15634
15635 bool debug = false;
15636
15637 /* extract pointer array to parameters */
15638 NET_COMMAND* nc_in = (NET_COMMAND *) buffer;
15639
15640 /* convert header format (byte swapping) */
15641 if (convert_flags) {
15642 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15643 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15644 }
15645
15646 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15647 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15648 //}
15649
15650 /* no result return as requested */
15651 if (nc_in->header.routine_id & RPC_NO_REPLY)
15652 sock = 0;
15653
15654 int routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15655
15656 assert(xroutine_id == routine_id);
15657
15658#if 0
15659 if (routine_id == RPC_TEST2)
15660 debug = true;
15661
15662 if (routine_id == RPC_TEST2_CXX)
15663 debug = true;
15664
15665 if (routine_id == RPC_TEST3_CXX)
15666 debug = true;
15667
15668 if (routine_id == RPC_TEST4_CXX)
15669 debug = true;
15670#endif
15671
15672 /* find entry in rpc_list */
15673
15674 char* in_param_ptr = (char*)nc_in->param;
15675
15676 if (debug)
15677 printf("rpc_execute_cxx: routine_id %d, name \"%s\"\n", routine_id, rl.name);
15678
15679 void *prpc_param[MAX_RPC_PARAMS];
15680
15681 size_t in_param_size[MAX_RPC_PARAMS];
15682 size_t in_param_offset[MAX_RPC_PARAMS];
15683
15684 std::vector<RPE> params;
15685
15686 int nparams = 0;
15687 for (int i = 0; rl.param[i].tid != 0; i++) {
15688 nparams++;
15689 }
15690
15691 params.resize(nparams);
15692
15693 size_t in_offset = 0;
15694
15695 for (int i = 0; i < nparams; i++) {
15696 in_param_size[i] = 0;
15697 in_param_offset[i] = 0;
15698
15699 int tid = rl.param[i].tid;
15700 int flags = rl.param[i].flags;
15701
15702 if (flags & RPC_IN) {
15703 int arg_size = rpc_tid_size(tid);
15704
15705 if (tid == TID_STRING || tid == TID_LINK) {
15706 arg_size = 1 + strlen((char *) (in_param_ptr));
15707 }
15708
15709 if (flags & RPC_VARARRAY) {
15710 /* for arrays, the size is stored as a INT in front of the array */
15711 int arg_size_align8 = *((INT *) in_param_ptr);
15712 if (convert_flags)
15713 rpc_convert_single(&arg_size_align8, TID_INT32, 0, convert_flags);
15714 in_param_ptr += ALIGN8(sizeof(INT));
15715 in_offset += ALIGN8(sizeof(INT));
15716 if (flags & RPC_CXX) {
15717 /* for std::vector<char> data, it is the true length of the array */
15718 arg_size = arg_size_align8;
15719 } else {
15720 /* true size is stored in the next parameter */
15721 arg_size = *((INT *) (((char*)in_param_ptr) + ALIGN8(arg_size_align8))); // NB: this ALIGN8() is redundant with ALIGN8() in the RPC client *encoder*
15722 if (convert_flags)
15723 rpc_convert_single(&arg_size, TID_INT32, 0, convert_flags);
15724 }
15725 //printf("RPC_VARARRAY: arg_size %d %d\n", arg_size_align8, arg_size);
15726
15727 if (ALIGN8(arg_size_align8) != ALIGN8(arg_size)) {
15728 cm_msg(MERROR, "rpc_execute_cxx", "RPC %d, param %d tid %d flags 0x%x size mismatch: header %d vs next param %d", routine_id, i, tid, flags, arg_size_align8, arg_size);
15729 return RPC_INVALID_ID;
15730 }
15731 }
15732
15733 if (tid == TID_STRUCT) {
15734 arg_size = rl.param[i].n;
15735 }
15736
15737 int param_size = ALIGN8(arg_size);
15738
15739 in_param_size[i] = param_size;
15740 in_param_offset[i] = in_offset;
15741
15742 params[i].offset = in_offset;
15743 params[i].arg_size = arg_size;
15744 params[i].param_size = param_size;
15745
15746 /* convert data format */
15747 if (convert_flags) {
15748 if (flags & RPC_VARARRAY) {
15749 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15750 } else {
15751 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid), convert_flags);
15752 }
15753 }
15754
15755 in_param_ptr += param_size;
15756 in_offset += param_size;
15757 }
15758
15759 if (flags & RPC_OUT) {
15760 params[i].out_max_size = rpc_tid_size(tid);
15761
15762 if (flags & RPC_CXX) {
15763 params[i].out_max_size = 0; // no max size!
15764 } else if (flags & RPC_VARARRAY || tid == TID_STRING) {
15765
15766 /* save maximum array length from the value of the next argument.
15767 * this means RPC_OUT arrays and strings should always be passed like this:
15768 * rpc_call(..., array_ptr, array_max_size, ...); */
15769
15770 params[i].out_max_size_offset = in_offset;
15771
15772 INT max_size = *((INT *) in_param_ptr);
15773
15774 if (convert_flags)
15775 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15776
15777 if (max_size < 0 || (tid == TID_STRING && max_size == 0)) {
15778 cm_msg(MERROR, "rpc_execute_cxx", "RPC %d, param %d tid %d flags 0x%x invalid maximum output size %d", routine_id, i, tid, flags, max_size);
15779 return RPC_INVALID_ID;
15780 }
15781
15782 params[i].out_max_size = max_size;
15783 }
15784
15785 if (rl.param[i].tid == TID_STRUCT) {
15786 params[i].out_max_size = rl.param[i].n;
15787 }
15788 }
15789
15790 if (flags & RPC_CXX) {
15791 if (tid == TID_STRING) {
15792 params[i].ps = new std::string;
15793 if (flags & RPC_IN) {
15794 *(params[i].ps) = (char*)nc_in->param + in_param_offset[i];
15795 //printf("STRING %d decode [%s]\n", i, (char*)nc_in->param + in_param_offset[i]);
15796 }
15797 prpc_param[i] = (void*) params[i].ps;
15798 } else if (tid == TID_ARRAY) {
15799 params[i].pv = new std::vector<char>;
15800 if (flags & RPC_IN) {
15801 params[i].pv->insert(params[i].pv->end(), (char*)nc_in->param + in_param_offset[i], (char*)nc_in->param + in_param_offset[i] + params[i].arg_size);
15802 //printf("VECTOR %d decode %zu bytes\n", i, params[i].pv->size());
15803 }
15804 prpc_param[i] = (void*) params[i].pv;
15805 } else {
15806 cm_msg(MERROR, "rpc_execute_cxx", "RPC %d: param %d tid %d flags 0x%x, TID not compatible with flag RPC_CXX", routine_id, i, tid, flags);
15807 return RPC_INVALID_ID;
15808 }
15809 } else {
15810 if ((flags & RPC_IN) && (flags & RPC_OUT)) {
15811 params[i].pv = new std::vector<char>;
15812 params[i].pv->insert(params[i].pv->end(), (char*)nc_in->param + in_param_offset[i], (char*)nc_in->param + in_param_offset[i] + params[i].arg_size);
15813 size_t want_size = params[i].out_max_size;
15814 //printf("param %d size %zu want %zu\n", i, params[i].pv->size(), want_size);
15815 if (params[i].pv->size() < want_size)
15816 params[i].pv->resize(want_size);
15817 prpc_param[i] = params[i].pv->data();
15818 } else if (flags & RPC_IN) {
15819 prpc_param[i] = (char*)nc_in->param + in_param_offset[i];
15820 //printf("param %d input value %d [%s]\n", i, *(int*)prpc_param[i], (char*)prpc_param[i]);
15821 } else if (flags & RPC_OUT) {
15822 params[i].pv = new std::vector<char>;
15823 params[i].pv->resize(params[i].out_max_size);
15824 prpc_param[i] = params[i].pv->data();
15825 //printf("param %d size %zu\n", i, params[i].pv->size());
15826 }
15827 }
15828
15829 if (debug)
15830 printf("rpc_execute_cxx: param %2d, tid %2d, flags 0x%04x, in %3zu+%-3zu+%-3zu, out max size %3zu at %3zu, ptr %p\n", i, tid, flags, params[i].offset, params[i].arg_size, params[i].param_size, params[i].out_max_size, params[i].out_max_size_offset, prpc_param[i]);
15831 }
15832
15833 if (debug)
15834 printf("rpc_execute_cxx: nc_in size %d, in_offset %zu\n", nc_in->header.param_size, in_offset);
15835
15836 if (routine_id == RPC_TEST2) {
15837 bool ok = true;
15838
15839 ok &= in_param_offset[ 0] == 0; ok &= in_param_size[ 0] == 8; // int_in
15840 ok &= in_param_offset[ 1] == 0; ok &= in_param_size[ 1] == 0; // int_out
15841 ok &= in_param_offset[ 2] == 8; ok &= in_param_size[ 2] == 8; // &int_inout
15842 ok &= in_param_offset[ 3] == 16; ok &= in_param_size[ 3] == 16; // string_in
15843 ok &= in_param_offset[ 4] == 0; ok &= in_param_size[ 4] == 0; // string_out
15844 ok &= in_param_offset[ 5] == 32; ok &= in_param_size[ 5] == 8; // string_out size
15845 ok &= in_param_offset[ 6] == 0; ok &= in_param_size[ 6] == 0; // string2_out
15846 ok &= in_param_offset[ 7] == 40; ok &= in_param_size[ 7] == 8; // string2_out size
15847 ok &= in_param_offset[ 8] == 48; ok &= in_param_size[ 8] == 16; // string_inout
15848 ok &= in_param_offset[ 9] == 64; ok &= in_param_size[ 9] == 8; // string_inout size
15849 ok &= in_param_offset[10] == 72; ok &= in_param_size[10] == 72; // struct_in
15850 ok &= in_param_offset[11] == 0; ok &= in_param_size[11] == 0; // struct_out
15851 ok &= in_param_offset[12] == 144; ok &= in_param_size[12] == 72; // struct_inout
15852 ok &= in_param_offset[13] == 224; ok &= in_param_size[13] == 40; // uint32_t[10] array inout
15853 ok &= in_param_offset[14] == 264; ok &= in_param_size[14] == 8; // &size
15854 ok &= in_param_offset[15] == 280; ok &= in_param_size[15] == 16; // char[10] array in
15855 ok &= in_param_offset[16] == 296; ok &= in_param_size[16] == 8; // size
15856 ok &= in_param_offset[17] == 0; ok &= in_param_size[17] == 0; // char[16] array out
15857 ok &= in_param_offset[18] == 304; ok &= in_param_size[18] == 8; // &size
15858 ok &= in_offset == 312;
15859
15860 if (!ok) {
15861 cm_msg(MERROR, "rpc_execute_cxx", "RPC_TEST2 parameters encoding error!");
15862 return RPC_INVALID_ID;
15863 }
15864 }
15865
15866 if (debug) {
15867 printf("rpc_execute_cxx: calling dispatch()\n");
15868 }
15869
15870 /*********************************\
15871 * call dispatch function *
15872 \*********************************/
15873 if (rl.dispatch)
15874 status = rl.dispatch(routine_id, prpc_param);
15875 else
15877
15878 if (debug) {
15879 printf("rpc_execute_cxx: dispatch() status %d\n", status);
15880 }
15881
15882 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15884
15885 /* return immediately for closed down client connections */
15886 if (!sock && routine_id == RPC_ID_EXIT) {
15887 return SS_EXIT;
15888 }
15889
15890 if (!sock && routine_id == RPC_ID_SHUTDOWN) {
15891 return RPC_SHUTDOWN;
15892 }
15893
15894 /* Return if TCP connection broken */
15895 if (status == SS_ABORT) {
15896 return SS_ABORT;
15897 }
15898
15899 /* if sock == 0, we are in FTCP mode and may not sent results */
15900 if (!sock) {
15901 return RPC_SUCCESS;
15902 }
15903
15904 std::vector<char> v_out;
15905
15906 v_out.resize(sizeof(NET_COMMAND_HEADER));
15907
15908 for (int i = 0; i < nparams; i++) {
15909 if (rl.param[i].flags & RPC_OUT) {
15910 int tid = rl.param[i].tid;
15911 int flags = rl.param[i].flags;
15912
15913 if (flags & RPC_CXX) {
15914 if (tid == TID_STRING) {
15915 size_t arg_size = 1 + params[i].ps->length();
15916 size_t param_size = ALIGN8(arg_size);
15917
15918 if (debug)
15919 printf("rpc_execute_cxx: param %2d, std::string arg_size %zu, param_size %zu, string [%s]\n", i, arg_size, param_size, params[i].ps->c_str());
15920
15921 v_out.insert(v_out.end(), params[i].ps->c_str(), params[i].ps->c_str() + arg_size);
15922 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15923 } else if (tid == TID_ARRAY) {
15924 size_t arg_size = params[i].pv->size();
15925 size_t param_size = ALIGN8(arg_size);
15926
15927 if (debug)
15928 printf("rpc_execute_cxx: param %2d, std::vector arg_size %zu, param_size %zu\n", i, arg_size, param_size);
15929
15930 char buf[ALIGN8(sizeof(INT))];
15931 *((INT *) buf) = arg_size; // store new array size
15932 if (convert_flags)
15933 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15934 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15935 v_out.insert(v_out.end(), params[i].pv->data(), params[i].pv->data() + arg_size); // data
15936 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15937 } else {
15938 cm_msg(MERROR, "rpc_execute_cxx", "RPC %d: param %d tid %d flags 0x%x, TID not compatible with flag RPC_CXX", routine_id, i, tid, flags);
15939 return RPC_INVALID_ID;
15940 }
15941 } else {
15942 size_t convert_offset = 0;
15943 size_t convert_size = 0;
15944
15945 if (tid == TID_STRING) {
15946 size_t max_size = params[i].out_max_size;
15947 char* param_ptr = (char *) prpc_param[i];
15948 //printf("param %d string param [%s] max_size %zu\n", i, param_ptr, max_size);
15949 char* string_end = (char*)memchr(param_ptr, 0, max_size);
15950 size_t arg_size = string_end ? string_end - param_ptr : max_size;
15951 if (!string_end) {
15952 param_ptr[max_size - 1] = 0; // truncate!
15953 arg_size = max_size - 1;
15954 }
15955 arg_size++;
15956 size_t param_size = ALIGN8(arg_size);
15957
15958 if (debug)
15959 printf("rpc_execute_cxx: param %2d, string max_size %zu, string_size %zu, param_size %zu\n", i, max_size, arg_size, param_size);
15960
15961 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size);
15962 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15963 } else if (flags & RPC_VARARRAY) {
15964 size_t max_size = params[i].out_max_size;
15965 INT arg_size_int = *((INT *) prpc_param[i + 1]);
15966 if (arg_size_int < 0 || (size_t) arg_size_int > max_size) {
15967 cm_msg(MERROR, "rpc_execute_cxx", "RPC %d, param %d output array size %d exceeds maximum size %zu", routine_id, i, arg_size_int, max_size);
15968 return RPC_EXCEED_BUFFER;
15969 }
15970 size_t arg_size = arg_size_int;
15971 char* param_ptr = (char*)prpc_param[i];
15972 size_t param_size = ALIGN8(arg_size);
15973
15974 if (debug)
15975 printf("rpc_execute_cxx: param %2d, array max_size %zu, param_size %zu\n", i, max_size, param_size);
15976
15977 char buf[ALIGN8(sizeof(INT))];
15978 *((INT *) buf) = arg_size; // store new array size
15979 if (convert_flags)
15980 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15981 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15982 convert_offset = v_out.size();
15983 convert_size = arg_size;
15984 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
15985 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15986 } else {
15987 char* param_ptr = (char*)prpc_param[i];
15988 size_t arg_size = rpc_tid_size(tid);
15989 if (tid == TID_STRUCT)
15990 arg_size = rl.param[i].n;
15991 size_t param_size = ALIGN8(arg_size);
15992
15993 if (debug) {
15994 if (tid == TID_INT) {
15995 printf("rpc_execute_cxx: param %2d, tid %2d, arg_size %zu, param_size %zu, value %d\n", i, tid, arg_size, param_size, *(int*)param_ptr);
15996 } else {
15997 printf("rpc_execute_cxx: param %2d, tid %2d, arg_size %zu, param_size %zu\n", i, tid, arg_size, param_size);
15998 }
15999 }
16000
16001 convert_offset = v_out.size();
16002 convert_size = arg_size;
16003 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
16004 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
16005 }
16006
16007 /* convert data format */
16008 if (convert_flags) {
16009 if (flags & RPC_VARARRAY)
16010 rpc_convert_data(v_out.data()+convert_offset, tid, rl.param[i].flags | RPC_OUTGOING, convert_size, convert_flags);
16011 else
16012 rpc_convert_data(v_out.data()+convert_offset, tid, rl.param[i].flags | RPC_OUTGOING, rl.param[i].n * rpc_tid_size(tid), convert_flags);
16013 }
16014 }
16015 }
16016 }
16017
16018 NET_COMMAND* nc_out = (NET_COMMAND*)v_out.data();
16019
16020 /* send return parameters */
16021 nc_out->header.routine_id = status;
16022 nc_out->header.param_size = v_out.size() - sizeof(NET_COMMAND_HEADER);
16023
16024
16025 /* convert header format (byte swapping) if necessary */
16026 if (convert_flags) {
16027 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
16028 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
16029 }
16030
16031 status = send_tcp(sock, v_out.data(), v_out.size(), 0);
16032
16033 if (status < 0) {
16034 cm_msg(MERROR, "rpc_execute_cxx", "send_tcp() failed, status %d", status);
16035 return RPC_NET_ERROR;
16036 }
16037
16038 if (debug)
16039 printf("rpc_execute_cxx: send_tcp() sent %d bytes\n", status);
16040
16041 /* return SS_EXIT if RPC_EXIT is called */
16042 if (routine_id == RPC_ID_EXIT)
16043 return SS_EXIT;
16044
16045 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
16046 if (routine_id == RPC_ID_SHUTDOWN)
16047 return RPC_SHUTDOWN;
16048
16049 return RPC_SUCCESS;
16050}
16051
16052/********************************************************************/
16054/********************************************************************\
16055 Routine: rpc_test_rpc
16056
16057 Purpose: Test RPC parameters encoding and decoding
16058
16059 Input:
16060 none
16061
16062 Output:
16063 none
16064
16065 Function value:
16066 RPC_SUCCESS Successful completion
16067
16068\********************************************************************/
16069{
16070 int status = RPC_SUCCESS;
16071
16072 printf("rpc_test_rpc_test2!\n");
16073
16074 int int_out = 0;
16075 int int_inout = 456;
16076
16077 char string_out[33];
16078 char string2_out[49];
16079
16080 char string_inout[25];
16081 strcpy(string_inout, "string_inout");
16082
16083 KEY struct_in;
16084
16085 struct_in.type = 111;
16086 struct_in.num_values = 222;
16087 strcpy(struct_in.name, "name");
16088 struct_in.last_written = 333;
16089
16090 KEY struct_out;
16091 KEY struct_inout;
16092
16093 struct_inout.type = 111111;
16094 struct_inout.num_values = 222222;
16095 strcpy(struct_inout.name, "name_name");
16096 struct_inout.last_written = 333333;
16097
16098 uint32_t dwordarray_inout[9];
16099 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16100
16101 for (int i=0; i<9; i++) {
16102 dwordarray_inout[i] = i*10;
16103 }
16104
16105 char array_in[10];
16106
16107 for (size_t i=0; i<sizeof(array_in); i++) {
16108 array_in[i] = 'a' + i;
16109 }
16110
16111 char array_out[16];
16112 size_t array_out_size = sizeof(array_out);
16113
16114 for (size_t i=0; i<sizeof(array_out); i++) {
16115 array_out[i] = 'Z';
16116 }
16117
16119 123,
16120 &int_out,
16121 &int_inout,
16122 "test string",
16123 string_out, sizeof(string_out),
16124 string2_out, sizeof(string2_out),
16125 string_inout, sizeof(string_inout),
16126 &struct_in,
16127 &struct_out,
16128 &struct_inout,
16129 dwordarray_inout, &dwordarray_inout_size,
16130 array_in, sizeof(array_in),
16131 array_out, &array_out_size
16132 );
16133
16134 if (status != RPC_SUCCESS) {
16135 printf("rpc_call(RPC_TEST2) status %d\n", status);
16136 return status;
16137 }
16138
16139 if (int_out != 789) {
16140 printf("int_out mismatch!\n");
16141 status = 0;
16142 }
16143
16144 if (int_inout != 456*2) {
16145 printf("int_inout mismatch!\n");
16146 status = 0;
16147 }
16148
16149 if (strcmp(string_out, "string_out") != 0) {
16150 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16151 status = 0;
16152 }
16153
16154 if (strcmp(string2_out, "second string_out") != 0) {
16155 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
16156 status = 0;
16157 }
16158
16159 if (strcmp(string_inout, "return string_inout") != 0) {
16160 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
16161 status = 0;
16162 }
16163
16164 KEY* pkey;
16165
16166 pkey = &struct_in;
16167
16168 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16169
16170 pkey = &struct_out;
16171
16172 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16173 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);
16174 status = 0;
16175 }
16176
16177 pkey = &struct_inout;
16178
16179 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16180 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);
16181 status = 0;
16182 }
16183
16184 if (dwordarray_inout_size != 4*5) {
16185 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16186 status = 0;
16187 } else {
16188 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16189 if (dwordarray_inout[i] != i*10+i) {
16190 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16191 status = 0;
16192 }
16193 }
16194 }
16195
16196 //printf("array_out_size %d\n", array_out_size);
16197 //for (int i=0; i<array_out_size; i++) {
16198 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16199 //}
16200
16201 if (array_out_size != 15) {
16202 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16203 status = 0;
16204 } else {
16205 if (strcmp(array_out, "test test test") != 0) {
16206 printf("array_out data mismatch\n");
16207 status = 0;
16208 }
16209 }
16210
16211 return status;
16212}
16213
16214/********************************************************************/
16216/********************************************************************\
16217 Routine: rpc_test_rpc
16218
16219 Purpose: Test RPC parameters encoding and decoding
16220
16221 Input:
16222 none
16223
16224 Output:
16225 none
16226
16227 Function value:
16228 RPC_SUCCESS Successful completion
16229
16230\********************************************************************/
16231{
16232 int status = RPC_SUCCESS;
16233
16234 printf("rpc_test_rpc_test2_cxx!\n");
16235
16236 int int_out = 0;
16237 int int_inout = 456;
16238
16239 char string_out[33];
16240 std::string string2_out;
16241 std::string string_inout = "string_inout";
16242
16243 KEY struct_in;
16244
16245 struct_in.type = 111;
16246 struct_in.num_values = 222;
16247 strcpy(struct_in.name, "name");
16248 struct_in.last_written = 333;
16249
16250 KEY struct_out;
16251 KEY struct_inout;
16252
16253 struct_inout.type = 111111;
16254 struct_inout.num_values = 222222;
16255 strcpy(struct_inout.name, "name_name");
16256 struct_inout.last_written = 333333;
16257
16258 uint32_t dwordarray_inout[9];
16259 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16260
16261 for (int i=0; i<9; i++) {
16262 dwordarray_inout[i] = i*10;
16263 }
16264
16265 std::vector<char> array_in;
16266 int array_in_size = 10;
16267
16268 for (int i=0; i<array_in_size; i++) {
16269 array_in.push_back('a' + i);
16270 }
16271
16272 std::vector<char> array_out;
16273 size_t array_out_size = 16;
16274
16276 123,
16277 &int_out,
16278 &int_inout,
16279 "test string",
16280 string_out, sizeof(string_out),
16281 &string2_out, 48,
16282 &string_inout, 25,
16283 &struct_in,
16284 &struct_out,
16285 &struct_inout,
16286 dwordarray_inout, &dwordarray_inout_size,
16287 &array_in, array_in_size,
16288 &array_out, &array_out_size
16289 );
16290
16291 if (status != RPC_SUCCESS) {
16292 printf("rpc_call(RPC_TEST2_CXX) status %d\n", status);
16293 return status;
16294 }
16295
16296 if (int_out != 789) {
16297 printf("int_out mismatch!\n");
16298 status = 0;
16299 }
16300
16301 if (int_inout != 456*2) {
16302 printf("int_inout mismatch!\n");
16303 status = 0;
16304 }
16305
16306 if (strcmp(string_out, "string_out") != 0) {
16307 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16308 status = 0;
16309 }
16310
16311 if (string2_out != "second string_out") {
16312 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16313 status = 0;
16314 }
16315
16316 if (string_inout != "return string_inout") {
16317 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16318 status = 0;
16319 }
16320
16321 KEY* pkey;
16322
16323 pkey = &struct_in;
16324
16325 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16326
16327 pkey = &struct_out;
16328
16329 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16330 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);
16331 status = 0;
16332 }
16333
16334 pkey = &struct_inout;
16335
16336 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16337 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);
16338 status = 0;
16339 }
16340
16341 if (dwordarray_inout_size != 4*5) {
16342 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16343 status = 0;
16344 } else {
16345 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16346 if (dwordarray_inout[i] != i*10+i) {
16347 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16348 status = 0;
16349 }
16350 }
16351 }
16352
16353 //printf("array_out_size %d\n", array_out_size);
16354 //for (int i=0; i<array_out_size; i++) {
16355 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16356 //}
16357
16358 if (array_out_size != 15) {
16359 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16360 status = 0;
16361 } else if (array_out.size() != 15) {
16362 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16363 status = 0;
16364 } else {
16365 if (strcmp(array_out.data(), "test test test") != 0) {
16366 printf("array_out data mismatch\n");
16367 status = 0;
16368 }
16369 }
16370
16371 return status;
16372}
16373
16374/********************************************************************/
16376/********************************************************************\
16377 Routine: rpc_test_rpc
16378
16379 Purpose: Test RPC parameters encoding and decoding
16380
16381 Input:
16382 none
16383
16384 Output:
16385 none
16386
16387 Function value:
16388 RPC_SUCCESS Successful completion
16389
16390\********************************************************************/
16391{
16392 int status = RPC_SUCCESS;
16393
16394 printf("rpc_test_rpc_test3_cxx!\n");
16395
16396 int int_out = 0;
16397 int int_inout = 456;
16398
16399 char string_out[33];
16400 std::string string2_out;
16401 std::string string_inout = "string_inout";
16402
16403 KEY struct_in;
16404
16405 struct_in.type = 111;
16406 struct_in.num_values = 222;
16407 strcpy(struct_in.name, "name");
16408 struct_in.last_written = 333;
16409
16410 KEY struct_out;
16411 KEY struct_inout;
16412
16413 struct_inout.type = 111111;
16414 struct_inout.num_values = 222222;
16415 strcpy(struct_inout.name, "name_name");
16416 struct_inout.last_written = 333333;
16417
16418 uint32_t dwordarray_inout[9];
16419 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16420
16421 for (int i=0; i<9; i++) {
16422 dwordarray_inout[i] = i*10;
16423 }
16424
16425 std::vector<char> array_in;
16426 int array_in_size = 10;
16427
16428 for (int i=0; i<array_in_size; i++) {
16429 array_in.push_back('a' + i);
16430 }
16431
16432 std::vector<char> array_out;
16433
16435 123,
16436 &int_out,
16437 &int_inout,
16438 "test string",
16439 string_out, sizeof(string_out),
16440 &string2_out,
16441 &string_inout,
16442 &struct_in,
16443 &struct_out,
16444 &struct_inout,
16445 dwordarray_inout, &dwordarray_inout_size,
16446 &array_in,
16447 &array_out
16448 );
16449
16450 if (status != RPC_SUCCESS) {
16451 printf("rpc_call(RPC_TEST3_CXX) status %d\n", status);
16452 return status;
16453 }
16454
16455 if (int_out != 789) {
16456 printf("int_out mismatch!\n");
16457 status = 0;
16458 }
16459
16460 if (int_inout != 456*2) {
16461 printf("int_inout mismatch!\n");
16462 status = 0;
16463 }
16464
16465 if (strcmp(string_out, "string_out") != 0) {
16466 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16467 status = 0;
16468 }
16469
16470 if (string2_out != "second string_out") {
16471 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16472 status = 0;
16473 }
16474
16475 if (string_inout != "return string_inout") {
16476 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16477 status = 0;
16478 }
16479
16480 KEY* pkey;
16481
16482 pkey = &struct_in;
16483
16484 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16485
16486 pkey = &struct_out;
16487
16488 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16489 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);
16490 status = 0;
16491 }
16492
16493 pkey = &struct_inout;
16494
16495 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16496 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);
16497 status = 0;
16498 }
16499
16500 if (dwordarray_inout_size != 4*5) {
16501 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16502 status = 0;
16503 } else {
16504 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16505 if (dwordarray_inout[i] != i*10+i) {
16506 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16507 status = 0;
16508 }
16509 }
16510 }
16511
16512 //printf("array_out_size %d\n", array_out_size);
16513 //for (int i=0; i<array_out_size; i++) {
16514 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16515 //}
16516
16517 if (array_out.size() != 15) {
16518 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16519 status = 0;
16520 } else {
16521 if (strcmp(array_out.data(), "test test test") != 0) {
16522 printf("array_out data mismatch\n");
16523 status = 0;
16524 }
16525 }
16526
16527 return status;
16528}
16529
16530/********************************************************************/
16532/********************************************************************\
16533 Routine: rpc_test_rpc
16534
16535 Purpose: Test RPC parameters encoding and decoding
16536
16537 Input:
16538 none
16539
16540 Output:
16541 none
16542
16543 Function value:
16544 RPC_SUCCESS Successful completion
16545
16546\********************************************************************/
16547{
16548 int status = RPC_SUCCESS;
16549
16550 printf("rpc_test_rpc_test4_cxx!\n");
16551
16552 int int_out = 0;
16553 int int_inout = 456;
16554
16555 std::string string_in = "test string";
16556 std::string string_out;
16557 std::string string_inout = "string_inout";
16558
16559 std::vector<char> array_in;
16560 int array_in_size = 10;
16561
16562 for (int i=0; i<array_in_size; i++) {
16563 array_in.push_back('a' + i);
16564 }
16565
16566 std::vector<char> array_out;
16567
16568 std::vector<char> array_inout;
16569 int array_inout_size = 6;
16570
16571 for (int i=0; i<array_inout_size; i++) {
16572 array_inout.push_back('0' + i);
16573 }
16574
16576 123,
16577 &int_out,
16578 &int_inout,
16579 &string_in,
16580 &string_out,
16581 &string_inout,
16582 &array_in,
16583 &array_out,
16584 &array_inout
16585 );
16586
16587 if (status != RPC_SUCCESS) {
16588 printf("rpc_call(RPC_TEST4_CXX) status %d\n", status);
16589 return status;
16590 }
16591
16592 if (int_out != 789) {
16593 printf("int_out mismatch!\n");
16594 status = 0;
16595 }
16596
16597 if (int_inout != 456*2) {
16598 printf("int_inout mismatch!\n");
16599 status = 0;
16600 }
16601
16602 if (string_out != "return string_out") {
16603 printf("string_out mismatch [%s] vs [%s]\n", string_out.c_str(), "return string_out");
16604 status = 0;
16605 }
16606
16607 if (string_inout != "return string_inout") {
16608 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16609 status = 0;
16610 }
16611
16612 if (array_out.size() != 15) {
16613 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16614 status = 0;
16615 } else {
16616 if (strcmp(array_out.data(), "test test test") != 0) {
16617 printf("array_out data mismatch\n");
16618 status = 0;
16619 }
16620 }
16621
16622 if (array_inout.size() != 12) {
16623 printf("array_inout.size() mismatch %d vs %d\n", (int)array_inout.size(), 12);
16624 status = 0;
16625 } else {
16626 for (int i=0; i<6; i++) {
16627 if (array_inout[i] != '0' + i) {
16628 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], ('0'+i));
16629 status = 0;
16630 }
16631 }
16632 for (int i=6; i<12; i++) {
16633 if (array_inout[i] != 2*('0' + (i-6))) {
16634 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], 2*('0'+i));
16635 status = 0;
16636 }
16637 }
16638 }
16639
16640 return status;
16641}
16642
16643/********************************************************************/
16645/********************************************************************\
16646 Routine: rpc_test_rpc
16647
16648 Purpose: Test RPC parameters encoding and decoding
16649
16650 Input:
16651 none
16652
16653 Output:
16654 none
16655
16656 Function value:
16657 RPC_SUCCESS Successful completion
16658
16659\********************************************************************/
16660{
16661 int status;
16663 if (status != RPC_SUCCESS)
16664 return status;
16666 if (status != RPC_SUCCESS)
16667 return status;
16669 if (status != RPC_SUCCESS)
16670 return status;
16672 if (status != RPC_SUCCESS)
16673 return status;
16674 return RPC_SUCCESS;
16675}
16676
16677static std::atomic_bool gAllowedHostsEnabled(false);
16678static std::vector<std::string> gAllowedHosts;
16679static std::mutex gAllowedHostsMutex;
16680
16681/********************************************************************/
16683/********************************************************************\
16684 Routine: rpc_clear_allowed_hosts
16685
16686 Purpose: Clear list of allowed hosts and permit connections from anybody
16687
16688 Input:
16689 none
16690
16691 Output:
16692 none
16693
16694 Function value:
16695 RPC_SUCCESS Successful completion
16696
16697\********************************************************************/
16698{
16699 gAllowedHostsMutex.lock();
16700 gAllowedHosts.clear();
16701 gAllowedHostsEnabled = false;
16702 gAllowedHostsMutex.unlock();
16703 return RPC_SUCCESS;
16704}
16705
16706/********************************************************************/
16707INT rpc_add_allowed_host(const char *hostname)
16708/********************************************************************\
16709 Routine: rpc_add_allowed_host
16710
16711 Purpose: Permit connections from listed hosts only
16712
16713 Input:
16714 none
16715
16716 Output:
16717 none
16718
16719 Function value:
16720 RPC_SUCCESS Successful completion
16721 RPC_NO_MEMORY Too many allowed hosts
16722
16723\********************************************************************/
16724{
16725 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
16726
16727 gAllowedHostsMutex.lock();
16728 gAllowedHosts.push_back(hostname);
16729 gAllowedHostsEnabled = true;
16730 gAllowedHostsMutex.unlock();
16731
16732 return RPC_SUCCESS;
16733}
16734
16735/********************************************************************/
16736INT rpc_check_allowed_host(const char *hostname)
16737/********************************************************************\
16738 Routine: rpc_check_allowed_host
16739
16740 Purpose: Check if hostname is permitted to connect
16741
16742 Function value:
16743 RPC_SUCCESS hostname is permitted to connect
16744 RPC_NOT_REGISTERED hostname not permitted to connect
16745
16746\********************************************************************/
16747{
16748 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
16749
16751 return RPC_SUCCESS;
16752
16753 if (strcmp(hostname, "localhost") == 0)
16754 return RPC_SUCCESS;
16755
16756 if (strcmp(hostname, "localhost.localdomain") == 0)
16757 return RPC_SUCCESS;
16758
16759 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
16760 return RPC_SUCCESS;
16761
16762 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
16763 return RPC_SUCCESS;
16764
16766
16767 gAllowedHostsMutex.lock();
16768
16769 for (const auto& h: gAllowedHosts) {
16770 if (h == hostname) {
16772 break;
16773 }
16774 }
16775
16776 gAllowedHostsMutex.unlock();
16777
16778 //if (status != RPC_SUCCESS)
16779 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
16780
16781 return status;
16782}
16783
16784/*------------------------------------------------------------------*/
16786{
16787 std::string hostname;
16788
16789 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
16790
16791 if (status != SS_SUCCESS)
16792 return status;
16793
16794 status = rpc_check_allowed_host(hostname.c_str());
16795
16796 if (status == RPC_SUCCESS)
16797 return RPC_SUCCESS;
16798
16799 static std::atomic_int max_report(10);
16800 if (max_report > 0) {
16801 max_report--;
16802 if (max_report == 0) {
16803 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
16804 } else {
16805 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());
16806 }
16807 }
16808
16809 return RPC_NET_ERROR;
16810}
16811
16812/********************************************************************/
16814/********************************************************************\
16815
16816 Routine: rpc_server_accept
16817
16818 Purpose: Accept new incoming connections
16819
16820 Input:
16821 INT lscok Listen socket
16822
16823 Output:
16824 none
16825
16826 Function value:
16827 RPC_SUCCESS Successful completion
16828 RPC_NET_ERROR Error in socket call
16829 RPC_CONNCLOSED Connection was closed
16830 RPC_SHUTDOWN Listener shutdown
16831 RPC_EXCEED_BUFFER Not enough memory for network buffer
16832
16833\********************************************************************/
16834{
16835 INT i;
16836 INT sock;
16837 char version[NAME_LENGTH], v1[32];
16838 char experiment[NAME_LENGTH];
16839 INT port1, port2, port3;
16840 char *ptr;
16841 char net_buffer[256];
16842 struct linger ling;
16843
16844 static struct callback_addr callback;
16845
16846 if (lsock > 0) {
16847 sock = accept(lsock, NULL, NULL);
16848
16849 if (sock == -1)
16850 return RPC_NET_ERROR;
16851 } else {
16852 /* lsock is stdin -> already connected from inetd */
16853
16854 sock = lsock;
16855 }
16856
16857 /* check access control list */
16860
16861 if (status != RPC_SUCCESS) {
16862 ss_socket_close(&sock);
16863 return RPC_NET_ERROR;
16864 }
16865 }
16866
16867 /* receive string with timeout */
16868 i = recv_string(sock, net_buffer, 256, 10000);
16869 rpc_debug_printf("Received command: %s", net_buffer);
16870
16871 if (i > 0) {
16872 char command = (char) toupper(net_buffer[0]);
16873
16874 //printf("rpc_server_accept: command [%c]\n", command);
16875
16876 switch (command) {
16877 case 'S': {
16878
16879 /*----------- shutdown listener ----------------------*/
16880 ss_socket_close(&sock);
16881 return RPC_SHUTDOWN;
16882 }
16883 case 'I': {
16884
16885 /*----------- return available experiments -----------*/
16886#ifdef LOCAL_ROUTINES
16887 exptab_struct exptab;
16888 cm_read_exptab(&exptab); // thread safe!
16889 for (unsigned i=0; i<exptab.exptab.size(); i++) {
16890 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
16891 const char* str = exptab.exptab[i].name.c_str();
16892 send(sock, str, strlen(str) + 1, 0);
16893 }
16894 send(sock, "", 1, 0);
16895#endif
16896 ss_socket_close(&sock);
16897 break;
16898 }
16899 case 'C': {
16900
16901 /*----------- connect to experiment -----------*/
16902
16903 /* get callback information */
16904 callback.experiment[0] = 0;
16905 port1 = port2 = version[0] = 0;
16906
16907 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
16908
16909 /* parse string in format "C port1 port2 port3 version expt" */
16910 /* example: C 51046 45838 56832 2.0.0 alpha */
16911
16912 port1 = strtoul(net_buffer + 2, &ptr, 0);
16913 port2 = strtoul(ptr, &ptr, 0);
16914 port3 = strtoul(ptr, &ptr, 0);
16915
16916 while (*ptr == ' ')
16917 ptr++;
16918
16919 i = 0;
16920 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
16921 version[i++] = *ptr++;
16922
16923 // ensure that we do not overwrite buffer "version"
16924 assert(i < (int) sizeof(version));
16925 version[i] = 0;
16926
16927 // skip wjatever is left from the "version" string
16928 for (; *ptr != 0 && *ptr != ' ';)
16929 ptr++;
16930
16931 while (*ptr == ' ')
16932 ptr++;
16933
16934 i = 0;
16935 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
16936 experiment[i++] = *ptr++;
16937
16938 // ensure that we do not overwrite buffer "experiment"
16939 assert(i < (int) sizeof(experiment));
16940 experiment[i] = 0;
16941
16943
16944 /* print warning if version patch level doesn't agree */
16945 mstrlcpy(v1, version, sizeof(v1));
16946 if (strchr(v1, '.'))
16947 if (strchr(strchr(v1, '.') + 1, '.'))
16948 *strchr(strchr(v1, '.') + 1, '.') = 0;
16949
16950 char str[100];
16951 mstrlcpy(str, cm_get_version(), sizeof(str));
16952 if (strchr(str, '.'))
16953 if (strchr(strchr(str, '.') + 1, '.'))
16954 *strchr(strchr(str, '.') + 1, '.') = 0;
16955
16956 if (strcmp(v1, str) != 0) {
16957 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
16958 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
16959 }
16960
16961 callback.host_port1 = (short) port1;
16962 callback.host_port2 = (short) port2;
16963 callback.host_port3 = (short) port3;
16965
16967
16968 if (status != SS_SUCCESS) {
16969 ss_socket_close(&sock);
16970 break;
16971 }
16972
16973#ifdef LOCAL_ROUTINES
16974 /* update experiment definition */
16975 exptab_struct exptab;
16976 cm_read_exptab(&exptab); // thread safe!
16977
16978 unsigned idx = 0;
16979 bool found = false;
16980 /* lookup experiment */
16981 if (equal_ustring(callback.experiment.c_str(), "Default")) {
16982 found = true;
16983 idx = 0;
16984 } else {
16985 for (idx = 0; idx < exptab.exptab.size(); idx++) {
16986 if (exptab.exptab[idx].name == callback.experiment) {
16987 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
16988 found = true;
16989 break;
16990 }
16991 }
16992 }
16993 }
16994
16995 if (!found) {
16996 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
16997
16998 send(sock, "2", 2, 0); /* 2 means exp. not found */
16999 ss_socket_close(&sock);
17000 break;
17001 }
17002
17003 callback.directory = exptab.exptab[idx].directory;
17004 callback.user = exptab.exptab[idx].user;
17005
17006 /* create a new process */
17007 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
17008 char debug_str[30];
17009
17010 sprintf(host_port1_str, "%d", callback.host_port1);
17011 sprintf(host_port2_str, "%d", callback.host_port2);
17012 sprintf(host_port3_str, "%d", callback.host_port3);
17013 sprintf(debug_str, "%d", callback.debug);
17014
17015 const char *mserver_path = rpc_get_mserver_path();
17016
17017 const char *argv[10];
17018 argv[0] = mserver_path;
17019 argv[1] = callback.host_name.c_str();
17020 argv[2] = host_port1_str;
17021 argv[3] = host_port2_str;
17022 argv[4] = host_port3_str;
17023 argv[5] = debug_str;
17024 argv[6] = callback.experiment.c_str();
17025 argv[7] = callback.directory.c_str();
17026 argv[8] = callback.user.c_str();
17027 argv[9] = NULL;
17028
17029 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
17030 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
17031 argv[9]);
17032
17033 status = ss_spawnv(P_NOWAIT, mserver_path, argv);
17034
17035 if (status != SS_SUCCESS) {
17036 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
17037
17038 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
17039 send(sock, str, strlen(str) + 1, 0);
17040 ss_socket_close(&sock);
17041 break;
17042 }
17043
17044 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
17045 send(sock, str, strlen(str) + 1, 0);
17046#endif // LOCAL_ROUTINES
17047 ss_socket_close(&sock);
17048
17049 break;
17050 }
17051 default: {
17052 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
17053 ss_socket_close(&sock);
17054 break;
17055 }
17056 }
17057 } else { /* if i>0 */
17058
17059 /* lingering needed for PCTCP */
17060 ling.l_onoff = 1;
17061 ling.l_linger = 0;
17062 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17063 ss_socket_close(&sock);
17064 }
17065
17066 return RPC_SUCCESS;
17067}
17068
17069/********************************************************************/
17071/********************************************************************\
17072
17073 Routine: rpc_client_accept
17074
17075 Purpose: midas program accept new RPC connection (run transitions, etc)
17076
17077 Input:
17078 INT lsock Listen socket
17079
17080 Output:
17081 none
17082
17083 Function value:
17084 RPC_SUCCESS Successful completion
17085 RPC_NET_ERROR Error in socket call
17086 RPC_CONNCLOSED Connection was closed
17087 RPC_SHUTDOWN Listener shutdown
17088 RPC_EXCEED_BUFFER Not enough memory for network buffer
17089
17090\********************************************************************/
17091{
17092 INT i, status;
17093 INT client_hw_type = 0, hw_type;
17094 std::string client_program;
17095 std::string host_name;
17096 INT convert_flags;
17097 char net_buffer[256], *p;
17098
17099 int sock = accept(lsock, NULL, NULL);
17100
17101 if (sock == -1)
17102 return RPC_NET_ERROR;
17103
17104 /* check access control list */
17107
17108 if (status != RPC_SUCCESS) {
17109 ss_socket_close(&sock);
17110 return RPC_NET_ERROR;
17111 }
17112 }
17113
17114 host_name = "(unknown)";
17115 client_program = "(unknown)";
17116
17117 /* receive string with timeout */
17118 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
17119 if (i <= 0) {
17120 ss_socket_close(&sock);
17121 return RPC_NET_ERROR;
17122 }
17123
17124 /* get remote computer info */
17125 p = strtok(net_buffer, " ");
17126 if (p != NULL) {
17127 client_hw_type = atoi(p);
17128 p = strtok(NULL, " ");
17129 }
17130 if (p != NULL) {
17131 //version = atoi(p);
17132 p = strtok(NULL, " ");
17133 }
17134 if (p != NULL) {
17135 client_program = p;
17136 p = strtok(NULL, " ");
17137 }
17138 if (p != NULL) {
17139 host_name = p;
17140 p = strtok(NULL, " ");
17141 }
17142
17143 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
17144
17146
17147 /* save information in _server_acception structure */
17148 sa->recv_sock = sock;
17149 sa->send_sock = 0;
17150 sa->event_sock = 0;
17151 sa->remote_hw_type = client_hw_type;
17152 sa->host_name = host_name;
17153 sa->prog_name = client_program;
17155 sa->watchdog_timeout = 0;
17156 sa->is_mserver = FALSE;
17157
17158 /* send my own computer id */
17159 hw_type = rpc_get_hw_type();
17160 std::string str = msprintf("%d %s", hw_type, cm_get_version());
17161 status = send(sock, str.c_str(), str.length() + 1, 0);
17162 if (status != (INT) str.length() + 1)
17163 return RPC_NET_ERROR;
17164
17165 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17166 sa->convert_flags = convert_flags;
17167
17169
17170 return RPC_SUCCESS;
17171}
17172
17173/********************************************************************/
17175/********************************************************************\
17176
17177 Routine: rpc_server_callback
17178
17179 Purpose: Callback a remote client. Setup _server_acception entry
17180 with optional conversion flags and establish two-way
17181 TCP connection.
17182
17183 Input:
17184 callback_addr pcallback Pointer to a callback structure
17185
17186 Output:
17187 none
17188
17189 Function value:
17190 RPC_SUCCESS Successful completion
17191
17192\********************************************************************/
17193{
17194 INT status;
17195 int recv_sock, send_sock, event_sock;
17196 char str[100];
17197 std::string client_program;
17198 INT client_hw_type, hw_type;
17199 INT convert_flags;
17200 char net_buffer[256];
17201 char *p;
17202 int flag;
17203
17204 /* copy callback information */
17205 struct callback_addr callback = *pcallback;
17206 //idx = callback.index;
17207
17208 std::string errmsg;
17209
17210 /* create new sockets for TCP */
17211 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
17212
17213 if (status != SS_SUCCESS) {
17214 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());
17215 ss_socket_close(&recv_sock);
17216 //ss_socket_close(&send_sock);
17217 //ss_socket_close(&event_sock);
17218 return RPC_NET_ERROR;
17219 }
17220
17221 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
17222
17223 if (status != SS_SUCCESS) {
17224 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());
17225 ss_socket_close(&recv_sock);
17226 ss_socket_close(&send_sock);
17227 //ss_socket_close(&event_sock);
17228 return RPC_NET_ERROR;
17229 }
17230
17231 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
17232
17233 if (status != SS_SUCCESS) {
17234 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());
17235 ss_socket_close(&recv_sock);
17236 ss_socket_close(&send_sock);
17237 ss_socket_close(&event_sock);
17238 return RPC_NET_ERROR;
17239 }
17240#ifndef OS_ULTRIX /* crashes ULTRIX... */
17241 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
17242 flag = 2 * 1024 * 1024;
17243 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
17244 if (status != 0)
17245 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
17246 strerror(errno));
17247#endif
17248
17249 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
17250 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
17251 ss_socket_close(&recv_sock);
17252 ss_socket_close(&send_sock);
17253 ss_socket_close(&event_sock);
17254 return RPC_NET_ERROR;
17255 }
17256 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
17257
17258 /* get remote computer info */
17259 client_hw_type = strtoul(net_buffer, &p, 0);
17260
17261 while (*p == ' ')
17262 p++;
17263
17264 client_program = p;
17265
17266 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
17267
17268 std::string host_name;
17269
17270 status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
17271
17272 if (status != SS_SUCCESS)
17273 host_name = "unknown";
17274
17275 //printf("rpc_server_callback: mserver acception\n");
17276
17278
17279 /* save information in _server_acception structure */
17280 sa->recv_sock = recv_sock;
17281 sa->send_sock = send_sock;
17282 sa->event_sock = event_sock;
17283 sa->remote_hw_type = client_hw_type;
17284 sa->host_name = host_name;
17285 sa->prog_name = client_program;
17287 sa->watchdog_timeout = 0;
17288 sa->is_mserver = TRUE;
17289
17290 assert(_mserver_acception == NULL);
17291
17292 _mserver_acception = sa;
17293
17294 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
17295
17296 /* send my own computer id */
17297 hw_type = rpc_get_hw_type();
17298 sprintf(str, "%d", hw_type);
17299 send(recv_sock, str, strlen(str) + 1, 0);
17300
17301 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17302 sa->convert_flags = convert_flags;
17303
17305
17306 if (rpc_is_mserver()) {
17307 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
17308 }
17309
17310 return RPC_SUCCESS;
17311}
17312
17313
17314/********************************************************************/
17316/********************************************************************\
17317
17318 Routine: rpc_server_loop
17319
17320 Purpose: mserver main event loop
17321
17322\********************************************************************/
17323{
17324 while (1) {
17325 int status = ss_suspend(1000, 0);
17326
17327 if (status == SS_ABORT || status == SS_EXIT)
17328 break;
17329
17331 break;
17332
17333 /* check alarms, etc */
17335
17337 }
17338
17339 return RPC_SUCCESS;
17340}
17341
17342/********************************************************************/
17344/********************************************************************\
17345
17346 Routine: rpc_server_receive_rpc
17347
17348 Purpose: Receive rpc commands and execute them. Close the connection
17349 if client has broken TCP pipe.
17350
17351 Function value:
17352 RPC_SUCCESS Successful completion
17353 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17354 SS_EXIT Server connection was closed
17355 SS_ABORT Server connection was broken
17356
17357\********************************************************************/
17358{
17359 int status = 0;
17360 int remaining = 0;
17361
17362 char *buf = NULL;
17363 int bufsize = 0;
17364
17365 do {
17366 int n_received = recv_net_command_realloc(sa, &buf, &bufsize, &remaining);
17367
17368 if (n_received <= 0) {
17369 status = SS_ABORT;
17370 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d", n_received);
17371 goto error;
17372 }
17373
17374 /* extract pointer array to parameters */
17375 NET_COMMAND nc_in;
17376
17377 memcpy(&nc_in, buf, sizeof(nc_in));
17378
17379 /* convert header format (byte swapping) */
17380 if (sa->convert_flags) {
17383 }
17384
17385 int routine_id = nc_in.header.routine_id & ~RPC_NO_REPLY;
17386
17387 RPC_LIST rpc_entry;
17388 bool rpc_cxx = false;
17389
17390 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
17391
17392 if (status != RPC_SUCCESS) {
17393 cm_msg(MERROR, "rpc_server_receive_rpc", "Unknown RPC routine_id %d", routine_id);
17394 goto error;
17395 }
17396
17397 if (rpc_cxx)
17398 status = rpc_execute_cxx(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17399 else
17400 status = rpc_execute_old(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17401
17402 if (status == SS_ABORT) {
17403 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
17404 goto error;
17405 }
17406
17407 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
17408 if (rpc_is_mserver())
17409 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
17410 goto exit;
17411 }
17412
17413 } while (remaining);
17414
17415 if (buf) {
17416 free(buf);
17417 buf = NULL;
17418 bufsize = 0;
17419 }
17420
17421 return RPC_SUCCESS;
17422
17423 error:
17424
17425 {
17426 char str[80];
17427 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17428 if (strchr(str, '.'))
17429 *strchr(str, '.') = 0;
17430 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17431 }
17432
17433 exit:
17434
17436
17437 if (buf) {
17438 free(buf);
17439 buf = NULL;
17440 bufsize = 0;
17441 }
17442
17443 /* disconnect from experiment as MIDAS server */
17444 if (rpc_is_mserver()) {
17445
17446 if (status != SS_EXIT)
17447 cm_msg(MERROR, "rpc_server_receive_rpc", "mserver unexpected shutdown, status %d", status);
17448
17449 HNDLE hDB, hKey;
17450
17452
17453 /* only disconnect from experiment if previously connected.
17454 Necessary for pure RPC servers (RPC_SRVR) */
17455 if (hDB) {
17459
17461
17463 }
17464 }
17465
17466 bool is_mserver = sa->is_mserver;
17467
17468 sa->close();
17469
17470 /* signal caller a shutdonw */
17471 if (status == RPC_SHUTDOWN)
17472 return status;
17473
17474 /* only the mserver should stop on server connection closure */
17475 if (!is_mserver) {
17476 return SS_SUCCESS;
17477 }
17478
17479 return status;
17480}
17481
17482/********************************************************************/
17484/********************************************************************\
17485
17486 Routine: rpc_server_receive_event
17487
17488 Purpose: Receive event and dispatch it
17489
17490 Function value:
17491 RPC_SUCCESS Successful completion
17492 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17493 SS_EXIT Server connection was closed
17494 SS_ABORT Server connection was broken
17495
17496\********************************************************************/
17497{
17498 int status = 0;
17499
17500 DWORD start_time = ss_millitime();
17501
17502 //
17503 // THIS IS NOT THREAD SAFE!!!
17504 //
17505 // IT IS ONLY USED BY THE MSERVER
17506 // MSERVER IS SINGLE-THREADED!!!
17507 //
17508
17509 static char *xbuf = NULL;
17510 static int xbufsize = 0;
17511 static bool xbufempty = true;
17512
17513 // short cut
17514 if (sa == NULL && xbufempty)
17515 return RPC_SUCCESS;
17516
17517 static bool recurse = false;
17518
17519 if (recurse) {
17520 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
17521 // do not do anything if we are called recursively
17522 // via recursive ss_suspend() or otherwise. K.O.
17523 if (xbufempty)
17524 return RPC_SUCCESS;
17525 else
17526 return BM_ASYNC_RETURN;
17527 }
17528
17529 recurse = true;
17530
17531 do {
17532 if (xbufempty && sa) {
17533 int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
17534
17535 if (n_received < 0) {
17536 status = SS_ABORT;
17537 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
17538 goto error;
17539 }
17540
17541 if (n_received == 0) {
17542 // no more data in the tcp socket
17543 recurse = false;
17544 return RPC_SUCCESS;
17545 }
17546
17547 xbufempty = false;
17548 }
17549
17550 if (xbufempty) {
17551 // no event in xbuf buffer
17552 recurse = false;
17553 return RPC_SUCCESS;
17554 }
17555
17556 /* send event to buffer */
17557 INT *pbh = (INT *) xbuf;
17558 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
17559
17560 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
17561
17562 //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);
17563
17564 if (status == SS_ABORT) {
17565 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
17566 goto error;
17567 }
17568
17569 if (status == BM_ASYNC_RETURN) {
17570 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
17571 recurse = false;
17572 return status;
17573 }
17574
17575 if (status != BM_SUCCESS) {
17576 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
17577 }
17578
17579 xbufempty = true;
17580
17581 /* repeat for maximum 0.5 sec */
17582 } while (ss_millitime() - start_time < 500);
17583
17584 recurse = false;
17585 return RPC_SUCCESS;
17586
17587 error:
17588
17589 {
17590 char str[80];
17591 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17592 if (strchr(str, '.'))
17593 *strchr(str, '.') = 0;
17594 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17595 }
17596
17597 //exit:
17598
17600
17601 /* disconnect from experiment as MIDAS server */
17602 if (rpc_is_mserver()) {
17603 HNDLE hDB, hKey;
17604
17606
17607 /* only disconnect from experiment if previously connected.
17608 Necessary for pure RPC servers (RPC_SRVR) */
17609 if (hDB) {
17613
17615
17617 }
17618 }
17619
17620 bool is_mserver = sa->is_mserver;
17621
17622 sa->close();
17623
17624 /* signal caller a shutdonw */
17625 if (status == RPC_SHUTDOWN)
17626 return status;
17627
17628 /* only the mserver should stop on server connection closure */
17629 if (!is_mserver) {
17630 return SS_SUCCESS;
17631 }
17632
17633 return status;
17634}
17635
17636
17637/********************************************************************/
17638int rpc_flush_event_socket(int timeout_msec)
17639/********************************************************************\
17640
17641 Routine: rpc_flush_event_socket
17642
17643 Purpose: Receive and en-buffer events from the mserver event socket
17644
17645 Function value:
17646 BM_SUCCESS Event socket is empty, all data was read an en-buffered
17647 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
17648 SS_EXIT Server connection was closed
17649 SS_ABORT Server connection was broken
17650
17651\********************************************************************/
17652{
17653 bool has_data = ss_event_socket_has_data();
17654
17655 //printf("ss_event_socket_has_data() returned %d\n", has_data);
17656
17657 if (has_data) {
17658 if (timeout_msec == BM_NO_WAIT) {
17659 return BM_ASYNC_RETURN;
17660 } else if (timeout_msec == BM_WAIT) {
17661 return BM_ASYNC_RETURN;
17662 } else {
17663 int status = ss_suspend(timeout_msec, MSG_BM);
17664 if (status == SS_ABORT || status == SS_EXIT)
17665 return status;
17666 return BM_ASYNC_RETURN;
17667 }
17668 }
17669
17670 int status = rpc_server_receive_event(0, NULL, timeout_msec);
17671
17672 //printf("rpc_server_receive_event() status %d\n", status);
17673
17674 if (status == BM_ASYNC_RETURN) {
17675 return BM_ASYNC_RETURN;
17676 }
17677
17678 if (status == SS_ABORT || status == SS_EXIT)
17679 return status;
17680
17681 return BM_SUCCESS;
17682}
17683
17684/********************************************************************/
17686/********************************************************************\
17687
17688 Routine: rpc_server_shutdown
17689
17690 Purpose: Shutdown RPC server, abort all connections
17691
17692 Input:
17693 none
17694
17695 Output:
17696 none
17697
17698 Function value:
17699 RPC_SUCCESS Successful completion
17700
17701\********************************************************************/
17702{
17703 //printf("rpc_server_shutdown!\n");
17704
17705 struct linger ling;
17706
17707 /* close all open connections */
17708 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17709 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
17711 /* lingering needed for PCTCP */
17712 ling.l_onoff = 1;
17713 ling.l_linger = 0;
17714 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17716
17717 if (sa->send_sock) {
17718 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17720 }
17721
17722 if (sa->event_sock) {
17723 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17725 }
17726 }
17727 }
17728
17729 /* avoid memory leak */
17730 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17732 if (sa) {
17733 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
17734 if (sa == _mserver_acception) {
17735 // do not leave behind a stale pointer!
17736 _mserver_acception = NULL;
17737 }
17738 delete sa;
17739 _server_acceptions[idx] = NULL;
17740 }
17741 }
17742
17743 if (_rpc_registered) {
17746 }
17747
17748 /* free suspend structures */
17750
17751 return RPC_SUCCESS;
17752}
17753
17754
17755/********************************************************************/
17757/********************************************************************\
17758
17759 Routine: rpc_check_channels
17760
17761 Purpose: Check open rpc channels by sending watchdog messages
17762
17763 Input:
17764 none
17765
17766 Output:
17767 none
17768
17769 Function value:
17770 RPC_SUCCESS Channel is still alive
17771 RPC_NET_ERROR Connection is broken
17772
17773\********************************************************************/
17774{
17775 INT status;
17776 NET_COMMAND nc;
17777 fd_set readfds;
17778 struct timeval timeout;
17779
17780 //printf("rpc_check_channels!\n");
17781
17782 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17783 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
17785 if (sa == NULL)
17786 continue;
17787
17788 if (sa->watchdog_timeout == 0) {
17789 continue;
17790 }
17791
17792 DWORD elapsed = ss_millitime() - sa->last_activity;
17793
17794 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
17795
17796 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
17797
17798 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
17799
17800 /* send a watchdog message */
17802 nc.header.param_size = 0;
17803
17804 int convert_flags = sa->convert_flags;
17805 if (convert_flags) {
17808 }
17809
17810 /* send the header to the client */
17811 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
17812
17813 if (i < 0) {
17814 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
17815 sa->prog_name.c_str(),
17816 sa->host_name.c_str(),
17817 sa->watchdog_timeout / 1000,
17818 i);
17819
17820 /* disconnect from experiment */
17821 if (rpc_is_mserver()) {
17823 return RPC_NET_ERROR;
17824 }
17825
17826 sa->close();
17827 return RPC_NET_ERROR;
17828 }
17829
17830 DWORD timeout_end_ms = ss_millitime() + sa->watchdog_timeout;
17831
17832 while (1) {
17833 FD_ZERO(&readfds);
17834 FD_SET(sa->send_sock, &readfds);
17835 FD_SET(sa->recv_sock, &readfds);
17836
17837 timeout.tv_sec = 1;
17838 timeout.tv_usec = 0;
17839
17840 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
17841
17842 DWORD now = ss_millitime();
17843
17844 //printf("waiting for reply: %d %d, diff %d, select() status %d\n", now, timeout_end_ms, timeout_end_ms - now, status);
17845
17846 if (now > timeout_end_ms) // timeout
17847 break;
17848
17849 if (status > 0) // select has something to read
17850 break;
17851
17852 // select() returned 0, timeout
17853 // select() returned -1, error, likely EAGAIN or EINTR
17854
17857 }
17858
17859 if (!FD_ISSET(sa->send_sock, &readfds) &&
17860 !FD_ISSET(sa->recv_sock, &readfds)) {
17861
17862 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
17863 sa->prog_name.c_str(),
17864 sa->host_name.c_str(),
17865 sa->watchdog_timeout / 1000);
17866
17867 /* disconnect from experiment */
17868 if (rpc_is_mserver()) {
17870 return RPC_NET_ERROR;
17871 }
17872
17873 sa->close();
17874 return RPC_NET_ERROR;
17875 }
17876
17877 /* receive result on send socket */
17878 if (FD_ISSET(sa->send_sock, &readfds)) {
17879 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
17880 if (i <= 0) {
17881 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
17882 sa->prog_name.c_str(),
17883 sa->host_name.c_str(),
17884 sa->watchdog_timeout / 1000,
17885 i);
17886
17887 /* disconnect from experiment */
17888 if (rpc_is_mserver()) {
17890 return RPC_NET_ERROR;
17891 }
17892
17893 sa->close();
17894 return RPC_NET_ERROR;
17895 }
17896 }
17897 }
17898 }
17899 }
17900
17901 return RPC_SUCCESS;
17902}
17903
17911/********************************************************************\
17912* *
17913* Bank functions *
17914* *
17915\********************************************************************/
17916
17917/********************************************************************/
17923void bk_init(void *event) {
17924 ((BANK_HEADER *) event)->data_size = 0;
17925 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
17926}
17927
17929#ifndef DOXYGEN_SHOULD_SKIP_THIS
17930
17931/********************************************************************/
17932BOOL bk_is32(const void *event)
17933/********************************************************************\
17934
17935 Routine: bk_is32
17936
17937 Purpose: Return true if banks inside event are 32-bit banks
17938
17939 Input:
17940 void *event pointer to the event
17941
17942 Output:
17943 none
17944
17945 Function value:
17946 none
17947
17948\********************************************************************/
17949{
17950 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
17951}
17952
17953/********************************************************************/
17954BOOL bk_is32a(const void *event)
17955/********************************************************************\
17956
17957 Routine: bk_is32a
17958
17959 Purpose: Return true if banks inside event are 32-bit banks
17960 and banks are 64-bit aligned
17961
17962 Input:
17963 void *event pointer to the event
17964
17965 Output:
17966 none
17967
17968 Function value:
17969 none
17970
17971\********************************************************************/
17972{
17973 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
17974}
17975
17977#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17978
17979/********************************************************************/
17986void bk_init32(void *event) {
17987 ((BANK_HEADER *) event)->data_size = 0;
17988 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
17989}
17990
17991/********************************************************************/
17999void bk_init32a(void *event) {
18000 ((BANK_HEADER *) event)->data_size = 0;
18002}
18003
18004/********************************************************************/
18012INT bk_size(const void *event) {
18013 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
18014}
18015
18016static void copy_bk_name(char* dst, const char* src)
18017{
18018 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
18019
18020 if (src[0] == 0) {
18021 // invalid empty name
18022 dst[0] = 0;
18023 dst[1] = 0;
18024 dst[2] = 0;
18025 dst[3] = 0;
18026 return;
18027 }
18028
18029 dst[0] = src[0];
18030
18031 if (src[1] == 0) {
18032 dst[1] = 0;
18033 dst[2] = 0;
18034 dst[3] = 0;
18035 return;
18036 }
18037
18038 dst[1] = src[1];
18039
18040 if (src[2] == 0) {
18041 dst[2] = 0;
18042 dst[3] = 0;
18043 return;
18044 }
18045
18046 dst[2] = src[2];
18047
18048 if (src[3] == 0) {
18049 dst[3] = 0;
18050 return;
18051 }
18052
18053 dst[3] = src[3];
18054}
18055
18056/********************************************************************/
18078void bk_create(void *event, const char *name, WORD type, void **pdata) {
18079 if (bk_is32a((BANK_HEADER *) event)) {
18080 if (((PTYPE) event & 0x07) != 0) {
18081 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
18082 return;
18083 }
18084 BANK32A *pbk32a;
18085
18086 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18087 copy_bk_name(pbk32a->name, name);
18088 pbk32a->type = type;
18089 pbk32a->data_size = 0;
18090 *pdata = pbk32a + 1;
18091 } else if (bk_is32((BANK_HEADER *) event)) {
18092 BANK32 *pbk32;
18093
18094 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18095 copy_bk_name(pbk32->name, name);
18096 pbk32->type = type;
18097 pbk32->data_size = 0;
18098 *pdata = pbk32 + 1;
18099 } else {
18100 BANK *pbk;
18101
18102 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18103 copy_bk_name(pbk->name, name);
18104 pbk->type = type;
18105 pbk->data_size = 0;
18106 *pdata = pbk + 1;
18107 }
18108}
18109
18111#ifndef DOXYGEN_SHOULD_SKIP_THIS
18112
18113/********************************************************************/
18121INT bk_copy(char *pevent, char *psrce, const char *bkname) {
18122
18123 INT status;
18124 DWORD bklen, bktype, bksze;
18125 BANK_HEADER *psBkh;
18126 BANK *psbkh;
18127 char *pdest;
18128 void *psdata;
18129
18130 // source pointing on the BANKxx
18131 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
18132 // Find requested bank
18133 status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
18134 // Return 0 if not found
18135 if (status != SUCCESS) return 0;
18136
18137 // Check bank type...
18138 // You cannot mix BANK and BANK32 so make sure all the FE use either
18139 // bk_init(pevent) or bk_init32(pevent).
18140 if (bk_is32a(psBkh)) {
18141
18142 // pointer to the source bank header
18143 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
18144 // Data size in the bank
18145 bksze = psbkh32a->data_size;
18146
18147 // Get to the end of the event
18148 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18149 // Copy from BANK32 to end of Data
18150 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
18151 // Bring pointer to the next free location
18152 pdest += ALIGN8(bksze) + sizeof(BANK32A);
18153
18154 } else if (bk_is32(psBkh)) {
18155
18156 // pointer to the source bank header
18157 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
18158 // Data size in the bank
18159 bksze = psbkh32->data_size;
18160
18161 // Get to the end of the event
18162 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18163 // Copy from BANK32 to end of Data
18164 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
18165 // Bring pointer to the next free location
18166 pdest += ALIGN8(bksze) + sizeof(BANK32);
18167
18168 } else {
18169
18170 // pointer to the source bank header
18171 psbkh = ((BANK *) psdata - 1);
18172 // Data size in the bank
18173 bksze = psbkh->data_size;
18174
18175 // Get to the end of the event
18176 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18177 // Copy from BANK to end of Data
18178 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
18179 // Bring pointer to the next free location
18180 pdest += ALIGN8(bksze) + sizeof(BANK);
18181 }
18182
18183 // Close bank (adjust BANK_HEADER size)
18184 bk_close(pevent, pdest);
18185 // Adjust EVENT_HEADER size
18186 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
18187 return SUCCESS;
18188}
18189
18190/********************************************************************/
18191int bk_delete(void *event, const char *name)
18192/********************************************************************\
18193
18194 Routine: bk_delete
18195
18196 Purpose: Delete a MIDAS bank inside an event
18197
18198 Input:
18199 void *event pointer to the event
18200 char *name Name of bank (exactly four letters)
18201
18202 Function value:
18203 CM_SUCCESS Bank has been deleted
18204 0 Bank has not been found
18205
18206\********************************************************************/
18207{
18208 BANK *pbk;
18209 DWORD dname;
18210 int remaining;
18211
18212 if (bk_is32a((BANK_HEADER *) event)) {
18213 /* locate bank */
18214 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18215 copy_bk_name((char *) &dname, name);
18216 do {
18217 if (*((DWORD *) pbk32a->name) == dname) {
18218 /* bank found, delete it */
18219 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18220 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18221
18222 /* reduce total event size */
18223 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
18224
18225 /* copy remaining bytes */
18226 if (remaining > 0)
18227 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
18228 return CM_SUCCESS;
18229 }
18230
18231 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18232 } while ((DWORD) ((char *) pbk32a - (char *) event) <
18233 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18234 } else if (bk_is32((BANK_HEADER *) event)) {
18235 /* locate bank */
18236 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18237 copy_bk_name((char *) &dname, name);
18238 do {
18239 if (*((DWORD *) pbk32->name) == dname) {
18240 /* bank found, delete it */
18241 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18242 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18243
18244 /* reduce total event size */
18245 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
18246
18247 /* copy remaining bytes */
18248 if (remaining > 0)
18249 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
18250 return CM_SUCCESS;
18251 }
18252
18253 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18254 } while ((DWORD) ((char *) pbk32 - (char *) event) <
18255 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18256 } else {
18257 /* locate bank */
18258 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18259 copy_bk_name((char *) &dname, name);
18260 do {
18261 if (*((DWORD *) pbk->name) == dname) {
18262 /* bank found, delete it */
18263 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18264 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18265
18266 /* reduce total event size */
18267 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
18268
18269 /* copy remaining bytes */
18270 if (remaining > 0)
18271 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
18272 return CM_SUCCESS;
18273 }
18274
18275 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18276 } while ((DWORD) ((char *) pbk - (char *) event) <
18277 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18278 }
18279
18280 return 0;
18281}
18282
18284#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18285
18286/********************************************************************/
18297INT bk_close(void *event, void *pdata) {
18298 if (bk_is32a((BANK_HEADER *) event)) {
18299 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18300 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
18301 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
18302 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]);
18303 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
18304 return pbk32a->data_size;
18305 } else if (bk_is32((BANK_HEADER *) event)) {
18306 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18307 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
18308 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
18309 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]);
18310 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
18311 return pbk32->data_size;
18312 } else {
18313 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18314 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
18315 if (size > 0xFFFF) {
18316 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18317 size = 0;
18318 }
18319 pbk->data_size = (WORD) (size);
18320 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
18321 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]);
18322 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
18323 if (size > 0xFFFF) {
18324 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18325 size = 0;
18326 }
18327 ((BANK_HEADER *) event)->data_size = size;
18328 return pbk->data_size;
18329 }
18330}
18331
18332/********************************************************************/
18357INT bk_list(const void *event, char *bklist) { /* Full event */
18358 INT nbk;
18359 BANK *pmbk = NULL;
18360 BANK32 *pmbk32 = NULL;
18361 BANK32A *pmbk32a = NULL;
18362 char *pdata;
18363
18364 /* compose bank list */
18365 bklist[0] = 0;
18366 nbk = 0;
18367 do {
18368 /* scan all banks for bank name only */
18369 if (bk_is32a(event)) {
18370 bk_iterate32a(event, &pmbk32a, &pdata);
18371 if (pmbk32a == NULL)
18372 break;
18373 } else if (bk_is32(event)) {
18374 bk_iterate32(event, &pmbk32, &pdata);
18375 if (pmbk32 == NULL)
18376 break;
18377 } else {
18378 bk_iterate(event, &pmbk, &pdata);
18379 if (pmbk == NULL)
18380 break;
18381 }
18382 nbk++;
18383
18384 if (nbk > BANKLIST_MAX) {
18385 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
18386 return (nbk - 1);
18387 }
18388 if (bk_is32a(event))
18389 strncat(bklist, (char *) pmbk32a->name, 4);
18390 else if (bk_is32(event))
18391 strncat(bklist, (char *) pmbk32->name, 4);
18392 else
18393 strncat(bklist, (char *) pmbk->name, 4);
18394 } while (1);
18395 return (nbk);
18396}
18397
18398/********************************************************************/
18406INT bk_locate(const void *event, const char *name, void *pdata) {
18407 BANK *pbk;
18408 BANK32 *pbk32;
18409 BANK32A *pbk32a;
18410 DWORD dname;
18411 const BANK_HEADER *pbh = (const BANK_HEADER *) event;
18412 const size_t event_size = (size_t) pbh->data_size + sizeof(BANK_HEADER);
18413
18414 auto range_fits = [event, event_size](const void *ptr, size_t size) {
18415 ptrdiff_t offset = (const char *) ptr - (const char *) event;
18416 return offset >= 0 && (size_t) offset <= event_size && size <= event_size - (size_t) offset;
18417 };
18418
18419 auto report_corrupt_bank = [pdata]() {
18420 cm_msg(MERROR, "bk_locate", "corrupted bank in event");
18421 *((void **) pdata) = NULL;
18422 return 0;
18423 };
18424
18425 if (bk_is32a(event)) {
18426 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18427 copy_bk_name((char *) &dname, name);
18428 while (range_fits(pbk32a, 1)) {
18429 if (!range_fits(pbk32a, sizeof(BANK32A)))
18430 return report_corrupt_bank();
18431 size_t remaining = event_size - ((char *) pbk32a - (char *) event) - sizeof(BANK32A);
18432 size_t bank_size = pbk32a->data_size;
18433 size_t padding = (8 - bank_size % 8) % 8;
18434 if (bank_size > remaining || padding > remaining - bank_size)
18435 return report_corrupt_bank();
18436 if (*((DWORD *) pbk32a->name) == dname) {
18437 int tid = pbk32a->type & 0xFF;
18438 if (tid >= TID_LAST)
18439 return report_corrupt_bank();
18440 *((void **) pdata) = pbk32a + 1;
18441 if (tid_size[tid] == 0)
18442 return pbk32a->data_size;
18443 return pbk32a->data_size / tid_size[tid];
18444 }
18445 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + bank_size + padding);
18446 }
18447 } else if (bk_is32(event)) {
18448 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18449 copy_bk_name((char *) &dname, name);
18450 while (range_fits(pbk32, 1)) {
18451 if (!range_fits(pbk32, sizeof(BANK32)))
18452 return report_corrupt_bank();
18453 size_t remaining = event_size - ((char *) pbk32 - (char *) event) - sizeof(BANK32);
18454 size_t bank_size = pbk32->data_size;
18455 size_t padding = (8 - bank_size % 8) % 8;
18456 if (bank_size > remaining || padding > remaining - bank_size)
18457 return report_corrupt_bank();
18458 if (*((DWORD *) pbk32->name) == dname) {
18459 int tid = pbk32->type & 0xFF;
18460 if (tid >= TID_LAST)
18461 return report_corrupt_bank();
18462 *((void **) pdata) = pbk32 + 1;
18463 if (tid_size[tid] == 0)
18464 return pbk32->data_size;
18465 return pbk32->data_size / tid_size[tid];
18466 }
18467 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + bank_size + padding);
18468 }
18469 } else {
18470 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18471 copy_bk_name((char *) &dname, name);
18472 while (range_fits(pbk, 1)) {
18473 if (!range_fits(pbk, sizeof(BANK)))
18474 return report_corrupt_bank();
18475 size_t remaining = event_size - ((char *) pbk - (char *) event) - sizeof(BANK);
18476 size_t bank_size = pbk->data_size;
18477 size_t padding = (8 - bank_size % 8) % 8;
18478 if (bank_size > remaining || padding > remaining - bank_size)
18479 return report_corrupt_bank();
18480 if (*((DWORD *) pbk->name) == dname) {
18481 int tid = pbk->type & 0xFF;
18482 if (tid >= TID_LAST)
18483 return report_corrupt_bank();
18484 *((void **) pdata) = pbk + 1;
18485 if (tid_size[tid] == 0)
18486 return pbk->data_size;
18487 return pbk->data_size / tid_size[tid];
18488 }
18489 pbk = (BANK *) ((char *) (pbk + 1) + bank_size + padding);
18490 }
18491
18492 }
18493
18494 /* bank not found */
18495 *((void **) pdata) = NULL;
18496 return 0;
18497}
18498
18499/********************************************************************/
18509INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
18510 DWORD dname;
18511
18512 if (bk_is32a(pbkh)) {
18513 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
18514 copy_bk_name((char *) &dname, name);
18515 do {
18516 if (*((DWORD *) pbk32a->name) == dname) {
18517 int tid = pbk32a->type & 0xFF;
18518 if (tid >= TID_LAST) {
18519 *((void **) pdata) = NULL;
18520 return 0;
18521 }
18522 *((void **) pdata) = pbk32a + 1;
18523 if (tid_size[tid] == 0)
18524 *bklen = pbk32a->data_size;
18525 else
18526 *bklen = pbk32a->data_size / tid_size[tid];
18527
18528 *bktype = pbk32a->type;
18529 return 1;
18530 }
18531 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18532 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18533 } else if (bk_is32(pbkh)) {
18534 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
18535 copy_bk_name((char *) &dname, name);
18536 do {
18537 if (*((DWORD *) pbk32->name) == dname) {
18538 int tid = pbk32->type & 0xFF;
18539 if (tid >= TID_LAST) {
18540 *((void **) pdata) = NULL;
18541 return 0;
18542 }
18543 *((void **) pdata) = pbk32 + 1;
18544 if (tid_size[tid] == 0)
18545 *bklen = pbk32->data_size;
18546 else
18547 *bklen = pbk32->data_size / tid_size[tid];
18548
18549 *bktype = pbk32->type;
18550 return 1;
18551 }
18552 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18553 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18554 } else {
18555 BANK *pbk = (BANK *) (pbkh + 1);
18556 copy_bk_name((char *) &dname, name);
18557 do {
18558 if (*((DWORD *) pbk->name) == dname) {
18559 int tid = pbk->type & 0xFF;
18560 if (tid >= TID_LAST) {
18561 *((void **) pdata) = NULL;
18562 return 0;
18563 }
18564 *((void **) pdata) = pbk + 1;
18565 if (tid_size[tid] == 0)
18566 *bklen = pbk->data_size;
18567 else
18568 *bklen = pbk->data_size / tid_size[tid];
18569
18570 *bktype = pbk->type;
18571 return 1;
18572 }
18573 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18574 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18575 }
18576
18577 /* bank not found */
18578 *((void **) pdata) = NULL;
18579 return 0;
18580}
18581
18582/********************************************************************/
18618INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
18619 if (*pbk == NULL)
18620 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18621 else
18622 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18623
18624 *((void **) pdata) = (*pbk) + 1;
18625
18626 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18627 *pbk = *((BANK **) pdata) = NULL;
18628 return 0;
18629 }
18630
18631 return (*pbk)->data_size;
18632}
18633
18634
18636#ifndef DOXYGEN_SHOULD_SKIP_THIS
18637
18638/********************************************************************/
18639INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
18640/********************************************************************\
18641
18642 Routine: bk_iterate32
18643
18644 Purpose: Iterate through 32 bit MIDAS banks inside an event
18645
18646 Input:
18647 void *event pointer to the event
18648 BANK32 **pbk32 must be NULL for the first call to bk_iterate
18649
18650 Output:
18651 BANK32 **pbk32 pointer to the bank header
18652 void *pdata pointer to data area of the bank
18653
18654 Function value:
18655 INT size of the bank in bytes
18656
18657\********************************************************************/
18658{
18659 if (*pbk == NULL)
18660 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
18661 else
18662 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18663
18664 *((void **) pdata) = (*pbk) + 1;
18665
18666 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18667 *pbk = NULL;
18668 pdata = NULL;
18669 return 0;
18670 }
18671
18672 return (*pbk)->data_size;
18673}
18674
18675INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
18676/********************************************************************\
18677
18678 Routine: bk_iterate32a
18679
18680 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
18681
18682 Input:
18683 void *event pointer to the event
18684 BANK32A **pbk32a must be NULL for the first call to bk_iterate
18685
18686 Output:
18687 BANK32A **pbk32 pointer to the bank header
18688 void *pdata pointer to data area of the bank
18689
18690 Function value:
18691 INT size of the bank in bytes
18692
18693\********************************************************************/
18694{
18695 if (*pbk32a == NULL)
18696 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18697 else
18698 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
18699
18700 *((void **) pdata) = (*pbk32a) + 1;
18701
18702 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18703 *pbk32a = NULL;
18704 pdata = NULL;
18705 return 0;
18706 }
18707
18708 return (*pbk32a)->data_size;
18709}
18710
18712#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18713
18714/********************************************************************/
18729INT bk_swap(void *event, BOOL force) {
18730 BANK_HEADER *pbh;
18731 BANK *pbk;
18732 BANK32 *pbk32;
18733 BANK32A *pbk32a;
18734 void *pdata;
18735 WORD type;
18736
18737 pbh = (BANK_HEADER *) event;
18738
18739 /* only swap if flags in high 16-bit */
18740 if (pbh->flags < 0x10000 && !force)
18741 return 0;
18742
18743 /* swap bank header */
18744 DWORD_SWAP(&pbh->data_size);
18745 DWORD_SWAP(&pbh->flags);
18746
18747 pbk = (BANK *) (pbh + 1);
18748 pbk32 = (BANK32 *) pbk;
18749 pbk32a = (BANK32A *) pbk;
18750
18751 /* scan event */
18752 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
18753 /* swap bank header */
18754 if (bk_is32a(event)) {
18755 DWORD_SWAP(&pbk32a->type);
18756 DWORD_SWAP(&pbk32a->data_size);
18757 pdata = pbk32a + 1;
18758 type = (WORD) pbk32a->type;
18759 } else if (bk_is32(event)) {
18760 DWORD_SWAP(&pbk32->type);
18761 DWORD_SWAP(&pbk32->data_size);
18762 pdata = pbk32 + 1;
18763 type = (WORD) pbk32->type;
18764 } else {
18765 WORD_SWAP(&pbk->type);
18766 WORD_SWAP(&pbk->data_size);
18767 pdata = pbk + 1;
18768 type = pbk->type;
18769 }
18770
18771 /* pbk points to next bank */
18772 if (bk_is32a(event)) {
18773 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18774 pbk = (BANK *) pbk32a;
18775 } else if (bk_is32(event)) {
18776 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18777 pbk = (BANK *) pbk32;
18778 } else {
18779 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18780 pbk32 = (BANK32 *) pbk;
18781 }
18782
18783 switch (type) {
18784 case TID_UINT16:
18785 case TID_INT16:
18786 while ((char *) pdata < (char *) pbk) {
18787 WORD_SWAP(pdata);
18788 pdata = (void *) (((WORD *) pdata) + 1);
18789 }
18790 break;
18791
18792 case TID_UINT32:
18793 case TID_INT32:
18794 case TID_BOOL:
18795 case TID_FLOAT:
18796 while ((char *) pdata < (char *) pbk) {
18797 DWORD_SWAP(pdata);
18798 pdata = (void *) (((DWORD *) pdata) + 1);
18799 }
18800 break;
18801
18802 case TID_DOUBLE:
18803 case TID_INT64:
18804 case TID_UINT64:
18805 while ((char *) pdata < (char *) pbk) {
18806 QWORD_SWAP(pdata);
18807 pdata = (void *) (((double *) pdata) + 1);
18808 }
18809 break;
18810 }
18811 }
18812
18813 return CM_SUCCESS;
18814}
18815
/* end of bkfunctionc */
18819
18820
18827#ifndef DOXYGEN_SHOULD_SKIP_THIS
18828/********************************************************************/
18829
18830/********************************************************************\
18831* *
18832* Ring buffer functions *
18833* *
18834* Provide an inter-thread buffer scheme for handling front-end *
18835* events. This code allows concurrent data acquisition, calibration *
18836* and network transfer on a multi-CPU machine. One thread reads *
18837* out the data, passes it vis the ring buffer functions *
18838* to another thread running on the other CPU, which can then *
18839* calibrate and/or send the data over the network. *
18840* *
18841\********************************************************************/
18842
18843typedef struct {
18844 unsigned char *buffer;
18845 unsigned int size;
18846 unsigned int max_event_size;
18847 unsigned char *rp;
18848 unsigned char *wp;
18849 unsigned char *ep;
18850} RING_BUFFER;
18851
18852#define MAX_RING_BUFFER 100
18853
18855
18856static volatile int _rb_nonblocking = 0;
18857
18859#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18860
18861/********************************************************************/
18868/********************************************************************\
18869
18870 Routine: rb_set_nonblocking
18871
18872 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
18873 environments for stopping all theads without deadlock
18874
18875 Input:
18876 NONE
18877
18878 Output:
18879 NONE
18880
18881 Function value:
18882 DB_SUCCESS Successful completion
18883
18884\********************************************************************/
18885{
18886 _rb_nonblocking = 1;
18887
18888 return DB_SUCCESS;
18889}
18890
18891/********************************************************************/
18908int rb_create(int size, int max_event_size, int *handle)
18909/********************************************************************\
18910
18911 Routine: rb_create
18912
18913 Purpose: Create a ring buffer with a given size
18914
18915 Input:
18916 int size Size of ring buffer, must be larger than
18917 2*max_event_size
18918 int max_event_size Maximum event size to be placed into
18919 ring buffer
18920 Output:
18921 int *handle Handle to ring buffer
18922
18923 Function value:
18924 DB_SUCCESS Successful completion
18925 DB_NO_MEMORY Maximum number of ring buffers exceeded
18926 DB_INVALID_PARAM Invalid event size specified
18927
18928\********************************************************************/
18929{
18930 int i;
18931
18932 if (handle == NULL || size <= 0 || max_event_size <= 0 || max_event_size > size / 2)
18933 return DB_INVALID_PARAM;
18934
18935 for (i = 0; i < MAX_RING_BUFFER; i++)
18936 if (rb[i].buffer == NULL)
18937 break;
18938
18939 if (i == MAX_RING_BUFFER)
18940 return DB_NO_MEMORY;
18941
18942 memset(&rb[i], 0, sizeof(RING_BUFFER));
18943 rb[i].buffer = (unsigned char *) M_MALLOC(size);
18944 if (rb[i].buffer == NULL)
18945 return DB_NO_MEMORY;
18946 rb[i].size = size;
18948 rb[i].rp = rb[i].buffer;
18949 rb[i].wp = rb[i].buffer;
18950 rb[i].ep = rb[i].buffer;
18951
18952 *handle = i + 1;
18953
18954 return DB_SUCCESS;
18955}
18956
18957/********************************************************************/
18963int rb_delete(int handle)
18964/********************************************************************\
18965
18966 Routine: rb_delete
18967
18968 Purpose: Delete a ring buffer
18969
18970 Input:
18971 none
18972 Output:
18973 int handle Handle to ring buffer
18974
18975 Function value:
18976 DB_SUCCESS Successful completion
18977
18978\********************************************************************/
18979{
18980 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18981 return DB_INVALID_HANDLE;
18982
18983 M_FREE(rb[handle - 1].buffer);
18984 rb[handle - 1].buffer = NULL;
18985 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
18986
18987 return DB_SUCCESS;
18988}
18989
18990/********************************************************************/
19000int rb_get_wp(int handle, void **p, int millisec)
19001/********************************************************************\
19002
19003Routine: rb_get_wp
19004
19005 Purpose: Retrieve write pointer where new data can be written
19006
19007 Input:
19008 int handle Ring buffer handle
19009 int millisec Optional timeout in milliseconds if
19010 buffer is full. Zero to not wait at
19011 all (non-blocking)
19012
19013 Output:
19014 char **p Write pointer
19015
19016 Function value:
19017 DB_SUCCESS Successful completion
19018
19019\********************************************************************/
19020{
19021 int h, i;
19022 unsigned char *rp;
19023
19024 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19025 return DB_INVALID_HANDLE;
19026
19027 h = handle - 1;
19028
19029 for (i = 0; i <= millisec / 10; i++) {
19030
19031 rp = rb[h].rp; // keep local copy for convenience
19032
19033 /* check if enough size for wp >= rp without wrap-around */
19034 if (rb[h].wp >= rp
19035 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
19036 *p = rb[h].wp;
19037 return DB_SUCCESS;
19038 }
19039
19040 /* check if enough size for wp >= rp with wrap-around */
19041 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
19042 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
19043 *p = rb[h].wp;
19044 return DB_SUCCESS;
19045 }
19046
19047 /* check if enough size for wp < rp */
19048 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
19049 *p = rb[h].wp;
19050 return DB_SUCCESS;
19051 }
19052
19053 if (millisec == 0)
19054 return DB_TIMEOUT;
19055
19056 if (_rb_nonblocking)
19057 return DB_TIMEOUT;
19058
19059 /* wait one time slice */
19060 ss_sleep(10);
19061 }
19062
19063 return DB_TIMEOUT;
19064}
19065
19066/********************************************************************/
19075int rb_increment_wp(int handle, int size)
19076/********************************************************************\
19077
19078 Routine: rb_increment_wp
19079
19080 Purpose: Increment current write pointer, making the data at
19081 the write pointer available to the receiving thread
19082
19083 Input:
19084 int handle Ring buffer handle
19085 int size Number of bytes placed at the WP
19086
19087 Output:
19088 NONE
19089
19090 Function value:
19091 DB_SUCCESS Successful completion
19092 DB_INVALID_PARAM Event size too large or invalid handle
19093\********************************************************************/
19094{
19095 int h;
19096 unsigned char *new_wp;
19097
19098 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19099 return DB_INVALID_HANDLE;
19100
19101 h = handle - 1;
19102
19103 if (size < 0 || (DWORD) size > rb[h].max_event_size) {
19104 cm_msg(MERROR, "rb_increment_wp", "invalid event size of %d bytes, max_event_size is %u bytes",
19105 size, rb[h].max_event_size);
19106 return DB_INVALID_PARAM;
19107 }
19108
19109 new_wp = rb[h].wp + size;
19110
19111 /* wrap around wp if not enough space */
19112 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
19113 rb[h].ep = new_wp;
19114 new_wp = rb[h].buffer;
19115 assert(rb[h].rp != rb[h].buffer);
19116 } else
19117 if (new_wp > rb[h].ep)
19118 rb[h].ep = new_wp;
19119
19120 rb[h].wp = new_wp;
19121
19122 return DB_SUCCESS;
19123}
19124
19125/********************************************************************/
19141int rb_get_rp(int handle, void **p, int millisec)
19142/********************************************************************\
19143
19144 Routine: rb_get_rp
19145
19146 Purpose: Obtain the current read pointer at which new data is
19147 available with optional timeout
19148
19149 Input:
19150 int handle Ring buffer handle
19151 int millisec Optional timeout in milliseconds if
19152 buffer is full. Zero to not wait at
19153 all (non-blocking)
19154
19155 Output:
19156 char **p Address of pointer pointing to newly
19157 available data. If p == NULL, only
19158 return status.
19159
19160 Function value:
19161 DB_SUCCESS Successful completion
19162
19163\********************************************************************/
19164{
19165 int i, h;
19166
19167 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19168 return DB_INVALID_HANDLE;
19169
19170 h = handle - 1;
19171
19172 for (i = 0; i <= millisec / 10; i++) {
19173
19174 if (rb[h].wp != rb[h].rp) {
19175 if (p != NULL)
19176 *p = rb[handle - 1].rp;
19177 return DB_SUCCESS;
19178 }
19179
19180 if (millisec == 0)
19181 return DB_TIMEOUT;
19182
19183 if (_rb_nonblocking)
19184 return DB_TIMEOUT;
19185
19186 /* wait one time slice */
19187 ss_sleep(10);
19188 }
19189
19190 return DB_TIMEOUT;
19191}
19192
19193/********************************************************************/
19203int rb_increment_rp(int handle, int size)
19204/********************************************************************\
19205
19206 Routine: rb_increment_rp
19207
19208 Purpose: Increment current read pointer, freeing up space for
19209 the writing thread.
19210
19211 Input:
19212 int handle Ring buffer handle
19213 int size Number of bytes to free up at current
19214 read pointer
19215
19216 Output:
19217 NONE
19218
19219 Function value:
19220 DB_SUCCESS Successful completion
19221 DB_INVALID_PARAM Event size too large or invalid handle
19222
19223\********************************************************************/
19224{
19225 int h;
19226
19227 unsigned char *new_rp;
19228 unsigned char *ep;
19229
19230 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19231 return DB_INVALID_HANDLE;
19232
19233 h = handle - 1;
19234
19235 if ((DWORD) size > rb[h].max_event_size)
19236 return DB_INVALID_PARAM;
19237
19238 new_rp = rb[h].rp + size;
19239 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
19240
19241 /* wrap around if end pointer reached */
19242 if (new_rp >= ep && rb[h].wp < ep)
19243 new_rp = rb[h].buffer;
19244
19245 rb[handle - 1].rp = new_rp;
19246
19247 return DB_SUCCESS;
19248}
19249
19250/********************************************************************/
19258int rb_get_buffer_level(int handle, int *n_bytes)
19259/********************************************************************\
19260
19261 Routine: rb_get_buffer_level
19262
19263 Purpose: Return number of bytes in a ring buffer
19264
19265 Input:
19266 int handle Handle of the buffer to get the info
19267
19268 Output:
19269 int *n_bytes Number of bytes in buffer
19270
19271 Function value:
19272 DB_SUCCESS Successful completion
19273 DB_INVALID_HANDLE Buffer handle is invalid
19274
19275\********************************************************************/
19276{
19277 int h;
19278
19279 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19280 return DB_INVALID_HANDLE;
19281
19282 h = handle - 1;
19283
19284 if (rb[h].wp >= rb[h].rp)
19285 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
19286 else
19287 *n_bytes =
19288 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
19289
19290 return DB_SUCCESS;
19291}
19292
/* end of rbfunctionc */
19294
19295
19297{
19298 if (format == FORMAT_FIXED) {
19299 int status;
19300 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
19301 if (status != DB_SUCCESS) {
19302 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
19303 return status;
19304 }
19305 return SUCCESS;
19306 } else if (format == FORMAT_MIDAS) {
19307 INT size, i, status, n_data;
19308 int n;
19309 char *pdata, *pdata0;
19310
19311 char name[5];
19312 BANK_HEADER *pbh;
19313 BANK *pbk;
19314 BANK32 *pbk32;
19315 BANK32A *pbk32a;
19316 DWORD bkname;
19317 WORD bktype;
19318 HNDLE hKeyRoot, hKeyl, *hKeys;
19319 KEY key;
19320
19321 pbh = (BANK_HEADER *) (pevent + 1);
19322 pbk = NULL;
19323 pbk32 = NULL;
19324 pbk32a = NULL;
19325
19326 /* count number of banks */
19327 for (n=0 ; ; n++) {
19328 if (bk_is32a(pbh)) {
19329 bk_iterate32a(pbh, &pbk32a, &pdata);
19330 if (pbk32a == NULL)
19331 break;
19332 } else if (bk_is32(pbh)) {
19333 bk_iterate32(pbh, &pbk32, &pdata);
19334 if (pbk32 == NULL)
19335 break;
19336 } else {
19337 bk_iterate(pbh, &pbk, &pdata);
19338 if (pbk == NULL)
19339 break;
19340 }
19341 }
19342
19343 /* build array of keys */
19344 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
19345
19346 pbk = NULL;
19347 pbk32 = NULL;
19348 n = 0;
19349 do {
19350 /* scan all banks */
19351 if (bk_is32a(pbh)) {
19352 size = bk_iterate32a(pbh, &pbk32a, &pdata);
19353 if (pbk32a == NULL)
19354 break;
19355 bkname = *((DWORD *) pbk32a->name);
19356 bktype = (WORD) pbk32a->type;
19357 } else if (bk_is32(pbh)) {
19358 size = bk_iterate32(pbh, &pbk32, &pdata);
19359 if (pbk32 == NULL)
19360 break;
19361 bkname = *((DWORD *) pbk32->name);
19362 bktype = (WORD) pbk32->type;
19363 } else {
19364 size = bk_iterate(pbh, &pbk, &pdata);
19365 if (pbk == NULL)
19366 break;
19367 bkname = *((DWORD *) pbk->name);
19368 bktype = (WORD) pbk->type;
19369 }
19370
19371 n_data = size;
19372 if (rpc_tid_size(bktype & 0xFF))
19373 n_data /= rpc_tid_size(bktype & 0xFF);
19374
19375 /* get bank key */
19376 *((DWORD *) name) = bkname; // NB: "name" is only byte-aligned, and may have wrong alignement for DWORD, flagged by UBSAN. K.O.
19377
19378 //name[0] = bkname>>0 & 0xFF;
19379 //name[1] = bkname>>8 & 0xFF;
19380 //name[2] = bkname>>16 & 0xFF;
19381 //name[3] = bkname>>24 & 0xFF;
19382 name[4] = 0;
19383
19384 //printf("bkname 0x%08x, name [%s]\n", bkname, name);
19385
19386 /* record the start of the data in case it is struct */
19387 pdata0 = pdata;
19388 if (bktype == TID_STRUCT) {
19389 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19390 if (status != DB_SUCCESS) {
19391 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
19392 continue;
19393 }
19394
19395 /* write structured bank */
19396 for (i = 0;; i++) {
19397 status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
19399 break;
19400
19401 db_get_key(hDB, hKeyl, &key);
19402
19403 /* adjust for alignment */
19404 if (key.type != TID_STRING && key.type != TID_LINK)
19405 pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
19406
19408 if (status != DB_SUCCESS) {
19409 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19410 continue;
19411 }
19412 hKeys[n++] = hKeyl;
19413
19414 /* shift data pointer to next item */
19415 pdata += key.item_size * key.num_values;
19416 }
19417 } else {
19418 /* write variable length bank */
19419 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19420 if (status != DB_SUCCESS) {
19421 status = db_create_key(hDB, hKey, name, bktype);
19422 if (status != DB_SUCCESS) {
19423 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);
19424 continue;
19425 }
19426 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19427 if (status != DB_SUCCESS) {
19428 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);
19429 continue;
19430 }
19431 }
19432 if (n_data > 0) {
19433 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
19434 if (status != DB_SUCCESS) {
19435 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19436 }
19437 hKeys[n++] = hKeyRoot;
19438 }
19439 }
19440 } while (1);
19441
19442 /* notify all hot-lined clients in one go */
19443 db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
19444
19445 free(hKeys);
19446
19447 return SUCCESS;
19448 } else {
19449 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
19450 return CM_DB_ERROR;
19451 }
19452}
19453
19454/* emacs
19455 * Local Variables:
19456 * tab-width: 8
19457 * c-basic-offset: 3
19458 * indent-tabs-mode: nil
19459 * End:
19460 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11676
std::atomic_bool connected
Definition midas.cxx:11675
std::string client_name
Definition midas.cxx:11680
void * pparam
Definition midas.cxx:15587
size_t param_size
Definition midas.cxx:15582
size_t offset
Definition midas.cxx:15580
size_t out_max_size
Definition midas.cxx:15584
size_t out_max_size_offset
Definition midas.cxx:15583
std::string * ps
Definition midas.cxx:15585
std::vector< char > * pv
Definition midas.cxx:15586
size_t arg_size
Definition midas.cxx:15581
BUFFER * get_pbuf() const
Definition midas.cxx:3253
bool is_error() const
Definition midas.cxx:3243
int get_status() const
Definition midas.cxx:3248
bool is_locked() const
Definition midas.cxx:3238
bm_lock_buffer_guard(BUFFER *pbuf, bool do_not_lock=false)
Definition midas.cxx:3157
bm_lock_buffer_guard & operator=(const bm_lock_buffer_guard &)=delete
bm_lock_buffer_guard(const bm_lock_buffer_guard &)=delete
void set_string_size(std::string s, int size)
Definition odbxx.cxx:1627
static bool exists(const std::string &name)
Definition odbxx.cxx:76
void connect(const std::string &path, const std::string &name, bool write_defaults, bool delete_keys_not_in_defaults=false)
Definition odbxx.cxx:1390
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:924
INT al_check()
Definition alarm.cxx:692
void bk_init32a(void *event)
Definition midas.cxx:17999
INT bk_close(void *event, void *pdata)
Definition midas.cxx:18297
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:18675
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:18016
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:18729
BOOL bk_is32a(const void *event)
Definition midas.cxx:17954
int bk_delete(void *event, const char *name)
Definition midas.cxx:18191
BOOL bk_is32(const void *event)
Definition midas.cxx:17932
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:18639
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:18406
void bk_init(void *event)
Definition midas.cxx:17923
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:18357
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:18121
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:18618
void bk_init32(void *event)
Definition midas.cxx:17986
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:18078
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:18509
INT bk_size(const void *event)
Definition midas.cxx:18012
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8900
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:10611
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6782
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6261
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9791
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:10100
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:6529
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:11074
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10191
static INT bm_receive_event_rpc_cxx(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER **ppevent, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10719
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7354
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11513
INT bm_delete_request(INT request_id)
Definition midas.cxx:8697
INT bm_close_all_buffers(void)
Definition midas.cxx:7317
INT bm_poll_event()
Definition midas.cxx:11367
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:8427
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6651
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6295
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:11050
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9716
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9702
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9079
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6138
INT bm_empty_buffers()
Definition midas.cxx:11481
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8833
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9182
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:8578
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5994
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8253
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6379
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10891
INT bm_check_buffers()
Definition midas.cxx:11195
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9105
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:8405
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8631
static double _bm_mutex_timeout_sec
Definition midas.cxx:5992
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7170
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:9891
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8394
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:9617
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11522
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6328
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6087
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:9049
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8978
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6189
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8968
static DWORD _bm_max_event_size
Definition midas.cxx:5986
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6480
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10335
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8936
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8735
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:10392
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:9193
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7149
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9818
static int _bm_lock_timeout
Definition midas.cxx:5991
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:9004
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10972
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6463
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6107
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:11143
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:9505
INT cm_set_path(const char *path)
Definition midas.cxx:1547
INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number)
Definition midas.cxx:3673
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7474
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4245
INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:2905
static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition midas.cxx:3433
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7397
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4162
INT cm_yield(INT millisec)
Definition midas.cxx:5716
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3083
INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names)
Definition midas.cxx:2684
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3397
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11525
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2838
INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void(*func)(char *))
Definition midas.cxx:2352
static BUFFER_CLIENT * bm_get_my_client_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:6071
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2658
INT cm_start_watchdog_thread()
Definition midas.cxx:7429
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:11159
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
Definition midas.cxx:3105
static int tr_finish(HNDLE hDB, TrState *tr, int transition, int status, const char *errorstr)
Definition midas.cxx:4076
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3863
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:8017
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4109
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5360
INT cm_stop_watchdog_thread()
Definition midas.cxx:7444
std::string cm_asctime()
Definition midas.cxx:1462
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5862
INT cm_exist(const char *name, BOOL bClientName)
Definition midas.cxx:7594
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:2371
INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:1919
static int xbm_lock_buffer(BUFFER *pbuf)
Definition midas.cxx:8088
INT cm_periodic_tasks()
Definition midas.cxx:5653
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8150
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5459
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5312
INT cm_select_experiment_remote(const char *host_name, std::string *exp_name)
Definition midas.cxx:2791
INT cm_register_server(void)
Definition midas.cxx:3532
static BOOL _ctrlc_pressed
Definition midas.cxx:5513
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3484
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5530
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5797
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3416
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7723
void cm_check_connect(void)
Definition midas.cxx:2275
static BUFFER * bm_get_buffer(const char *who, INT buffer_handle, int *pstatus)
Definition midas.cxx:6687
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2742
std::string cm_expand_env(const char *str)
Definition midas.cxx:7823
std::string cm_get_client_name()
Definition midas.cxx:2133
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:8059
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5535
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1595
INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name, const char *program_name, INT hw_type, const char *password, DWORD watchdog_timeout)
Definition midas.cxx:1943
static void rpc_client_shutdown()
Definition midas.cxx:12865
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7396
static exptab_struct _exptab
Definition midas.cxx:1655
INT cm_disconnect_experiment(void)
Definition midas.cxx:2918
static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition midas.cxx:6224
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11289
int cm_get_exptab(const char *expname, std::string *dir, std::string *user)
Definition midas.cxx:1849
static void xcm_watchdog_thread()
Definition midas.cxx:7423
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7855
INT cm_synchronize(DWORD *seconds)
Definition midas.cxx:1419
std::string cm_get_exptab_filename()
Definition midas.cxx:1838
std::string cm_get_path()
Definition midas.cxx:1587
static DWORD _deferred_transition_mask
Definition midas.cxx:3899
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5915
void cm_test_expand_env()
Definition midas.cxx:7870
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3917
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2240
static INT _requested_transition
Definition midas.cxx:3898
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2208
const char * cm_get_version()
Definition midas.cxx:1526
INT cm_read_exptab(exptab_struct *exptab)
Definition midas.cxx:1664
static int bm_lock_buffer_write_cache(BUFFER *pbuf)
Definition midas.cxx:8038
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3749
INT cm_check_deferred_transition()
Definition midas.cxx:3969
std::string cm_get_experiment_name()
Definition midas.cxx:1630
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3803
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:4056
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1902
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7395
const char * cm_get_revision()
Definition midas.cxx:1534
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7403
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:3011
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5526
static INT tr_main_thread(void *param)
Definition midas.cxx:5328
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5515
INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3316
INT cm_time(DWORD *t)
Definition midas.cxx:1484
INT cm_transition_cleanup()
Definition midas.cxx:5341
INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3355
static int cm_transition_call_direct(TrClient *tr_client)
Definition midas.cxx:4480
INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
Definition midas.cxx:3030
static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4603
INT cm_set_experiment_name(const char *name)
Definition midas.cxx:1608
#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_TIMEOUT
Definition midas.h:625
#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:636
#define DB_INVALID_PARAM
Definition midas.h:640
#define DB_SUCCESS
Definition midas.h:632
#define DB_NO_MEMORY
Definition midas.h:634
#define DB_NO_KEY
Definition midas.h:643
#define DB_TYPE_MISMATCH
Definition midas.h:646
#define DB_CREATED
Definition midas.h:633
#define DB_TIMEOUT
Definition midas.h:656
#define DB_NO_MORE_SUBKEYS
Definition midas.h:647
#define SS_SUCCESS
Definition midas.h:664
#define SS_ABORT
Definition midas.h:678
#define SS_FILE_ERROR
Definition midas.h:670
#define SS_TIMEOUT
Definition midas.h:675
#define SS_CREATED
Definition midas.h:665
#define SS_EXIT
Definition midas.h:679
#define RPC_SHUTDOWN
Definition midas.h:708
#define RPC_SUCCESS
Definition midas.h:699
#define RPC_EXCEED_BUFFER
Definition midas.h:704
#define RPC_INVALID_ID
Definition midas.h:707
#define RPC_DOUBLE_DEFINED
Definition midas.h:710
#define RPC_NOT_REGISTERED
Definition midas.h:705
#define RPC_MUTEX_TIMEOUT
Definition midas.h:711
#define RPC_TIMEOUT
Definition midas.h:703
#define RPC_NO_CONNECTION
Definition midas.h:701
#define RPC_NET_ERROR
Definition midas.h:702
#define AL_TRIGGERED
Definition midas.h:759
#define FE_ERR_HW
Definition midas.h:720
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_INT
Definition midas.h:338
#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:787
#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:302
#define DRI_16
Definition msystem.h:45
#define NET_BUFFER_SIZE
Definition msystem.h:114
#define FD_SETSIZE
Definition msystem.h:206
#define O_TEXT
Definition msystem.h:227
#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:307
#define MSG_ODB
Definition msystem.h:303
void() EVENT_HANDLER(HNDLE buffer_handler, HNDLE request_id, EVENT_HEADER *event_header, void *event_data)
Definition midas.h:919
INT() RPC_HANDLER(INT index, void *prpc_param[])
Definition midas.h:923
std::string ss_gethostname()
Definition system.cxx:5791
INT ss_suspend(INT millisec, INT msg)
Definition system.cxx:4622
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3236
INT ss_suspend_init_odb_port()
Definition system.cxx:4384
bool ss_event_socket_has_data()
Definition system.cxx:4599
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3444
DWORD ss_millitime()
Definition system.cxx:3472
int ss_file_exist(const char *path)
Definition system.cxx:7203
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2532
INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
Definition system.cxx:5641
INT ss_suspend_set_client_listener(int listen_socket)
Definition system.cxx:4363
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5325
int ss_file_link_exist(const char *path)
Definition system.cxx:7239
std::string ss_getcwd()
Definition system.cxx:5855
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3290
int ss_socket_wait(int sock, INT millisec)
Definition system.cxx:4977
INT ss_suspend_set_server_acceptions(RPC_SERVER_ACCEPTION_LIST *acceptions)
Definition system.cxx:4377
INT ss_getpid(void)
Definition system.cxx:1379
DWORD ss_settime(DWORD seconds)
Definition system.cxx:3554
char * ss_getpass(const char *prompt)
Definition system.cxx:7525
INT ss_suspend_set_client_connection(RPC_SERVER_CONNECTION *connection)
Definition system.cxx:4370
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3020
void ss_tzset()
Definition system.cxx:3434
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:326
INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
Definition system.cxx:5478
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5431
int ss_dir_exist(const char *path)
Definition system.cxx:7271
bool ss_timed_mutex_wait_for_sec(std::timed_mutex &mutex, const char *mutex_name, double timeout_sec)
Definition system.cxx:3344
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2860
std::string ss_get_cmdline(void)
Definition system.cxx:1519
int ss_file_copy(const char *src, const char *dst, bool append)
Definition system.cxx:7304
DWORD ss_time()
Definition system.cxx:3541
INT ss_suspend_exit()
Definition system.cxx:4305
INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5533
INT ss_resume(INT port, const char *message)
Definition system.cxx:4923
midas_thread_t ss_gettid(void)
Definition system.cxx:1591
std::string ss_asctime()
Definition system.cxx:3628
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2948
INT ss_sleep(INT millisec)
Definition system.cxx:3707
INT ss_socket_connect_tcp(const char *hostname, int tcp_port, int *sockp, std::string *error_msg_p)
Definition system.cxx:5046
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2718
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:5141
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7976
INT ss_spawnv(INT mode, const char *cmdname, const char *const argv[])
Definition system.cxx:1702
INT ss_suspend_get_buffer_port(midas_thread_t thread_id, INT *port)
Definition system.cxx:4432
INT ss_socket_close(int *sockp)
Definition system.cxx:5310
char * ss_gets(char *string, int size)
Definition system.cxx:7855
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5714
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:757
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5364
void * ss_ctrlc_handler(void(*func)(int))
Definition system.cxx:3978
BOOL ss_pid_exists(int pid)
Definition system.cxx:1442
INT ss_system(const char *command)
Definition system.cxx:2188
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3116
INT ss_file_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6798
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:1120
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:997
int cm_msg_early_init(void)
Definition midas.cxx:485
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:522
int cm_msg_open_buffer(void)
Definition midas.cxx:492
int cm_msg_close_buffer(void)
Definition midas.cxx:505
static std::mutex gMsgBufMutex
Definition midas.cxx:881
static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message)
Definition midas.cxx:1089
INT cm_msg_register(EVENT_HANDLER *func)
Definition midas.cxx:1075
INT cm_msg_log(INT message_type, const char *facility, const char *message)
Definition midas.cxx:682
INT cm_msg_flush_buffer()
Definition midas.cxx:889
static std::deque< msg_buffer_entry > gMsgBuf
Definition midas.cxx:880
static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message)
Definition midas.cxx:848
std::string cm_get_error(INT code)
Definition midas.cxx:473
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:939
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:769
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1381
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1311
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:557
INT cm_set_msg_print(INT system_mask, INT user_mask, int(*func)(const char *))
Definition midas.cxx:665
#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:3780
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2801
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:7412
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:4428
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3638
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5680
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:13883
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:2315
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:3406
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:7058
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3887
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8535
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6538
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:14528
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3593
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:8163
INT db_close_all_records()
Definition odb.cxx:14107
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:14406
INT db_close_all_databases(void)
Definition odb.cxx:2893
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7734
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:4494
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:11426
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:3271
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7832
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:5523
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4751
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:14145
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:3555
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:14192
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:3476
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:3371
static DATABASE * db_lock_database(HNDLE hDB, int *pstatus, const char *caller, bool check_attached=true)
Definition odb.cxx:3003
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2935
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:13215
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12881
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5852
static void db_unlock_database(DATABASE *pdb, const char *caller)
Definition odb.cxx:3165
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14599
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:3243
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:8018
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3746
int rb_get_rp(int handle, void **p, int millisec)
Definition midas.cxx:19141
#define MAX_RING_BUFFER
Definition midas.cxx:18852
int rb_delete(int handle)
Definition midas.cxx:18963
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:19000
int rb_increment_rp(int handle, int size)
Definition midas.cxx:19203
int rb_set_nonblocking()
Definition midas.cxx:18867
static volatile int _rb_nonblocking
Definition midas.cxx:18856
int rb_increment_wp(int handle, int size)
Definition midas.cxx:19075
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:18908
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:19258
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:18854
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:16707
#define RPC_TEST3_CXX
Definition mrpc.h:126
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11939
#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:12245
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:15086
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:15195
#define RPC_TEST2
Definition mrpc.h:124
#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:14935
INT rpc_get_opt_tcp_size()
Definition midas.cxx:14422
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12909
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11809
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:14028
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:12060
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:17174
static std::mutex _client_connections_mutex
Definition midas.cxx:11736
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13231
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
#define RPC_TEST4_CXX
Definition mrpc.h:127
static std::mutex gAllowedHostsMutex
Definition midas.cxx:16679
INT recv_tcp_check(int sock)
Definition midas.cxx:14905
#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:13284
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11746
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11816
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12614
std::string rpc_get_name()
Definition midas.cxx:13317
int rpc_test_rpc_test2_cxx()
Definition midas.cxx:16215
#define RPC_ID_WATCHDOG
Definition mrpc.h:139
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11751
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
INT rpc_server_receive_rpc(RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:17343
bool rpc_is_remote(void)
Definition midas.cxx:12994
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13394
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:12004
#define RPC_TEST2_CXX
Definition mrpc.h:125
static int _tr_fifo_rp
Definition midas.cxx:14604
int rpc_test_rpc_test2()
Definition midas.cxx:16053
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14670
#define RPC_RC_TRANSITION
Definition mrpc.h:117
int rpc_test_rpc_test3_cxx()
Definition midas.cxx:16375
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13426
#define RPC_ID_EXIT
Definition mrpc.h:141
static INT rpc_execute_old(INT sock, int xroutine_id, const RPC_LIST &rl, char *buffer, INT convert_flags)
Definition midas.cxx:15198
INT rpc_server_loop(void)
Definition midas.cxx:17315
INT rpc_clear_allowed_hosts()
Definition midas.cxx:16682
int rpc_test_rpc()
Definition midas.cxx:16644
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:13038
INT rpc_get_hw_type()
Definition midas.cxx:13067
INT rpc_deregister_functions()
Definition midas.cxx:12103
bool rpc_is_connected(void)
Definition midas.cxx:13016
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13297
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11806
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:15194
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11737
#define RPC_ID_SHUTDOWN
Definition mrpc.h:140
static void rpc_call_encode(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13464
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12845
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:16678
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:14217
static std::mutex rpc_list_mutex
Definition midas.cxx:11807
void rpc_client_check()
Definition midas.cxx:12503
const char * rpc_tid_name(INT id)
Definition midas.cxx:11997
static void rpc_call_encode_cxx(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13709
INT rpc_flush_event()
Definition midas.cxx:14588
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:12041
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11743
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:16785
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:17685
static int _tr_fifo_wp
Definition midas.cxx:14603
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:17638
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:14451
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14602
static std::string _mserver_path
Definition midas.cxx:13281
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:13206
INT rpc_server_disconnect()
Definition midas.cxx:12938
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13367
INT rpc_client_accept(int lsock)
Definition midas.cxx:17070
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11859
static int rpc_find_rpc(int routine_id, RPC_LIST *pentry, bool *prpc_cxx)
Definition midas.cxx:14002
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:14414
#define RPC_BM_RECEIVE_EVENT_CXX
Definition mrpc.h:51
#define RPC_BM_GET_BUFFER_LEVEL
Definition mrpc.h:40
int rpc_name_tid(const char *name)
Definition midas.cxx:12011
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:15127
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:17483
#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:16813
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14601
bool rpc_is_mserver(void)
Definition midas.cxx:13051
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11744
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11844
#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:14469
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11740
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13637
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:14475
INT rpc_set_name(const char *name)
Definition midas.cxx:13341
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:12130
#define RPC_CM_MSG_RETRIEVE
Definition mrpc.h:32
#define RPC_BM_SKIP_EVENT
Definition mrpc.h:50
static INT rpc_execute_cxx(INT sock, int xroutine_id, const RPC_LIST &rl, char *buffer, INT convert_flags)
Definition midas.cxx:15605
INT rpc_client_dispatch(int sock)
Definition midas.cxx:12178
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11739
INT rpc_check_channels(void)
Definition midas.cxx:17756
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14606
int rpc_test_rpc_test4_cxx()
Definition midas.cxx:16531
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:12164
#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:11894
static int rpc_call_decode_cxx(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13909
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11875
#define RPC_CM_GET_WATCHDOG_INFO
Definition mrpc.h:24
static int recv_net_command_realloc(RPC_SERVER_ACCEPTION *sa, char **pbuf, int *pbufsize, INT *remaining)
Definition midas.cxx:14747
INT rpc_get_convert_flags(void)
Definition midas.cxx:13263
INT rpc_tid_size(INT id)
Definition midas.cxx:11990
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:16736
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11914
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
static int offset
Definition mgd.cxx:1500
#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:398
static std::vector< TRANS_TABLE > _trans_table
Definition midas.cxx:248
static std::atomic_int _message_mask_system
Definition midas.cxx:453
INT _semaphore_alarm
Definition midas.cxx:1512
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:1506
int(* MessagePrintCallback)(const char *)
Definition midas.cxx:449
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:1508
static std::string _path_name
Definition midas.cxx:1510
INT _database_entries
static std::string _client_name
Definition midas.cxx:1509
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:1511
INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
Definition midas.cxx:7904
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:346
static BOOL _rpc_registered
Definition midas.cxx:258
static std::atomic< MessagePrintCallback > _message_print
Definition midas.cxx:451
bool ends_with_char(const std::string &s, char c)
Definition midas.cxx:412
void dbg_free(void *adr, char *file, int line)
Definition midas.cxx:356
static std::atomic_int _message_mask_user
Definition midas.cxx:454
static std::mutex _request_list_mutex
Definition midas.cxx:219
INT _semaphore_elog
Definition midas.cxx:1513
INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
Definition midas.cxx:7951
INT _semaphore_history
Definition midas.cxx:1514
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:1507
std::string msprintf(const char *format,...)
Definition midas.cxx:419
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:381
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:19296
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8176
#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:1547
#define M_MALLOC(x)
Definition midas.h:1487
#define CINT(_i)
Definition midas.h:1558
#define RPC_OUT
Definition midas.h:1516
#define RPC_CXX
Definition midas.h:1521
#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:1515
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1542
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1543
#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:1531
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:908
#define RPC_OUTGOING
Definition midas.h:1520
#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:1582
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1517
#define EVENTID_MESSAGE
Definition midas.h:903
#define MIDAS_VERSION
Definition midas.h:37
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define M_FREE(x)
Definition midas.h:1489
#define TRUE
Definition midas.h:182
#define BANKLIST_MAX
Definition midas.h:278
#define EVENTID_FRAG1
Definition midas.h:907
#define CF_IEEE2VAX
Definition midas.h:1548
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1519
#define POINTER_T
Definition midas.h:166
#define BANK_FORMAT_64BIT_ALIGNED
Definition midas.h:1204
#define BANK_FORMAT_32BIT
Definition midas.h:1203
#define CF_VAX2IEEE
Definition midas.h:1549
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:1518
#define BANK_FORMAT_VERSION
Definition midas.h:1202
#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
char name[4]
Definition midas.h:1227
DWORD type
Definition midas.h:1228
DWORD data_size
Definition midas.h:1229
char name[4]
Definition midas.h:1221
DWORD data_size
Definition midas.h:1223
DWORD type
Definition midas.h:1222
DWORD flags
Definition midas.h:1211
DWORD data_size
Definition midas.h:1210
Definition midas.h:1214
char name[4]
Definition midas.h:1215
WORD type
Definition midas.h:1216
WORD data_size
Definition midas.h:1217
INT unused1
Definition midas.h:945
DWORD watchdog_timeout
Definition midas.h:952
INT write_wait
Definition midas.h:948
BOOL read_wait
Definition midas.h:947
DWORD last_activity
Definition midas.h:951
char name[NAME_LENGTH]
Definition midas.h:936
BOOL all_flag
Definition midas.h:950
EVENT_REQUEST event_request[MAX_EVENT_REQUESTS]
Definition midas.h:954
INT max_request_index
Definition midas.h:942
INT read_pointer
Definition midas.h:941
INT num_in_events
Definition midas.h:965
INT write_pointer
Definition midas.h:964
INT num_clients
Definition midas.h:960
char name[NAME_LENGTH]
Definition midas.h:959
INT max_client_index
Definition midas.h:961
BUFFER_CLIENT client[MAX_CLIENTS]
Definition midas.h:968
INT num_out_events
Definition midas.h:966
INT read_pointer
Definition midas.h:963
int count_sent
Definition midas.cxx:6492
int last_count_lock
Definition midas.cxx:6496
BOOL get_all_flag
Definition midas.cxx:6488
int count_lock
Definition midas.cxx:6491
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6505
int count_write_wait
Definition midas.cxx:6494
double bytes_read
Definition midas.cxx:6501
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6502
int wait_client_index
Definition midas.cxx:6498
DWORD time_write_wait
Definition midas.cxx:6495
int count_read
Definition midas.cxx:6500
double bytes_sent
Definition midas.cxx:6493
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6503
DWORD wait_start_time
Definition midas.cxx:6497
int max_requested_space
Definition midas.cxx:6499
HNDLE semaphore
Definition midas.h:1004
double bytes_read
Definition midas.h:1022
std::timed_mutex buffer_mutex
Definition midas.h:989
size_t read_cache_rp
Definition midas.h:997
std::timed_mutex read_cache_mutex
Definition midas.h:994
BOOL get_all_flag
Definition midas.h:1009
int last_count_lock
Definition midas.h:1017
INT client_index
Definition midas.h:990
char client_name[NAME_LENGTH]
Definition midas.h:991
std::timed_mutex write_cache_mutex
Definition midas.h:999
size_t shm_size
Definition midas.h:1006
char * read_cache
Definition midas.h:996
int max_requested_space
Definition midas.h:1020
int count_sent
Definition midas.h:1013
size_t write_cache_rp
Definition midas.h:1002
int client_count_write_wait[MAX_CLIENTS]
Definition midas.h:1023
int count_write_wait
Definition midas.h:1015
int wait_client_index
Definition midas.h:1019
BOOL locked
Definition midas.h:1008
size_t write_cache_wp
Definition midas.h:1003
DWORD time_write_wait
Definition midas.h:1016
int count_read
Definition midas.h:1021
BOOL callback
Definition midas.h:1007
char * write_cache
Definition midas.h:1001
BUFFER_HEADER * buffer_header
Definition midas.h:993
size_t read_cache_wp
Definition midas.h:998
std::atomic< size_t > read_cache_size
Definition midas.h:995
std::atomic< size_t > write_cache_size
Definition midas.h:1000
double bytes_sent
Definition midas.h:1014
DWORD wait_start_time
Definition midas.h:1018
INT shm_handle
Definition midas.h:1005
char buffer_name[NAME_LENGTH]
Definition midas.h:992
std::atomic_bool attached
Definition midas.h:988
int count_lock
Definition midas.h:1012
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.h:1024
void * adr
Definition midas.cxx:280
const char * string
Definition midas.cxx:267
EVENT_HEADER * pevent
Definition midas.cxx:11519
short int event_id
Definition midas.h:853
DWORD data_size
Definition midas.h:857
DWORD serial_number
Definition midas.h:855
DWORD time_stamp
Definition midas.h:856
short int trigger_mask
Definition midas.h:854
short int event_id
Definition midas.h:930
short int trigger_mask
Definition midas.h:931
INT sampling_type
Definition midas.h:932
BOOL valid
Definition midas.h:929
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:1027
INT num_values
Definition midas.h:1029
DWORD type
Definition midas.h:1028
INT last_written
Definition midas.h:1038
char name[NAME_LENGTH]
Definition midas.h:1030
INT item_size
Definition midas.h:1033
NET_COMMAND_HEADER header
Definition msystem.h:293
char param[32]
Definition msystem.h:294
unsigned char * wp
Definition midas.cxx:18848
unsigned char * buffer
Definition midas.cxx:18844
unsigned int max_event_size
Definition midas.cxx:18846
unsigned int size
Definition midas.cxx:18845
unsigned char * rp
Definition midas.cxx:18847
unsigned char * ep
Definition midas.cxx:18849
RPC_PARAM param[MAX_RPC_PARAMS]
Definition midas.h:1536
INT id
Definition midas.h:1534
RPC_HANDLER * dispatch
Definition midas.h:1537
const char * name
Definition midas.h:1535
WORD flags
Definition midas.h:1527
WORD tid
Definition midas.h:1526
INT n
Definition midas.h:1528
midas_thread_t thread_id
Definition midas.cxx:15189
int buffer_size
Definition midas.cxx:15190
char * buffer
Definition midas.cxx:15191
int transition
Definition midas.cxx:14595
time_t trans_time
Definition midas.cxx:14597
int run_number
Definition midas.cxx:14596
int sequence_number
Definition midas.cxx:14598
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:4025
int transition
Definition midas.cxx:4010
std::string host_name
Definition midas.cxx:4016
DWORD connect_end_time
Definition midas.cxx:4027
std::atomic_int status
Definition midas.cxx:4020
~TrClient()
Definition midas.cxx:4038
std::vector< int > wait_for_index
Definition midas.cxx:4015
DWORD init_time
Definition midas.cxx:4023
std::string key_name
Definition midas.cxx:4019
DWORD rpc_end_time
Definition midas.cxx:4030
std::string waiting_for_client
Definition midas.cxx:4024
DWORD rpc_timeout
Definition midas.cxx:4028
int async_flag
Definition midas.cxx:4012
int port
Definition midas.cxx:4018
DWORD rpc_start_time
Definition midas.cxx:4029
int sequence_number
Definition midas.cxx:4014
int debug_flag
Definition midas.cxx:4013
int run_number
Definition midas.cxx:4011
DWORD end_time
Definition midas.cxx:4031
DWORD connect_start_time
Definition midas.cxx:4026
std::string client_name
Definition midas.cxx:4017
std::string errorstr
Definition midas.cxx:4022
void Print() const
Definition midas.cxx:4044
std::thread * thread
Definition midas.cxx:4021
int transition
Definition midas.cxx:4063
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:4071
int async_flag
Definition midas.cxx:4065
DWORD end_time
Definition midas.cxx:4070
int run_number
Definition midas.cxx:4064
int status
Definition midas.cxx:4067
DWORD start_time
Definition midas.cxx:4069
std::string errorstr
Definition midas.cxx:4068
int debug_flag
Definition midas.cxx:4066
unsigned short host_port1
Definition msystem.h:319
std::string user
Definition msystem.h:325
unsigned short host_port2
Definition msystem.h:320
std::string experiment
Definition msystem.h:323
unsigned short host_port3
Definition msystem.h:321
std::string directory
Definition msystem.h:324
std::string host_name
Definition msystem.h:318
Definition midas.cxx:1644
std::string name
Definition midas.cxx:1645
std::string directory
Definition midas.cxx:1646
std::string user
Definition midas.cxx:1647
std::string filename
Definition midas.cxx:1651
std::vector< exptab_entry > exptab
Definition midas.cxx:1652
Definition midas.cxx:874
DWORD ts
Definition midas.cxx:875
int message_type
Definition midas.cxx:876
std::string message
Definition midas.cxx:877
double d
Definition system.cxx:1313
char c
Definition system.cxx:1312
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