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 size_t size = vsnprintf(nullptr, 0, format, ap1) + 1;
425 char *buffer = (char *)malloc(size);
426 if (!buffer) {
427 va_end(ap);
428 va_end(ap1);
429 return "";
430 }
431 vsnprintf(buffer, size, format, ap);
432 va_end(ap);
433 va_end(ap1);
434 std::string s(buffer);
435 free(buffer);
436 return s;
437}
438
439/********************************************************************\
440* *
441* Common message functions *
442* *
443\********************************************************************/
444
445typedef int (*MessagePrintCallback)(const char *);
446
447static std::atomic<MessagePrintCallback> _message_print{puts};
448
449static std::atomic_int _message_mask_system{MT_ALL};
450static std::atomic_int _message_mask_user{MT_ALL};
451
452
454#endif /* DOXYGEN_SHOULD_SKIP_THIS */
455
461/********************************************************************/
469std::string cm_get_error(INT code)
470{
471 for (int i = 0; _error_table[i].code; i++) {
472 if (_error_table[i].code == code) {
473 return _error_table[i].string;
474 }
475 }
476
477 return msprintf("unlisted status code %d", code);
478}
479
480/********************************************************************/
482
483 return CM_SUCCESS;
484}
485
486/********************************************************************/
487
489 //printf("cm_msg_open_buffer!\n");
490 if (_msg_buffer == 0) {
492 if (status != BM_SUCCESS && status != BM_CREATED) {
493 return status;
494 }
495 }
496 return CM_SUCCESS;
497}
498
499/********************************************************************/
500
502 //printf("cm_msg_close_buffer!\n");
503 if (_msg_buffer) {
505 _msg_buffer = 0;
506 }
507 return CM_SUCCESS;
508}
509
510/********************************************************************/
511
519 std::string path;
520
521 cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
522
523 /* extract directory name from full path name of midas.log */
524 size_t pos = path.rfind(DIR_SEPARATOR);
525 if (pos != std::string::npos) {
526 path.resize(pos);
527 } else {
528 path = "";
529 }
530
531 //printf("cm_msg_facilities: path [%s]\n", path.c_str());
532
533 STRING_LIST flist;
534
535 ss_file_find(path.c_str(), "*.log", &flist);
536
537 for (size_t i = 0; i < flist.size(); i++) {
538 const char *p = flist[i].c_str();
539 if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
540 size_t pos = flist[i].rfind('.');
541 if (pos != std::string::npos) {
542 flist[i].resize(pos);
543 }
544 list->push_back(flist[i]);
545 }
546 }
547
548 return SUCCESS;
549}
550
551/********************************************************************/
552
553void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
554 HNDLE hDB;
555 int status;
556
558
559 // check for call to cm_msg() before MIDAS is fully initialized
560 // or after MIDAS is partially shutdown.
561 if (status != CM_SUCCESS) {
562 if (filename)
563 *filename = std::string(fac) + ".log";
564 if (linkname)
565 *linkname = "";
566 if (linktarget)
567 *linktarget = "";
568 return;
569 }
570
571 if (filename)
572 *filename = "";
573 if (linkname)
574 *linkname = "";
575 if (linktarget)
576 *linktarget = "";
577
578 std::string facility;
579 if (fac && fac[0])
580 facility = fac;
581 else
582 facility = "midas";
583
584 std::string message_format;
585 db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
586 if (message_format.find('%') != std::string::npos) {
587 /* replace stings such as %y%m%d with current date */
588 struct tm tms;
589
590 ss_tzset();
591 if (t == 0)
592 time(&t);
593 localtime_r(&t, &tms);
594
595 char de[256];
596 de[0] = '_';
597 strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
598 message_format = de;
599 }
600
601 std::string message_dir;
602 db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
603 if (message_dir.empty()) {
604 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
605 if (message_dir.empty()) {
606 message_dir = cm_get_path();
607 if (message_dir.empty()) {
608 message_dir = ss_getcwd();
609 }
610 }
611 }
612
613 // prepend experiment directory
614 if (message_dir[0] != DIR_SEPARATOR)
615 message_dir = cm_get_path() + message_dir;
616
617 if (message_dir.back() != DIR_SEPARATOR)
618 message_dir.push_back(DIR_SEPARATOR);
619
620 if (filename)
621 *filename = message_dir + facility + message_format + ".log";
622 if (!message_format.empty()) {
623 if (linkname)
624 *linkname = message_dir + facility + ".log";
625 if (linktarget)
626 *linktarget = facility + message_format + ".log";
627 }
628}
629
630/********************************************************************/
661INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
662 _message_mask_system = system_mask;
663 _message_mask_user = user_mask;
664 _message_print = func;
665
666 return BM_SUCCESS;
667}
668
669/********************************************************************/
678INT cm_msg_log(INT message_type, const char *facility, const char *message) {
679 INT status;
680
681 if (rpc_is_remote()) {
682 if (rpc_is_connected()) {
683 status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
684 if (status != RPC_SUCCESS) {
685 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);
686 }
687 return status;
688 } else {
689 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
690 return RPC_NET_ERROR;
691 }
692 }
693
694 if (message_type != MT_DEBUG) {
695 std::string filename, linkname, linktarget;
696
697 cm_msg_get_logfile(facility, 0, &filename, &linkname, &linktarget);
698
699#ifdef OS_LINUX
700 if (!linkname.empty()) {
701 //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
702 // If filename does not exist, user just switched from non-date format to date format.
703 // In that case we must copy linkname to filename, otherwise messages might get lost.
704 if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
705 ss_file_copy(linkname.c_str(), filename.c_str(), true);
706 }
707
708 unlink(linkname.c_str());
709 status = symlink(linktarget.c_str(), linkname.c_str());
710 if (status != 0) {
711 fprintf(stderr,
712 "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
713 linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
714 }
715 }
716#endif
717
718 int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
719 if (fh < 0) {
720 fprintf(stderr,
721 "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
722 message, filename.c_str(), errno, strerror(errno));
723 } else {
724
725 struct timeval tv;
726 struct tm tms;
727
728 ss_tzset();
729 gettimeofday(&tv, NULL);
730 localtime_r(&tv.tv_sec, &tms);
731
732 char str[256];
733 strftime(str, sizeof(str), "%H:%M:%S", &tms);
734 sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
735 strftime(str + strlen(str), sizeof(str), "%Y/%m/%d", &tms);
736
737 std::string msg;
738 msg += str;
739 msg += " ";
740 msg += message;
741 msg += "\n";
742
743 /* avoid c++ complaint about comparison between
744 unsigned size_t returned by msg.length() and
745 signed ssize_t returned by write() */
746 ssize_t len = msg.length();
747
748 /* atomic write, no need to take a semaphore */
749 ssize_t wr = write(fh, msg.c_str(), len);
750
751 if (wr < 0) {
752 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
753 } else if (wr != len) {
754 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);
755 }
756
757 close(fh);
758 }
759 }
760
761 return CM_SUCCESS;
762}
763
764
765static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
766{
767 /* strip path */
768 const char* pc = filename + strlen(filename);
769 while (*pc != '\\' && *pc != '/' && pc != filename)
770 pc--;
771 if (pc != filename)
772 pc++;
773
774 /* convert type to string */
775 std::string type_str;
776 if (message_type & MT_ERROR)
777 type_str += MT_ERROR_STR;
778 if (message_type & MT_INFO)
779 type_str += MT_INFO_STR;
780 if (message_type & MT_DEBUG)
781 type_str += MT_DEBUG_STR;
782 if (message_type & MT_USER)
783 type_str += MT_USER_STR;
784 if (message_type & MT_LOG)
785 type_str += MT_LOG_STR;
786 if (message_type & MT_TALK)
787 type_str += MT_TALK_STR;
788
789 std::string message;
790
791 /* print client name into string */
792 if (message_type == MT_USER)
793 message = msprintf("[%s] ", routine);
794 else {
795 std::string name = rpc_get_name();
796 if (name.length() > 0)
797 message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
798 else
799 message = "";
800 }
801
802 /* preceed error messages with file and line info */
803 if (message_type == MT_ERROR) {
804 message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
805 } else if (message_type == MT_USER) {
806 message = msprintf("[%s,%s] ", routine, type_str.c_str());
807 }
808
809 int bufsize = 1024;
810 char* buf = (char*)malloc(bufsize);
811 assert(buf);
812
813 for (int i=0; i<10; i++) {
814 va_list ap;
815 va_copy(ap, *argptr);
816
817 /* print argument list into message */
818 int n = vsnprintf(buf, bufsize-1, format, ap);
819
820 //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
821
822 va_end(ap);
823
824 if (n < bufsize) {
825 break;
826 }
827
828 bufsize += 100;
829 bufsize *= 2;
830 buf = (char*)realloc(buf, bufsize);
831 assert(buf);
832 }
833
834 message += buf;
835 free(buf);
836
837 return message;
838}
839
840static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
841 //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
842
843 /* send event if not of type MLOG */
844 if (message_type != MT_LOG) {
845 if (_msg_buffer) {
846 /* copy message to event */
847 size_t len = strlen(send_message);
848 int event_length = sizeof(EVENT_HEADER) + len + 1;
849 char event[event_length];
850 EVENT_HEADER *pevent = (EVENT_HEADER *) event;
851
852 memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
853
854 /* setup the event header and send the message */
855 bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
856 if (ts)
857 pevent->time_stamp = ts;
858 //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)));
859 bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
860 }
861 }
862
863 return CM_SUCCESS;
864}
865
869 std::string message;
870};
871
872static std::deque<msg_buffer_entry> gMsgBuf;
873static std::mutex gMsgBufMutex;
874
875/********************************************************************/
882 int i;
883
884 //printf("cm_msg_flush_buffer!\n");
885
886 for (i = 0; i < 100; i++) {
888 {
889 std::lock_guard<std::mutex> lock(gMsgBufMutex);
890 if (gMsgBuf.empty())
891 break;
892 e = gMsgBuf.front();
893 gMsgBuf.pop_front();
894 // implicit unlock
895 }
896
897 /* log message */
898 cm_msg_log(e.message_type, "midas", e.message.c_str());
899
900 /* send message to SYSMSG */
901 int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
902 if (status != CM_SUCCESS)
903 return status;
904 }
905
906 return CM_SUCCESS;
907}
908
909/********************************************************************/
931INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
932{
933 DWORD ts = ss_time();
934
935 /* print argument list into message */
936 std::string message;
937 va_list argptr;
938 va_start(argptr, format);
939 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
940 va_end(argptr);
941
942 //printf("message [%s]\n", message.c_str());
943
944 /* call user function if set via cm_set_msg_print */
946 if (f != NULL && (message_type & _message_mask_user) != 0) {
947 if (message_type != MT_LOG) { // do not print MLOG messages
948 (*f)(message.c_str());
949 }
950 }
951
952 /* return if system mask is not set */
953 if ((message_type & _message_mask_system) == 0) {
954 return CM_SUCCESS;
955 }
956
957 gMsgBufMutex.lock();
958 gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
959 gMsgBufMutex.unlock();
960
961 return CM_SUCCESS;
962}
963
964/********************************************************************/
989INT cm_msg1(INT message_type, const char *filename, INT line,
990 const char *facility, const char *routine, const char *format, ...) {
991 va_list argptr;
992 std::string message;
993 static BOOL in_routine = FALSE;
994
995 /* avoid recursive calles */
996 if (in_routine)
997 return 0;
998
999 in_routine = TRUE;
1000
1001 /* print argument list into message */
1002 va_start(argptr, format);
1003 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
1004 va_end(argptr);
1005
1006 /* call user function if set via cm_set_msg_print */
1008 if (f != NULL && (message_type & _message_mask_user) != 0)
1009 (*f)(message.c_str());
1010
1011 /* return if system mask is not set */
1012 if ((message_type & _message_mask_system) == 0) {
1013 in_routine = FALSE;
1014 return CM_SUCCESS;
1015 }
1016
1017 /* send message to SYSMSG */
1018 cm_msg_send_event(0, message_type, message.c_str());
1019
1020 /* log message */
1021 cm_msg_log(message_type, facility, message.c_str());
1022
1023 in_routine = FALSE;
1024
1025 return CM_SUCCESS;
1026}
1027
1028/********************************************************************/
1068 INT status, id;
1069
1070 // we should only come here after the message buffer
1071 // was opened by cm_connect_experiment()
1072 assert(_msg_buffer);
1073
1074 _msg_dispatch = func;
1075
1077
1078 return status;
1079}
1080
1081static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1082 int new_message_length = strlen(new_message);
1083 int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1084 char buf[100];
1085 int buf_length;
1086
1087 //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);
1088
1089 if (*length + new_message_length + 100 > *allocated) {
1090 *messages = (char *) realloc(*messages, new_allocated);
1091 assert(*messages != NULL);
1092 *allocated = new_allocated;
1093 }
1094
1095 if (*length > 0)
1096 if ((*messages)[(*length) - 1] != '\n') {
1097 (*messages)[*length] = '\n'; // separator between messages
1098 (*length) += 1;
1099 }
1100
1101 sprintf(buf, "%ld ", tstamp);
1102 buf_length = strlen(buf);
1103 memcpy(&((*messages)[*length]), buf, buf_length);
1104 (*length) += buf_length;
1105
1106 memcpy(&((*messages)[*length]), new_message, new_message_length);
1107 (*length) += new_message_length;
1108 (*messages)[*length] = 0; // make sure string is NUL terminated
1109}
1110
1111/* Retrieve message from an individual file. Internal use only */
1112static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1113 int *num_messages) {
1114 BOOL stop;
1115 int fh;
1116 char *p, str[1000];
1117 struct stat stat_buf;
1118 time_t tstamp, tstamp_valid, tstamp_last;
1119
1120 ss_tzset(); // required by localtime_r()
1121
1122 *num_messages = 0;
1123
1124 fh = open(filename, O_RDONLY | O_TEXT, 0644);
1125 if (fh < 0) {
1126 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1127 strerror(errno));
1128 return SS_FILE_ERROR;
1129 }
1130
1131 /* read whole file into memory */
1132 fstat(fh, &stat_buf);
1133 ssize_t size = stat_buf.st_size;
1134
1135 /* if file is too big, only read tail of file */
1136 ssize_t maxsize = 10 * 1024 * 1024;
1137 if (size > maxsize) {
1138 lseek(fh, -maxsize, SEEK_END);
1139 //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1140 size = maxsize;
1141 }
1142
1143 char *buffer = (char *) malloc(size + 1);
1144
1145 if (buffer == NULL) {
1146 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1147 filename, errno, strerror(errno));
1148 close(fh);
1149 return SS_FILE_ERROR;
1150 }
1151
1152 ssize_t rd = read(fh, buffer, size);
1153
1154 if (rd != size) {
1155 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1156 (int) size, filename, (int) rd, errno, strerror(errno));
1157 close(fh);
1158 return SS_FILE_ERROR;
1159 }
1160
1161 buffer[size] = 0;
1162 close(fh);
1163
1164 p = buffer + size - 1;
1165 tstamp_last = tstamp_valid = 0;
1166 stop = FALSE;
1167
1168 while (*p == '\n' || *p == '\r')
1169 p--;
1170
1171 int n;
1172 for (n = 0; !stop && p > buffer;) {
1173
1174 /* go to beginning of line */
1175 int i;
1176 for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1177 p--;
1178
1179 /* limit line length to sizeof(str) */
1180 if (i >= (int) sizeof(str))
1181 i = sizeof(str) - 1;
1182
1183 if (p == buffer) {
1184 i++;
1185 memcpy(str, p, i);
1186 } else
1187 memcpy(str, p + 1, i);
1188 str[i] = 0;
1189 if (strchr(str, '\n'))
1190 *strchr(str, '\n') = 0;
1191 if (strchr(str, '\r'))
1192 *strchr(str, '\r') = 0;
1193 mstrlcat(str, "\n", sizeof(str));
1194
1195 // extract time tag
1196 time_t now;
1197 time(&now);
1198
1199 struct tm tms;
1200 localtime_r(&now, &tms); // must call tzset() beforehand!
1201
1202 if (str[0] >= '0' && str[0] <= '9') {
1203 // new format
1204 tms.tm_hour = atoi(str);
1205 tms.tm_min = atoi(str + 3);
1206 tms.tm_sec = atoi(str + 6);
1207 tms.tm_year = atoi(str + 13) - 1900;
1208 tms.tm_mon = atoi(str + 18) - 1;
1209 tms.tm_mday = atoi(str + 21);
1210 } else {
1211 // old format
1212 tms.tm_hour = atoi(str + 11);
1213 tms.tm_min = atoi(str + 14);
1214 tms.tm_sec = atoi(str + 17);
1215 tms.tm_year = atoi(str + 20) - 1900;
1216 for (i = 0; i < 12; i++)
1217 if (strncmp(str + 4, mname[i], 3) == 0)
1218 break;
1219 tms.tm_mon = i;
1220 tms.tm_mday = atoi(str + 8);
1221 }
1222 tstamp = ss_mktime(&tms);
1223 if (tstamp != -1)
1224 tstamp_valid = tstamp;
1225
1226 // for new messages (n=0!), stop when t reached
1227 if (n_messages == 0) {
1228 if (tstamp_valid < t)
1229 break;
1230 }
1231
1232 // for old messages, stop when all messages belonging to tstamp_last are sent
1233 if (n_messages != 0) {
1234 if (tstamp_last > 0 && tstamp_valid < tstamp_last)
1235 break;
1236 }
1237
1238 if (t == 0 || tstamp == -1 ||
1239 (n_messages > 0 && tstamp <= t) ||
1240 (n_messages == 0 && tstamp >= t)) {
1241
1242 n++;
1243
1244 add_message(messages, length, allocated, tstamp, str);
1245 }
1246
1247 while (*p == '\n' || *p == '\r')
1248 p--;
1249
1250 if (n_messages == 1)
1251 stop = TRUE;
1252 else if (n_messages > 1) {
1253 // continue collecting messages until time stamp differs from current one
1254 if (n == n_messages)
1255 tstamp_last = tstamp_valid;
1256
1257 // if all messages without time tags, just return after n
1258 if (n == n_messages && tstamp_valid == 0)
1259 break;
1260 }
1261 }
1262
1263 free(buffer);
1264
1265 *num_messages = n;
1266
1267 return CM_SUCCESS;
1268}
1269
1270/********************************************************************/
1280INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages) {
1281 std::string filename, linkname;
1282 INT n, i;
1283 time_t filedate;
1284 int length = 0;
1285 int allocated = 0;
1286
1287 time(&filedate);
1288 cm_msg_get_logfile(facility, filedate, &filename, &linkname, NULL);
1289
1290 //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1291
1292 // see if file exists, use linkname if not
1293 if (!linkname.empty()) {
1294 if (!ss_file_exist(filename.c_str()))
1295 filename = linkname;
1296 }
1297
1298 if (ss_file_exist(filename.c_str())) {
1299 cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1300 } else {
1301 n = 0;
1302 }
1303
1304 /* if there is no symlink, then there is no additional log files to read */
1305 if (linkname.empty()) {
1306 *num_messages = n;
1307 return CM_SUCCESS;
1308 }
1309
1310 //printf("read more messages %d %d!\n", n, n_message);
1311
1312 int missing = 0;
1313 while (n < n_message) {
1314 filedate -= 3600 * 24; // go one day back
1315
1316 cm_msg_get_logfile(facility, filedate, &filename, NULL, NULL);
1317
1318 //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1319
1320 if (ss_file_exist(filename.c_str())) {
1321 cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1322 n += i;
1323 missing = 0;
1324 } else {
1325 missing++;
1326 }
1327
1328 // stop if ten consecutive files are not found
1329 if (missing > 10)
1330 break;
1331 }
1332
1333 *num_messages = n;
1334
1335 return CM_SUCCESS;
1336}
1337
1338/********************************************************************/
1350INT cm_msg_retrieve(INT n_message, char *message, INT buf_size) {
1351 int status;
1352 char *messages = NULL;
1353 int num_messages = 0;
1354
1355 if (rpc_is_remote())
1356 return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1357
1358 status = cm_msg_retrieve2("midas", 0, n_message, &messages, &num_messages);
1359
1360 if (messages) {
1361 mstrlcpy(message, messages, buf_size);
1362 int len = strlen(messages);
1363 if (len > buf_size)
1365 free(messages);
1366 }
1367
1368 return status;
1369}
1370
/* end of msgfunctionc */
1373
1379/********************************************************************/
1386 INT sec, status;
1387
1388 /* if connected to server, get time from there */
1389 if (rpc_is_remote()) {
1391
1392 /* set local time */
1393 if (status == CM_SUCCESS)
1394 ss_settime(sec);
1395 }
1396
1397 /* return time to caller */
1398 if (seconds != NULL) {
1399 *seconds = ss_time();
1400 }
1401
1402 return CM_SUCCESS;
1403}
1404
1405/********************************************************************/
1412INT cm_asctime(char *str, INT buf_size) {
1413 /* if connected to server, get time from there */
1414 if (rpc_is_remote())
1415 return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1416
1417 /* return local time */
1418 mstrlcpy(str, ss_asctime().c_str(), buf_size);
1419
1420 return CM_SUCCESS;
1421}
1422
1423/********************************************************************/
1428std::string cm_asctime() {
1429 /* if connected to server, get time from there */
1430 if (rpc_is_remote()) {
1431 char buf[256];
1432 int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1433 if (status == CM_SUCCESS) {
1434 return buf;
1435 } else {
1436 return "";
1437 }
1438 }
1439
1440 /* return local time */
1441 return ss_asctime();
1442}
1443
1444/********************************************************************/
1451 /* if connected to server, get time from there */
1452 if (rpc_is_remote())
1453 return rpc_call(RPC_CM_TIME, t);
1454
1455 /* return local time */
1456 *t = ss_time();
1457
1458 return CM_SUCCESS;
1459}
1460
/* end of cmfunctionc */
1463
1464/********************************************************************\
1465* *
1466* cm_xxx - Common Functions to buffer & database *
1467* *
1468\********************************************************************/
1469
1470/* Globals */
1471
1472static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1473static HNDLE _hDB = 0; /* Database handle */
1474static std::string _experiment_name;
1475static std::string _client_name;
1476static std::string _path_name;
1481//INT _semaphore_msg = -1;
1482
1492const char *cm_get_version() {
1493 return MIDAS_VERSION;
1494}
1495
1500const char *cm_get_revision() {
1501 return GIT_REVISION;
1502}
1503
1504/********************************************************************/
1513INT cm_set_path(const char *path) {
1514 assert(path);
1515 assert(path[0] != 0);
1516
1517 _path_name = path;
1518
1519 if (_path_name.back() != DIR_SEPARATOR) {
1521 }
1522
1523 //printf("cm_set_path [%s]\n", _path_name.c_str());
1524
1525 return CM_SUCCESS;
1526}
1527
1528/********************************************************************/
1534INT cm_get_path(char *path, int path_size) {
1535 // check that we were not accidentally called
1536 // with the size of the pointer to a string
1537 // instead of the size of the string buffer
1538 assert(path_size != sizeof(char *));
1539 assert(path);
1540 assert(_path_name.length() > 0);
1541
1542 mstrlcpy(path, _path_name.c_str(), path_size);
1543
1544 return CM_SUCCESS;
1545}
1546
1547/********************************************************************/
1553std::string cm_get_path() {
1554 assert(_path_name.length() > 0);
1555 return _path_name;
1556}
1557
1558/********************************************************************/
1559/* C++ wrapper for cm_get_path */
1560
1561INT EXPRT cm_get_path_string(std::string *path) {
1562 assert(path != NULL);
1563 assert(_path_name.length() > 0);
1564 *path = _path_name;
1565 return CM_SUCCESS;
1566}
1567
1568/********************************************************************/
1576 return CM_SUCCESS;
1577}
1578
1579/********************************************************************/
1586INT cm_get_experiment_name(char *name, int name_length) {
1587 mstrlcpy(name, _experiment_name.c_str(), name_length);
1588 return CM_SUCCESS;
1589}
1590
1591/********************************************************************/
1597 return _experiment_name;
1598}
1599
/* end of cmfunctionc */
1602
1608#ifdef LOCAL_ROUTINES
1609
1611 std::string name;
1612 std::string directory;
1613 std::string user;
1614};
1615
1617 std::string filename;
1618 std::vector<exptab_entry> exptab;
1619};
1620
1621static exptab_struct _exptab; // contents of exptab file
1622
1631{
1632 exptab->exptab.clear();
1633
1634 /* MIDAS_DIR overrides exptab */
1635 if (getenv("MIDAS_DIR")) {
1636 exptab->filename = "MIDAS_DIR";
1637
1639
1640 if (getenv("MIDAS_EXPT_NAME")) {
1641 e.name = getenv("MIDAS_EXPT_NAME");
1642 } else {
1643 e.name = "Default";
1644 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());
1645 }
1646
1647 e.directory = getenv("MIDAS_DIR");
1648 e.user = "";
1649
1650 exptab->exptab.push_back(e);
1651
1652 return CM_SUCCESS;
1653 }
1654
1655 /* default directory for different OSes */
1656#if defined (OS_WINNT)
1657 std::string str;
1658 if (getenv("SystemRoot"))
1659 str = getenv("SystemRoot");
1660 else if (getenv("windir"))
1661 str = getenv("windir");
1662 else
1663 str = "";
1664
1665 std::string alt_str = str;
1666 str += "\\system32\\exptab";
1667 alt_str += "\\system\\exptab";
1668#elif defined (OS_UNIX)
1669 std::string str = "/etc/exptab";
1670 std::string alt_str = "/exptab";
1671#else
1672 std::strint str = "exptab";
1673 std::string alt_str = "exptab";
1674#endif
1675
1676 /* MIDAS_EXPTAB overrides default directory */
1677 if (getenv("MIDAS_EXPTAB")) {
1678 str = getenv("MIDAS_EXPTAB");
1679 alt_str = getenv("MIDAS_EXPTAB");
1680 }
1681
1682 exptab->filename = str;
1683
1684 /* read list of available experiments */
1685 FILE* f = fopen(str.c_str(), "r");
1686 if (f == NULL) {
1687 f = fopen(alt_str.c_str(), "r");
1688 if (f == NULL)
1689 return CM_UNDEF_ENVIRON;
1690 exptab->filename = alt_str;
1691 }
1692
1693 if (f != NULL) {
1694 do {
1695 char buf[256];
1696 memset(buf, 0, sizeof(buf));
1697 char* str = fgets(buf, sizeof(buf)-1, f);
1698 if (str == NULL)
1699 break;
1700 if (str[0] == 0) continue; // empty line
1701 if (str[0] == '#') continue; // comment line
1702
1704
1705 // following code emulates the function of this sprintf():
1706 //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1707
1708 // skip leading spaces
1709 while (*str && isspace(*str))
1710 str++;
1711
1712 char* p1 = str;
1713 char* p2 = str;
1714
1715 while (*p2 && !isspace(*p2))
1716 p2++;
1717
1718 ssize_t len = p2-p1;
1719
1720 if (len<1)
1721 continue;
1722
1723 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1724
1725 e.name = std::string(p1, len);
1726
1727 if (*p2 == 0)
1728 continue;
1729
1730 str = p2;
1731
1732 // skip leading spaces
1733 while (*str && isspace(*str))
1734 str++;
1735
1736 p1 = str;
1737 p2 = str;
1738
1739 while (*p2 && !isspace(*p2))
1740 p2++;
1741
1742 len = p2-p1;
1743
1744 if (len<1)
1745 continue;
1746
1747 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1748
1749 e.directory = std::string(p1, len);
1750
1751 if (*p2 == 0)
1752 continue;
1753
1754 str = p2;
1755
1756 // skip leading spaces
1757 while (*str && isspace(*str))
1758 str++;
1759
1760 p1 = str;
1761 p2 = str;
1762
1763 while (*p2 && !isspace(*p2))
1764 p2++;
1765
1766 len = p2-p1;
1767
1768 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1769
1770 e.user = std::string(p1, len);
1771
1772 /* check for trailing directory separator */
1773 if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1774 e.directory += DIR_SEPARATOR_STR;
1775 }
1776
1777 exptab->exptab.push_back(e);
1778 } while (!feof(f));
1779 fclose(f);
1780 }
1781
1782#if 0
1783 cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1784 for (unsigned j=0; j<exptab->exptab.size(); j++) {
1785 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());
1786 }
1787#endif
1788
1789 return CM_SUCCESS;
1790}
1791
1792/********************************************************************/
1799int cm_get_exptab_filename(char *s, int size) {
1800 mstrlcpy(s, _exptab.filename.c_str(), size);
1801 return CM_SUCCESS;
1802}
1803
1805 return _exptab.filename;
1806}
1807
1808/********************************************************************/
1815int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1816
1817 if (_exptab.exptab.size() == 0) {
1819 if (status != CM_SUCCESS)
1820 return status;
1821 }
1822
1823 for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1824 if (_exptab.exptab[i].name == expname) {
1825 if (dir)
1826 *dir = _exptab.exptab[i].directory;
1827 if (user)
1828 *user = _exptab.exptab[i].user;
1829 return CM_SUCCESS;
1830 }
1831 }
1832 if (dir)
1833 *dir = "";
1834 if (user)
1835 *user = "";
1836 return CM_UNDEF_EXP;
1837}
1838
1839/********************************************************************/
1846int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1847 std::string sdir, suser;
1848 int status = cm_get_exptab(expname, &sdir, &suser);
1849 if (status == CM_SUCCESS) {
1850 if (dir)
1851 mstrlcpy(dir, sdir.c_str(), dir_size);
1852 if (user)
1853 mstrlcpy(user, suser.c_str(), user_size);
1854 return CM_SUCCESS;
1855 }
1856 return CM_UNDEF_EXP;
1857}
1858
1859#endif // LOCAL_ROUTINES
1860
1861/********************************************************************/
1869 /* only do it if local */
1870 if (!rpc_is_remote()) {
1872 }
1873 return CM_SUCCESS;
1874}
1875
1876/********************************************************************/
1886 if (rpc_is_remote())
1887 return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
1888
1889#ifdef LOCAL_ROUTINES
1890 return db_check_client(hDB, hKeyClient);
1891#endif /*LOCAL_ROUTINES */
1892 return CM_SUCCESS;
1893}
1894
1895/********************************************************************/
1909INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name,
1910 char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1911 if (rpc_is_remote())
1912 return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
1913 host_name, client_name, hw_type, password, watchdog_timeout);
1914
1915#ifdef LOCAL_ROUTINES
1916 {
1917 INT status, pid, data, i, idx, size;
1919 char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
1920 BOOL call_watchdog, allow;
1921 PROGRAM_INFO_STR(program_info_str);
1922
1923 /* check security if password is present */
1924 status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1925 if (hKey) {
1926 /* get password */
1927 size = sizeof(pwd);
1928 db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1929
1930 /* first check allowed hosts list */
1931 allow = FALSE;
1932 db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1934 allow = TRUE;
1935
1936 /* check allowed programs list */
1937 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1938 if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
1939 allow = TRUE;
1940
1941 /* now check password */
1942 if (!allow && strcmp(password, pwd) != 0) {
1943 if (password[0])
1944 cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
1945 return CM_WRONG_PASSWORD;
1946 }
1947 }
1948
1949 /* make following operation atomic by locking database */
1951
1952 /* check if entry with this pid exists already */
1953 pid = ss_getpid();
1954
1955 sprintf(str, "System/Clients/%0d", pid);
1956 status = db_find_key(hDB, 0, str, &hKey);
1957 if (status == DB_SUCCESS) {
1960 }
1961
1962 if (strlen(client_name) >= NAME_LENGTH)
1963 client_name[NAME_LENGTH] = 0;
1964
1965 strcpy(name, client_name);
1966 strcpy(orig_name, client_name);
1967
1968 /* check if client name already exists */
1969 status = db_find_key(hDB, 0, "System/Clients", &hKey);
1970
1971 for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
1972 for (i = 0;; i++) {
1975 break;
1976
1977 if (status == DB_SUCCESS) {
1978 size = sizeof(str);
1979 status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
1980 if (status != DB_SUCCESS)
1981 continue;
1982 }
1983
1984 /* check if client is living */
1986 continue;
1987
1988 if (equal_ustring(str, name)) {
1989 sprintf(name, "%s%d", client_name, idx);
1990 break;
1991 }
1992 }
1993 }
1994
1995 /* set name */
1996 sprintf(str, "System/Clients/%0d/Name", pid);
1998 if (status != DB_SUCCESS) {
2000 cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
2001 return status;
2002 }
2003
2004 /* copy new client name */
2005 strcpy(client_name, name);
2006 db_set_client_name(hDB, client_name);
2007
2008 /* set also as rpc name */
2009 rpc_set_name(client_name);
2010
2011 /* use /system/clients/PID as root */
2012 sprintf(str, "System/Clients/%0d", pid);
2013 db_find_key(hDB, 0, str, &hKey);
2014
2015 /* set host name */
2017 if (status != DB_SUCCESS) {
2019 return status;
2020 }
2021
2022 /* set computer id */
2023 status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2024 if (status != DB_SUCCESS) {
2026 return status;
2027 }
2028
2029 /* set server port */
2030 data = 0;
2031 status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2032 if (status != DB_SUCCESS) {
2034 return status;
2035 }
2036
2037 /* lock client entry */
2039
2040 /* get (set) default watchdog timeout */
2041 size = sizeof(watchdog_timeout);
2042 sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
2043 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2044
2045 /* define /programs entry */
2046 sprintf(str, "/Programs/%s", orig_name);
2047 db_create_record(hDB, 0, str, strcomb1(program_info_str).c_str());
2048
2049 /* save handle for ODB and client */
2051
2052 /* save watchdog timeout */
2053 cm_get_watchdog_params(&call_watchdog, NULL);
2054 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2055
2056 /* end of atomic operations */
2058
2059 /* touch notify key to inform others */
2060 data = 0;
2061 db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2062
2063 *hKeyClient = hKey;
2064 }
2065#endif /* LOCAL_ROUTINES */
2066
2067 return CM_SUCCESS;
2068}
2069
2070/********************************************************************/
2076{
2077 INT status;
2078 HNDLE hDB, hKey;
2079
2080 /* get root key of client */
2082 if (!hDB) {
2083 return "unknown";
2084 }
2085
2086 std::string name;
2087
2088 status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2089 if (status != DB_SUCCESS) {
2090 return "unknown";
2091 }
2092
2093 //printf("get client name: [%s]\n", name.c_str());
2094
2095 return name;
2096}
2097
2098/********************************************************************/
2150INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
2151 if (host_name)
2152 host_name[0] = 0;
2153 if (exp_name)
2154 exp_name[0] = 0;
2155
2156 if (host_name && getenv("MIDAS_SERVER_HOST"))
2157 mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2158
2159 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2160 mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2161
2162 return CM_SUCCESS;
2163}
2164
2165INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2166 if (host_name)
2167 *host_name = "";
2168 if (exp_name)
2169 *exp_name = "";
2170
2171 if (host_name && getenv("MIDAS_SERVER_HOST"))
2172 *host_name = getenv("MIDAS_SERVER_HOST");
2173
2174 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2175 *exp_name = getenv("MIDAS_EXPT_NAME");
2176
2177 return CM_SUCCESS;
2178}
2179
2180#ifdef LOCAL_ROUTINES
2181
2183{
2184 std::string exp_name1;
2185
2186 if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2187 exp_name1 = exp_name;
2188 } else {
2189 int status = cm_select_experiment_local(&exp_name1);
2190 if (status != CM_SUCCESS)
2191 return status;
2192 }
2193
2194 std::string expdir, expuser;
2195
2196 int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2197
2198 if (status != CM_SUCCESS) {
2199 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());
2200 return CM_UNDEF_EXP;
2201 }
2202
2203 if (!ss_dir_exist(expdir.c_str())) {
2204 cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2205 return CM_UNDEF_EXP;
2206 }
2207
2208 cm_set_experiment_name(exp_name1.c_str());
2209 cm_set_path(expdir.c_str());
2210
2211 return CM_SUCCESS;
2212}
2213
2214#endif // LOCAL_ROUTINES
2215
2216/********************************************************************/
2218 if (_hKeyClient) {
2219 cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2221 }
2222}
2223
2224/********************************************************************/
2294INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2295 INT status;
2296
2299 if (status != CM_SUCCESS) {
2300 std::string s = cm_get_error(status);
2301 puts(s.c_str());
2302 }
2303
2304 return status;
2305}
2306
2307/********************************************************************/
2313INT cm_connect_experiment1(const char *host_name, const char *default_exp_name,
2314 const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2315 INT status, size;
2316 char client_name1[NAME_LENGTH];
2317 char password[NAME_LENGTH], str[256];
2318 HNDLE hDB = 0, hKeyClient = 0;
2319 BOOL call_watchdog;
2320
2321 ss_tzset(); // required for localtime_r()
2322
2323 if (_hKeyClient)
2325
2327
2328 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2329 //cm_msg_flush_buffer();
2330
2331 rpc_set_name(client_name);
2332
2333 /* check for local host */
2334 if (equal_ustring(host_name, "local"))
2335 host_name = NULL;
2336
2337#ifdef OS_WINNT
2338 {
2339 WSADATA WSAData;
2340
2341 /* Start windows sockets */
2342 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2343 return RPC_NET_ERROR;
2344 }
2345#endif
2346
2347 std::string default_exp_name1;
2348 if (default_exp_name)
2349 default_exp_name1 = default_exp_name;
2350
2351 /* connect to MIDAS server */
2352 if (host_name && host_name[0]) {
2353 if (default_exp_name1.length() == 0) {
2354 status = cm_select_experiment_remote(host_name, &default_exp_name1);
2355 if (status != CM_SUCCESS)
2356 return status;
2357 }
2358
2359 cm_set_experiment_name(default_exp_name1.c_str());
2360
2361 status = rpc_server_connect(host_name, default_exp_name1.c_str());
2362 if (status != RPC_SUCCESS)
2363 return status;
2364
2365 /* register MIDAS library functions */
2367 if (status != RPC_SUCCESS)
2368 return status;
2369 } else {
2370 /* lookup path for *SHM files and save it */
2371
2372#ifdef LOCAL_ROUTINES
2373 status = cm_set_experiment_local(default_exp_name1.c_str());
2374 if (status != CM_SUCCESS)
2375 return status;
2376
2377 default_exp_name1 = cm_get_experiment_name();
2378
2380
2381 INT semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg;
2382
2383 /* create alarm and elog semaphores */
2384 status = ss_semaphore_create("ALARM", &semaphore_alarm);
2385 if (status != SS_CREATED && status != SS_SUCCESS) {
2386 cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2387 return status;
2388 }
2389 status = ss_semaphore_create("ELOG", &semaphore_elog);
2390 if (status != SS_CREATED && status != SS_SUCCESS) {
2391 cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2392 return status;
2393 }
2394 status = ss_semaphore_create("HISTORY", &semaphore_history);
2395 if (status != SS_CREATED && status != SS_SUCCESS) {
2396 cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2397 return status;
2398 }
2399 status = ss_semaphore_create("MSG", &semaphore_msg);
2400 if (status != SS_CREATED && status != SS_SUCCESS) {
2401 cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2402 return status;
2403 }
2404
2405 cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
2406#else
2407 return CM_UNDEF_EXP;
2408#endif
2409 }
2410
2411 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2412 //cm_msg_flush_buffer();
2413
2414 /* open ODB */
2415 if (odb_size == 0)
2417
2418 status = db_open_database("ODB", odb_size, &hDB, client_name);
2419 if (status != DB_SUCCESS && status != DB_CREATED) {
2420 cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2421 return status;
2422 }
2423
2424 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2425 //cm_msg_flush_buffer();
2426
2427 int odb_timeout = db_set_lock_timeout(hDB, 0);
2428 size = sizeof(odb_timeout);
2429 status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2430 if (status != DB_SUCCESS) {
2431 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2432 }
2433
2434 if (odb_timeout > 0) {
2435 db_set_lock_timeout(hDB, odb_timeout);
2436 }
2437
2438 BOOL protect_odb = FALSE;
2439 size = sizeof(protect_odb);
2440 status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2441 if (status != DB_SUCCESS) {
2442 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2443 }
2444
2445 if (protect_odb) {
2447 }
2448
2449 BOOL enable_core_dumps = FALSE;
2450 size = sizeof(enable_core_dumps);
2451 status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2452 if (status != DB_SUCCESS) {
2453 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2454 }
2455
2456 if (enable_core_dumps) {
2457#ifdef RLIMIT_CORE
2458 struct rlimit limit;
2459 limit.rlim_cur = RLIM_INFINITY;
2460 limit.rlim_max = RLIM_INFINITY;
2461 status = setrlimit(RLIMIT_CORE, &limit);
2462 if (status != 0) {
2463 cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2464 strerror(errno));
2465 }
2466#else
2467#warning setrlimit(RLIMIT_CORE) is not available
2468#endif
2469 }
2470
2471 size = sizeof(disable_bind_rpc_to_localhost);
2472 status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2473 TID_BOOL, TRUE);
2474 if (status != DB_SUCCESS) {
2475 cm_msg(MERROR, "cm_connect_experiment1",
2476 "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2477 }
2478
2479 std::string local_host_name;
2480
2481 /* now setup client info */
2483 local_host_name = "localhost";
2484 else
2485 local_host_name = ss_gethostname();
2486
2487 /* check watchdog timeout */
2488 if (watchdog_timeout == 0)
2489 watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2490
2491 strcpy(client_name1, client_name);
2492 password[0] = 0;
2493 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2494
2495 if (status == CM_WRONG_PASSWORD) {
2496 if (func == NULL)
2497 strcpy(str, ss_getpass("Password: "));
2498 else
2499 func(str);
2500
2501 strcpy(password, ss_crypt(str, "mi"));
2502 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2503 if (status != CM_SUCCESS) {
2504 /* disconnect */
2505 if (rpc_is_remote())
2508
2509 return status;
2510 }
2511 }
2512
2513 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2514 //cm_msg_flush_buffer();
2515
2516 /* tell the rest of MIDAS that ODB is open for business */
2517
2518 cm_set_experiment_database(hDB, hKeyClient);
2519
2520 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2521 //cm_msg_flush_buffer();
2522
2523 /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2524 * to get event buffer size, etc */
2525
2527 if (status != CM_SUCCESS) {
2528 cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2529 return status;
2530 }
2531
2532 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2533 //cm_msg_flush_buffer();
2534
2535 /* set experiment name in ODB if not present */
2536 std::string current_name;
2537 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
2538 if (current_name.length() == 0 || current_name == "Default") {
2539 db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2540 }
2541
2542 if (!rpc_is_remote()) {
2543 /* experiment path is only set for local connections */
2544 /* set data dir in ODB */
2545 std::string path = cm_get_path();
2546 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2547 }
2548
2549 /* register server to be able to be called by other clients */
2551 if (status != CM_SUCCESS) {
2552 cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2553 if (!equal_ustring(client_name, "odbedit")) {
2554 return status;
2555 }
2556 }
2557
2558 /* set watchdog timeout */
2559 cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2560 size = sizeof(watchdog_timeout);
2561 sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2562 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2563 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2564
2565 /* set command line */
2566 std::string cmdline = ss_get_cmdline();
2567 std::string path = "/Programs/" + std::string(client_name);
2568 midas::odb prog(path);
2569 if (!midas::odb::exists(path + "/Start command") ||
2570 prog["Start command"] == std::string(""))
2571 prog["Start command"].set_string_size(cmdline, 256);
2572
2573 /* get final client name */
2574 std::string xclient_name = rpc_get_name();
2575
2576 /* startup message is not displayed */
2577 cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2578
2579 /* enable system and user messages to stdout as default */
2581
2582 /* call cm_check_connect when exiting */
2583 atexit((void (*)(void)) cm_check_connect);
2584
2585 /* register ctrl-c handler */
2587
2588 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2589 //cm_msg_flush_buffer();
2590
2591 return CM_SUCCESS;
2592}
2593
2594#ifdef LOCAL_ROUTINES
2595/********************************************************************/
2603 assert(exp_names != NULL);
2604 exp_names->clear();
2605
2606 if (_exptab.exptab.size() == 0) {
2608 if (status != CM_SUCCESS)
2609 return status;
2610 }
2611
2612 for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2613 exp_names->push_back(_exptab.exptab[i].name);
2614 }
2615
2616 return CM_SUCCESS;
2617}
2618#endif // LOCAL_ROUTINES
2619
2620/********************************************************************/
2629 INT status;
2630 INT sock;
2631 int port = MIDAS_TCP_PORT;
2632 char hname[256];
2633 char *s;
2634
2635 assert(exp_names != NULL);
2636 exp_names->clear();
2637
2638 /* extract port number from host_name */
2639 mstrlcpy(hname, host_name, sizeof(hname));
2640 s = strchr(hname, ':');
2641 if (s) {
2642 *s = 0;
2643 port = strtoul(s + 1, NULL, 0);
2644 }
2645
2646 std::string errmsg;
2647
2648 status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2649
2650 if (status != SS_SUCCESS) {
2651 cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2652 return RPC_NET_ERROR;
2653 }
2654
2655 /* request experiment list */
2656 send(sock, "I", 2, 0);
2657
2658 while (1) {
2659 char str[256];
2660
2661 status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2662
2663 if (status < 0)
2664 return RPC_NET_ERROR;
2665
2666 if (status == 0)
2667 break;
2668
2669 exp_names->push_back(str);
2670 }
2671
2672 ss_socket_close(&sock);
2673
2674 return CM_SUCCESS;
2675}
2676
2677#ifdef LOCAL_ROUTINES
2678/********************************************************************/
2687 INT status;
2688 STRING_LIST expts;
2689
2690 assert(exp_name != NULL);
2691
2692 /* retrieve list of experiments and make selection */
2694 if (status != CM_SUCCESS)
2695 return status;
2696
2697 if (expts.size() == 1) {
2698 *exp_name = expts[0];
2699 } else if (expts.size() > 1) {
2700 printf("Available experiments on local computer:\n");
2701
2702 for (unsigned i = 0; i < expts.size(); i++) {
2703 printf("%d : %s\n", i, expts[i].c_str());
2704 }
2705
2706 while (1) {
2707 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2708 char str[32];
2709 ss_gets(str, 32);
2710 int isel = atoi(str);
2711 if (isel < 0)
2712 continue;
2713 if (isel >= (int)expts.size())
2714 continue;
2715 *exp_name = expts[isel];
2716 break;
2717 }
2718 } else {
2719 return CM_UNDEF_EXP;
2720 }
2721
2722 return CM_SUCCESS;
2723}
2724#endif // LOCAL_ROUTINES
2725
2726/********************************************************************/
2736 INT status;
2737 STRING_LIST expts;
2738
2739 assert(exp_name != NULL);
2740
2741 /* retrieve list of experiments and make selection */
2743 if (status != CM_SUCCESS)
2744 return status;
2745
2746 if (expts.size() > 1) {
2747 printf("Available experiments on server %s:\n", host_name);
2748
2749 for (unsigned i = 0; i < expts.size(); i++) {
2750 printf("%d : %s\n", i, expts[i].c_str());
2751 }
2752
2753 while (1) {
2754 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2755 char str[32];
2756 ss_gets(str, 32);
2757 int isel = atoi(str);
2758 if (isel < 0)
2759 continue;
2760 if (isel >= (int)expts.size())
2761 continue;
2762 *exp_name = expts[isel];
2763 break;
2764 }
2765 } else {
2766 *exp_name = expts[0];
2767 }
2768
2769 return CM_SUCCESS;
2770}
2771
2772/********************************************************************/
2782INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2783 HNDLE hDB, hKeyRoot, hSubkey, hKey;
2784 INT status, i, length, port;
2786
2787 /* find client entry in ODB */
2789
2790 status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2791 if (status != DB_SUCCESS)
2792 return status;
2793
2794 i = 0;
2795 do {
2796 /* search for client with specific name */
2797 status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
2799 return CM_NO_CLIENT;
2800
2801 status = db_find_key(hDB, hSubkey, "Name", &hKey);
2802 if (status != DB_SUCCESS)
2803 return status;
2804
2805 length = NAME_LENGTH;
2806 status = db_get_data(hDB, hKey, name, &length, TID_STRING);
2807 if (status != DB_SUCCESS)
2808 return status;
2809
2810 if (equal_ustring(name, client_name)) {
2811 status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2812 if (status != DB_SUCCESS)
2813 return status;
2814
2815 length = sizeof(INT);
2816 status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2817 if (status != DB_SUCCESS)
2818 return status;
2819
2820 status = db_find_key(hDB, hSubkey, "Host", &hKey);
2821 if (status != DB_SUCCESS)
2822 return status;
2823
2824 length = sizeof(host_name);
2826 if (status != DB_SUCCESS)
2827 return status;
2828
2829 /* client found -> connect to its server port */
2830 return rpc_client_connect(host_name, port, client_name, hConn);
2831 }
2832
2833
2834 } while (TRUE);
2835}
2836
2837static void rpc_client_shutdown();
2838
2839/********************************************************************/
2850 return rpc_client_disconnect(hConn, bShutdown);
2851}
2852
2853/********************************************************************/
2863 HNDLE hDB, hKey;
2864
2865 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2866 //cm_msg_flush_buffer();
2867
2868 /* wait on any transition thread */
2869 if (_trp.transition && !_trp.finished) {
2870 printf("Waiting for transition to finish...\n");
2871 do {
2872 ss_sleep(10);
2873 } while (!_trp.finished);
2874 }
2875
2876 /* stop the watchdog thread */
2878
2879 /* send shutdown notification */
2880 std::string client_name = rpc_get_name();
2881
2882 std::string local_host_name;
2883
2885 local_host_name = "localhost";
2886 else {
2887 local_host_name = ss_gethostname();
2888 //if (strchr(local_host_name, '.'))
2889 // *strchr(local_host_name, '.') = 0;
2890 }
2891
2892 /* disconnect message not displayed */
2893 cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2895
2896 if (rpc_is_remote()) {
2897 if (rpc_is_connected()) {
2898 /* close open records */
2900
2902 }
2903
2906
2908 } else {
2910
2911 /* delete client info */
2913
2914 if (hDB)
2916
2917 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2918 //cm_msg_flush_buffer();
2919
2923
2925
2926 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2927 //cm_msg_flush_buffer();
2928 }
2929
2930 if (!rpc_is_mserver())
2932
2933 /* free RPC list */
2935
2936 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2937 //cm_msg_flush_buffer();
2938
2939 /* last flush before we delete the message ring buffer */
2941
2942 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2943 //cm_msg_flush_buffer();
2944
2945 return CM_SUCCESS;
2946}
2947
2948/********************************************************************/
2956 //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
2957
2958 _hDB = hDB;
2959 _hKeyClient = hKeyClient;
2960
2961 //if (hDB == 0) {
2962 // rpc_set_server_option(RPC_ODB_HANDLE, 0);
2963 //}
2964
2965 return CM_SUCCESS;
2966}
2967
2968
2969
2971#ifndef DOXYGEN_SHOULD_SKIP_THIS
2972
2973/********************************************************************/
2974INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
2975/********************************************************************\
2976
2977 Routine: cm_set_experiment_semaphore
2978
2979 Purpose: Set the handle to the experiment wide semaphorees
2980
2981 Input:
2982 INT semaphore_alarm Alarm semaphore
2983 INT semaphore_elog Elog semaphore
2984 INT semaphore_history History semaphore
2985 INT semaphore_msg Message semaphore
2986
2987 Output:
2988 none
2989
2990 Function value:
2991 CM_SUCCESS Successful completion
2992
2993\********************************************************************/
2994{
2995 _semaphore_alarm = semaphore_alarm;
2996 _semaphore_elog = semaphore_elog;
2997 _semaphore_history = semaphore_history;
2998 //_semaphore_msg = semaphore_msg;
2999
3000 return CM_SUCCESS;
3001}
3002
3004#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3005
3006/********************************************************************/
3028 if (_hDB) {
3029 //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3030 if (hDB != NULL)
3031 *hDB = _hDB;
3032 if (hKeyClient != NULL)
3033 *hKeyClient = _hKeyClient;
3034 return CM_SUCCESS;
3035 } else {
3036 //printf("cm_get_experiment_database no init\n");
3037 if (hDB != NULL)
3038 *hDB = 0;
3039 if (hKeyClient != NULL)
3040 *hKeyClient = 0;
3041 return CM_DB_ERROR;
3042 }
3043}
3044
3046#ifndef DOXYGEN_SHOULD_SKIP_THIS
3047
3048/********************************************************************/
3049INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
3050/********************************************************************\
3051
3052 Routine: cm_get_experiment_semaphore
3053
3054 Purpose: Get the handle to the experiment wide semaphores
3055
3056 Input:
3057 none
3058
3059 Output:
3060 INT semaphore_alarm Alarm semaphore
3061 INT semaphore_elog Elog semaphore
3062 INT semaphore_history History semaphore
3063 INT semaphore_msg Message semaphore
3064
3065 Function value:
3066 CM_SUCCESS Successful completion
3067
3068\********************************************************************/
3069{
3070 if (semaphore_alarm)
3071 *semaphore_alarm = _semaphore_alarm;
3072 if (semaphore_elog)
3073 *semaphore_elog = _semaphore_elog;
3074 if (semaphore_history)
3075 *semaphore_history = _semaphore_history;
3076 //if (semaphore_msg)
3077 // *semaphore_msg = _semaphore_msg;
3078 if (semaphore_msg)
3079 *semaphore_msg = -1;
3080
3081 return CM_SUCCESS;
3082}
3083
3085#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3086
3087#ifdef LOCAL_ROUTINES
3088static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3089static int bm_lock_buffer_read_cache(BUFFER *pbuf);
3090static int bm_lock_buffer_write_cache(BUFFER *pbuf);
3091static int bm_lock_buffer_mutex(BUFFER *pbuf);
3092static int xbm_lock_buffer(BUFFER *pbuf);
3093static void xbm_unlock_buffer(BUFFER *pbuf);
3094
3096{
3097public:
3098 bool fDebug = false;
3099
3100public:
3101 bm_lock_buffer_guard(BUFFER* pbuf, bool do_not_lock=false) // ctor
3102 {
3103 assert(pbuf != NULL);
3104 fBuf = pbuf;
3105 if (do_not_lock) {
3106 if (fDebug)
3107 printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3108 return;
3109 }
3110 if (fDebug)
3111 printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3113 if (status != BM_SUCCESS) {
3114 fLocked = false;
3115 fError = true;
3116 fStatus = status;
3117 } else {
3118 fLocked = true;
3119 }
3120 }
3121
3123 {
3124 if (fInvalid) {
3125 if (fDebug)
3126 printf("lock_buffer_guard(invalid) dtor\n");
3127 } else {
3128 assert(fBuf != NULL);
3129 if (fDebug)
3130 printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3131 if (fLocked) {
3133 fLocked = false;
3134 fError = false;
3135 }
3136 fBuf = NULL;
3137 }
3138 }
3139
3140 // make object uncopyable
3143
3144 void unlock()
3145 {
3146 assert(fBuf != NULL);
3147 if (fDebug)
3148 printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3149 assert(fLocked);
3151 fLocked = false;
3152 fError = false;
3153 }
3154
3155 bool relock()
3156 {
3157 assert(fBuf != NULL);
3158 if (fDebug)
3159 printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3160 assert(!fLocked);
3162 if (status != BM_SUCCESS) {
3163 fLocked = false;
3164 fError = true;
3165 fStatus = status;
3166 } else {
3167 fLocked = true;
3168 }
3169 return fLocked;
3170 }
3171
3173 {
3174 assert(fBuf != NULL);
3175 if (fDebug)
3176 printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3177 assert(!fLocked);
3178 fInvalid = true;
3179 fBuf = NULL;
3180 }
3181
3182 bool is_locked() const
3183 {
3184 return fLocked;
3185 }
3186
3187 bool is_error() const
3188 {
3189 return fError;
3190 }
3191
3192 int get_status() const
3193 {
3194 return fStatus;
3195 }
3196
3198 {
3199 assert(!fInvalid); // pbuf was deleted
3200 assert(fBuf); // we do not return NULL
3201 return fBuf;
3202 }
3203
3204private:
3205 BUFFER* fBuf = NULL;
3206 bool fLocked = false;
3207 bool fError = false;
3208 bool fInvalid = false;
3209 int fStatus = 0;
3210};
3211
3213
3214#endif
3215
3216static INT bm_notify_client(const char *buffer_name, int s);
3217
3218static INT bm_push_event(const char *buffer_name);
3219
3220static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3221 EVENT_HEADER *pevent, void *pdata,
3222 EVENT_HANDLER *dispatcher);
3223
3224/********************************************************************/
3261{
3262#ifdef LOCAL_ROUTINES
3263 _watchdog_timeout = timeout;
3264
3265 std::vector<BUFFER*> mybuffers;
3266
3267 gBuffersMutex.lock();
3268 mybuffers = gBuffers;
3269 gBuffersMutex.unlock();
3270
3271 /* set watchdog timeout of all open buffers */
3272 for (BUFFER* pbuf : mybuffers) {
3273
3274 if (!pbuf || !pbuf->attached)
3275 continue;
3276
3277 bm_lock_buffer_guard pbuf_guard(pbuf);
3278
3279 if (!pbuf_guard.is_locked())
3280 continue;
3281
3282 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
3283
3284 /* clear entry from client structure in buffer header */
3285 pclient->watchdog_timeout = timeout;
3286
3287 /* show activity */
3288 pclient->last_activity = ss_millitime();
3289 }
3290
3291 /* set watchdog timeout for ODB */
3292 db_set_watchdog_params(timeout);
3293
3294#endif /* LOCAL_ROUTINES */
3295
3296 return CM_SUCCESS;
3297}
3298
3299INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
3300{
3301 /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3302 _watchdog_timeout = timeout;
3303
3304 if (rpc_is_remote()) { // we are connected remotely
3305
3306 return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
3307
3308 } else if (rpc_is_mserver()) { // we are the mserver
3309
3311 if (sa)
3312 sa->watchdog_timeout = timeout;
3313
3314 /* write timeout value to client entry in ODB */
3315 HNDLE hDB, hKey;
3317
3318 if (hDB) {
3320 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3322 }
3323
3324 /* set the watchdog for the local mserver program */
3325 return cm_set_watchdog_params_local(call_watchdog, timeout);
3326
3327 } else { // only running locally
3328
3329 return cm_set_watchdog_params_local(call_watchdog, timeout);
3330
3331 }
3332}
3333
3334/********************************************************************/
3341INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout) {
3342 if (call_watchdog)
3343 *call_watchdog = FALSE;
3344 if (timeout)
3345 *timeout = _watchdog_timeout;
3346
3347 return CM_SUCCESS;
3348}
3349
3350/********************************************************************/
3360INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3361 if (rpc_is_remote())
3362 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3363
3364#ifdef LOCAL_ROUTINES
3365 return db_get_watchdog_info(hDB, client_name, timeout, last);
3366#else /* LOCAL_ROUTINES */
3367 return CM_SUCCESS;
3368#endif /* LOCAL_ROUTINES */
3369}
3370
3371
3373#ifndef DOXYGEN_SHOULD_SKIP_THIS
3374
3375/********************************************************************/
3376
3377static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3378 int status;
3379 int i, last;
3380 KEY key;
3381 int max_size;
3382 char *str;
3383
3384// if (index != -99)
3385// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3386
3388
3389 if (status != DB_SUCCESS)
3390 return;
3391
3392 //printf("clear rpc hosts!\n");
3394
3395 max_size = key.item_size;
3396 str = (char *) malloc(max_size);
3397
3398 last = 0;
3399 for (i = 0; i < key.num_values; i++) {
3400 int size = max_size;
3402 if (status != DB_SUCCESS)
3403 break;
3404
3405 if (strlen(str) < 1) // skip emties
3406 continue;
3407
3408 if (str[0] == '#') // skip commented-out entries
3409 continue;
3410
3411 //printf("add rpc hosts %d [%s]\n", i, str);
3413 last = i;
3414 }
3415
3416 if (key.num_values - last < 10) {
3417 int new_size = last + 10;
3418 status = db_set_num_values(hDB, hKey, new_size);
3419 if (status != DB_SUCCESS) {
3420 cm_msg(MERROR, "load_rpc_hosts",
3421 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3422 }
3423 }
3424
3425 free(str);
3426}
3427
3429 int status;
3430 char buf[256];
3431 int size, i;
3432 HNDLE hKey;
3433
3434 strcpy(buf, "localhost");
3435 size = sizeof(buf);
3436
3437 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3438
3439 if (status != DB_SUCCESS) {
3440 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3441 status);
3442 return;
3443 }
3444
3445 size = sizeof(i);
3446 i = 0;
3447 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3448
3449 if (status != DB_SUCCESS) {
3450 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3451 return;
3452 }
3453
3454 if (i != 0) // RPC hosts check is disabled
3455 return;
3456
3457 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3458
3459 if (status != DB_SUCCESS || hKey == 0) {
3460 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3461 status);
3462 return;
3463 }
3464
3465 load_rpc_hosts(hDB, hKey, -99, NULL);
3466
3468
3469 if (status != DB_SUCCESS) {
3470 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3471 return;
3472 }
3473}
3474
3475/********************************************************************/
3477/********************************************************************\
3478
3479 Routine: cm_register_server
3480
3481 Purpose: Register a server which can be called from other clients
3482 of a specific experiment.
3483
3484 Input:
3485 none
3486
3487 Output:
3488 none
3489
3490 Function value:
3491 CM_SUCCESS Successful completion
3492
3493\********************************************************************/
3494{
3495 if (!_rpc_registered) {
3496 INT status;
3497 int size;
3498 HNDLE hDB, hKey;
3499 char name[NAME_LENGTH];
3500 char str[256];
3501 int port = 0;
3502
3504
3505 size = sizeof(name);
3506 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3507
3508 if (status != DB_SUCCESS) {
3509 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3510 return status;
3511 }
3512
3513 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3514 mstrlcat(str, name, sizeof(str));
3515
3516 size = sizeof(port);
3517 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3518
3519 if (status != DB_SUCCESS) {
3520 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3521 return status;
3522 }
3523
3524 int lport = 0; // actual port number assigned to us by the OS
3525
3527 if (status != RPC_SUCCESS) {
3528 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3529 return status;
3530 }
3531
3533
3534 /* register MIDAS library functions */
3536
3537 /* store port number in ODB */
3538
3539 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3540 if (status != DB_SUCCESS) {
3541 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3542 return status;
3543 }
3544
3545 /* unlock database */
3547
3548 /* set value */
3549 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3550 if (status != DB_SUCCESS) {
3551 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3552 return status;
3553 }
3554
3555 /* lock database */
3557
3559 }
3560
3561 return CM_SUCCESS;
3562}
3563
3565#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3566
3567/********************************************************************/
3617INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3618 INT status;
3619 HNDLE hDB, hKey, hKeyTrans;
3620 KEY key;
3621 char str[256];
3622
3623 /* check for valid transition */
3625 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3626 return CM_INVALID_TRANSITION;
3627 }
3628
3630
3632
3633 /* register new transition request */
3634
3635 {
3636 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3637
3638 for (size_t i = 0; i < _trans_table.size(); i++) {
3639 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3640 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3641 return CM_INVALID_TRANSITION;
3642 }
3643 }
3644
3645 bool found = false;
3646 for (size_t i = 0; i < _trans_table.size(); i++) {
3647 if (!_trans_table[i].transition) {
3648 _trans_table[i].transition = transition;
3649 _trans_table[i].sequence_number = sequence_number;
3650 _trans_table[i].func = func;
3651 found = true;
3652 break;
3653 }
3654 }
3655
3656 if (!found) {
3657 TRANS_TABLE tt;
3659 tt.sequence_number = sequence_number;
3660 tt.func = func;
3661 _trans_table.push_back(tt);
3662 }
3663
3664 // implicit unlock
3665 }
3666
3667 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3668
3669 /* unlock database */
3671
3672 /* set value */
3673 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3674 if (!hKeyTrans) {
3675 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3676 if (status != DB_SUCCESS)
3677 return status;
3678 } else {
3679 status = db_get_key(hDB, hKeyTrans, &key);
3680 if (status != DB_SUCCESS)
3681 return status;
3682 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3683 if (status != DB_SUCCESS)
3684 return status;
3685 }
3686
3687 /* re-lock database */
3689
3690 return CM_SUCCESS;
3691}
3692
3694 INT status;
3695 HNDLE hDB, hKey, hKeyTrans;
3696 char str[256];
3697
3698 /* check for valid transition */
3700 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3701 return CM_INVALID_TRANSITION;
3702 }
3703
3705
3706 {
3707 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3708
3709 /* remove existing transition request */
3710 for (size_t i = 0; i < _trans_table.size(); i++) {
3712 _trans_table[i].transition = 0;
3713 _trans_table[i].sequence_number = 0;
3714 _trans_table[i].func = NULL;
3715 }
3716 }
3717
3718 // implicit unlock
3719 }
3720
3721 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3722
3723 /* unlock database */
3725
3726 /* set value */
3727 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3728 if (hKeyTrans) {
3729 status = db_delete_key(hDB, hKeyTrans);
3730 if (status != DB_SUCCESS)
3731 return status;
3732 }
3733
3734 /* re-lock database */
3736
3737 return CM_SUCCESS;
3738}
3739
3740/********************************************************************/
3748 INT status;
3749 HNDLE hDB, hKey;
3750 char str[256];
3751
3752 /* check for valid transition */
3754 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3755 return CM_INVALID_TRANSITION;
3756 }
3757
3758 {
3759 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3760
3761 int count = 0;
3762 for (size_t i = 0; i < _trans_table.size(); i++) {
3764 _trans_table[i].sequence_number = sequence_number;
3765 count++;
3766 }
3767 }
3768
3769 if (count == 0) {
3770 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3771 return CM_INVALID_TRANSITION;
3772 } else if (count > 1) {
3773 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3774 return CM_INVALID_TRANSITION;
3775 }
3776
3777 /* Change local sequence number for this transition type */
3778
3779 for (size_t i = 0; i < _trans_table.size(); i++) {
3781 _trans_table[i].sequence_number = sequence_number;
3782 }
3783 }
3784
3785 // implicit unlock
3786 }
3787
3789
3790 /* unlock database */
3792
3793 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3794
3795 /* set value */
3796 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3797 if (status != DB_SUCCESS)
3798 return status;
3799
3800 /* re-lock database */
3802
3803 return CM_SUCCESS;
3804
3805}
3806
3808 INT status;
3809 HNDLE hDB, hKey;
3810 KEY key;
3811
3813
3814 /* check that hKey is still valid */
3816
3817 if (status != DB_SUCCESS) {
3818 cm_msg(MERROR, "cm_set_client_run_state",
3819 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3820 hKey);
3821 return status;
3822 }
3823
3824 /* unlock database */
3826
3827 /* set value */
3828 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3829 if (status != DB_SUCCESS)
3830 return status;
3831
3832 /* re-lock database */
3834
3835 return CM_SUCCESS;
3836
3837}
3838
3840#ifndef DOXYGEN_SHOULD_SKIP_THIS
3841
3844
3846#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3847
3848/********************************************************************/
3862 INT status, size;
3863 char tr_key_name[256];
3864 HNDLE hDB, hKey;
3865
3867
3868 for (int i = 0; _deferred_trans_table[i].transition; i++)
3870 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3871
3872 /* set new transition mask */
3874
3875 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3876
3877 /* unlock database */
3879
3880 /* set value */
3881 int i = 0;
3882 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3883 if (status != DB_SUCCESS)
3884 return status;
3885
3886 /* re-lock database */
3888
3889 /* hot link requested transition */
3890 size = sizeof(_requested_transition);
3891 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3892 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3893 status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
3894 if (status != DB_SUCCESS) {
3895 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3896 return status;
3897 }
3898
3899 return CM_SUCCESS;
3900}
3901
3902/********************************************************************/
3914 INT i, status;
3915 char str[256];
3916 static BOOL first;
3917
3918 if (_requested_transition == 0)
3919 first = TRUE;
3920
3922 for (i = 0; _deferred_trans_table[i].transition; i++)
3924 break;
3925
3927 if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func)(_requested_transition, first)) {
3929 if (status != CM_SUCCESS)
3930 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3931
3932 /* bypass hotlink and set _requested_transition directly to zero */
3934
3935 return status;
3936 }
3937 first = FALSE;
3938 }
3939 }
3940
3941 return SUCCESS;
3942}
3943
3944
3946#ifndef DOXYGEN_SHOULD_SKIP_THIS
3947
3948/********************************************************************/
3949
3951#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3952
3953struct TrClient {
3954 int transition = 0;
3955 int run_number = 0;
3956 int async_flag = 0;
3957 int debug_flag = 0;
3959 std::vector<int> wait_for_index;
3960 std::string host_name;
3961 std::string client_name;
3962 int port = 0;
3963 std::string key_name; /* this client key name in /System/Clients */
3964 std::atomic_int status{0};
3965 std::thread* thread = NULL;
3966 std::string errorstr;
3967 DWORD init_time = 0; // time when tr_client created
3968 std::string waiting_for_client; // name of client we are waiting for
3970 DWORD connect_start_time = 0; // time when client rpc connection is started
3971 DWORD connect_end_time = 0; // time when client rpc connection is finished
3973 DWORD rpc_start_time = 0; // time client rpc call is started
3974 DWORD rpc_end_time = 0; // time client rpc call is finished
3975 DWORD end_time = 0; // time client thread is finished
3976
3977 TrClient() // ctor
3978 {
3979 // empty
3980 }
3981
3982 ~TrClient() // dtor
3983 {
3984 //printf("TrClient::dtor: client \"%s\"\n", client_name);
3985 assert(thread == NULL);
3986 }
3987
3988 void Print() const
3989 {
3990 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3991 if (wait_for_index.size() > 0) {
3992 printf(", wait for:");
3993 for (size_t i=0; i<wait_for_index.size(); i++) {
3994 printf(" %d", wait_for_index[i]);
3995 }
3996 }
3997 }
3998};
3999
4000static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
4001 return arg1->sequence_number < arg2->sequence_number;
4002}
4003
4004/*------------------------------------------------------------------*/
4005
4006struct TrState {
4007 int transition = 0;
4008 int run_number = 0;
4009 int async_flag = 0;
4010 int debug_flag = 0;
4011 int status = 0;
4012 std::string errorstr;
4015 std::vector<std::unique_ptr<TrClient>> clients;
4016};
4017
4018/*------------------------------------------------------------------*/
4019
4020static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4021{
4022 DWORD end_time = ss_millitime();
4023
4024 if (transition != TR_STARTABORT) {
4025 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4026 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4027
4028 if (errorstr) {
4029 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4030 } else if (status == CM_SUCCESS) {
4031 const char *buf = "Success";
4032 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4033 } else {
4034 char buf[256];
4035 sprintf(buf, "status %d", status);
4036 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4037 }
4038 }
4039
4040 tr->status = status;
4041 tr->end_time = end_time;
4042 if (errorstr) {
4043 tr->errorstr = errorstr;
4044 } else {
4045 tr->errorstr = "(null)";
4046 }
4047
4048 return status;
4049}
4050
4051/*------------------------------------------------------------------*/
4052
4053static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client) {
4054 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4055
4056 int status;
4057 HNDLE hKey;
4058
4059 if (tr_client->transition == TR_STARTABORT) {
4060 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4061 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4062 if (status != DB_SUCCESS)
4063 return;
4064 } else {
4065 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4066 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4067 if (status != DB_SUCCESS)
4068 return;
4069 }
4070
4071 // same client_name can exist with different sequence numbers!
4072 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4073
4074 status = db_create_key(hDB, hKey, keyname.c_str(), TID_KEY);
4075 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4076 if (status != DB_SUCCESS)
4077 return;
4078
4079 DWORD now = ss_millitime();
4080
4081 //int transition;
4082 //int run_number;
4083 //int async_flag;
4084 //int debug_flag;
4085 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4086 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4087 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4088 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4089 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4090 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);
4091 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4092 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4093 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4094 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4095 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4096 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4097 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4098 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4099 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4100 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4101}
4102
4103/*------------------------------------------------------------------*/
4104
4105/* Perform a detached transition through the external "mtransition" program */
4106static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4107 HNDLE hDB;
4108 int status;
4109 const char *args[100];
4110 std::string path;
4111 char debug_arg[256];
4112 char start_arg[256];
4113 std::string expt_name;
4114 std::string mserver_hostname;
4115
4116 int iarg = 0;
4117
4119
4120 const char *midassys = getenv("MIDASSYS");
4121 if (midassys) {
4122 path += midassys;
4123 path += DIR_SEPARATOR_STR;
4124 path += "bin";
4125 path += DIR_SEPARATOR_STR;
4126 }
4127 path += "mtransition";
4128
4129 args[iarg++] = path.c_str();
4130
4131 if (rpc_is_remote()) {
4132 /* if connected to mserver, pass connection info to mtransition */
4133 mserver_hostname = rpc_get_mserver_hostname();
4134 args[iarg++] = "-h";
4135 args[iarg++] = mserver_hostname.c_str();
4136 }
4137
4138 /* get experiment name from ODB */
4139 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4140
4141 if (expt_name.length() > 0) {
4142 args[iarg++] = "-e";
4143 args[iarg++] = expt_name.c_str();
4144 }
4145
4146 if (debug_flag) {
4147 args[iarg++] = "-d";
4148
4149 sprintf(debug_arg, "%d", debug_flag);
4150 args[iarg++] = debug_arg;
4151 }
4152
4153 if (transition == TR_STOP)
4154 args[iarg++] = "STOP";
4155 else if (transition == TR_PAUSE)
4156 args[iarg++] = "PAUSE";
4157 else if (transition == TR_RESUME)
4158 args[iarg++] = "RESUME";
4159 else if (transition == TR_START) {
4160 args[iarg++] = "START";
4161
4162 sprintf(start_arg, "%d", run_number);
4163 args[iarg++] = start_arg;
4164 }
4165
4166 args[iarg++] = NULL;
4167
4168#if 0
4169 for (iarg = 0; args[iarg] != NULL; iarg++) {
4170 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4171 }
4172#endif
4173
4174 status = ss_spawnv(P_DETACH, args[0], args);
4175
4176 if (status != SS_SUCCESS) {
4177 if (errstr != NULL) {
4178 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4179 }
4180 return CM_SET_ERROR;
4181 }
4182
4183 return CM_SUCCESS;
4184}
4185
4186/*------------------------------------------------------------------*/
4187
4188/* contact a client via RPC and execute the remote transition */
4189static int cm_transition_call(TrState* s, int idx) {
4190 INT old_timeout, status, i, t1, t0, size;
4191 HNDLE hDB;
4192 HNDLE hConn = -1;
4193 int connect_timeout = 10000;
4194 int timeout = 120000;
4195
4197 assert(hDB);
4198
4199 TrClient *tr_client = s->clients[idx].get();
4200
4201 tr_client->errorstr = "";
4202 //tr_client->init_time = ss_millitime();
4203 tr_client->waiting_for_client = "";
4204 tr_client->connect_timeout = 0;
4205 tr_client->connect_start_time = 0;
4206 tr_client->connect_end_time = 0;
4207 tr_client->rpc_timeout = 0;
4208 tr_client->rpc_start_time = 0;
4209 tr_client->rpc_end_time = 0;
4210 tr_client->end_time = 0;
4211
4212 write_tr_client_to_odb(hDB, tr_client);
4213
4214 /* wait for predecessor if set */
4215 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4216 while (1) {
4217 TrClient* wait_for = NULL;
4218
4219 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4220 int wait_for_index = tr_client->wait_for_index[i];
4221
4222 assert(wait_for_index >= 0);
4223 assert(wait_for_index < (int)s->clients.size());
4224
4225 TrClient *t = s->clients[wait_for_index].get();
4226
4227 if (!t)
4228 continue;
4229
4230 if (t->status == 0) {
4231 wait_for = t;
4232 break;
4233 }
4234
4235 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4236 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));
4237 tr_client->status = -1;
4238 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4239 tr_client->end_time = ss_millitime();
4240 write_tr_client_to_odb(hDB, tr_client);
4241 return CM_SUCCESS;
4242 }
4243 }
4244
4245 if (wait_for == NULL)
4246 break;
4247
4248 tr_client->waiting_for_client = wait_for->client_name;
4249 write_tr_client_to_odb(hDB, tr_client);
4250
4251 if (tr_client->debug_flag == 1)
4252 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4253
4254 i = 0;
4255 size = sizeof(i);
4256 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4257
4258 if (status == DB_SUCCESS && i == 0) {
4259 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());
4260 tr_client->status = -1;
4261 tr_client->errorstr = "Canceled";
4262 tr_client->end_time = ss_millitime();
4263 write_tr_client_to_odb(hDB, tr_client);
4264 return CM_SUCCESS;
4265 }
4266
4267 ss_sleep(100);
4268 };
4269 }
4270
4271 tr_client->waiting_for_client[0] = 0;
4272
4273 /* contact client if transition mask set */
4274 if (tr_client->debug_flag == 1)
4275 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4276 if (tr_client->debug_flag == 2)
4277 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());
4278
4279 /* get transition timeout for rpc connect */
4280 size = sizeof(timeout);
4281 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4282
4283 if (connect_timeout < 1000)
4284 connect_timeout = 1000;
4285
4286 /* get transition timeout */
4287 size = sizeof(timeout);
4288 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4289
4290 if (timeout < 1000)
4291 timeout = 1000;
4292
4293 /* set our timeout for rpc_client_connect() */
4294 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4295 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4296
4297 tr_client->connect_timeout = connect_timeout;
4298 tr_client->connect_start_time = ss_millitime();
4299
4300 write_tr_client_to_odb(hDB, tr_client);
4301
4302 /* client found -> connect to its server port */
4303 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4304
4305 rpc_set_timeout(RPC_HNDLE_CONNECT, old_timeout);
4306
4307 tr_client->connect_end_time = ss_millitime();
4308 write_tr_client_to_odb(hDB, tr_client);
4309
4310 if (status != RPC_SUCCESS) {
4311 cm_msg(MERROR, "cm_transition_call",
4312 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4313 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4314 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4315
4316 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4317 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4318 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4319
4320 if (tr_client->transition != TR_STOP) {
4321 /* indicate abort */
4322 i = 1;
4323 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4324 i = 0;
4325 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4326 }
4327
4328 tr_client->status = status;
4329 tr_client->end_time = ss_millitime();
4330
4331 write_tr_client_to_odb(hDB, tr_client);
4332 return status;
4333 }
4334
4335 if (tr_client->debug_flag == 1)
4336 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4337 if (tr_client->debug_flag == 2)
4338 cm_msg(MINFO, "cm_transition_call",
4339 "cm_transition: Connection established to client \"%s\" on host %s",
4340 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4341
4342 /* call RC_TRANSITION on remote client with increased timeout */
4343 //old_timeout = rpc_get_timeout(hConn);
4344 rpc_set_timeout(hConn, timeout, &old_timeout);
4345
4346 tr_client->rpc_timeout = timeout;
4347 tr_client->rpc_start_time = ss_millitime();
4348 write_tr_client_to_odb(hDB, tr_client);
4349
4350 if (tr_client->debug_flag == 1)
4351 printf("Executing RPC transition client \"%s\" on host %s...\n",
4352 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4353 if (tr_client->debug_flag == 2)
4354 cm_msg(MINFO, "cm_transition_call",
4355 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4356 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4357
4358 t0 = ss_millitime();
4359
4360 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4361 errorstr[0] = 0;
4362
4363 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4364
4365 tr_client->errorstr = errorstr;
4366
4367 t1 = ss_millitime();
4368
4369 tr_client->rpc_end_time = ss_millitime();
4370
4371 write_tr_client_to_odb(hDB, tr_client);
4372
4373 /* fix for clients returning 0 as error code */
4374 if (status == 0)
4375 status = FE_ERR_HW;
4376
4377 /* reset timeout */
4378 rpc_set_timeout(hConn, old_timeout);
4379
4380 //DWORD t2 = ss_millitime();
4381
4382 if (tr_client->debug_flag == 1)
4383 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4384 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4385 if (tr_client->debug_flag == 2)
4386 cm_msg(MINFO, "cm_transition_call",
4387 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4388 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4389
4390 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4391 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());
4392 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4393 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4394 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4395 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4396 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());
4397 }
4398
4399 tr_client->status = status;
4400 tr_client->end_time = ss_millitime();
4401
4402 // write updated status and end_time to ODB
4403
4404 write_tr_client_to_odb(hDB, tr_client);
4405
4406#if 0
4407 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4408 hConn,
4409 tr_client->client_name.c_str(),
4410 tr_client->init_time - tr_client->init_time,
4411 tr_client->connect_start_time - tr_client->init_time,
4412 tr_client->connect_end_time - tr_client->init_time,
4413 tr_client->rpc_start_time - tr_client->init_time,
4414 tr_client->rpc_end_time - tr_client->init_time,
4415 t2 - tr_client->init_time,
4416 tr_client->end_time - tr_client->init_time);
4417#endif
4418
4419 return CM_SUCCESS;
4420}
4421
4422/*------------------------------------------------------------------*/
4423
4425{
4426 HNDLE hDB;
4427
4429
4430 DWORD now = ss_millitime();
4431
4432 tr_client->errorstr = "";
4433 //tr_client->init_time = now;
4434 tr_client->waiting_for_client = "";
4435 tr_client->connect_timeout = 0;
4436 tr_client->connect_start_time = now;
4437 tr_client->connect_end_time = now;
4438 tr_client->rpc_timeout = 0;
4439 tr_client->rpc_start_time = 0;
4440 tr_client->rpc_end_time = 0;
4441 tr_client->end_time = 0;
4442
4443 write_tr_client_to_odb(hDB, tr_client);
4444
4445 // find registered handler
4446 // NB: this code should match same code in rpc_transition_dispatch()
4447 // NB: only use the first handler, this is how MIDAS always worked
4448 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4449
4450 _trans_table_mutex.lock();
4451 size_t n = _trans_table.size();
4452 _trans_table_mutex.unlock();
4453
4454 for (size_t i = 0; i < n; i++) {
4455 _trans_table_mutex.lock();
4457 _trans_table_mutex.unlock();
4458 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4459 /* call registered function */
4460 if (tt.func) {
4461 if (tr_client->debug_flag == 1)
4462 printf("Calling local transition callback\n");
4463 if (tr_client->debug_flag == 2)
4464 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4465
4466 tr_client->rpc_start_time = ss_millitime();
4467
4468 write_tr_client_to_odb(hDB, tr_client);
4469
4470 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4471 errorstr[0] = 0;
4472
4473 tr_client->status = tt.func(tr_client->run_number, errorstr);
4474
4475 tr_client->errorstr = errorstr;
4476
4477 tr_client->rpc_end_time = ss_millitime();
4478
4479 if (tr_client->debug_flag == 1)
4480 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4481 if (tr_client->debug_flag == 2)
4482 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4483
4484 tr_client->end_time = ss_millitime();
4485
4486 // write status and end_time to ODB
4487
4488 write_tr_client_to_odb(hDB, tr_client);
4489
4490 return tr_client->status;
4491 }
4492 }
4493 }
4494
4495 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4496
4497 tr_client->status = CM_SUCCESS;
4498 tr_client->end_time = ss_millitime();
4499
4500 // write status and end_time to ODB
4501
4502 write_tr_client_to_odb(hDB, tr_client);
4503
4504 return CM_SUCCESS;
4505}
4506
4507/********************************************************************/
4547static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4548{
4549 INT i, status, size, sequence_number, port, state;
4550 HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hKeyTrans;
4551 DWORD seconds;
4552 char tr_key_name[256];
4553 KEY key;
4554 BOOL deferred;
4555 char xerrstr[TRANSITION_ERROR_STRING_LENGTH];
4556
4557 //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);
4558
4559 /* if needed, use internal error string */
4560 if (!errstr) {
4561 errstr = xerrstr;
4562 errstr_size = sizeof(xerrstr);
4563 }
4564
4565 /* erase error string */
4566 errstr[0] = 0;
4567
4568 /* get key of local client */
4569 cm_get_experiment_database(&hDB, &hKeylocal);
4570
4571 deferred = (transition & TR_DEFERRED) > 0;
4572 transition &= ~TR_DEFERRED;
4573
4574 /* check for valid transition */
4576 && transition != TR_STARTABORT) {
4577 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4578 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4579 return CM_INVALID_TRANSITION;
4580 }
4581
4582 /* check if transition in progress */
4583 if (!deferred) {
4584 i = 0;
4585 size = sizeof(i);
4586 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4587 if (i == 1) {
4588 if (errstr) {
4589 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4590 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4591 }
4592 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4594 }
4595 }
4596
4597 /* indicate transition in progress */
4598 i = transition;
4599 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4600
4601 /* clear run abort flag */
4602 i = 0;
4603 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4604
4605 /* construct new transition state */
4606
4607 TrState s;
4608
4611 s.async_flag = async_flag;
4612 s.debug_flag = debug_flag;
4613 s.status = 0;
4614 s.errorstr[0] = 0;
4616 s.end_time = 0;
4617
4618 /* construct the ODB tree /System/Transition */
4619
4620 status = db_delete(hDB, 0, "/System/Transition/TR_STARTABORT");
4621
4622 if (transition != TR_STARTABORT) {
4623 status = db_delete(hDB, 0, "/System/Transition/Clients");
4624 }
4625
4626 if (transition != TR_STARTABORT) {
4627 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4628 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4629 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4630 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4631 status = 0;
4632 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4633 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4634 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4635 }
4636
4637 /* check for alarms */
4638 i = 0;
4639 size = sizeof(i);
4640 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4641 if (i == TRUE && transition == TR_START) {
4642 al_check();
4643 std::string alarms;
4644 if (al_get_alarms(&alarms) > 0) {
4645 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4646 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4647 mstrlcat(errstr, alarms.c_str(), errstr_size);
4648 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4649 }
4650 }
4651
4652 /* check for required programs */
4653 i = 0;
4654 size = sizeof(i);
4655 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4656 if (i == TRUE && transition == TR_START) {
4657
4658 HNDLE hkeyroot, hkey;
4659
4660 /* check /programs alarms */
4661 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4662 if (hkeyroot) {
4663 for (i = 0;; i++) {
4664 BOOL program_info_required = FALSE;
4665 status = db_enum_key(hDB, hkeyroot, i, &hkey);
4667 break;
4668
4669 db_get_key(hDB, hkey, &key);
4670
4671 /* don't check "execute on xxx" */
4672 if (key.type != TID_KEY)
4673 continue;
4674
4675 size = sizeof(program_info_required);
4676 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4677 if (status != DB_SUCCESS) {
4678 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4679 continue;
4680 }
4681
4682 if (program_info_required) {
4683 std::string name = rpc_get_name();
4684 std::string str = name;
4685 str.resize(strlen(key.name));
4686 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4687 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4688 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4689 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4690 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4691 }
4692 }
4693 }
4694 }
4695 }
4696
4697 /* do detached transition via mtransition tool */
4698 if (async_flag & TR_DETACH) {
4699 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4700 return tr_finish(hDB, &s, transition, status, errstr);
4701 }
4702
4703 mstrlcpy(errstr, "Unknown error", errstr_size);
4704
4705 if (debug_flag == 0) {
4706 size = sizeof(i);
4707 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4708 }
4709
4710 /* if no run number is given, get it from ODB and increment it */
4711 if (run_number == 0) {
4712 size = sizeof(run_number);
4713 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4714 assert(status == SUCCESS);
4715 if (transition == TR_START) {
4716 run_number++;
4717 }
4719
4720 if (transition != TR_STARTABORT) {
4721 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4722 }
4723 }
4724
4725 if (run_number <= 0) {
4726 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4727 abort();
4728 }
4729
4730 /* Set new run number in ODB */
4731 if (transition == TR_START) {
4732 if (debug_flag == 1)
4733 printf("Setting run number %d in ODB\n", run_number);
4734 if (debug_flag == 2)
4735 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4736
4737 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4738 if (status != DB_SUCCESS) {
4739 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4740 abort();
4741 }
4742 }
4743
4744 if (deferred) {
4745 if (debug_flag == 1)
4746 printf("Clearing /Runinfo/Requested transition\n");
4747 if (debug_flag == 2)
4748 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4749
4750 /* remove transition request */
4751 i = 0;
4752 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4753 } else {
4754 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4755 if (status != DB_SUCCESS) {
4756 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4757 if (errstr)
4758 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4759 return tr_finish(hDB, &s, transition, status, errstr);
4760 }
4761
4762 /* check if deferred transition already in progress */
4763 size = sizeof(i);
4764 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4765 if (i) {
4766 if (errstr) {
4767 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4768 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4769 }
4770 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4771 }
4772
4773 std::string trname = cm_transition_name(transition);
4774
4775 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4776
4777 /* search database for clients with deferred transition request */
4778 for (i = 0, status = 0;; i++) {
4779 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4781 break;
4782
4783 if (status == DB_SUCCESS) {
4784 size = sizeof(sequence_number);
4785 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4786
4787 /* if registered for deferred transition, set flag in ODB and return */
4788 if (status == DB_SUCCESS) {
4789 char str[256];
4790 size = NAME_LENGTH;
4791 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4792
4793 if (debug_flag == 1)
4794 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4795 if (debug_flag == 2)
4796 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4797
4798 if (debug_flag == 1)
4799 printf("Setting /Runinfo/Requested transition\n");
4800 if (debug_flag == 2)
4801 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4802
4803 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4804 * will activate the deferred transition code in the frontend.
4805 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4806
4807 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4808
4809 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4810
4811 if (errstr)
4812 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4813
4814 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4815 }
4816 }
4817 }
4818 }
4819
4820 /* execute programs on start */
4821 if (transition == TR_START) {
4822 char str[256];
4823 str[0] = 0;
4824 size = sizeof(str);
4825 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4826 if (str[0])
4827 ss_system(str);
4828
4829 db_find_key(hDB, 0, "/Programs", &hRootKey);
4830 if (hRootKey) {
4831 for (i = 0;; i++) {
4832 BOOL program_info_auto_start = FALSE;
4833 status = db_enum_key(hDB, hRootKey, i, &hKey);
4835 break;
4836
4837 db_get_key(hDB, hKey, &key);
4838
4839 /* don't check "execute on xxx" */
4840 if (key.type != TID_KEY)
4841 continue;
4842
4843 size = sizeof(program_info_auto_start);
4844 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4845 if (status != DB_SUCCESS) {
4846 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4847 continue;
4848 }
4849
4850 if (program_info_auto_start) {
4851 char start_command[MAX_STRING_LENGTH];
4852 start_command[0] = 0;
4853
4854 size = sizeof(start_command);
4855 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4856 if (status != DB_SUCCESS) {
4857 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4858 continue;
4859 }
4860
4861 if (start_command[0]) {
4862 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4863 start_command);
4864 ss_system(start_command);
4865 }
4866 }
4867 }
4868 }
4869 }
4870
4871 /* execute programs on startabort */
4872 if (transition == TR_STARTABORT) {
4873 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4874 std::string cmd;
4875 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4876
4877 if (!cmd.empty())
4878 ss_system(cmd.c_str());
4879 }
4880
4881 /* set new start time in database */
4882 if (transition == TR_START) {
4883 /* ASCII format */
4884 std::string now = cm_asctime();
4885 now.reserve(32);
4886 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4887
4888 /* reset stop time */
4889 seconds = 0;
4890 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4891
4892 /* Seconds since 1.1.1970 */
4893 cm_time(&seconds);
4894 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4895 }
4896
4897 size = sizeof(state);
4898 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4899
4900 /* set stop time in database */
4901 if (transition == TR_STOP) {
4902 if (status != DB_SUCCESS)
4903 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4904
4905 if (state != STATE_STOPPED) {
4906 /* stop time binary */
4907 cm_time(&seconds);
4908 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4909 if (status != DB_SUCCESS)
4910 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4911
4912 /* stop time ascii */
4913 std::string now = cm_asctime();
4914 now.reserve(32);
4915 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4916 if (status != DB_SUCCESS)
4917 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4918 }
4919 }
4920
4921 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4922 if (status != DB_SUCCESS) {
4923 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4924 if (errstr)
4925 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4926 return tr_finish(hDB, &s, transition, status, errstr);
4927 }
4928
4929 std::string trname = cm_transition_name(transition);
4930
4931 /* check that all transition clients are alive */
4932 for (int i = 0;;) {
4933 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4934 if (status != DB_SUCCESS)
4935 break;
4936
4938
4939 if (status == DB_SUCCESS) {
4940 /* this client is alive. Check next one! */
4941 i++;
4942 continue;
4943 }
4944
4945 assert(status == CM_NO_CLIENT);
4946
4947 /* start from scratch: removing odb entries as we iterate over them
4948 * does strange things to db_enum_key() */
4949 i = 0;
4950 }
4951
4952 /* check for broken RPC connections */
4954
4955 if (debug_flag == 1)
4956 printf("---- Transition %s started ----\n", trname.c_str());
4957 if (debug_flag == 2)
4958 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4959
4960 sprintf(tr_key_name, "Transition %s", trname.c_str());
4961
4962 /* search database for clients which registered for transition */
4963
4964 for (int i = 0, status = 0;; i++) {
4965 KEY subkey;
4966 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4968 break;
4969
4970 status = db_get_key(hDB, hSubkey, &subkey);
4971 assert(status == DB_SUCCESS);
4972
4973 if (status == DB_SUCCESS) {
4974 status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
4975
4976 if (status == DB_SUCCESS) {
4977
4978 db_get_key(hDB, hKeyTrans, &key);
4979
4980 for (int j = 0; j < key.num_values; j++) {
4981 size = sizeof(sequence_number);
4982 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4983 assert(status == DB_SUCCESS);
4984
4985 TrClient *c = new TrClient;
4986
4988 c->transition = transition;
4989 c->run_number = run_number;
4990 c->async_flag = async_flag;
4991 c->debug_flag = debug_flag;
4992 c->sequence_number = sequence_number;
4993 c->status = 0;
4994 c->key_name = subkey.name;
4995
4996 /* get client info */
4997 char client_name[NAME_LENGTH];
4998 size = sizeof(client_name);
4999 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
5000 c->client_name = client_name;
5001
5003 size = sizeof(host_name);
5004 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
5005 c->host_name = host_name;
5006
5007 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
5008
5009 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5010 /* remember own client */
5011 c->port = 0;
5012 } else {
5013 size = sizeof(port);
5014 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5015 c->port = port;
5016 }
5017
5018 /* check for duplicates */
5019
5020 bool found = false;
5021 for (size_t k=0; k<s.clients.size(); k++) {
5022 TrClient* cc = s.clients[k].get();
5023 if (cc->client_name == c->client_name)
5024 if (cc->host_name == c->host_name)
5025 if (cc->port == c->port)
5026 if (cc->sequence_number == c->sequence_number)
5027 found = true;
5028 }
5029
5030 if (!found) {
5031 s.clients.push_back(std::unique_ptr<TrClient>(c));
5032 c = NULL;
5033 } else {
5034 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);
5035 delete c;
5036 c = NULL;
5037 }
5038 }
5039 }
5040 }
5041 }
5042
5043 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5044
5045 /* set predecessor for multi-threaded transitions */
5046 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5047 if (s.clients[idx]->sequence_number == 0) {
5048 // sequence number 0 means "don't care"
5049 } else {
5050 /* find clients with smaller sequence number */
5051 if (idx > 0) {
5052 for (size_t i = idx - 1; ; i--) {
5053 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5054 if (s.clients[i]->sequence_number > 0) {
5055 s.clients[idx]->wait_for_index.push_back(i);
5056 }
5057 }
5058 if (i==0)
5059 break;
5060 }
5061 }
5062 }
5063 }
5064
5065 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5066 write_tr_client_to_odb(hDB, s.clients[idx].get());
5067 }
5068
5069#if 0
5070 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5071 printf("TrClient[%d]: ", int(idx));
5072 s.clients[idx]->Print();
5073 printf("\n");
5074 }
5075#endif
5076
5077 /* contact ordered clients for transition -----------------------*/
5079 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5080 if (debug_flag == 1)
5081 printf("\n==== Found client \"%s\" with sequence number %d\n",
5082 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5083 if (debug_flag == 2)
5084 cm_msg(MINFO, "cm_transition",
5085 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5086 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5087
5088 if (async_flag & TR_MTHREAD) {
5090 assert(s.clients[idx]->thread == NULL);
5091 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5092 } else {
5093 if (s.clients[idx]->port == 0) {
5094 /* if own client call transition callback directly */
5096 } else {
5097 /* if other client call transition via RPC layer */
5098 status = cm_transition_call(&s, idx);
5099 }
5100
5101 if (status == CM_SUCCESS && transition != TR_STOP)
5102 if (s.clients[idx]->status != SUCCESS) {
5103 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5104 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5105 break;
5106 }
5107 }
5108
5109 if (status != CM_SUCCESS)
5110 break;
5111 }
5112
5113 /* wait until all threads have finished */
5114 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5115 if (s.clients[idx]->thread) {
5116 // join() will wait forever until thread finishes
5117 s.clients[idx]->thread->join();
5118 delete s.clients[idx]->thread;
5119 s.clients[idx]->thread = NULL;
5120 }
5121 }
5122
5123 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5124
5125 i = 0;
5126 size = sizeof(i);
5127 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5128
5129 if (status == DB_SUCCESS && i == 0) {
5130 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5131
5132 if (errstr != NULL)
5133 mstrlcpy(errstr, "Canceled", errstr_size);
5134
5135 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5136 }
5137
5138 /* search for any error */
5139 for (size_t idx = 0; idx < s.clients.size(); idx++)
5140 if (s.clients[idx]->status != CM_SUCCESS) {
5141 status = s.clients[idx]->status;
5142 if (errstr)
5143 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5144 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5145 break;
5146 }
5147
5148 if (transition != TR_STOP && status != CM_SUCCESS) {
5149 /* indicate abort */
5150 i = 1;
5151 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5152 i = 0;
5153 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5154
5155 return tr_finish(hDB, &s, transition, status, errstr);
5156 }
5157
5158 if (debug_flag == 1)
5159 printf("\n---- Transition %s finished ----\n", trname.c_str());
5160 if (debug_flag == 2)
5161 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5162
5163 /* set new run state in database */
5166
5167 if (transition == TR_PAUSE)
5169
5170 if (transition == TR_STOP)
5172
5175
5176 size = sizeof(state);
5177 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5178 if (status != DB_SUCCESS)
5179 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5180
5181 /* send notification message */
5182 if (transition == TR_START)
5183 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5184 if (transition == TR_STOP)
5185 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5186 if (transition == TR_PAUSE)
5187 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5188 if (transition == TR_RESUME)
5189 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5191 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5192
5193 /* lock/unlock ODB values if present */
5194 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5195 if (hKey) {
5196 if (state == STATE_STOPPED)
5198 else
5200 }
5201
5202 /* flush online database */
5203 if (transition == TR_STOP)
5205
5206 /* execute/stop programs on stop */
5207 if (transition == TR_STOP) {
5208 std::string cmd;
5209 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5210 if (!cmd.empty())
5211 ss_system(cmd.c_str());
5212
5213 db_find_key(hDB, 0, "/Programs", &hRootKey);
5214 if (hRootKey) {
5215 for (i = 0;; i++) {
5216 BOOL program_info_auto_stop = FALSE;
5217 status = db_enum_key(hDB, hRootKey, i, &hKey);
5219 break;
5220
5221 db_get_key(hDB, hKey, &key);
5222
5223 /* don't check "execute on xxx" */
5224 if (key.type != TID_KEY)
5225 continue;
5226
5227 size = sizeof(program_info_auto_stop);
5228 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5229 if (status != DB_SUCCESS) {
5230 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5231 continue;
5232 }
5233
5234 if (program_info_auto_stop) {
5235 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5237 }
5238 }
5239 }
5240 }
5241
5242
5243 /* indicate success */
5244 i = 0;
5245 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5246
5247 if (errstr != NULL)
5248 mstrlcpy(errstr, "Success", errstr_size);
5249
5250 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5251}
5252
5253/*------------------------------------------------------------------*/
5254
5255/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5256static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5257 int status;
5258
5259 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5260
5261 if (transition == TR_START && status != CM_SUCCESS) {
5262 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5263 errstr);
5264 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5265 }
5266
5267 return status;
5268}
5269
5270/*------------------------------------------------------------------*/
5271
5272static INT tr_main_thread(void *param) {
5273 INT status;
5274 TR_PARAM *trp;
5275
5276 trp = (TR_PARAM *) param;
5277 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5278
5279 trp->status = status;
5280 trp->finished = TRUE;
5281
5282 return 0;
5283}
5284
5286{
5287 if (_trp.thread && !_trp.finished) {
5288 //printf("main transition thread did not finish yet!\n");
5290 }
5291
5292 std::thread* t = _trp.thread.exchange(NULL);
5293
5294 if (t) {
5295 t->join();
5296 delete t;
5297 t = NULL;
5298 }
5299
5300 return CM_SUCCESS;
5301}
5302
5303/* wrapper around cm_transition1() for detached multi-threaded transitions */
5304INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5305 int mflag = async_flag & TR_MTHREAD;
5306 int sflag = async_flag & TR_SYNC;
5307
5309
5310 if (status != CM_SUCCESS) {
5311 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5313 }
5314
5315 /* get key of local client */
5316 HNDLE hDB;
5318
5319 bool deferred = (transition & TR_DEFERRED) > 0;
5320 INT trans_raw = (transition & ~TR_DEFERRED);
5321
5322 /* check for valid transition */
5323 if (trans_raw != TR_START && trans_raw != TR_STOP && trans_raw != TR_PAUSE && trans_raw != TR_RESUME && trans_raw != TR_STARTABORT) {
5324 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5325 if (errstr) {
5326 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5327 }
5328 return CM_INVALID_TRANSITION;
5329 }
5330
5331 /* check if transition in progress */
5332 if (!deferred) {
5333 int i = 0;
5334 int size = sizeof(i);
5335 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5336 if (i == 1) {
5337 if (errstr) {
5338 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5339 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5340 }
5341 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5343 }
5344 }
5345
5346 if (mflag) {
5349 if (sflag) {
5350 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5351 _trp.errstr = errstr;
5352 _trp.errstr_size = errstr_size;
5353 } else {
5354 /* in normal MTHREAD mode, we return right away and
5355 * if errstr is a local variable in the caller and they return too,
5356 * errstr becomes a stale reference and writing into it will corrupt the stack
5357 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5358 * and we definitely corrupt mlogger memory with out this: */
5359 _trp.errstr = NULL;
5360 _trp.errstr_size = 0;
5361 }
5362 _trp.async_flag = async_flag;
5363 _trp.debug_flag = debug_flag;
5364 _trp.status = 0;
5366
5367 if (errstr)
5368 *errstr = 0; // null error string
5369
5370 //ss_thread_create(tr_main_thread, &_trp);
5371
5372 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5373
5374 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5375
5376 if (sflag) {
5377
5378 /* wait until main thread has finished */
5379 do {
5380 ss_sleep(10);
5381 } while (!_trp.finished);
5382
5383 std::thread* t = _trp.thread.exchange(NULL);
5384
5385 if (t) {
5386 t->join();
5387 delete t;
5388 t = NULL;
5389 }
5390
5391 return _trp.status;
5392 }
5393 } else
5394 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5395
5396 return CM_SUCCESS;
5397}
5398
5400#ifndef DOXYGEN_SHOULD_SKIP_THIS
5401
5402/********************************************************************/
5403INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
5404/********************************************************************\
5405
5406 Routine: cm_dispatch_ipc
5407
5408 Purpose: Called from ss_suspend if an IPC message arrives
5409
5410 Input:
5411 INT msg IPC message we got, MSG_ODB/MSG_BM
5412 INT p1, p2 Optional parameters
5413 int s Optional server socket
5414
5415 Output:
5416 none
5417
5418 Function value:
5419 CM_SUCCESS Successful completion
5420
5421\********************************************************************/
5422{
5423 if (message[0] == 'O') {
5424 HNDLE hDB, hKey, hKeyRoot;
5425 INT index;
5426 index = 0;
5427 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5428 if (client_socket) {
5429 return db_update_record_mserver(hDB, hKeyRoot, hKey, index, client_socket);
5430 } else {
5431 return db_update_record_local(hDB, hKeyRoot, hKey, index);
5432 }
5433 }
5434
5435 /* message == "B" means "resume event sender" */
5436 if (message[0] == 'B' && message[2] != ' ') {
5437 char str[NAME_LENGTH];
5438
5439 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5440
5441 mstrlcpy(str, message + 2, sizeof(str));
5442 if (strchr(str, ' '))
5443 *strchr(str, ' ') = 0;
5444
5445 if (client_socket)
5446 return bm_notify_client(str, client_socket);
5447 else
5448 return bm_push_event(str);
5449 }
5450
5451 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5452
5453 return CM_SUCCESS;
5454}
5455
5456/********************************************************************/
5458
5459void cm_ctrlc_handler(int sig) {
5460 if (_ctrlc_pressed) {
5461 printf("Received 2nd Ctrl-C, hard abort\n");
5462 exit(0);
5463 }
5464 printf("Received Ctrl-C, aborting...\n");
5466
5468}
5469
5473
5477
5478/********************************************************************/
5479int cm_exec_script(const char *odb_path_to_script)
5480/********************************************************************\
5481
5482 Routine: cm_exec_script
5483
5484 Purpose: Execute script from /Script tree
5485
5486 exec_script is enabled by the tree /Script
5487 The /Script struct is composed of list of keys
5488 from which the name of the key is the button name
5489 and the sub-structure is a record as follow:
5490
5491 /Script/<button_name> = <script command> (TID_STRING)
5492
5493 The "Script command", containing possible arguements,
5494 is directly executed.
5495
5496 /Script/<button_name>/<script command>
5497 <soft link1>|<arg1>
5498 <soft link2>|<arg2>
5499 ...
5500
5501 The arguments for the script are derived from the
5502 subtree below <button_name>, where <button_name> must be
5503 TID_KEY. The subtree may then contain arguments or links
5504 to other values in the ODB, like run number etc.
5505
5506\********************************************************************/
5507{
5508 HNDLE hDB, hkey;
5509 KEY key;
5510 int status;
5511
5513 if (status != DB_SUCCESS)
5514 return status;
5515
5516 status = db_find_key(hDB, 0, odb_path_to_script, &hkey);
5517 if (status != DB_SUCCESS)
5518 return status;
5519
5520 status = db_get_key(hDB, hkey, &key);
5521 if (status != DB_SUCCESS)
5522 return status;
5523
5524 std::string command;
5525
5526 if (key.type == TID_STRING) {
5527 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5528 if (status != DB_SUCCESS) {
5529 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5530 odb_path_to_script, status);
5531 return status;
5532 }
5533 } else if (key.type == TID_KEY) {
5534 for (int i = 0;; i++) {
5535 HNDLE hsubkey;
5536 KEY subkey;
5537 db_enum_key(hDB, hkey, i, &hsubkey);
5538 if (!hsubkey)
5539 break;
5540 db_get_key(hDB, hsubkey, &subkey);
5541
5542 if (i > 0)
5543 command += " ";
5544
5545 if (subkey.type == TID_KEY) {
5546 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5547 subkey.name);
5548 return DB_TYPE_MISMATCH;
5549 } else {
5550 int size = subkey.item_size;
5551 char *buf = (char *) malloc(size);
5552 assert(buf != NULL);
5553 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5554 if (status != DB_SUCCESS) {
5555 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5556 odb_path_to_script, subkey.name, subkey.type, status);
5557 free(buf);
5558 return status;
5559 }
5560 if (subkey.type == TID_STRING) {
5561 command += buf;
5562 } else {
5563 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5564 }
5565 free(buf);
5566 }
5567 }
5568 } else {
5569 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5570 odb_path_to_script, key.type);
5571 return DB_TYPE_MISMATCH;
5572 }
5573
5574 // printf("exec_script: %s\n", command.c_str());
5575
5576 if (command.length() > 0) {
5577 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5578 ss_system(command.c_str());
5579 }
5580
5581 return SUCCESS;
5582}
5583
5585#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5586
5587static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5588
5589/********************************************************************/
5598 static DWORD alarm_last_checked_sec = 0;
5599 DWORD now_sec = ss_time();
5600
5601 DWORD now_millitime = ss_millitime();
5602 static DWORD last_millitime = 0;
5603 DWORD tdiff_millitime = now_millitime - last_millitime;
5604 const DWORD kPeriod = 1000;
5605 if (last_millitime == 0) {
5606 last_millitime = now_millitime;
5607 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5608 }
5609
5610 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5611
5612 //if (now_millitime < last_millitime) {
5613 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5614 //}
5615
5616 /* check alarms once every 10 seconds */
5617 if (now_sec - alarm_last_checked_sec > 10) {
5618 al_check();
5619 alarm_last_checked_sec = now_sec;
5620 }
5621
5622 /* run periodic checks previously done by cm_watchdog */
5623
5624 if (tdiff_millitime >= kPeriod) {
5625 BOOL wrong_interval = FALSE;
5626 if (tdiff_millitime > 60000)
5627 wrong_interval = TRUE;
5628
5629 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5630
5631 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5632 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5633
5635
5636 last_millitime = now_millitime;
5637 }
5638
5639 /* reap transition thread */
5640
5642
5643 return CM_SUCCESS;
5644}
5645
5646/********************************************************************/
5660INT cm_yield(INT millisec) {
5661 INT status;
5662 INT bMore;
5663 //static DWORD last_yield = 0;
5664 //static DWORD last_yield_time = 0;
5665 //DWORD start_yield = ss_millitime();
5666
5667 /* check for ctrl-c */
5668 if (_ctrlc_pressed)
5669 return RPC_SHUTDOWN;
5670
5671 /* flush the cm_msg buffer */
5673
5674 if (!rpc_is_remote()) {
5675 /* flush the ODB to its binary file */
5676 /* for remote clients, ODB is flushed by the mserver */
5677 HNDLE hDB;
5680 }
5681
5682 /* check for available events */
5683 if (rpc_is_remote()) {
5684 //printf("cm_yield() calling bm_poll_event()\n");
5686
5687 if (status == SS_ABORT) {
5688 return status;
5689 }
5690
5691 if (status == BM_SUCCESS) {
5692 /* one or more events received by bm_poll_event() */
5693 status = ss_suspend(0, 0);
5694 } else {
5695 status = ss_suspend(millisec, 0);
5696 }
5697
5698 return status;
5699 }
5700
5702
5703 if (status != CM_SUCCESS)
5704 return status;
5705
5706 //DWORD start_check = ss_millitime();
5707
5708 bMore = bm_check_buffers();
5709
5710 //DWORD end_check = ss_millitime();
5711 //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);
5712 //fflush(stdout);
5713
5714 if (bMore == BM_CORRUPTED) {
5715 status = SS_ABORT;
5716 } else if (bMore) {
5717 /* if events available, quickly check other IPC channels */
5718 status = ss_suspend(0, 0);
5719 } else {
5720 status = ss_suspend(millisec, 0);
5721 }
5722
5723 /* flush the cm_msg buffer */
5725
5726 //DWORD end_yield = ss_millitime();
5727 //last_yield_time = end_yield - start_yield;
5728 //last_yield = start_yield;
5729
5730 return status;
5731}
5732
5733/********************************************************************/
5741INT cm_execute(const char *command, char *result, INT bufsize) {
5742 char str[256];
5743 INT n;
5744 int fh;
5745 int status = 0;
5746 static int check_cm_execute = 1;
5747 static int enable_cm_execute = 0;
5748
5749 if (rpc_is_remote())
5750 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5751
5752 if (check_cm_execute) {
5753 int status;
5754 int size;
5755 HNDLE hDB;
5756 check_cm_execute = 0;
5757
5759 assert(status == DB_SUCCESS);
5760
5761 size = sizeof(enable_cm_execute);
5762 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5763 assert(status == DB_SUCCESS);
5764
5765 //printf("enable_cm_execute %d\n", enable_cm_execute);
5766 }
5767
5768 if (!enable_cm_execute) {
5769 char buf[32];
5770 mstrlcpy(buf, command, sizeof(buf));
5771 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5772 return CM_WRONG_PASSWORD;
5773 }
5774
5775 if (bufsize > 0) {
5776 strcpy(str, command);
5777 sprintf(str, "%s > %d.tmp", command, ss_getpid());
5778
5779 status = system(str);
5780
5781 sprintf(str, "%d.tmp", ss_getpid());
5782 fh = open(str, O_RDONLY, 0644);
5783 result[0] = 0;
5784 if (fh) {
5785 n = read(fh, result, bufsize - 1);
5786 result[MAX(0, n)] = 0;
5787 close(fh);
5788 }
5789 remove(str);
5790 } else {
5791 status = system(command);
5792 }
5793
5794 if (status < 0) {
5795 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5796 return CM_SET_ERROR;
5797 }
5798
5799 return CM_SUCCESS;
5800}
5801
5802
5803
5805#ifndef DOXYGEN_SHOULD_SKIP_THIS
5806
5807/********************************************************************/
5808INT cm_register_function(INT id, INT(*func)(INT, void **))
5809/********************************************************************\
5810
5811 Routine: cm_register_function
5812
5813 Purpose: Call rpc_register_function and publish the registered
5814 function under system/clients/<pid>/RPC
5815
5816 Input:
5817 INT id RPC ID
5818 INT *func New dispatch function
5819
5820 Output:
5821 <implicit: func gets copied to rpc_list>
5822
5823 Function value:
5824 CM_SUCCESS Successful completion
5825 RPC_INVALID_ID RPC ID not found
5826
5827\********************************************************************/
5828{
5829 HNDLE hDB, hKey;
5830 INT status;
5831 char str[80];
5832
5833 status = rpc_register_function(id, func);
5834 if (status != RPC_SUCCESS)
5835 return status;
5836
5838
5839 /* create new key for this id */
5840 status = 1;
5841 sprintf(str, "RPC/%d", id);
5842
5844 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5846
5847 if (status != DB_SUCCESS)
5848 return status;
5849
5850 return CM_SUCCESS;
5851}
5852
5853
5855#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5856
5857//
5858// Return "/"-terminated file path for given history channel
5859//
5860
5861std::string cm_get_history_path(const char* history_channel)
5862{
5863 int status;
5864 HNDLE hDB;
5865 std::string path;
5866
5868
5869 if (history_channel && (strlen(history_channel) > 0)) {
5870 std::string p;
5871 p += "/Logger/History/";
5872 p += history_channel;
5873 p += "/History dir";
5874
5875 // NB: be careful to avoid creating odb entries under /logger
5876 // for whatever values of "history_channel" we get called with!
5877 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5878 if (status == DB_SUCCESS && path.length() > 0) {
5879 // if not absolute path, prepend with experiment directory
5880 if (path[0] != DIR_SEPARATOR)
5881 path = cm_get_path() + path;
5882 // append directory separator
5883 if (path.back() != DIR_SEPARATOR)
5884 path += DIR_SEPARATOR_STR;
5885 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5886 return path;
5887 }
5888 }
5889
5890 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5891 if (status == DB_SUCCESS && path.length() > 0) {
5892 // if not absolute path, prepend with experiment directory
5893 if (path[0] != DIR_SEPARATOR)
5894 path = cm_get_path() + path;
5895 // append directory separator
5896 if (path.back() != DIR_SEPARATOR)
5897 path += DIR_SEPARATOR_STR;
5898 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5899 return path;
5900 }
5901
5902 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5903 if (status == DB_SUCCESS && path.length() > 0) {
5904 // if not absolute path, prepend with experiment directory
5905 if (path[0] != DIR_SEPARATOR)
5906 path = cm_get_path() + path;
5907 // append directory separator
5908 if (path.back() != DIR_SEPARATOR)
5909 path += DIR_SEPARATOR_STR;
5910 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5911 return path;
5912 }
5913
5914 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5915 return cm_get_path();
5916}
5917
/* end of cmfunctionc */
5920
5926/********************************************************************\
5927* *
5928* bm_xxx - Buffer Manager Functions *
5929* *
5930\********************************************************************/
5931
5933
5934#ifdef LOCAL_ROUTINES
5935
5936// see locking code in xbm_lock_buffer()
5937static int _bm_lock_timeout = 5 * 60 * 1000;
5938static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5939
5941{
5942 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5943
5944 bool badindex = false;
5945 bool badclient = false;
5946
5947 int idx = pbuf->client_index;
5948
5949 if (idx < 0) {
5950 badindex = true;
5951 } else if (idx > pbuf->buffer_header->max_client_index) {
5952 badindex = true;
5953 } else {
5954 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5955 if (pclient->name[0] == 0)
5956 badclient = true;
5957 else if (pclient->pid != ss_getpid())
5958 badclient = true;
5959
5960 //if (strcmp(pclient->name,"mdump")==0) {
5961 // for (int i=0; i<15; i++) {
5962 // printf("sleep %d\n", i);
5963 // ::sleep(1);
5964 // }
5965 //}
5966 }
5967
5968#if 0
5969 if (badindex) {
5970 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5971 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5972 badindex, ss_getpid());
5973 } else if (badclient) {
5974 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",
5975 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5976 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5977 ss_getpid(), badclient);
5978 } else {
5979 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, client_name=\'%s\', client_pid=%d, pid=%d, goodclient\n",
5980 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5981 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5982 ss_getpid());
5983 }
5984#endif
5985
5986 if (badindex || badclient) {
5987 static int prevent_recursion = 1;
5988
5989 if (prevent_recursion) {
5990 prevent_recursion = 0;
5991
5992 if (badindex) {
5993 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());
5994 } else {
5995 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());
5996 }
5997
5998 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
5999 }
6000
6001 if (badindex) {
6002 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());
6003 } else {
6004 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());
6005 }
6006
6007 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
6008
6009 pbuf_guard.unlock();
6010
6011 abort();
6012 }
6013
6014 return idx;
6015}
6016
6018 int my_client_index = bm_validate_client_index_locked(pbuf_guard);
6019 return pbuf_guard.get_pbuf()->buffer_header->client + my_client_index;
6020}
6021
6022#endif // LOCAL_ROUTINES
6023
6024/********************************************************************/
6033INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6034 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6035 // because of mismatch in sign-extension between signed 16-bit event_id and
6036 // unsigned 16-bit constants. K.O.
6037
6038 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)))
6039 /* fragmented event */
6040 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6041 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6042
6043 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6044 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6045}
6046
6047#ifdef LOCAL_ROUTINES
6048
6049/********************************************************************/
6054 int k, nc;
6055 BUFFER_CLIENT *pbctmp;
6056
6057 /* clear entry from client structure in buffer header */
6058 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6059
6060 /* calculate new max_client_index entry */
6061 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6062 if (pheader->client[k].pid != 0)
6063 break;
6064 pheader->max_client_index = k + 1;
6065
6066 /* count new number of clients */
6067 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6068 if (pheader->client[k].pid != 0)
6069 nc++;
6070 pheader->num_clients = nc;
6071
6072 /* check if anyone is waiting and wake him up */
6073 pbctmp = pheader->client;
6074
6075 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6076 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6077 ss_resume(pbctmp->port, "B ");
6078}
6079
6080/********************************************************************/
6084static void bm_cleanup_buffer_locked(BUFFER* pbuf, const char *who, DWORD actual_time) {
6085 BUFFER_HEADER *pheader;
6086 BUFFER_CLIENT *pbclient;
6087 int j;
6088
6089 pheader = pbuf->buffer_header;
6090 pbclient = pheader->client;
6091
6092 /* now check other clients */
6093 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6094 if (pbclient->pid) {
6095 if (!ss_pid_exists(pbclient->pid)) {
6096 cm_msg(MINFO, "bm_cleanup",
6097 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6098 pheader->name, who, pbclient->pid);
6099
6100 bm_remove_client_locked(pheader, j);
6101 continue;
6102 }
6103 }
6104
6105 /* If client process has no activity, clear its buffer entry. */
6106 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6107 DWORD tdiff = actual_time - pbclient->last_activity;
6108#if 0
6109 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6110 pheader->name,
6111 pbclient->name,
6112 pbclient->last_activity,
6114 tdiff,
6115 tdiff,
6116 pbclient->watchdog_timeout);
6117#endif
6118 if (actual_time > pbclient->last_activity &&
6119 tdiff > pbclient->watchdog_timeout) {
6120
6121 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6122 pbclient->name, pheader->name, who,
6123 tdiff / 1000.0,
6124 pbclient->watchdog_timeout / 1000.0);
6125
6126 bm_remove_client_locked(pheader, j);
6127 }
6128 }
6129 }
6130}
6131
6135static void bm_update_last_activity(DWORD millitime) {
6136 int pid = ss_getpid();
6137
6138 std::vector<BUFFER*> mybuffers;
6139
6140 gBuffersMutex.lock();
6141 mybuffers = gBuffers;
6142 gBuffersMutex.unlock();
6143
6144 for (BUFFER* pbuf : mybuffers) {
6145 if (!pbuf)
6146 continue;
6147 if (pbuf->attached) {
6148
6149 bm_lock_buffer_guard pbuf_guard(pbuf);
6150
6151 if (!pbuf_guard.is_locked())
6152 continue;
6153
6154 BUFFER_HEADER *pheader = pbuf->buffer_header;
6155 for (int j = 0; j < pheader->max_client_index; j++) {
6156 BUFFER_CLIENT *pclient = pheader->client + j;
6157 if (pclient->pid == pid) {
6158 pclient->last_activity = millitime;
6159 }
6160 }
6161 }
6162 }
6163}
6164
6165#endif // LOCAL_ROUTINES
6166
6170static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
6171{
6172#ifdef LOCAL_ROUTINES
6173
6174 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6175
6176 std::vector<BUFFER*> mybuffers;
6177
6178 gBuffersMutex.lock();
6179 mybuffers = gBuffers;
6180 gBuffersMutex.unlock();
6181
6182 /* check buffers */
6183 for (BUFFER* pbuf : mybuffers) {
6184 if (!pbuf)
6185 continue;
6186 if (pbuf->attached) {
6187 /* update the last_activity entry to show that we are alive */
6188
6189 bm_lock_buffer_guard pbuf_guard(pbuf);
6190
6191 if (!pbuf_guard.is_locked())
6192 continue;
6193
6194 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
6195 pclient->last_activity = actual_time;
6196
6197 /* don't check other clients if interval is strange */
6198 if (!wrong_interval)
6200 }
6201 }
6202#endif // LOCAL_ROUTINES
6203}
6204
6205#ifdef LOCAL_ROUTINES
6206
6207static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6208 if (rp < 0 || rp > pheader->size) {
6209 cm_msg(MERROR, "bm_validate_rp",
6210 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6211 pheader->name,
6212 rp,
6213 pheader->read_pointer,
6214 pheader->write_pointer,
6215 pheader->size,
6216 who);
6217 return FALSE;
6218 }
6219
6220 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6221 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6222 cm_msg(MERROR, "bm_validate_rp",
6223 "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",
6224 pheader->name,
6225 rp,
6226 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6227 pheader->read_pointer,
6228 pheader->write_pointer,
6229 pheader->size,
6230 who);
6231 return FALSE;
6232 }
6233
6234 return TRUE;
6235}
6236
6237#if 0
6238static FILE* gRpLog = NULL;
6239#endif
6240
6241static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6242{
6243#if 0
6244 if (gRpLog == NULL) {
6245 gRpLog = fopen("rp.log", "a");
6246 }
6247 if (gRpLog && (total_size < 16)) {
6248 const char *pdata = (const char *) (pheader + 1);
6249 const DWORD *pevent = (const DWORD*) (pdata + rp);
6250 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,
6251 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6252 }
6253#endif
6254
6255 // these checks are already done before we come here.
6256 // but we check again as last-ressort protection. K.O.
6257 assert(total_size > 0);
6258 assert(total_size >= (int)sizeof(EVENT_HEADER));
6259
6260 rp += total_size;
6261 if (rp >= pheader->size) {
6262 rp -= pheader->size;
6263 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6264 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6265 // if at the end of the buffer, the remaining free space is exactly
6266 // equal to the size of an event header, the event header
6267 // is written there, the pointer is wrapped and the event data
6268 // is written to the beginning of the buffer.
6269 rp = 0;
6270 }
6271 return rp;
6272}
6273
6274static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6275 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6276 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6277 int total_size = ALIGN8(event_size);
6278
6279 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6280 cm_msg(MERROR, "bm_next_rp",
6281 "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",
6282 pheader->name,
6283 rp,
6284 pevent->data_size,
6285 event_size,
6286 total_size,
6287 pheader->read_pointer,
6288 pheader->write_pointer,
6289 pheader->size,
6290 who);
6291 return -1;
6292 }
6293
6294 int remaining = 0;
6295 if (rp < pheader->write_pointer) {
6296 remaining = pheader->write_pointer - rp;
6297 } else {
6298 remaining = pheader->size - rp;
6299 remaining += pheader->write_pointer;
6300 }
6301
6302 //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);
6303
6304 if (total_size > remaining) {
6305 cm_msg(MERROR, "bm_next_rp",
6306 "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",
6307 pheader->name,
6308 rp,
6309 pevent->data_size,
6310 event_size,
6311 total_size,
6312 pheader->read_pointer,
6313 pheader->write_pointer,
6314 pheader->size,
6315 remaining,
6316 who);
6317 return -1;
6318 }
6319
6320 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6321
6322 return rp;
6323}
6324
6325static int bm_validate_buffer_locked(const BUFFER *pbuf) {
6326 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6327 const char *pdata = (const char *) (pheader + 1);
6328
6329 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6330
6331 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6332
6333 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6334
6335 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6336 cm_msg(MERROR, "bm_validate_buffer",
6337 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6338 pheader->read_pointer, pheader->size, pheader->write_pointer);
6339 return BM_CORRUPTED;
6340 }
6341
6342 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6343 cm_msg(MERROR, "bm_validate_buffer",
6344 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6345 pheader->write_pointer, pheader->size, pheader->read_pointer);
6346 return BM_CORRUPTED;
6347 }
6348
6349 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6350 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6351 pheader->read_pointer);
6352 return BM_CORRUPTED;
6353 }
6354
6355 int rp = pheader->read_pointer;
6356 int rp0 = -1;
6357 while (rp != pheader->write_pointer) {
6358 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6359 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6360 pheader->name, rp, rp0);
6361 return BM_CORRUPTED;
6362 }
6363 //bm_print_event(pdata, rp);
6364 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6365 if (rp1 < 0) {
6366 cm_msg(MERROR, "bm_validate_buffer",
6367 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6368 return BM_CORRUPTED;
6369 }
6370 rp0 = rp;
6371 rp = rp1;
6372 }
6373
6374 int i;
6375 for (i = 0; i < MAX_CLIENTS; i++) {
6376 const BUFFER_CLIENT *c = &pheader->client[i];
6377 if (c->pid == 0)
6378 continue;
6379 BOOL get_all = FALSE;
6380 int j;
6381 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6382 const EVENT_REQUEST *r = &c->event_request[j];
6383 if (!r->valid)
6384 continue;
6385 BOOL xget_all = r->sampling_type == GET_ALL;
6386 get_all = (get_all || xget_all);
6387 //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);
6388 }
6389
6390 int rp = c->read_pointer;
6391 int rp0 = -1;
6392 while (rp != pheader->write_pointer) {
6393 //bm_print_event(pdata, rp);
6394 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6395 if (rp1 < 0) {
6396 cm_msg(MERROR, "bm_validate_buffer",
6397 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6398 pheader->name, c->name, c->read_pointer, rp, rp0);
6399 return BM_CORRUPTED;
6400 }
6401 rp0 = rp;
6402 rp = rp1;
6403 }
6404 }
6405
6406 return BM_SUCCESS;
6407}
6408
6409static void bm_reset_buffer_locked(BUFFER *pbuf) {
6410 BUFFER_HEADER *pheader = pbuf->buffer_header;
6411
6412 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6413
6414 pheader->read_pointer = 0;
6415 pheader->write_pointer = 0;
6416
6417 int i;
6418 for (i = 0; i < pheader->max_client_index; i++) {
6419 BUFFER_CLIENT *pc = pheader->client + i;
6420 if (pc->pid) {
6421 pc->read_pointer = 0;
6422 }
6423 }
6424}
6425
6427 std::string str = msprintf("/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6428 //printf("delete [%s]\n", str);
6429 db_delete(hDB, 0, str.c_str());
6430}
6431
6474
6475static 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)
6476{
6477 int status;
6478
6479 DWORD now = ss_millitime();
6480
6481 HNDLE hKey;
6482 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6483 if (status != DB_SUCCESS) {
6484 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6485 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6486 if (status != DB_SUCCESS)
6487 return;
6488 }
6489
6490 HNDLE hKeyBuffer;
6491 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6492 if (status != DB_SUCCESS) {
6494 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6495 if (status != DB_SUCCESS)
6496 return;
6497 }
6498
6499 double buf_size = pheader->size;
6500 double buf_rptr = pheader->read_pointer;
6501 double buf_wptr = pheader->write_pointer;
6502
6503 double buf_fill = 0;
6504 double buf_cptr = 0;
6505 double buf_cused = 0;
6506 double buf_cused_pct = 0;
6507
6508 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6509 buf_cptr = pheader->client[client_index].read_pointer;
6510
6511 if (buf_wptr == buf_cptr) {
6512 buf_cused = 0;
6513 } else if (buf_wptr > buf_cptr) {
6514 buf_cused = buf_wptr - buf_cptr;
6515 } else {
6516 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6517 }
6518
6519 buf_cused_pct = buf_cused / buf_size * 100.0;
6520
6521 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6522 // because some other GET_ALL client may have different buf_cused & etc,
6523 // so they must be written into the per-client statistics
6524 // and the web page should look at all the GET_ALL clients and used
6525 // the biggest buf_cused as the whole-buffer "bytes used" value.
6526 }
6527
6528 if (buf_wptr == buf_rptr) {
6529 buf_fill = 0;
6530 } else if (buf_wptr > buf_rptr) {
6531 buf_fill = buf_wptr - buf_rptr;
6532 } else {
6533 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6534 }
6535
6536 double buf_fill_pct = buf_fill / buf_size * 100.0;
6537
6538 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6539 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6540 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6541 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6542 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6543
6544 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6545 if (status != DB_SUCCESS) {
6546 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6547 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6548 if (status != DB_SUCCESS)
6549 return;
6550 }
6551
6552 HNDLE hKeyClient;
6553 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6554 if (status != DB_SUCCESS) {
6555 db_create_key(hDB, hKey, client_name, TID_KEY);
6556 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6557 if (status != DB_SUCCESS)
6558 return;
6559 }
6560
6561 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6562 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6563 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6564 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6565 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6566 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6567 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6568 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6569 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6570 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6571 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6572 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6573
6574 for (int i = 0; i < MAX_CLIENTS; i++) {
6575 if (!pbuf->client_count_write_wait[i])
6576 continue;
6577
6578 if (pheader->client[i].pid == 0)
6579 continue;
6580
6581 if (pheader->client[i].name[0] == 0)
6582 continue;
6583
6584 char str[100 + NAME_LENGTH];
6585
6586 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6587 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6588
6589 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6590 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6591 }
6592
6593 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6594 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6595}
6596
6598{
6599 //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);
6600
6601 bm_lock_buffer_guard pbuf_guard(pbuf);
6602
6603 if (!pbuf_guard.is_locked())
6604 return;
6605
6606 if (!force) {
6607 if (pbuf->count_lock == pbuf->last_count_lock) {
6608 return;
6609 }
6610 }
6611
6612 std::string buffer_name = pbuf->buffer_name;
6613 std::string client_name = pbuf->client_name;
6614
6615 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6616 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6617 pbuf_guard.unlock(); // unlock before cm_msg()
6618 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());
6619 return;
6620 }
6621
6622 pbuf->last_count_lock = pbuf->count_lock;
6623
6624 BUFFER_INFO xbuf(pbuf);
6625 BUFFER_HEADER xheader = *pbuf->buffer_header;
6626 int client_index = pbuf->client_index;
6627
6628 pbuf_guard.unlock();
6629
6630 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6631}
6632
6633static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6634{
6635 size_t sbuffer_handle = buffer_handle;
6636
6637 size_t nbuf = 0;
6638 BUFFER* pbuf = NULL;
6639
6640 gBuffersMutex.lock();
6641
6642 nbuf = gBuffers.size();
6643 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6644 pbuf = gBuffers[buffer_handle-1];
6645 }
6646
6647 gBuffersMutex.unlock();
6648
6649 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6650 if (who)
6651 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6652 if (pstatus)
6653 *pstatus = BM_INVALID_HANDLE;
6654 return NULL;
6655 }
6656
6657 if (!pbuf) {
6658 if (who)
6659 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6660 if (pstatus)
6661 *pstatus = BM_INVALID_HANDLE;
6662 return NULL;
6663 }
6664
6665 if (!pbuf->attached) {
6666 if (who)
6667 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6668 if (pstatus)
6669 *pstatus = BM_INVALID_HANDLE;
6670 return NULL;
6671 }
6672
6673 if (pstatus)
6674 *pstatus = BM_SUCCESS;
6675
6676 return pbuf;
6677}
6678
6679#endif // LOCAL_ROUTINES
6680
6681/********************************************************************/
6728INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6729 INT status;
6730
6731 if (rpc_is_remote()) {
6732 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6733
6734 HNDLE hDB;
6736 if (status != SUCCESS || hDB == 0) {
6737 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6738 return BM_NO_SHM;
6739 }
6740
6742
6743 int size = sizeof(INT);
6744 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6745
6746 if (status != DB_SUCCESS) {
6747 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6748 status);
6749 return status;
6750 }
6751
6752 return status;
6753 }
6754#ifdef LOCAL_ROUTINES
6755 {
6756 HNDLE shm_handle;
6757 size_t shm_size;
6758 HNDLE hDB;
6759 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6760
6761 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6762
6763 if (!buffer_name || !buffer_name[0]) {
6764 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6765 return BM_INVALID_PARAM;
6766 }
6767
6768 if (strlen(buffer_name) >= NAME_LENGTH) {
6769 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6770 return BM_INVALID_PARAM;
6771 }
6772
6774
6775 if (status != SUCCESS || hDB == 0) {
6776 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6777 return BM_NO_SHM;
6778 }
6779
6780 /* get buffer size from ODB, user parameter as default if not present in ODB */
6781 std::string odb_path;
6782 odb_path += "/Experiment/Buffer sizes/";
6783 odb_path += buffer_name;
6784
6785 int size = sizeof(INT);
6786 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6787
6788 if (buffer_size <= 0 || buffer_size > max_buffer_size) {
6789 cm_msg(MERROR, "bm_open_buffer",
6790 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6791 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6792
6793 // 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
6794
6795 if (strcmp(buffer_name, MESSAGE_BUFFER_NAME) != 0)
6796 return BM_INVALID_PARAM;
6797
6798 cm_msg(MERROR, "bm_open_buffer",
6799 "Will use default SYSMSG buffer size %d to open buffer \"%s\"",
6801 buffer_size = MESSAGE_BUFFER_SIZE;
6802 }
6803
6805
6806 size = sizeof(INT);
6807 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6808
6809 if (status != DB_SUCCESS) {
6810 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6811 status);
6812 return status;
6813 }
6814
6815 /* check if buffer already is open */
6816 gBuffersMutex.lock();
6817 for (size_t i = 0; i < gBuffers.size(); i++) {
6818 BUFFER* pbuf = gBuffers[i];
6819 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6820 *buffer_handle = i + 1;
6821 gBuffersMutex.unlock();
6822 return BM_SUCCESS;
6823 }
6824 }
6825 gBuffersMutex.unlock();
6826
6827 // only one thread at a time should create new buffers
6828
6829 static std::mutex gNewBufferMutex;
6830 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6831
6832 // if we had a race against another thread
6833 // and while we were waiting for gNewBufferMutex
6834 // the other thread created this buffer, we return it.
6835
6836 gBuffersMutex.lock();
6837 for (size_t i = 0; i < gBuffers.size(); i++) {
6838 BUFFER* pbuf = gBuffers[i];
6839 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6840 *buffer_handle = i + 1;
6841 gBuffersMutex.unlock();
6842 return BM_SUCCESS;
6843 }
6844 }
6845 gBuffersMutex.unlock();
6846
6847 /* allocate new BUFFER object */
6848
6849 BUFFER* pbuf = new BUFFER;
6850
6851 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6852
6853 for (int i=0; i<MAX_CLIENTS; i++) {
6854 pbuf->client_count_write_wait[i] = 0;
6855 pbuf->client_time_write_wait[i] = 0;
6856 }
6857
6858 /* create buffer semaphore */
6859
6861
6862 if (status != SS_CREATED && status != SS_SUCCESS) {
6863 *buffer_handle = 0;
6864 delete pbuf;
6865 return BM_NO_SEMAPHORE;
6866 }
6867
6868 std::string client_name = cm_get_client_name();
6869
6870 /* store client name */
6871 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6872
6873 /* store buffer name */
6874 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6875
6876 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6877
6878 pbuf->attached = true; // required by bm_lock_buffer()
6879
6880 bm_lock_buffer_guard pbuf_guard(pbuf);
6881
6882 if (!pbuf_guard.is_locked()) {
6883 // cannot happen, no other thread can see this pbuf
6884 abort();
6885 return BM_NO_SEMAPHORE;
6886 }
6887
6888 /* open shared memory */
6889
6890 void *p = NULL;
6891 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6892
6893 if (status != SS_SUCCESS && status != SS_CREATED) {
6894 *buffer_handle = 0;
6895 pbuf_guard.unlock();
6896 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6897 delete pbuf;
6898 return BM_NO_SHM;
6899 }
6900
6901 pbuf->buffer_header = (BUFFER_HEADER *) p;
6902
6903 BUFFER_HEADER *pheader = pbuf->buffer_header;
6904
6905 bool shm_created = (status == SS_CREATED);
6906
6907 if (shm_created) {
6908 /* initialize newly created shared memory */
6909
6910 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6911
6912 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6913 pheader->size = buffer_size;
6914
6915 } else {
6916 /* validate existing shared memory */
6917
6918 if (!equal_ustring(pheader->name, buffer_name)) {
6919 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6920 pbuf_guard.unlock();
6921 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6922 cm_msg(MERROR, "bm_open_buffer",
6923 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6924 pheader->name);
6925 *buffer_handle = 0;
6926 delete pbuf;
6927 return BM_CORRUPTED;
6928 }
6929
6930 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6931 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6932 pbuf_guard.unlock();
6933 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6934 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6936 *buffer_handle = 0;
6937 delete pbuf;
6938 return BM_CORRUPTED;
6939 }
6940
6941 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6942 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6943 pbuf_guard.unlock();
6944 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6945 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6947 *buffer_handle = 0;
6948 delete pbuf;
6949 return BM_CORRUPTED;
6950 }
6951
6952 /* check if buffer size is identical */
6953 if (pheader->size != buffer_size) {
6954 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6955 buffer_name, buffer_size, pheader->size);
6956
6957 buffer_size = pheader->size;
6958
6959 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6960
6961 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6962
6963 if (status != SS_SUCCESS) {
6964 *buffer_handle = 0;
6965 pbuf_guard.unlock();
6966 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6967 delete pbuf;
6968 return BM_NO_SHM;
6969 }
6970
6971 pbuf->buffer_header = (BUFFER_HEADER *) p;
6972 pheader = pbuf->buffer_header;
6973 }
6974 }
6975
6976 /* shared memory is good from here down */
6977
6978 pbuf->attached = true;
6979
6980 pbuf->shm_handle = shm_handle;
6981 pbuf->shm_size = shm_size;
6982 pbuf->callback = FALSE;
6983
6984 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6985
6987 if (status != BM_SUCCESS) {
6988 cm_msg(MERROR, "bm_open_buffer",
6989 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6990 status);
6992 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6993 }
6994
6995 /* add our client BUFFER_HEADER */
6996
6997 int iclient = 0;
6998 for (; iclient < MAX_CLIENTS; iclient++)
6999 if (pheader->client[iclient].pid == 0)
7000 break;
7001
7002 if (iclient == MAX_CLIENTS) {
7003 *buffer_handle = 0;
7004 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
7005 pbuf_guard.unlock();
7006 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
7007 delete pbuf;
7008 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
7009 return BM_NO_SLOT;
7010 }
7011
7012 /* store slot index in _buffer structure */
7013 pbuf->client_index = iclient;
7014
7015 /*
7016 Save the index of the last client of that buffer so that later only
7017 the clients 0..max_client_index-1 have to be searched through.
7018 */
7019 pheader->num_clients++;
7020 if (iclient + 1 > pheader->max_client_index)
7021 pheader->max_client_index = iclient + 1;
7022
7023 /* setup buffer header and client structure */
7024 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7025
7026 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7027
7028 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7029
7030 pclient->pid = ss_getpid();
7031
7033
7034 pclient->read_pointer = pheader->write_pointer;
7035 pclient->last_activity = ss_millitime();
7036
7038
7039 pbuf_guard.unlock();
7040
7041 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7042
7043 pheader = NULL;
7044
7045 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7046
7049
7050 /* add pbuf to buffer list */
7051
7052 gBuffersMutex.lock();
7053
7054 bool added = false;
7055 for (size_t i=0; i<gBuffers.size(); i++) {
7056 if (gBuffers[i] == NULL) {
7057 gBuffers[i] = pbuf;
7058 added = true;
7059 *buffer_handle = i+1;
7060 break;
7061 }
7062 }
7063 if (!added) {
7064 *buffer_handle = gBuffers.size() + 1;
7065 gBuffers.push_back(pbuf);
7066 }
7067
7068 /* from here down we should not touch pbuf without locking it */
7069
7070 pbuf = NULL;
7071
7072 gBuffersMutex.unlock();
7073
7074 /* new buffer is now ready for use */
7075
7076 /* initialize buffer counters */
7077 bm_init_buffer_counters(*buffer_handle);
7078
7079 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7080
7081 if (shm_created)
7082 return BM_CREATED;
7083 }
7084#endif /* LOCAL_ROUTINES */
7085
7086 return BM_SUCCESS;
7087}
7088
7089/********************************************************************/
7095INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7096{
7097 gBuffersMutex.lock();
7098 for (size_t i = 0; i < gBuffers.size(); i++) {
7099 BUFFER* pbuf = gBuffers[i];
7100 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7101 *buffer_handle = i + 1;
7102 gBuffersMutex.unlock();
7103 return BM_SUCCESS;
7104 }
7105 }
7106 gBuffersMutex.unlock();
7107 return BM_NOT_FOUND;
7108}
7109
7110/********************************************************************/
7116INT bm_close_buffer(INT buffer_handle) {
7117 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7118
7119 if (rpc_is_remote())
7120 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7121
7122#ifdef LOCAL_ROUTINES
7123 {
7124 int status = 0;
7125
7126 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7127
7128 if (!pbuf)
7129 return status;
7130
7131 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7132
7133 int i;
7134
7135 { /* delete all requests for this buffer */
7136 _request_list_mutex.lock();
7137 std::vector<EventRequest> request_list_copy = _request_list;
7138 _request_list_mutex.unlock();
7139 for (size_t i = 0; i < request_list_copy.size(); i++) {
7140 if (request_list_copy[i].buffer_handle == buffer_handle) {
7142 }
7143 }
7144 }
7145
7146 HNDLE hDB;
7148
7149 if (hDB) {
7150 /* write statistics to odb */
7152 }
7153
7154 /* lock buffer in correct order */
7155
7157
7158 if (status != BM_SUCCESS) {
7159 return status;
7160 }
7161
7163
7164 if (status != BM_SUCCESS) {
7165 pbuf->read_cache_mutex.unlock();
7166 return status;
7167 }
7168
7169 bm_lock_buffer_guard pbuf_guard(pbuf);
7170
7171 if (!pbuf_guard.is_locked()) {
7172 pbuf->write_cache_mutex.unlock();
7173 pbuf->read_cache_mutex.unlock();
7174 return pbuf_guard.get_status();
7175 }
7176
7177 BUFFER_HEADER *pheader = pbuf->buffer_header;
7178
7179 /* mark entry in _buffer as empty */
7180 pbuf->attached = false;
7181
7182 BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
7183
7184 if (pclient) {
7185 /* clear entry from client structure in buffer header */
7186 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7187 }
7188
7189 /* calculate new max_client_index entry */
7190 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7191 if (pheader->client[i].pid != 0)
7192 break;
7193 pheader->max_client_index = i + 1;
7194
7195 /* count new number of clients */
7196 int j = 0;
7197 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7198 if (pheader->client[i].pid != 0)
7199 j++;
7200 pheader->num_clients = j;
7201
7202 int destroy_flag = (pheader->num_clients == 0);
7203
7204 // we hold the locks on the read cache and the write cache.
7205
7206 /* free cache */
7207 if (pbuf->read_cache_size > 0) {
7208 free(pbuf->read_cache);
7209 pbuf->read_cache = NULL;
7210 pbuf->read_cache_size = 0;
7211 pbuf->read_cache_rp = 0;
7212 pbuf->read_cache_wp = 0;
7213 }
7214
7215 if (pbuf->write_cache_size > 0) {
7216 free(pbuf->write_cache);
7217 pbuf->write_cache = NULL;
7218 pbuf->write_cache_size = 0;
7219 pbuf->write_cache_rp = 0;
7220 pbuf->write_cache_wp = 0;
7221 }
7222
7223 /* check if anyone is waiting and wake him up */
7224
7225 for (int i = 0; i < pheader->max_client_index; i++) {
7226 BUFFER_CLIENT *pclient = pheader->client + i;
7227 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7228 ss_resume(pclient->port, "B ");
7229 }
7230
7231 /* unmap shared memory, delete it if we are the last */
7232
7233 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7234
7235 /* after ss_shm_close() these are invalid: */
7236
7237 pheader = NULL;
7238 pbuf->buffer_header = NULL;
7239 pbuf->shm_size = 0;
7240 pbuf->shm_handle = 0;
7241
7242 /* unlock buffer in correct order */
7243
7244 pbuf_guard.unlock();
7245
7246 pbuf->write_cache_mutex.unlock();
7247 pbuf->read_cache_mutex.unlock();
7248
7249 /* delete semaphore */
7250
7251 ss_semaphore_delete(pbuf->semaphore, destroy_flag);
7252 }
7253#endif /* LOCAL_ROUTINES */
7254
7255 return BM_SUCCESS;
7256}
7257
7258/********************************************************************/
7264 if (rpc_is_remote())
7266
7267#ifdef LOCAL_ROUTINES
7268 {
7270
7271 gBuffersMutex.lock();
7272 size_t nbuf = gBuffers.size();
7273 gBuffersMutex.unlock();
7274
7275 for (size_t i = nbuf; i > 0; i--) {
7277 }
7278
7279 gBuffersMutex.lock();
7280 for (size_t i=0; i< gBuffers.size(); i++) {
7281 BUFFER* pbuf = gBuffers[i];
7282 if (!pbuf)
7283 continue;
7284 delete pbuf;
7285 pbuf = NULL;
7286 gBuffers[i] = NULL;
7287 }
7288 gBuffersMutex.unlock();
7289 }
7290#endif /* LOCAL_ROUTINES */
7291
7292 return BM_SUCCESS;
7293}
7294
7295/********************************************************************/
7301#ifdef LOCAL_ROUTINES
7302 {
7303 int status;
7304 HNDLE hDB;
7305
7307
7308 if (status != CM_SUCCESS) {
7309 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7310 return BM_SUCCESS;
7311 }
7312
7313 std::vector<BUFFER*> mybuffers;
7314
7315 gBuffersMutex.lock();
7316 mybuffers = gBuffers;
7317 gBuffersMutex.unlock();
7318
7319 for (BUFFER* pbuf : mybuffers) {
7320 if (!pbuf || !pbuf->attached)
7321 continue;
7323 }
7324 }
7325#endif /* LOCAL_ROUTINES */
7326
7327 return BM_SUCCESS;
7328}
7329
/* end of bmfunctionc */
7332
7338/*-- Watchdog routines ---------------------------------------------*/
7339#ifdef LOCAL_ROUTINES
7340
7341static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7342static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7343static std::atomic<std::thread*> _watchdog_thread{NULL};
7344
7345/********************************************************************/
7351 //printf("cm_watchdog_thread started!\n");
7352 while (_watchdog_thread_run) {
7353 //printf("cm_watchdog_thread runs!\n");
7354 DWORD now = ss_millitime();
7357 int i;
7358 for (i = 0; i < 20; i++) {
7359 ss_sleep(100);
7361 break;
7362 }
7363 }
7364 //printf("cm_watchdog_thread stopped!\n");
7366 return 0;
7367}
7368
7369static void xcm_watchdog_thread() {
7370 cm_watchdog_thread(NULL);
7371}
7372
7373#endif
7374
7376 /* watchdog does not run inside remote clients.
7377 * watchdog timeout timers are maintained by the mserver */
7378 if (rpc_is_remote())
7379 return CM_SUCCESS;
7380#ifdef LOCAL_ROUTINES
7381 /* only start once */
7382 if (_watchdog_thread)
7383 return CM_SUCCESS;
7384 _watchdog_thread_run = true;
7385 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7386#endif
7387 return CM_SUCCESS;
7388}
7389
7391 /* watchdog does not run inside remote clients.
7392 * watchdog timeout timers are maintained by the mserver */
7393 if (rpc_is_remote())
7394 return CM_SUCCESS;
7395#ifdef LOCAL_ROUTINES
7396 _watchdog_thread_run = false;
7398 //printf("waiting for watchdog thread to shut down\n");
7399 ss_sleep(10);
7400 }
7401 if (_watchdog_thread != NULL) {
7402 _watchdog_thread.load()->join();
7403 delete static_cast<std::thread *>(_watchdog_thread);
7404 _watchdog_thread = NULL;
7405 }
7406#endif
7407 return CM_SUCCESS;
7408}
7409
7410/********************************************************************/
7420INT cm_shutdown(const char *name, BOOL bUnique) {
7421 INT status, return_status, i, size;
7422 HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
7423 KEY key;
7424 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7425 INT port;
7426 DWORD start_time;
7427 DWORD timeout;
7428 DWORD last;
7429
7430 cm_get_experiment_database(&hDB, &hKeyClient);
7431
7432 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7433 if (status != DB_SUCCESS)
7434 return DB_NO_KEY;
7435
7436 return_status = CM_NO_CLIENT;
7437
7438 /* loop over all clients */
7439 for (i = 0;; i++) {
7442 break;
7443
7444 /* don't shutdown ourselves */
7445 if (hSubkey == hKeyClient)
7446 continue;
7447
7448 if (status == DB_SUCCESS) {
7450
7451 /* contact client */
7452 size = sizeof(client_name);
7453 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7454 if (status != DB_SUCCESS)
7455 continue;
7456
7457 if (!bUnique)
7458 client_name[strlen(name)] = 0; /* strip number */
7459
7460 /* check if individual client */
7461 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7462 continue;
7463
7464 size = sizeof(port);
7465 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7466
7467 size = sizeof(remote_host);
7468 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7469
7470 cm_get_watchdog_info(hDB, name, &timeout, &last);
7471 if (timeout == 0)
7472 timeout = 5000;
7473
7474 /* client found -> connect to its server port */
7475 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7476 if (status != RPC_SUCCESS) {
7477 int client_pid = atoi(key.name);
7478 return_status = CM_NO_CLIENT;
7479 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7480 client_name, remote_host, port);
7481#ifdef SIGKILL
7482 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7483 client_pid);
7484 kill(client_pid, SIGKILL);
7485 return_status = CM_SUCCESS;
7486 status = cm_delete_client_info(hDB, client_pid);
7487 if (status != CM_SUCCESS)
7488 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7489 name, client_pid, status);
7490#endif
7491 } else {
7492 /* call disconnect with shutdown=TRUE */
7494
7495 /* wait until client has shut down */
7496 start_time = ss_millitime();
7497 do {
7498 ss_sleep(100);
7499 status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
7500 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7501
7502 if (status == DB_SUCCESS) {
7503 int client_pid = atoi(key.name);
7504 return_status = CM_NO_CLIENT;
7505 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7506#ifdef SIGKILL
7507 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7508 client_pid);
7509 kill(client_pid, SIGKILL);
7510 status = cm_delete_client_info(hDB, client_pid);
7511 if (status != CM_SUCCESS)
7512 cm_msg(MERROR, "cm_shutdown",
7513 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7514 status);
7515#endif
7516 return_status = CM_NO_CLIENT;
7517 } else {
7518 return_status = CM_SUCCESS;
7519 i--;
7520 }
7521 }
7522 }
7523
7524 /* display any message created during each shutdown */
7526 }
7527
7528 return return_status;
7529}
7530
7531/********************************************************************/
7540INT cm_exist(const char *name, BOOL bUnique) {
7541 INT status, i, size;
7542 HNDLE hDB, hKeyClient, hKey, hSubkey;
7543 char client_name[NAME_LENGTH];
7544
7545 if (rpc_is_remote())
7546 return rpc_call(RPC_CM_EXIST, name, bUnique);
7547
7548 cm_get_experiment_database(&hDB, &hKeyClient);
7549
7550 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7551 if (status != DB_SUCCESS)
7552 return DB_NO_KEY;
7553
7555
7556 /* loop over all clients */
7557 for (i = 0;; i++) {
7560 break;
7561
7562 if (hSubkey == hKeyClient)
7563 continue;
7564
7565 if (status == DB_SUCCESS) {
7566 /* get client name */
7567 size = sizeof(client_name);
7568 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7569
7570 if (status != DB_SUCCESS) {
7571 //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);
7572 continue;
7573 }
7574
7575 if (equal_ustring(client_name, name)) {
7577 return CM_SUCCESS;
7578 }
7579
7580 if (!bUnique) {
7581 client_name[strlen(name)] = 0; /* strip number */
7582 if (equal_ustring(client_name, name)) {
7584 return CM_SUCCESS;
7585 }
7586 }
7587 }
7588 }
7589
7591
7592 return CM_NO_CLIENT;
7593}
7594
7595/********************************************************************/
7630INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7631 if (rpc_is_remote())
7632 return rpc_call(RPC_CM_CLEANUP, client_name);
7633
7634#ifdef LOCAL_ROUTINES
7635 {
7636 DWORD interval;
7637 DWORD now = ss_millitime();
7638
7639 std::vector<BUFFER*> mybuffers;
7640
7641 gBuffersMutex.lock();
7642 mybuffers = gBuffers;
7643 gBuffersMutex.unlock();
7644
7645 /* check buffers */
7646 for (BUFFER* pbuf : mybuffers) {
7647 if (!pbuf)
7648 continue;
7649 if (pbuf->attached) {
7650 std::string msg;
7651
7652 bm_lock_buffer_guard pbuf_guard(pbuf);
7653
7654 if (!pbuf_guard.is_locked())
7655 continue;
7656
7657 /* update the last_activity entry to show that we are alive */
7658 BUFFER_HEADER *pheader = pbuf->buffer_header;
7659 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7660 pclient->last_activity = ss_millitime();
7661
7662 /* now check other clients */
7663 for (int j = 0; j < pheader->max_client_index; j++) {
7664 BUFFER_CLIENT *pbclient = &pheader->client[j];
7665 if (j != pbuf->client_index && pbclient->pid &&
7666 (client_name == NULL || client_name[0] == 0
7667 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7668 if (ignore_timeout)
7669 interval = 2 * WATCHDOG_INTERVAL;
7670 else
7671 interval = pbclient->watchdog_timeout;
7672
7673 /* If client process has no activity, clear its buffer entry. */
7674 if (interval > 0
7675 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7676
7677 /* now make again the check with the buffer locked */
7678 if (interval > 0
7679 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7680 msg = msprintf(
7681 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7682 pbclient->name, pheader->name,
7683 (ss_millitime() - pbclient->last_activity) / 1000.0,
7684 interval / 1000.0);
7685
7686 bm_remove_client_locked(pheader, j);
7687 }
7688
7689 /* go again through whole list */
7690 j = 0;
7691 }
7692 }
7693 }
7694
7695 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7696 pbuf_guard.unlock();
7697
7698 /* display info message after unlocking buffer */
7699 if (!msg.empty())
7700 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7701 }
7702 }
7703
7704 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7705 }
7706#endif /* LOCAL_ROUTINES */
7707
7708 return CM_SUCCESS;
7709}
7710
7711/********************************************************************/
7730std::string cm_expand_env(const char *str) {
7731 const char *s = str;
7732 std::string r;
7733 for (; *s;) {
7734 if (*s == '$') {
7735 s++;
7736 std::string envname;
7737 for (; *s;) {
7738 if (*s == DIR_SEPARATOR)
7739 break;
7740 envname += *s;
7741 s++;
7742 }
7743 const char *e = getenv(envname.c_str());
7744 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7745 if (!e) {
7746 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7747 r += '$';
7748 r += envname;
7749 } else {
7750 r += e;
7751 //if (r[r.length()-1] != DIR_SEPARATOR)
7752 //r += DIR_SEPARATOR_STR;
7753 }
7754 } else {
7755 r += *s;
7756 s++;
7757 }
7758 }
7759 return r;
7760}
7761
7762static bool test_cm_expand_env1(const char *str, const char *expected) {
7763 std::string s = cm_expand_env(str);
7764 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7765 str,
7766 s.c_str(),
7767 expected);
7768 if (s != expected) {
7769 printf(", MISMATCH!\n");
7770 return false;
7771 }
7772
7773 printf("\n");
7774 return true;
7775}
7776
7778 printf("Test expand_end()\n");
7779 setenv("FOO", "foo", 1);
7780 setenv("BAR", "bar", 1);
7781 setenv("EMPTY", "", 1);
7782 unsetenv("UNDEF");
7783
7784 bool ok = true;
7785
7786 ok &= test_cm_expand_env1("aaa", "aaa");
7787 ok &= test_cm_expand_env1("$FOO", "foo");
7788 ok &= test_cm_expand_env1("/$FOO", "/foo");
7789 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7790 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7791 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7792 ok &= test_cm_expand_env1("1$FOO", "1foo");
7793 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7794 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7795
7796 if (ok) {
7797 printf("test_expand_env: all tests passed!\n");
7798 } else {
7799 printf("test_expand_env: test FAILED!\n");
7800 }
7801}
7802
/* end of cmfunctionc */
7806
7808#ifndef DOXYGEN_SHOULD_SKIP_THIS
7809
7810/********************************************************************/
7811INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7812/********************************************************************\
7813
7814 Routine: bm_buffer_info
7815
7816 Purpose: Copies the current buffer header referenced by buffer_handle
7817 into the *buffer_header structure which must be supplied
7818 by the calling routine.
7819
7820 Input:
7821 INT buffer_handle Handle of the buffer to get the header from
7822
7823 Output:
7824 BUFFER_HEADER *buffer_header Destination address which gets a copy
7825 of the buffer header structure.
7826
7827 Function value:
7828 BM_SUCCESS Successful completion
7829 BM_INVALID_HANDLE Buffer handle is invalid
7830 RPC_NET_ERROR Network error
7831
7832\********************************************************************/
7833{
7834 if (rpc_is_remote())
7835 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7836
7837#ifdef LOCAL_ROUTINES
7838
7839 int status = 0;
7840 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7841
7842 if (!pbuf)
7843 return status;
7844
7845 bm_lock_buffer_guard pbuf_guard(pbuf);
7846
7847 if (!pbuf_guard.is_locked())
7848 return pbuf_guard.get_status();
7849
7850 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7851
7852#endif /* LOCAL_ROUTINES */
7853
7854 return BM_SUCCESS;
7855}
7856
7857/********************************************************************/
7858INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7859/********************************************************************\
7860
7861 Routine: bm_get_buffer_level
7862
7863 Purpose: Return number of bytes in buffer or in cache
7864
7865 Input:
7866 INT buffer_handle Handle of the buffer to get the info
7867
7868 Output:
7869 INT *n_bytes Number of bytes in buffer
7870
7871 Function value:
7872 BM_SUCCESS Successful completion
7873 BM_INVALID_HANDLE Buffer handle is invalid
7874 RPC_NET_ERROR Network error
7875
7876\********************************************************************/
7877{
7878 if (rpc_is_remote())
7879 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7880
7881#ifdef LOCAL_ROUTINES
7882 {
7883 int status = 0;
7884
7885 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7886
7887 if (!pbuf)
7888 return status;
7889
7890 bm_lock_buffer_guard pbuf_guard(pbuf);
7891
7892 if (!pbuf_guard.is_locked())
7893 return pbuf_guard.get_status();
7894
7895 BUFFER_HEADER *pheader = pbuf->buffer_header;
7896
7897 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7898
7899 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7900 if (*n_bytes < 0)
7901 *n_bytes += pheader->size;
7902
7903 pbuf_guard.unlock();
7904
7905 if (pbuf->read_cache_size) {
7907 if (status == BM_SUCCESS) {
7908 /* add bytes in cache */
7909 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7910 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7911 pbuf->read_cache_mutex.unlock();
7912 }
7913 }
7914 }
7915#endif /* LOCAL_ROUTINES */
7916
7917 return BM_SUCCESS;
7918}
7919
7920
7921#ifdef LOCAL_ROUTINES
7922
7923/********************************************************************/
7925{
7926 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7927
7928 if (!locked) {
7929 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);
7930 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);
7931 abort();
7932 /* DOES NOT RETURN */
7933 }
7934
7935 if (!pbuf->attached) {
7936 pbuf->read_cache_mutex.unlock();
7937 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);
7938 return BM_INVALID_HANDLE;
7939 }
7940
7941 return BM_SUCCESS;
7942}
7943
7944/********************************************************************/
7946{
7947 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7948
7949 if (!locked) {
7950 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);
7951 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);
7952 abort();
7953 /* DOES NOT RETURN */
7954 }
7955
7956 if (!pbuf->attached) {
7957 pbuf->write_cache_mutex.unlock();
7958 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);
7959 return BM_INVALID_HANDLE;
7960 }
7961
7962 return BM_SUCCESS;
7963}
7964
7965/********************************************************************/
7967{
7968 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7969
7970 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7971
7972 if (!locked) {
7973 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7974 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7975 abort();
7976 /* DOES NOT RETURN */
7977 }
7978
7979 if (!pbuf->attached) {
7980 pbuf->buffer_mutex.unlock();
7981 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);
7982 return BM_INVALID_HANDLE;
7983 }
7984
7985 //static int counter = 0;
7986 //counter++;
7987 //printf("locked %d!\n", counter);
7988 //if (counter > 50)
7989 // ::sleep(3);
7990
7991 return BM_SUCCESS;
7992}
7993
7994/********************************************************************/
7995static int xbm_lock_buffer(BUFFER *pbuf)
7996{
7997 int status;
7998
7999 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8000
8001 //if (pbuf->locked) {
8002 // fprintf(stderr, "double lock, abort!\n");
8003 // abort();
8004 //}
8005
8007
8008 if (status != BM_SUCCESS)
8009 return status;
8010
8011 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8012
8013 if (status != SS_SUCCESS) {
8014 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8015
8016 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8017
8018 if (status != SS_SUCCESS) {
8019 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);
8020
8021 if (pbuf->buffer_header) {
8022 for (int i=0; i<MAX_CLIENTS; i++) {
8023 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);
8024 }
8025 }
8026
8028
8029 if (status != SS_SUCCESS) {
8030 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8031 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8032 abort();
8033 /* DOES NOT RETURN */
8034 }
8035 }
8036 }
8037
8038 // protect against double lock
8039 assert(!pbuf->locked);
8040 pbuf->locked = TRUE;
8041
8042#if 0
8043 int x = MAX_CLIENTS - 1;
8044 if (pbuf->buffer_header->client[x].unused1 != 0) {
8045 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8046 }
8047 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8048 pbuf->buffer_header->client[x].unused1 = getpid();
8049#endif
8050
8051 pbuf->count_lock++;
8052
8053 return BM_SUCCESS;
8054}
8055
8056/********************************************************************/
8057static void xbm_unlock_buffer(BUFFER *pbuf) {
8058 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8059
8060#if 0
8061 int x = MAX_CLIENTS-1;
8062 if (pbuf->attached) {
8063 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8064 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8065 }
8066 pbuf->buffer_header->client[x].unused1 = 0;
8067 } else {
8068 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8069 }
8070#endif
8071
8072 // protect against double unlock
8073 assert(pbuf->locked);
8074 pbuf->locked = FALSE;
8075
8077 pbuf->buffer_mutex.unlock();
8078}
8079
8080#endif /* LOCAL_ROUTINES */
8081
8082/********************************************************************/
8084/********************************************************************\
8085
8086 Routine: bm_init_event_counters
8087
8088 Purpose: Initialize counters for a specific buffer. This routine
8089 should be called at the beginning of a run.
8090
8091 Input:
8092 INT buffer_handle Handle to the buffer to be
8093 initialized.
8094 Output:
8095 none
8096
8097 Function value:
8098 BM_SUCCESS Successful completion
8099 BM_INVALID_HANDLE Buffer handle is invalid
8100
8101\********************************************************************/
8102{
8103 if (rpc_is_remote())
8104 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8105
8106#ifdef LOCAL_ROUTINES
8107
8108 int status = 0;
8109
8110 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8111
8112 if (!pbuf)
8113 return status;
8114
8115 bm_lock_buffer_guard pbuf_guard(pbuf);
8116
8117 if (!pbuf_guard.is_locked())
8118 return pbuf_guard.get_status();
8119
8120 pbuf->buffer_header->num_in_events = 0;
8121 pbuf->buffer_header->num_out_events = 0;
8122
8123#endif /* LOCAL_ROUTINES */
8124
8125 return BM_SUCCESS;
8126}
8127
8129#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8130
8136/********************************************************************/
8160INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8161/*------------------------------------------------------------------*/
8162{
8163 if (rpc_is_remote())
8164 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8165
8166#ifdef LOCAL_ROUTINES
8167 {
8168 int status = 0;
8169
8170 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8171
8172 if (!pbuf)
8173 return status;
8174
8175 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8176
8178
8179 if (status != BM_SUCCESS)
8180 return status;
8181
8182 if (write_size < 0)
8183 write_size = 0;
8184
8185 if (write_size > 0) {
8186 if (write_size < MIN_WRITE_CACHE_SIZE) {
8187 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);
8188 write_size = MIN_WRITE_CACHE_SIZE;
8189 }
8190 }
8191
8192 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8193
8194 if (write_size > max_write_size) {
8195 size_t new_write_size = max_write_size;
8196 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);
8197 write_size = new_write_size;
8198 }
8199
8200 pbuf->buffer_mutex.unlock();
8201
8202 /* resize read cache */
8203
8205
8206 if (status != BM_SUCCESS) {
8207 return status;
8208 }
8209
8210 if (pbuf->read_cache_size > 0) {
8211 free(pbuf->read_cache);
8212 pbuf->read_cache = NULL;
8213 }
8214
8215 if (read_size > 0) {
8216 pbuf->read_cache = (char *) malloc(read_size);
8217 if (pbuf->read_cache == NULL) {
8218 pbuf->read_cache_size = 0;
8219 pbuf->read_cache_rp = 0;
8220 pbuf->read_cache_wp = 0;
8221 pbuf->read_cache_mutex.unlock();
8222 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);
8223 return BM_NO_MEMORY;
8224 }
8225 }
8226
8227 pbuf->read_cache_size = read_size;
8228 pbuf->read_cache_rp = 0;
8229 pbuf->read_cache_wp = 0;
8230
8231 pbuf->read_cache_mutex.unlock();
8232
8233 /* resize the write cache */
8234
8236
8237 if (status != BM_SUCCESS)
8238 return status;
8239
8240 // FIXME: should flush the write cache!
8241 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8242 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8243 }
8244
8245 /* manage write cache */
8246 if (pbuf->write_cache_size > 0) {
8247 free(pbuf->write_cache);
8248 pbuf->write_cache = NULL;
8249 }
8250
8251 if (write_size > 0) {
8252 pbuf->write_cache = (char *) M_MALLOC(write_size);
8253 if (pbuf->write_cache == NULL) {
8254 pbuf->write_cache_size = 0;
8255 pbuf->write_cache_rp = 0;
8256 pbuf->write_cache_wp = 0;
8257 pbuf->write_cache_mutex.unlock();
8258 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);
8259 return BM_NO_MEMORY;
8260 }
8261 }
8262
8263 pbuf->write_cache_size = write_size;
8264 pbuf->write_cache_rp = 0;
8265 pbuf->write_cache_wp = 0;
8266
8267 pbuf->write_cache_mutex.unlock();
8268 }
8269#endif /* LOCAL_ROUTINES */
8270
8271 return BM_SUCCESS;
8272}
8273
8274/********************************************************************/
8301INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
8302{
8303 event_header->event_id = event_id;
8304 event_header->trigger_mask = trigger_mask;
8305 event_header->data_size = data_size;
8306 event_header->time_stamp = ss_time();
8307 event_header->serial_number = serial;
8308
8309 return BM_SUCCESS;
8310}
8311
8312INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
8313{
8314 static std::mutex mutex;
8315
8316 event_header->event_id = event_id;
8317 event_header->trigger_mask = trigger_mask;
8318 event_header->data_size = data_size;
8319 event_header->time_stamp = ss_time();
8320 {
8321 std::lock_guard<std::mutex> lock(mutex);
8322 event_header->serial_number = *serial;
8323 *serial = *serial + 1;
8324 // implicit unlock
8325 }
8326
8327 return BM_SUCCESS;
8328}
8329
8331#ifndef DOXYGEN_SHOULD_SKIP_THIS
8332
8333/********************************************************************/
8334INT bm_add_event_request(INT buffer_handle, short int event_id,
8335 short int trigger_mask,
8336 INT sampling_type,
8337 EVENT_HANDLER *func,
8338 INT request_id)
8339/********************************************************************\
8340
8341 Routine: bm_add_event_request
8342
8343 Purpose: Place a request for a specific event type in the client
8344 structure of the buffer refereced by buffer_handle.
8345
8346 Input:
8347 INT buffer_handle Handle to the buffer where the re-
8348 quest should be placed in
8349
8350 short int event_id Event ID \
8351 short int trigger_mask Trigger mask / Event specification
8352
8353 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8354
8355
8356 Note: to request all types of events, use
8357 event_id = 0 (all others should be !=0 !)
8358 trigger_mask = TRIGGER_ALL
8359 sampling_typ = GET_ALL
8360
8361
8362 void *func Callback function
8363 INT request_id Request id (unique number assigned
8364 by bm_request_event)
8365
8366 Output:
8367 none
8368
8369 Function value:
8370 BM_SUCCESS Successful completion
8371 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8372 MIDAS.H should be increased.
8373 BM_INVALID_HANDLE Buffer handle is invalid
8374 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8375 RPC_NET_ERROR Network error
8376
8377\********************************************************************/
8378{
8379 if (rpc_is_remote())
8380 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8381 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8382
8383#ifdef LOCAL_ROUTINES
8384 {
8385 int status = 0;
8386
8387 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8388
8389 if (!pbuf)
8390 return status;
8391
8392 /* lock buffer */
8393 bm_lock_buffer_guard pbuf_guard(pbuf);
8394
8395 if (!pbuf_guard.is_locked())
8396 return pbuf_guard.get_status();
8397
8398 /* avoid callback/non callback requests */
8399 if (func == NULL && pbuf->callback) {
8400 pbuf_guard.unlock(); // unlock before cm_msg()
8401 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8402 return BM_INVALID_MIXING;
8403 }
8404
8405 /* do not allow GET_RECENT with nonzero cache size */
8406 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8407 pbuf_guard.unlock(); // unlock before cm_msg()
8408 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8409 return BM_INVALID_PARAM;
8410 }
8411
8412 /* get a pointer to the proper client structure */
8413 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8414
8415 /* look for a empty request entry */
8416 int i;
8417 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8418 if (!pclient->event_request[i].valid)
8419 break;
8420
8421 if (i == MAX_EVENT_REQUESTS) {
8422 // implicit unlock
8423 return BM_NO_MEMORY;
8424 }
8425
8426 /* setup event_request structure */
8427 pclient->event_request[i].id = request_id;
8428 pclient->event_request[i].valid = TRUE;
8429 pclient->event_request[i].event_id = event_id;
8431 pclient->event_request[i].sampling_type = sampling_type;
8432
8433 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8434
8435 pbuf->get_all_flag = pclient->all_flag;
8436
8437 /* set callback flag in buffer structure */
8438 if (func != NULL)
8439 pbuf->callback = TRUE;
8440
8441 /*
8442 Save the index of the last request in the list so that later only the
8443 requests 0..max_request_index-1 have to be searched through.
8444 */
8445
8446 if (i + 1 > pclient->max_request_index)
8447 pclient->max_request_index = i + 1;
8448 }
8449#endif /* LOCAL_ROUTINES */
8450
8451 return BM_SUCCESS;
8452}
8453
8455#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8456
8457/********************************************************************/
8485INT bm_request_event(HNDLE buffer_handle, short int event_id,
8486 short int trigger_mask,
8487 INT sampling_type, HNDLE *request_id,
8488 EVENT_HANDLER *func)
8489{
8490 assert(request_id != NULL);
8491
8492 EventRequest r;
8493 r.buffer_handle = buffer_handle;
8494 r.event_id = event_id;
8496 r.dispatcher = func;
8497
8498 {
8499 std::lock_guard<std::mutex> guard(_request_list_mutex);
8500
8501 bool found = false;
8502
8503 // find deleted entry
8504 for (size_t i = 0; i < _request_list.size(); i++) {
8505 if (_request_list[i].buffer_handle == 0) {
8506 _request_list[i] = r;
8507 *request_id = i;
8508 found = true;
8509 break;
8510 }
8511 }
8512
8513 if (!found) { // not found
8514 *request_id = _request_list.size();
8515 _request_list.push_back(r);
8516 }
8517
8518 // implicit unlock()
8519 }
8520
8521 /* add request in buffer structure */
8522 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8523 if (status != BM_SUCCESS)
8524 return status;
8525
8526 return BM_SUCCESS;
8527}
8528
8529/********************************************************************/
8538INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8539 if (rpc_is_remote())
8540 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8541
8542#ifdef LOCAL_ROUTINES
8543 {
8544 int status = 0;
8545
8546 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8547
8548 if (!pbuf)
8549 return status;
8550
8551 /* lock buffer */
8552 bm_lock_buffer_guard pbuf_guard(pbuf);
8553
8554 if (!pbuf_guard.is_locked())
8555 return pbuf_guard.get_status();
8556
8557 INT i, deleted;
8558
8559 /* get a pointer to the proper client structure */
8560 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8561
8562 /* check all requests and set to zero if matching */
8563 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8564 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8565 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8566 deleted++;
8567 }
8568
8569 /* calculate new max_request_index entry */
8570 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8571 if (pclient->event_request[i].valid)
8572 break;
8573
8574 pclient->max_request_index = i + 1;
8575
8576 /* calculate new all_flag */
8577 pclient->all_flag = FALSE;
8578
8579 for (i = 0; i < pclient->max_request_index; i++)
8580 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8581 pclient->all_flag = TRUE;
8582 break;
8583 }
8584
8585 pbuf->get_all_flag = pclient->all_flag;
8586
8587 if (!deleted)
8588 return BM_NOT_FOUND;
8589 }
8590#endif /* LOCAL_ROUTINES */
8591
8592 return BM_SUCCESS;
8593}
8594
8595/********************************************************************/
8605{
8606 _request_list_mutex.lock();
8607
8608 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8609 _request_list_mutex.unlock();
8610 return BM_INVALID_HANDLE;
8611 }
8612
8613 int buffer_handle = _request_list[request_id].buffer_handle;
8614
8615 _request_list[request_id].clear();
8616
8617 _request_list_mutex.unlock();
8618
8619 /* remove request entry from buffer */
8620 return bm_remove_event_request(buffer_handle, request_id);
8621}
8622
8623#if 0 // currently not used
8624static void bm_show_pointers(const BUFFER_HEADER * pheader)
8625{
8626 int i;
8627 const BUFFER_CLIENT *pclient;
8628
8629 pclient = pheader->client;
8630
8631 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8632 pheader->write_pointer, pheader->size);
8633 for (i = 0; i < pheader->max_client_index; i++)
8634 if (pclient[i].pid) {
8635 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8636 }
8637
8638 printf("done\n");
8639}
8640#endif
8641
8643 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8644 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8645
8646 if (pheader->read_pointer <= pheader->write_pointer) {
8647
8648 if (pclient->read_pointer < pheader->read_pointer) {
8649 cm_msg(MINFO, "bm_validate_client_pointers",
8650 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8651 pclient->name,
8652 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8653
8654 pclient->read_pointer = pheader->read_pointer;
8655 }
8656
8657 if (pclient->read_pointer > pheader->write_pointer) {
8658 cm_msg(MINFO, "bm_validate_client_pointers",
8659 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8660 pclient->name,
8661 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8662
8663 pclient->read_pointer = pheader->write_pointer;
8664 }
8665
8666 } else {
8667
8668 if (pclient->read_pointer < 0) {
8669 cm_msg(MINFO, "bm_validate_client_pointers",
8670 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8671 pclient->name,
8672 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8673
8674 pclient->read_pointer = pheader->read_pointer;
8675 }
8676
8677 if (pclient->read_pointer >= pheader->size) {
8678 cm_msg(MINFO, "bm_validate_client_pointers",
8679 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8680 pclient->name,
8681 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8682
8683 pclient->read_pointer = pheader->read_pointer;
8684 }
8685
8686 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8687 cm_msg(MINFO, "bm_validate_client_pointers",
8688 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8689 pclient->name,
8690 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8691
8692 pclient->read_pointer = pheader->read_pointer;
8693 }
8694 }
8695}
8696
8697#if 0 // currently not used
8698static void bm_validate_pointers(BUFFER_HEADER * pheader)
8699{
8700 BUFFER_CLIENT *pclient = pheader->client;
8701 int i;
8702
8703 for (i = 0; i < pheader->max_client_index; i++)
8704 if (pclient[i].pid) {
8705 bm_validate_client_pointers(pheader, &pclient[i]);
8706 }
8707}
8708#endif
8709
8710//
8711// Buffer pointers
8712//
8713// normal:
8714//
8715// zero -->
8716// ... free space
8717// read_pointer -->
8718// client1 rp -->
8719// client2 rp -->
8720// ... buffered data
8721// write_pointer -->
8722// ... free space
8723// pheader->size -->
8724//
8725// inverted:
8726//
8727// zero -->
8728// client3 rp -->
8729// ... buffered data
8730// client4 rp -->
8731// write_pointer -->
8732// ... free space
8733// read_pointer -->
8734// client1 rp -->
8735// client2 rp -->
8736// ... buffered data
8737// pheader->size -->
8738//
8739
8740static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
8741 assert(caller_name);
8742
8743 /* calculate global read pointer as "minimum" of client read pointers */
8744 int min_rp = pheader->write_pointer;
8745
8746 int i;
8747 for (i = 0; i < pheader->max_client_index; i++) {
8748 BUFFER_CLIENT *pc = pheader->client + i;
8749 if (pc->pid) {
8751
8752#if 0
8753 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8754 pheader->name,
8755 pheader->read_pointer,
8756 pheader->write_pointer,
8757 pheader->size,
8758 min_rp,
8759 pc->name,
8760 pc->read_pointer);
8761#endif
8762
8763 if (pheader->read_pointer <= pheader->write_pointer) {
8764 // normal pointers
8765 if (pc->read_pointer < min_rp)
8766 min_rp = pc->read_pointer;
8767 } else {
8768 // inverted pointers
8769 if (pc->read_pointer <= pheader->write_pointer) {
8770 // clients 3 and 4
8771 if (pc->read_pointer < min_rp)
8772 min_rp = pc->read_pointer;
8773 } else {
8774 // clients 1 and 2
8775 int xptr = pc->read_pointer - pheader->size;
8776 if (xptr < min_rp)
8777 min_rp = xptr;
8778 }
8779 }
8780 }
8781 }
8782
8783 if (min_rp < 0)
8784 min_rp += pheader->size;
8785
8786 assert(min_rp >= 0);
8787 assert(min_rp < pheader->size);
8788
8789 if (min_rp == pheader->read_pointer) {
8790 return FALSE;
8791 }
8792
8793#if 0
8794 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8795 pheader->name,
8796 pheader->read_pointer,
8797 pheader->write_pointer,
8798 pheader->size,
8799 min_rp);
8800#endif
8801
8802 pheader->read_pointer = min_rp;
8803
8804 return TRUE;
8805}
8806
8807static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8808 int i;
8809 int have_get_all_requests = 0;
8810
8811 for (i = 0; i < pc->max_request_index; i++)
8812 if (pc->event_request[i].valid)
8813 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8814
8815 /* only GET_ALL requests actually free space in the event buffer */
8816 if (!have_get_all_requests)
8817 return;
8818
8819 /*
8820 If read pointer has been changed, it may have freed up some space
8821 for waiting producers. So check if free space is now more than 50%
8822 of the buffer size and wake waiting producers.
8823 */
8824
8825 int free_space = pc->read_pointer - pheader->write_pointer;
8826 if (free_space <= 0)
8827 free_space += pheader->size;
8828
8829 if (free_space >= pheader->size * 0.5) {
8830 for (i = 0; i < pheader->max_client_index; i++) {
8831 const BUFFER_CLIENT *pc = pheader->client + i;
8832 if (pc->pid && pc->write_wait) {
8833 BOOL send_wakeup = (pc->write_wait < free_space);
8834 //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);
8835 if (send_wakeup) {
8836 ss_resume(pc->port, "B ");
8837 }
8838 }
8839 }
8840 }
8841}
8842
8843static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8844{
8845 _request_list_mutex.lock();
8846 bool locked = true;
8847 size_t n = _request_list.size();
8848 /* call dispatcher */
8849 for (size_t i = 0; i < n; i++) {
8850 if (!locked) {
8851 _request_list_mutex.lock();
8852 locked = true;
8853 }
8855 if (r.buffer_handle != buffer_handle)
8856 continue;
8857 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8858 continue;
8859 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8860 _request_list_mutex.unlock();
8861 locked = false;
8862 /* if event is fragmented, call defragmenter */
8863 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))) {
8864 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8865 } else {
8866 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8867 }
8868 }
8869 if (locked)
8870 _request_list_mutex.unlock();
8871}
8872
8873#ifdef LOCAL_ROUTINES
8874
8875static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8876 /* increment read cache read pointer */
8877 pbuf->read_cache_rp += total_size;
8878
8879 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8880 pbuf->read_cache_rp = 0;
8881 pbuf->read_cache_wp = 0;
8882 }
8883}
8884
8885static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8886{
8887 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8888 return FALSE;
8889
8890 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8891 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8892 int total_size = ALIGN8(event_size);
8893
8894 if (ppevent)
8895 *ppevent = pevent;
8896 if (pevent_size)
8897 *pevent_size = event_size;
8898 if (ptotal_size)
8899 *ptotal_size = total_size;
8900
8901 return TRUE;
8902}
8903
8904//
8905// return values:
8906// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8907// BM_ASYNC_RETURN - buffer is empty
8908// BM_CORRUPTED - buffer is corrupted
8909//
8910
8911static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8912{
8913 if (pc->read_pointer == pheader->write_pointer) {
8914 /* no more events buffered for this client */
8915 if (!pc->read_wait) {
8916 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8917 pc->read_wait = TRUE;
8918 }
8919 return BM_ASYNC_RETURN;
8920 }
8921
8922 if (pc->read_wait) {
8923 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8924 pc->read_wait = FALSE;
8925 }
8926
8927 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8928 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);
8929 return BM_CORRUPTED;
8930 }
8931
8932 char *pdata = (char *) (pheader + 1);
8933
8934 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8935 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8936 int total_size = ALIGN8(event_size);
8937
8938 if ((total_size <= 0) || (total_size > pheader->size)) {
8939 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);
8940 return BM_CORRUPTED;
8941 }
8942
8943 assert(total_size > 0);
8944 assert(total_size <= pheader->size);
8945
8946 if (ppevent)
8947 *ppevent = pevent;
8948 if (pevent_size)
8949 *pevent_size = event_size;
8950 if (ptotal_size)
8951 *ptotal_size = total_size;
8952
8953 return BM_SUCCESS;
8954}
8955
8956static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8957{
8958 const char *pdata = (const char *) (pheader + 1);
8959
8960 if (rp + event_size <= pheader->size) {
8961 /* copy event to cache */
8962 memcpy(buf, pdata + rp, event_size);
8963 } else {
8964 /* event is splitted */
8965 int size = pheader->size - rp;
8966 memcpy(buf, pdata + rp, size);
8967 memcpy(buf + size, pdata, event_size - size);
8968 }
8969}
8970
8971static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8972{
8973 const char *pdata = (const char *) (pheader + 1);
8974
8975 if (rp + event_size <= pheader->size) {
8976 /* copy event to cache */
8977 vecptr->assign(pdata + rp, pdata + rp + event_size);
8978 } else {
8979 /* event is splitted */
8980 int size = pheader->size - rp;
8981 vecptr->assign(pdata + rp, pdata + rp + size);
8982 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8983 }
8984}
8985
8986static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8987
8988 BOOL is_requested = FALSE;
8989 int i;
8990 for (i = 0; i < pc->max_request_index; i++) {
8991 const EVENT_REQUEST *prequest = pc->event_request + i;
8992 if (prequest->valid) {
8993 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8994 /* check if this is a recent event */
8995 if (prequest->sampling_type == GET_RECENT) {
8996 if (ss_time() - pevent->time_stamp > 1) {
8997 /* skip that event */
8998 continue;
8999 }
9000 }
9001
9002 is_requested = TRUE;
9003 break;
9004 }
9005 }
9006 }
9007 return is_requested;
9008}
9009
9010static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
9011
9012static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
9013{
9014 BUFFER* pbuf = pbuf_guard.get_pbuf();
9015 BUFFER_HEADER* pheader = pbuf->buffer_header;
9016 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9017 BOOL need_wakeup = FALSE;
9018
9019 //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);
9020
9021 /* loop over all events in the buffer */
9022
9023 while (1) {
9024 EVENT_HEADER *pevent = NULL;
9025 int event_size = 3; // poison value
9026 int total_size = 3; // poison value
9027
9028 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9029 if (status == BM_CORRUPTED) {
9030 return status;
9031 } else if (status != BM_SUCCESS) {
9032 /* event buffer is empty */
9033 if (timeout_msec == BM_NO_WAIT) {
9034 if (need_wakeup)
9035 bm_wakeup_producers_locked(pheader, pc);
9036 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9037 // read cache is empty
9038 return BM_ASYNC_RETURN;
9039 }
9040 return BM_SUCCESS;
9041 }
9042
9043 int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
9044
9045 if (status != BM_SUCCESS) {
9046 // we only come here with SS_ABORT & co
9047 return status;
9048 }
9049
9050 // make sure we wait for new event only once
9051 timeout_msec = BM_NO_WAIT;
9052 // go back to bm_peek_buffer_locked
9053 continue;
9054 }
9055
9056 /* loop over all requests: if this event matches a request,
9057 * copy it to the read cache */
9058
9059 BOOL is_requested = bm_check_requests(pc, pevent);
9060
9061 if (is_requested) {
9062 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9063 /* read cache is full */
9064 if (need_wakeup)
9065 bm_wakeup_producers_locked(pheader, pc);
9066 return BM_SUCCESS;
9067 }
9068
9070
9071 pbuf->read_cache_wp += total_size;
9072
9073 /* update statistics */
9074 pheader->num_out_events++;
9075 pbuf->count_read++;
9076 pbuf->bytes_read += event_size;
9077 }
9078
9079 /* shift read pointer */
9080
9081 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9082 pc->read_pointer = new_read_pointer;
9083
9084 need_wakeup = TRUE;
9085 }
9086 /* NOT REACHED */
9087}
9088
9089static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9090 /* now convert event header */
9091 if (convert_flags) {
9092 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9093 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9094 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9095 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9096 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9097 }
9098}
9099
9100static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
9101{
9102 // return values:
9103 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9104 // BM_CORRUPTED - shared memory is corrupted
9105 // BM_NO_MEMORY - asked for more than buffer size
9106 // BM_ASYNC_RETURN - timeout waiting for free space
9107 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9108 // SS_ABORT - we are told to shutdown (locks releases)
9109
9110 int status;
9111 BUFFER* pbuf = pbuf_guard.get_pbuf();
9112 BUFFER_HEADER *pheader = pbuf->buffer_header;
9113 char *pdata = (char *) (pheader + 1);
9114
9115 /* make sure the buffer never completely full:
9116 * read pointer and write pointer would coincide
9117 * and the code cannot tell if it means the
9118 * buffer is 100% full or 100% empty. It will explode
9119 * or lose events */
9120 requested_space += 100;
9121
9122 if (requested_space >= pheader->size)
9123 return BM_NO_MEMORY;
9124
9125 DWORD time_start = ss_millitime();
9126 DWORD time_end = time_start + timeout_msec;
9127
9128 //DWORD blocking_time = 0;
9129 //int blocking_loops = 0;
9130 int blocking_client_index = -1;
9131 char blocking_client_name[NAME_LENGTH];
9132 blocking_client_name[0] = 0;
9133
9134 while (1) {
9135 while (1) {
9136 /* check if enough space in buffer */
9137
9138 int free = pheader->read_pointer - pheader->write_pointer;
9139 if (free <= 0)
9140 free += pheader->size;
9141
9142 //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);
9143
9144 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9145 //if (blocking_loops) {
9146 // DWORD wait_time = ss_millitime() - blocking_time;
9147 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9148 //}
9149
9150 if (pbuf->wait_start_time != 0) {
9151 DWORD now = ss_millitime();
9152 DWORD wait_time = now - pbuf->wait_start_time;
9153 pbuf->time_write_wait += wait_time;
9154 pbuf->wait_start_time = 0;
9155 int iclient = pbuf->wait_client_index;
9156 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9157 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9158 pbuf->client_count_write_wait[iclient] += 1;
9159 pbuf->client_time_write_wait[iclient] += wait_time;
9160 }
9161 }
9162
9163 //if (blocking_loops > 0) {
9164 // 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);
9165 //}
9166
9167 return BM_SUCCESS;
9168 }
9169
9170 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9171 cm_msg(MERROR, "bm_wait_for_free_space",
9172 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9173 pheader->name,
9174 pheader->read_pointer,
9175 pheader->write_pointer,
9176 pheader->size,
9177 free,
9178 requested_space);
9179 return BM_CORRUPTED;
9180 }
9181
9182 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9183 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9184 int total_size = ALIGN8(event_size);
9185
9186#if 0
9187 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);
9188#endif
9189
9190 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9191 cm_msg(MERROR, "bm_wait_for_free_space",
9192 "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",
9193 pheader->name,
9194 pheader->read_pointer,
9195 pheader->write_pointer,
9196 pheader->size,
9197 free,
9198 requested_space,
9199 pevent->data_size,
9200 event_size,
9201 total_size);
9202 return BM_CORRUPTED;
9203 }
9204
9205 int blocking_client = -1;
9206
9207 int i;
9208 for (i = 0; i < pheader->max_client_index; i++) {
9209 BUFFER_CLIENT *pc = pheader->client + i;
9210 if (pc->pid) {
9211 if (pc->read_pointer == pheader->read_pointer) {
9212 /*
9213 First assume that the client with the "minimum" read pointer
9214 is not really blocking due to a GET_ALL request.
9215 */
9216 BOOL blocking = FALSE;
9217 //int blocking_request_id = -1;
9218
9219 int j;
9220 for (j = 0; j < pc->max_request_index; j++) {
9221 const EVENT_REQUEST *prequest = pc->event_request + j;
9222 if (prequest->valid
9223 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9224 if (prequest->sampling_type & GET_ALL) {
9225 blocking = TRUE;
9226 //blocking_request_id = prequest->id;
9227 break;
9228 }
9229 }
9230 }
9231
9232 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9233
9234 if (blocking) {
9235 blocking_client = i;
9236 break;
9237 }
9238
9239 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9240 }
9241 }
9242 } /* client loop */
9243
9244 if (blocking_client >= 0) {
9245 blocking_client_index = blocking_client;
9246 mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
9247 //if (!blocking_time) {
9248 // blocking_time = ss_millitime();
9249 //}
9250
9251 //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);
9252
9253 // from this "break" we go into timeout check and sleep/wait.
9254 break;
9255 }
9256
9257 /* no blocking clients. move the read pointer and again check for free space */
9258
9259 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9260
9261 if (!moved) {
9262 cm_msg(MERROR, "bm_wait_for_free_space",
9263 "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",
9264 pheader->name,
9265 pheader->read_pointer,
9266 pheader->write_pointer,
9267 pheader->size,
9268 free,
9269 requested_space);
9270 return BM_CORRUPTED;
9271 }
9272
9273 /* we freed one event, loop back to the check for free space */
9274 }
9275
9276 //blocking_loops++;
9277
9278 /* at least one client is blocking */
9279
9280 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9281 pc->write_wait = requested_space;
9282
9283 if (pbuf->wait_start_time == 0) {
9284 pbuf->wait_start_time = ss_millitime();
9285 pbuf->count_write_wait++;
9286 if (requested_space > pbuf->max_requested_space)
9287 pbuf->max_requested_space = requested_space;
9288 pbuf->wait_client_index = blocking_client_index;
9289 }
9290
9291 DWORD now = ss_millitime();
9292
9293 //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);
9294
9295 int sleep_time_msec = 1000;
9296
9297 if (timeout_msec == BM_WAIT) {
9298 // wait forever
9299 } else if (timeout_msec == BM_NO_WAIT) {
9300 // no wait
9301 return BM_ASYNC_RETURN;
9302 } else {
9303 // check timeout
9304 if (now >= time_end) {
9305 // timeout!
9306 return BM_ASYNC_RETURN;
9307 }
9308
9309 sleep_time_msec = time_end - now;
9310
9311 if (sleep_time_msec <= 0) {
9312 sleep_time_msec = 10;
9313 } else if (sleep_time_msec > 1000) {
9314 sleep_time_msec = 1000;
9315 }
9316 }
9317
9319
9320 /* before waiting, unlock everything in the correct order */
9321
9322 pbuf_guard.unlock();
9323
9324 if (unlock_write_cache)
9325 pbuf->write_cache_mutex.unlock();
9326
9327 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9328
9329#ifdef DEBUG_MSG
9330 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9331#endif
9332
9334 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9335 //if (idx >= 0)
9336 // pheader->client[idx].write_wait = requested_space;
9337
9338 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9339
9340 status = ss_suspend(sleep_time_msec, MSG_BM);
9341
9342 /* we are told to shutdown */
9343 if (status == SS_ABORT) {
9344 // NB: buffer is locked!
9345 return SS_ABORT;
9346 }
9347
9348 /* make sure we do sleep in this loop:
9349 * if we are the mserver receiving data on the event
9350 * socket and the data buffer is full, ss_suspend() will
9351 * never sleep: it will detect data on the event channel,
9352 * call rpc_server_receive() (recursively, we already *are* in
9353 * rpc_server_receive()) and return without sleeping. Result
9354 * is a busy loop waiting for free space in data buffer */
9355
9356 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9357 * the event socket, and should sleep now, so this sleep below
9358 * maybe is not needed now. but for safety, I keep it. K.O. */
9359
9360 if (status != SS_TIMEOUT) {
9361 //printf("ss_suspend: status %d\n", status);
9362 ss_sleep(1);
9363 }
9364
9365 /* we may be stuck in this loop for an arbitrary long time,
9366 * depending on how other buffer clients read the accumulated data
9367 * so we should update all the timeouts & etc. K.O. */
9368
9370
9371 /* lock things again in the correct order */
9372
9373 if (unlock_write_cache) {
9375
9376 if (status != BM_SUCCESS) {
9377 // bail out with all locks released
9378 return status;
9379 }
9380 }
9381
9382 if (!pbuf_guard.relock()) {
9383 if (unlock_write_cache) {
9384 pbuf->write_cache_mutex.unlock();
9385 }
9386
9387 // bail out with all locks released
9388 return pbuf_guard.get_status();
9389 }
9390
9391 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9392 pc = bm_get_my_client_locked(pbuf_guard);
9393
9394 pc->write_wait = 0;
9395
9397 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9398 //if (idx >= 0)
9399 // pheader->client[idx].write_wait = 0;
9400 //else {
9401 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9402 // status = SS_ABORT;
9403 //}
9404
9405#ifdef DEBUG_MSG
9406 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);
9407#endif
9408
9409 }
9410}
9411
9412static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
9413{
9414 BUFFER* pbuf = pbuf_guard.get_pbuf();
9415 BUFFER_HEADER* pheader = pbuf->buffer_header;
9416
9417 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9418
9419 if (pc->read_pointer != pheader->write_pointer) {
9420 // buffer has data
9421 return BM_SUCCESS;
9422 }
9423
9424 if (timeout_msec == BM_NO_WAIT) {
9425 /* event buffer is empty and we are told to not wait */
9426 if (!pc->read_wait) {
9427 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9428 pc->read_wait = TRUE;
9429 }
9430 return BM_ASYNC_RETURN;
9431 }
9432
9433 DWORD time_start = ss_millitime();
9434 DWORD time_wait = time_start + timeout_msec;
9435 DWORD sleep_time = 1000;
9436 if (timeout_msec == BM_NO_WAIT) {
9437 // default sleep time
9438 } else if (timeout_msec == BM_WAIT) {
9439 // default sleep time
9440 } else {
9441 if (sleep_time > (DWORD)timeout_msec)
9442 sleep_time = timeout_msec;
9443 }
9444
9445 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9446
9447 while (pc->read_pointer == pheader->write_pointer) {
9448 /* wait until there is data in the buffer (write pointer moves) */
9449
9450 if (!pc->read_wait) {
9451 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9452 pc->read_wait = TRUE;
9453 }
9454
9456
9458
9459 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9460
9461 pbuf_guard.unlock();
9462
9463 if (unlock_read_cache)
9464 pbuf->read_cache_mutex.unlock();
9465
9466 int status = ss_suspend(sleep_time, MSG_BM);
9467
9468 if (timeout_msec == BM_NO_WAIT) {
9469 // return immediately
9470 } else if (timeout_msec == BM_WAIT) {
9471 // wait forever
9472 } else {
9473 DWORD now = ss_millitime();
9474 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9475 if (now >= time_wait) {
9476 timeout_msec = BM_NO_WAIT; // cause immediate return
9477 } else {
9478 sleep_time = time_wait - now;
9479 if (sleep_time > 1000)
9480 sleep_time = 1000;
9481 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9482 }
9483 }
9484
9485 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9486
9487 if (unlock_read_cache) {
9489 if (status != BM_SUCCESS) {
9490 // bail out with all locks released
9491 return status;
9492 }
9493 }
9494
9495 if (!pbuf_guard.relock()) {
9496 if (unlock_read_cache) {
9497 pbuf->read_cache_mutex.unlock();
9498 }
9499 // bail out with all locks released
9500 return pbuf_guard.get_status();
9501 }
9502
9503 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9504 * because we may have been removed from the buffer by bm_cleanup() & co
9505 * due to a timeout or whatever. */
9506 pc = bm_get_my_client_locked(pbuf_guard);
9507
9508 /* return if TCP connection broken */
9509 if (status == SS_ABORT)
9510 return SS_ABORT;
9511
9512 if (timeout_msec == BM_NO_WAIT)
9513 return BM_ASYNC_RETURN;
9514 }
9515
9516 if (pc->read_wait) {
9517 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9518 pc->read_wait = FALSE;
9519 }
9520
9521 return BM_SUCCESS;
9522}
9523
9524static 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)
9525{
9526 char *pdata = (char *) (pheader + 1);
9527
9528 //int old_write_pointer = pheader->write_pointer;
9529
9530 /* new event fits into the remaining space? */
9531 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9532 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9533 char* wptr = pdata + pheader->write_pointer;
9534 for (int i=0; i<sg_n; i++) {
9535 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9536 memcpy(wptr, sg_ptr[i], sg_len[i]);
9537 wptr += sg_len[i];
9538 }
9539 pheader->write_pointer = pheader->write_pointer + total_size;
9540 assert(pheader->write_pointer <= pheader->size);
9541 /* remaining space is smaller than size of an event header? */
9542 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9543 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9544 // equal to the event header size, we will write the next event header here,
9545 // then wrap the pointer and write the event data at the beginning of the buffer.
9546 //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);
9547 pheader->write_pointer = 0;
9548 }
9549 } else {
9550 /* split event */
9551 size_t size = pheader->size - pheader->write_pointer;
9552
9553 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9554
9555 //memcpy(pdata + pheader->write_pointer, pevent, size);
9556 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9557
9558 char* wptr = pdata + pheader->write_pointer;
9559 size_t count = 0;
9560
9561 // copy first part
9562
9563 int i = 0;
9564 for (; i<sg_n; i++) {
9565 if (count + sg_len[i] > size)
9566 break;
9567 memcpy(wptr, sg_ptr[i], sg_len[i]);
9568 wptr += sg_len[i];
9569 count += sg_len[i];
9570 }
9571
9572 //printf("wptr %d, count %d\n", wptr-pdata, count);
9573
9574 // split segment
9575
9576 size_t first = size - count;
9577 size_t second = sg_len[i] - first;
9578 assert(first + second == sg_len[i]);
9579 assert(count + first == size);
9580
9581 //printf("first %d, second %d\n", first, second);
9582
9583 memcpy(wptr, sg_ptr[i], first);
9584 wptr = pdata + 0;
9585 count += first;
9586 memcpy(wptr, sg_ptr[i] + first, second);
9587 wptr += second;
9588 count += second;
9589 i++;
9590
9591 // copy remaining
9592
9593 for (; i<sg_n; i++) {
9594 memcpy(wptr, sg_ptr[i], sg_len[i]);
9595 wptr += sg_len[i];
9596 count += sg_len[i];
9597 }
9598
9599 //printf("wptr %d, count %d\n", wptr-pdata, count);
9600
9601 //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);
9602
9603 pheader->write_pointer = total_size - size;
9604 }
9605
9606 //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);
9607}
9608
9610 if (pc->pid) {
9611 int j;
9612 for (j = 0; j < pc->max_request_index; j++) {
9613 const EVENT_REQUEST *prequest = pc->event_request + j;
9614 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9615 return prequest->id;
9616 }
9617 }
9618 }
9619
9620 return -1;
9621}
9622
9623static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9624 if (request_id >= 0) {
9625 /* if that client has a request and is suspended, wake it up */
9626 if (pc->read_wait) {
9627 char str[80];
9628 sprintf(str, "B %s %d", pheader->name, request_id);
9629 ss_resume(pc->port, str);
9630 //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);
9631 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9632 pc->read_wait = FALSE;
9633 }
9634 }
9635}
9636
9637#endif // LOCAL_ROUTINES
9638
9639#if 0
9640INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9641{
9642 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9643
9644 DWORD time_start = ss_millitime();
9645 DWORD time_end = time_start + timeout_msec;
9646
9647 int xtimeout_msec = timeout_msec;
9648
9649 while (1) {
9650 if (timeout_msec == BM_WAIT) {
9651 xtimeout_msec = 1000;
9652 } else if (timeout_msec == BM_NO_WAIT) {
9653 xtimeout_msec = BM_NO_WAIT;
9654 } else {
9655 if (xtimeout_msec > 1000) {
9656 xtimeout_msec = 1000;
9657 }
9658 }
9659
9660 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9661
9662 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9663
9664 if (status == BM_ASYNC_RETURN) {
9665 if (timeout_msec == BM_WAIT) {
9666 // BM_WAIT means wait forever
9667 continue;
9668 } else if (timeout_msec == BM_NO_WAIT) {
9669 // BM_NO_WAIT means do not wait
9670 return status;
9671 } else {
9672 DWORD now = ss_millitime();
9673 if (now >= time_end) {
9674 // timeout, return BM_ASYNC_RETURN
9675 return status;
9676 }
9677
9678 DWORD remain = time_end - now;
9679
9680 if (remain < xtimeout_msec) {
9681 xtimeout_msec = remain;
9682 }
9683
9684 // keep asking for event...
9685 continue;
9686 }
9687 } else if (status == BM_SUCCESS) {
9688 // success, return BM_SUCCESS
9689 return status;
9690 } else {
9691 // error
9692 return status;
9693 }
9694 }
9695}
9696#endif
9697
9698INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9699{
9700 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9701 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9702
9703 if (data_size == 0) {
9704 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9705 return BM_INVALID_SIZE;
9706 }
9707
9708 if (data_size > MAX_DATA_SIZE) {
9709 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);
9710 return BM_INVALID_SIZE;
9711 }
9712
9713 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9714
9715 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9716
9717 if (rpc_is_remote()) {
9718 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9719 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9720 } else {
9721 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9722 }
9723}
9724
9725int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9726{
9727 const char* cptr = event.data();
9728 size_t clen = event.size();
9729 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9730}
9731
9732int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9733{
9734 int sg_n = event.size();
9735 const char* sg_ptr[sg_n];
9736 size_t sg_len[sg_n];
9737 for (int i=0; i<sg_n; i++) {
9738 sg_ptr[i] = event[i].data();
9739 sg_len[i] = event[i].size();
9740 }
9741 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9742}
9743
9744#ifdef LOCAL_ROUTINES
9745static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
9746#endif
9747
9748/********************************************************************/
9798int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9799{
9800 if (rpc_is_remote())
9801 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9802
9803 if (sg_n < 1) {
9804 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9805 return BM_INVALID_SIZE;
9806 }
9807
9808 if (sg_ptr[0] == NULL) {
9809 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9810 return BM_INVALID_SIZE;
9811 }
9812
9813 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9814 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));
9815 return BM_INVALID_SIZE;
9816 }
9817
9818 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9819
9820 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9821 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9822
9823 if (data_size == 0) {
9824 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9825 return BM_INVALID_SIZE;
9826 }
9827
9828 if (data_size > MAX_DATA_SIZE) {
9829 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);
9830 return BM_INVALID_SIZE;
9831 }
9832
9833 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9834
9835 size_t count = 0;
9836 for (int i=0; i<sg_n; i++) {
9837 count += sg_len[i];
9838 }
9839
9840 if (count != event_size) {
9841 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);
9842 return BM_INVALID_SIZE;
9843 }
9844
9845 //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);
9846
9847#ifdef LOCAL_ROUTINES
9848 {
9849 int status = 0;
9850 const size_t total_size = ALIGN8(event_size);
9851
9852 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9853
9854 if (!pbuf)
9855 return status;
9856
9857 /* round up total_size to next DWORD boundary */
9858 //int total_size = ALIGN8(event_size);
9859
9860 /* check if write cache is enabled */
9861 if (pbuf->write_cache_size) {
9863
9864 if (status != BM_SUCCESS)
9865 return status;
9866
9867 /* check if write cache is enabled */
9868 if (pbuf->write_cache_size) {
9870 bool too_big = event_size > max_event_size;
9871
9872 //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);
9873
9874 /* if this event does not fit into the write cache, flush the write cache */
9875 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9876 //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);
9877
9878 bm_lock_buffer_guard pbuf_guard(pbuf);
9879
9880 if (!pbuf_guard.is_locked()) {
9881 pbuf->write_cache_mutex.unlock();
9882 return pbuf_guard.get_status();
9883 }
9884
9885 int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
9886
9887 if (pbuf_guard.is_locked()) {
9888 // check if bm_wait_for_free_space() failed to relock the buffer
9889 pbuf_guard.unlock();
9890 }
9891
9892 if (status != BM_SUCCESS) {
9893 pbuf->write_cache_mutex.unlock();
9894 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9895 if (status == BM_NO_MEMORY)
9896 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9897 return status;
9898 }
9899
9900 // write cache must be empty here
9901 assert(pbuf->write_cache_wp == 0);
9902 }
9903
9904 /* write this event into the write cache, if it is not too big and if it fits */
9905 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9906 //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);
9907
9908 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9909
9910 for (int i=0; i<sg_n; i++) {
9911 memcpy(wptr, sg_ptr[i], sg_len[i]);
9912 wptr += sg_len[i];
9913 }
9914
9915 pbuf->write_cache_wp += total_size;
9916
9917 pbuf->write_cache_mutex.unlock();
9918 return BM_SUCCESS;
9919 }
9920 }
9921
9922 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9923 pbuf->write_cache_mutex.unlock();
9924 }
9925
9926 /* we come here only for events that are too big to fit into the cache */
9927
9928 /* lock the buffer */
9929 bm_lock_buffer_guard pbuf_guard(pbuf);
9930
9931 if (!pbuf_guard.is_locked()) {
9932 return pbuf_guard.get_status();
9933 }
9934
9935 /* calculate some shorthands */
9936 BUFFER_HEADER *pheader = pbuf->buffer_header;
9937
9938#if 0
9940 if (status != BM_SUCCESS) {
9941 printf("bm_send_event: corrupted 111!\n");
9942 abort();
9943 }
9944#endif
9945
9946 /* check if buffer is large enough */
9947 if (total_size >= (size_t)pheader->size) {
9948 pbuf_guard.unlock(); // unlock before cm_msg()
9949 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9950 return BM_NO_MEMORY;
9951 }
9952
9953 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
9954
9955 if (status != BM_SUCCESS) {
9956 // implicit unlock
9957 return status;
9958 }
9959
9960#if 0
9962 if (status != BM_SUCCESS) {
9963 printf("bm_send_event: corrupted 222!\n");
9964 abort();
9965 }
9966#endif
9967
9968 int old_write_pointer = pheader->write_pointer;
9969
9970 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9971
9972 /* write pointer was incremented, but there should
9973 * always be some free space in the buffer and the
9974 * write pointer should never cacth up to the read pointer:
9975 * the rest of the code gets confused this happens (buffer 100% full)
9976 * as it is write_pointer == read_pointer can be either
9977 * 100% full or 100% empty. My solution: never fill
9978 * the buffer to 100% */
9979 assert(pheader->write_pointer != pheader->read_pointer);
9980
9981 /* send wake up messages to all clients that want this event */
9982 int i;
9983 for (i = 0; i < pheader->max_client_index; i++) {
9984 BUFFER_CLIENT *pc = pheader->client + i;
9985 int request_id = bm_find_first_request_locked(pc, pevent);
9986 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9987 }
9988
9989#if 0
9991 if (status != BM_SUCCESS) {
9992 printf("bm_send_event: corrupted 333!\n");
9993 abort();
9994 }
9995#endif
9996
9997 /* update statistics */
9998 pheader->num_in_events++;
9999 pbuf->count_sent += 1;
10000 pbuf->bytes_sent += total_size;
10001 }
10002#endif /* LOCAL_ROUTINES */
10003
10004 return BM_SUCCESS;
10005}
10006
10007static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
10008{
10009 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10010
10011 DWORD time_start = ss_millitime();
10012 DWORD time_end = time_start + timeout_msec;
10013 DWORD time_bombout = time_end;
10014
10015 if (timeout_msec < 10000)
10016 time_bombout = time_start + 10000; // 10 seconds
10017
10018 int xtimeout_msec = timeout_msec;
10019
10020 while (1) {
10021 if (timeout_msec == BM_WAIT) {
10022 xtimeout_msec = 1000;
10023 } else if (timeout_msec == BM_NO_WAIT) {
10024 xtimeout_msec = BM_NO_WAIT;
10025 } else {
10026 if (xtimeout_msec > 1000) {
10027 xtimeout_msec = 1000;
10028 }
10029 }
10030
10031 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10032
10033 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10034
10035 if (status == BM_ASYNC_RETURN) {
10036 if (timeout_msec == BM_WAIT) {
10037 DWORD now = ss_millitime();
10038 if (now >= time_bombout) {
10039 // timeout
10040 return BM_TIMEOUT;
10041 }
10042
10043 // BM_WAIT means wait forever
10044 continue;
10045 } else if (timeout_msec == BM_NO_WAIT) {
10046 // BM_NO_WAIT means do not wait
10047 return status;
10048 } else {
10049 DWORD now = ss_millitime();
10050 if (now >= time_end) {
10051 // timeout, return BM_ASYNC_RETURN
10052 return status;
10053 }
10054
10055 DWORD remain = time_end - now;
10056
10057 if (remain < (DWORD)xtimeout_msec) {
10058 xtimeout_msec = remain;
10059 }
10060
10061 if (now >= time_bombout) {
10062 // timeout
10063 return BM_TIMEOUT;
10064 }
10065
10066 // keep asking for event...
10067 continue;
10068 }
10069 } else if (status == BM_SUCCESS) {
10070 // success, return BM_SUCCESS
10071 return status;
10072 } else {
10073 // error
10074 return status;
10075 }
10076 }
10077}
10078
10079/********************************************************************/
10097#ifdef LOCAL_ROUTINES
10098static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
10099{
10100 // NB we come here with write cache locked and buffer locked.
10101
10102 {
10103 INT status = 0;
10104
10105 //printf("bm_flush_cache_locked!\n");
10106
10107 BUFFER* pbuf = pbuf_guard.get_pbuf();
10108 BUFFER_HEADER* pheader = pbuf->buffer_header;
10109
10110 //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);
10111
10112 int old_write_pointer = pheader->write_pointer;
10113
10114 int request_id[MAX_CLIENTS];
10115 for (int i = 0; i < pheader->max_client_index; i++) {
10116 request_id[i] = -1;
10117 }
10118
10119 size_t ask_rp = pbuf->write_cache_rp;
10120 size_t ask_wp = pbuf->write_cache_wp;
10121
10122 if (ask_wp == 0) { // nothing to do
10123 return BM_SUCCESS;
10124 }
10125
10126 if (ask_rp == ask_wp) { // nothing to do
10127 return BM_SUCCESS;
10128 }
10129
10130 assert(ask_rp < ask_wp);
10131
10132 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10133
10134 if (ask_free == 0) { // nothing to do
10135 return BM_SUCCESS;
10136 }
10137
10138#if 0
10140 if (status != BM_SUCCESS) {
10141 printf("bm_flush_cache: corrupted 111!\n");
10142 abort();
10143 }
10144#endif
10145
10146 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
10147
10148 if (status != BM_SUCCESS) {
10149 return status;
10150 }
10151
10152 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10153 //
10154 // wait_for_free_space() will sleep with all locks released,
10155 // during this time, another thread may call bm_send_event() that will
10156 // add one or more events to the write cache and after wait_for_free_space()
10157 // returns, size of data in cache will be bigger than the amount
10158 // of free space we requested. so we need to keep track of how
10159 // much data we write to the buffer and ask for more data
10160 // if we run short. This is the reason for the big loop
10161 // around wait_for_free_space(). We ask for slightly too little free
10162 // space to make sure all this code is always used and does work. K.O.
10163
10164 if (pbuf->write_cache_wp == 0) {
10165 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10166 return BM_SUCCESS;
10167 }
10168
10169 //size_t written = 0;
10170 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10171 /* loop over all events in cache */
10172
10173 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10174 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10175 size_t total_size = ALIGN8(event_size);
10176
10177#if 0
10178 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",
10179 int(pbuf->write_cache_size),
10180 int(pbuf->write_cache_wp),
10181 int(pbuf->write_cache_rp),
10182 int(pevent->data_size),
10183 int(event_size),
10184 int(total_size),
10185 int(ask_free),
10186 int(written));
10187#endif
10188
10189 // check for crazy event size
10190 assert(total_size >= sizeof(EVENT_HEADER));
10191 assert(total_size <= (size_t)pheader->size);
10192
10193 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10194
10195 /* update statistics */
10196 pheader->num_in_events++;
10197 pbuf->count_sent += 1;
10198 pbuf->bytes_sent += total_size;
10199
10200 /* see comment for the same code in bm_send_event().
10201 * We make sure the buffer is never 100% full */
10202 assert(pheader->write_pointer != pheader->read_pointer);
10203
10204 /* check if anybody has a request for this event */
10205 for (int i = 0; i < pheader->max_client_index; i++) {
10206 BUFFER_CLIENT *pc = pheader->client + i;
10207 int r = bm_find_first_request_locked(pc, pevent);
10208 if (r >= 0) {
10209 request_id[i] = r;
10210 }
10211 }
10212
10213 /* this loop does not loop forever because rp
10214 * is monotonously incremented here. write_cache_wp does
10215 * not change */
10216
10217 pbuf->write_cache_rp += total_size;
10218 //written += total_size;
10219
10220 assert(pbuf->write_cache_rp > 0);
10221 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10222 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10223 }
10224
10225 /* the write cache is now empty */
10226 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10227 pbuf->write_cache_wp = 0;
10228 pbuf->write_cache_rp = 0;
10229
10230 /* check which clients are waiting */
10231 for (int i = 0; i < pheader->max_client_index; i++) {
10232 BUFFER_CLIENT *pc = pheader->client + i;
10233 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10234 }
10235 }
10236
10237 return BM_SUCCESS;
10238}
10239
10240#endif /* LOCAL_ROUTINES */
10241
10242INT bm_flush_cache(int buffer_handle, int timeout_msec)
10243{
10244 if (rpc_is_remote()) {
10245 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10246 }
10247
10248#ifdef LOCAL_ROUTINES
10249 {
10250 INT status = 0;
10251
10252 //printf("bm_flush_cache!\n");
10253
10254 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10255
10256 if (!pbuf)
10257 return status;
10258
10259 if (pbuf->write_cache_size == 0)
10260 return BM_SUCCESS;
10261
10263
10264 if (status != BM_SUCCESS)
10265 return status;
10266
10267 /* check if anything needs to be flushed */
10268 if (pbuf->write_cache_wp == 0) {
10269 pbuf->write_cache_mutex.unlock();
10270 return BM_SUCCESS;
10271 }
10272
10273 /* lock the buffer */
10274 bm_lock_buffer_guard pbuf_guard(pbuf);
10275
10276 if (!pbuf_guard.is_locked())
10277 return pbuf_guard.get_status();
10278
10279 status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
10280
10281 /* unlock in correct order */
10282
10283 if (pbuf_guard.is_locked()) {
10284 // check if bm_wait_for_free_space() failed to relock the buffer
10285 pbuf_guard.unlock();
10286 }
10287
10288 pbuf->write_cache_mutex.unlock();
10289
10290 return status;
10291 }
10292#endif /* LOCAL_ROUTINES */
10293
10294 return BM_SUCCESS;
10295}
10296
10297#ifdef LOCAL_ROUTINES
10298
10299static 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) {
10301
10302 int max_size = 0;
10303 if (buf_size) {
10304 max_size = *buf_size;
10305 *buf_size = 0;
10306 }
10307
10308 //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);
10309
10310 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10311
10312 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10313
10314 /* look if there is anything in the cache */
10315 if (pbuf->read_cache_size > 0) {
10316
10318
10319 if (status != BM_SUCCESS)
10320 return status;
10321
10322 if (pbuf->read_cache_wp == 0) {
10323
10324 // lock buffer for the first time
10325
10326 if (!pbuf_guard.relock()) {
10327 pbuf->read_cache_mutex.unlock();
10328 return pbuf_guard.get_status();
10329 }
10330
10331 status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
10332 if (status != BM_SUCCESS) {
10333 // unlock in correct order
10334 if (pbuf_guard.is_locked()) {
10335 // check if bm_wait_for_more_events() failed to relock the buffer
10336 pbuf_guard.unlock();
10337 }
10338 pbuf->read_cache_mutex.unlock();
10339 return status;
10340 }
10341
10342 // buffer remains locked here
10343 }
10344 EVENT_HEADER *pevent;
10345 int event_size;
10346 int total_size;
10347 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10348 if (pbuf_guard.is_locked()) {
10349 // do not need to keep the event buffer locked
10350 // when reading from the read cache
10351 pbuf_guard.unlock();
10352 }
10353 //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);
10355 if (buf) {
10356 if (event_size > max_size) {
10357 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);
10358 event_size = max_size;
10360 }
10361
10362 memcpy(buf, pevent, event_size);
10363
10364 if (buf_size) {
10365 *buf_size = event_size;
10366 }
10367 if (convert_flags) {
10368 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10369 }
10370 } else if (bufptr) {
10371 *bufptr = malloc(event_size);
10372 memcpy(*bufptr, pevent, event_size);
10374 } else if (vecptr) {
10375 vecptr->resize(0);
10376 char* cptr = (char*)pevent;
10377 vecptr->assign(cptr, cptr+event_size);
10378 }
10379 bm_incr_read_cache_locked(pbuf, total_size);
10380 pbuf->read_cache_mutex.unlock();
10381 if (dispatch) {
10382 // FIXME need to protect currently dispatched event against
10383 // another thread overwriting it by refilling the read cache
10384 bm_dispatch_event(buffer_handle, pevent);
10385 return BM_MORE_EVENTS;
10386 }
10387 // buffer is unlocked here
10388 return status;
10389 }
10390 pbuf->read_cache_mutex.unlock();
10391 }
10392
10393 /* we come here if the read cache is disabled */
10394 /* we come here if the next event is too big to fit into the read cache */
10395
10396 if (!pbuf_guard.is_locked()) {
10397 if (!pbuf_guard.relock())
10398 return pbuf_guard.get_status();
10399 }
10400
10401 EVENT_HEADER *event_buffer = NULL;
10402
10403 BUFFER_HEADER *pheader = pbuf->buffer_header;
10404
10405 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
10406
10407 while (1) {
10408 /* loop over events in the event buffer */
10409
10410 status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
10411
10412 if (status != BM_SUCCESS) {
10413 // implicit unlock
10414 return status;
10415 }
10416
10417 /* check if event at current read pointer matches a request */
10418
10419 EVENT_HEADER *pevent;
10420 int event_size;
10421 int total_size;
10422
10423 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10424 if (status == BM_CORRUPTED) {
10425 // implicit unlock
10426 return status;
10427 } else if (status != BM_SUCCESS) {
10428 /* event buffer is empty */
10429 break;
10430 }
10431
10432 BOOL is_requested = bm_check_requests(pc, pevent);
10433
10434 if (is_requested) {
10435 //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);
10436
10438
10439 if (buf) {
10440 if (event_size > max_size) {
10441 cm_msg(MERROR, "bm_read_buffer",
10442 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10443 event_size, pheader->name);
10444 event_size = max_size;
10446 }
10447
10448 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10449
10450 if (buf_size) {
10451 *buf_size = event_size;
10452 }
10453
10454 if (convert_flags) {
10455 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10456 }
10457
10458 pbuf->count_read++;
10459 pbuf->bytes_read += event_size;
10460 } else if (dispatch || bufptr) {
10461 assert(event_buffer == NULL); // make sure we only come here once
10462 event_buffer = (EVENT_HEADER *) malloc(event_size);
10464 pbuf->count_read++;
10465 pbuf->bytes_read += event_size;
10466 } else if (vecptr) {
10467 bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
10468 pbuf->count_read++;
10469 pbuf->bytes_read += event_size;
10470 }
10471
10472 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10473 pc->read_pointer = new_read_pointer;
10474
10475 pheader->num_out_events++;
10476 /* exit loop over events */
10477 break;
10478 }
10479
10480 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10481 pc->read_pointer = new_read_pointer;
10482 pheader->num_out_events++;
10483 }
10484
10485 /*
10486 If read pointer has been changed, it may have freed up some space
10487 for waiting producers. So check if free space is now more than 50%
10488 of the buffer size and wake waiting producers.
10489 */
10490
10491 bm_wakeup_producers_locked(pheader, pc);
10492
10493 pbuf_guard.unlock();
10494
10495 if (dispatch && event_buffer) {
10496 bm_dispatch_event(buffer_handle, event_buffer);
10497 free(event_buffer);
10498 event_buffer = NULL;
10499 return BM_MORE_EVENTS;
10500 }
10501
10502 if (bufptr && event_buffer) {
10503 *bufptr = event_buffer;
10504 event_buffer = NULL;
10506 }
10507
10508 if (event_buffer) {
10509 free(event_buffer);
10510 event_buffer = NULL;
10511 }
10512
10513 return status;
10514}
10515
10516#endif
10517
10518static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10519{
10520 //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);
10521
10522 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10523
10524 void *xbuf = NULL;
10525 int xbuf_size = 0;
10526
10527 if (buf) {
10528 xbuf = buf;
10529 xbuf_size = *buf_size;
10530 } else if (ppevent) {
10531 *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
10532 xbuf_size = _bm_max_event_size;
10533 } else if (pvec) {
10534 pvec->resize(_bm_max_event_size);
10535 xbuf = pvec->data();
10536 xbuf_size = pvec->size();
10537 } else {
10538 assert(!"incorrect call to bm_receivent_event_rpc()");
10539 }
10540
10541 int status;
10542 DWORD time_start = ss_millitime();
10543 DWORD time_end = time_start + timeout_msec;
10544
10545 int xtimeout_msec = timeout_msec;
10546
10547 int zbuf_size = xbuf_size;
10548
10549 while (1) {
10550 if (timeout_msec == BM_WAIT) {
10551 xtimeout_msec = 1000;
10552 } else if (timeout_msec == BM_NO_WAIT) {
10553 xtimeout_msec = BM_NO_WAIT;
10554 } else {
10555 if (xtimeout_msec > 1000) {
10556 xtimeout_msec = 1000;
10557 }
10558 }
10559
10560 zbuf_size = xbuf_size;
10561
10562 status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
10563
10564 //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);
10565
10566 if (status == BM_ASYNC_RETURN) {
10567 if (timeout_msec == BM_WAIT) {
10568 // BM_WAIT means wait forever
10569 continue;
10570 } else if (timeout_msec == BM_NO_WAIT) {
10571 // BM_NO_WAIT means do not wait
10572 break;
10573 } else {
10574 DWORD now = ss_millitime();
10575 if (now >= time_end) {
10576 // timeout, return BM_ASYNC_RETURN
10577 break;
10578 }
10579
10580 DWORD remain = time_end - now;
10581
10582 if (remain < (DWORD)xtimeout_msec) {
10583 xtimeout_msec = remain;
10584 }
10585
10586 // keep asking for event...
10587 continue;
10588 }
10589 } else if (status == BM_SUCCESS) {
10590 // success, return BM_SUCCESS
10591 break;
10592 }
10593
10594 // RPC error
10595
10596 if (buf) {
10597 *buf_size = 0;
10598 } else if (ppevent) {
10599 free(*ppevent);
10600 *ppevent = NULL;
10601 } else if (pvec) {
10602 pvec->resize(0);
10603 } else {
10604 assert(!"incorrect call to bm_receivent_event_rpc()");
10605 }
10606
10607 return status;
10608 }
10609
10610 // status is BM_SUCCESS or BM_ASYNC_RETURN
10611
10612 if (buf) {
10613 *buf_size = zbuf_size;
10614 } else if (ppevent) {
10615 // nothing to do
10616 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10617 } else if (pvec) {
10618 pvec->resize(zbuf_size);
10619 } else {
10620 assert(!"incorrect call to bm_receivent_event_rpc()");
10621 }
10622
10623 return status;
10624}
10625
10626static INT bm_receive_event_rpc_cxx(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10627{
10628 //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);
10629
10630 std::vector<char> *pv;
10631
10632 if (pvec == NULL)
10633 pv = new std::vector<char>;
10634 else
10635 pv = pvec;
10636
10637 pv->clear();
10638
10639 int status;
10640 DWORD time_start = ss_millitime();
10641 DWORD time_end = time_start + timeout_msec;
10642
10643 int xtimeout_msec = timeout_msec;
10644
10645 while (1) {
10646 if (timeout_msec == BM_WAIT) {
10647 xtimeout_msec = 1000;
10648 } else if (timeout_msec == BM_NO_WAIT) {
10649 xtimeout_msec = BM_NO_WAIT;
10650 } else {
10651 if (xtimeout_msec > 1000) {
10652 xtimeout_msec = 1000;
10653 }
10654 }
10655
10656 status = rpc_call(RPC_BM_RECEIVE_EVENT_CXX, buffer_handle, pv, xtimeout_msec);
10657
10658 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());
10659
10660 if (status == BM_ASYNC_RETURN) {
10661 if (timeout_msec == BM_WAIT) {
10662 // BM_WAIT means wait forever
10663 continue;
10664 } else if (timeout_msec == BM_NO_WAIT) {
10665 // BM_NO_WAIT means do not wait
10666 break;
10667 } else {
10668 DWORD now = ss_millitime();
10669 if (now >= time_end) {
10670 // timeout, return BM_ASYNC_RETURN
10671 break;
10672 }
10673
10674 DWORD remain = time_end - now;
10675
10676 if (remain < (DWORD)xtimeout_msec) {
10677 xtimeout_msec = remain;
10678 }
10679
10680 // keep asking for event...
10681 continue;
10682 }
10683 } else if (status == BM_SUCCESS) {
10684 // success, return BM_SUCCESS
10685 break;
10686 }
10687
10688 // RPC error
10689
10690 if (buf) {
10691 *buf_size = 0;
10692 } else if (ppevent) {
10693 free(*ppevent);
10694 *ppevent = NULL;
10695 } else if (pvec) {
10696 pvec->clear();
10697 } else {
10698 assert(!"incorrect call to bm_receivent_event_rpc_cxx()");
10699 }
10700
10701 if (pvec == NULL)
10702 delete pv;
10703
10704 return status;
10705 }
10706
10707 // status is BM_SUCCESS or BM_ASYNC_RETURN
10708
10709 if (buf) {
10710 if (pv->size() > (size_t)*buf_size) {
10712 memcpy(buf, pv->data(), *buf_size);
10713 } else {
10714 *buf_size = pv->size();
10715 memcpy(buf, pv->data(), *buf_size);
10716 }
10717 } else if (ppevent) {
10718 if (*ppevent == NULL) {
10719 *ppevent = (EVENT_HEADER*)malloc(pv->size());
10720 assert(*ppevent != NULL);
10721 memcpy(*ppevent, pv->data(), pv->size());
10722 } else {
10723 *ppevent = (EVENT_HEADER*)realloc(*ppevent, pv->size()); // shrink memory allocation
10724 assert(*ppevent != NULL);
10725 memcpy(*ppevent, pv->data(), pv->size());
10726 }
10727 } else if (pvec) {
10728 // nothing to do
10729 } else {
10730 assert(!"incorrect call to bm_receivent_event_rpc()");
10731 }
10732
10733 if (!pvec)
10734 delete pv;
10735
10736 return status;
10737}
10738
10739/********************************************************************/
10798INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10799 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10800 if (rpc_is_remote()) {
10801 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10802 }
10803#ifdef LOCAL_ROUTINES
10804 {
10806
10807 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10808
10809 if (!pbuf)
10810 return status;
10811
10812 int convert_flags = rpc_get_convert_flags();
10813
10814 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10815 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10816 return status;
10817 }
10818#else /* LOCAL_ROUTINES */
10819
10820 return BM_SUCCESS;
10821#endif
10822}
10823
10824/********************************************************************/
10879INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
10880 if (rpc_is_remote()) {
10881 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10882 }
10883#ifdef LOCAL_ROUTINES
10884 {
10886
10887 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10888
10889 if (!pbuf)
10890 return status;
10891
10892 int convert_flags = rpc_get_convert_flags();
10893
10894 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10895 }
10896#else /* LOCAL_ROUTINES */
10897
10898 return BM_SUCCESS;
10899#endif
10900}
10901
10902/********************************************************************/
10957INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10958 if (rpc_is_remote()) {
10959 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10960 }
10961#ifdef LOCAL_ROUTINES
10962 {
10964
10965 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10966
10967 if (!pbuf)
10968 return status;
10969
10970 int convert_flags = rpc_get_convert_flags();
10971
10972 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10973 }
10974#else /* LOCAL_ROUTINES */
10975 return BM_SUCCESS;
10976#endif
10977}
10978
10979#ifdef LOCAL_ROUTINES
10980
10981static int bm_skip_event(BUFFER* pbuf)
10982{
10983 /* clear read cache */
10984 if (pbuf->read_cache_size > 0) {
10985
10987
10988 if (status != BM_SUCCESS)
10989 return status;
10990
10991 pbuf->read_cache_rp = 0;
10992 pbuf->read_cache_wp = 0;
10993
10994 pbuf->read_cache_mutex.unlock();
10995 }
10996
10997 bm_lock_buffer_guard pbuf_guard(pbuf);
10998
10999 if (!pbuf_guard.is_locked())
11000 return pbuf_guard.get_status();
11001
11002 BUFFER_HEADER *pheader = pbuf->buffer_header;
11003
11004 /* forward read pointer to global write pointer */
11005 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
11006 pclient->read_pointer = pheader->write_pointer;
11007
11008 return BM_SUCCESS;
11009}
11010
11011#endif /* LOCAL_ROUTINES */
11012
11013/********************************************************************/
11022INT bm_skip_event(INT buffer_handle) {
11023 if (rpc_is_remote())
11024 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
11025
11026#ifdef LOCAL_ROUTINES
11027 {
11028 int status = 0;
11029
11030 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
11031
11032 if (!pbuf)
11033 return status;
11034
11035 return bm_skip_event(pbuf);
11036 }
11037#endif
11038
11039 return BM_SUCCESS;
11040}
11041
11042#ifdef LOCAL_ROUTINES
11043/********************************************************************/
11050static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
11051 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
11052
11053 /* return immediately if no callback routine is defined */
11054 if (!pbuf->callback)
11055 return BM_SUCCESS;
11056
11057 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
11058}
11059
11060/********************************************************************/
11066static INT bm_push_event(const char *buffer_name)
11067{
11068 std::vector<BUFFER*> mybuffers;
11069
11070 gBuffersMutex.lock();
11071 mybuffers = gBuffers;
11072 gBuffersMutex.unlock();
11073
11074 for (size_t i = 0; i < mybuffers.size(); i++) {
11075 BUFFER *pbuf = mybuffers[i];
11076 if (!pbuf || !pbuf->attached)
11077 continue;
11078 // FIXME: unlocked read access to pbuf->buffer_name!
11079 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
11080 return bm_push_buffer(pbuf, i + 1);
11081 }
11082 }
11083
11084 return BM_INVALID_HANDLE;
11085}
11086
11087#else
11088
11089static INT bm_push_event(const char *buffer_name)
11090{
11091 return BM_SUCCESS;
11092}
11093
11094#endif /* LOCAL_ROUTINES */
11095
11096/********************************************************************/
11103#ifdef LOCAL_ROUTINES
11104 {
11105 INT status = 0;
11106 BOOL bMore;
11107 DWORD start_time;
11108 //static DWORD last_time = 0;
11109
11110 /* if running as a server, buffer checking is done by client
11111 via ASYNC bm_receive_event */
11112 if (rpc_is_mserver()) {
11113 return FALSE;
11114 }
11115
11116 bMore = FALSE;
11117 start_time = ss_millitime();
11118
11119 std::vector<BUFFER*> mybuffers;
11120
11121 gBuffersMutex.lock();
11122 mybuffers = gBuffers;
11123 gBuffersMutex.unlock();
11124
11125 /* go through all buffers */
11126 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11127 BUFFER* pbuf = mybuffers[idx];
11128
11129 if (!pbuf || !pbuf->attached)
11130 continue;
11131
11132 //int count_loops = 0;
11133 while (1) {
11134 if (pbuf->attached) {
11135 /* one bm_push_event could cause a run stop and a buffer close, which
11136 * would crash the next call to bm_push_event(). So check for valid
11137 * buffer on each call */
11138
11139 /* this is what happens:
11140 * bm_push_buffer() may call a user callback function
11141 * user callback function may indirectly call bm_close() of this buffer,
11142 * i.e. if it stops the run,
11143 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11144 * here we will see pbuf->attched is false and quit this loop
11145 */
11146
11147 status = bm_push_buffer(pbuf, idx + 1);
11148
11149 if (status == BM_CORRUPTED) {
11150 return status;
11151 }
11152
11153 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11154
11155 if (status != BM_MORE_EVENTS) {
11156 //DWORD t = ss_millitime() - start_time;
11157 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11158 break;
11159 }
11160
11161 // count_loops++;
11162 }
11163
11164 // NB: this code has a logic error: if 2 buffers always have data,
11165 // this timeout will cause us to exit reading the 1st buffer
11166 // after 1000 msec, then we read the 2nd buffer exactly once,
11167 // and exit the loop because the timeout is still active -
11168 // we did not reset "start_time" when we started reading
11169 // from the 2nd buffer. Result is that we always read all
11170 // the data in a loop from the 1st buffer, but read just
11171 // one event from the 2nd buffer, resulting in severe unfairness.
11172
11173 /* stop after one second */
11174 DWORD t = ss_millitime() - start_time;
11175 if (t > 1000) {
11176 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11177 bMore = TRUE;
11178 break;
11179 }
11180 }
11181 }
11182
11183 //last_time = start_time;
11184
11185 return bMore;
11186
11187 }
11188#else /* LOCAL_ROUTINES */
11189
11190 return FALSE;
11191
11192#endif
11193}
11194
11195/********************************************************************/
11196static INT bm_notify_client(const char *buffer_name, int client_socket)
11197/********************************************************************\
11198
11199 Routine: bm_notify_client
11200
11201 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11202 the connected client. Used by mserver to relay the BM_MSG
11203 buffer message from local UDP socket to the remote connected client.
11204
11205 Input:
11206 char *buffer_name Name of buffer
11207 int client_socket Network socket to client
11208
11209 Output:
11210 none
11211
11212 Function value:
11213 BM_SUCCESS Successful completion
11214
11215\********************************************************************/
11216{
11217 static DWORD last_time = 0;
11218 DWORD now = ss_millitime();
11219
11220 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11221
11222 BUFFER* fbuf = NULL;
11223
11224 gBuffersMutex.lock();
11225
11226 for (size_t i = 0; i < gBuffers.size(); i++) {
11227 BUFFER* pbuf = gBuffers[i];
11228 if (!pbuf || !pbuf->attached)
11229 continue;
11230 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11231 fbuf = pbuf;
11232 break;
11233 }
11234 }
11235
11236 gBuffersMutex.unlock();
11237
11238 if (!fbuf)
11239 return BM_INVALID_HANDLE;
11240
11241 /* don't send notification if client has no callback defined
11242 to receive events -> client calls bm_receive_event manually */
11243 if (!fbuf->callback)
11244 return DB_SUCCESS;
11245
11246 int convert_flags = rpc_get_convert_flags();
11247
11248 /* only send notification once each 500ms */
11249 if (now - last_time < 500)
11250 return DB_SUCCESS;
11251
11252 last_time = now;
11253
11254 char buffer[32];
11255 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11256
11257 nc->header.routine_id = MSG_BM;
11258 nc->header.param_size = 0;
11259
11260 if (convert_flags) {
11263 }
11264
11265 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11266
11267 /* send the update notification to the client */
11268 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11269
11270 return BM_SUCCESS;
11271}
11272
11273/********************************************************************/
11275/********************************************************************\
11276
11277 Routine: bm_poll_event
11278
11279 Purpose: Poll an event from a remote server. Gets called by
11280 rpc_client_dispatch() and by cm_yield()
11281
11282 Function value:
11283 BM_SUCCESS At least one event was received and dispatched
11284 BM_ASYNC_RETURN No events received
11285 SS_ABORT Network connection broken
11286
11287\********************************************************************/
11288{
11289 BOOL dispatched_something = FALSE;
11290
11291 //printf("bm_poll_event!\n");
11292
11293 DWORD start_time = ss_millitime();
11294
11295 std::vector<char> vec;
11296
11297 /* loop over all requests */
11298 _request_list_mutex.lock();
11299 bool locked = true;
11300 size_t n = _request_list.size();
11301 for (size_t i = 0; i < n; i++) {
11302 if (!locked) {
11303 _request_list_mutex.lock();
11304 locked = true;
11305 }
11306 /* continue if no dispatcher set (manual bm_receive_event) */
11307 if (_request_list[i].dispatcher == NULL)
11308 continue;
11309
11310 int buffer_handle = _request_list[i].buffer_handle;
11311
11312 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11313 _request_list_mutex.unlock();
11314 locked = false;
11315
11316 do {
11317 /* receive event */
11318 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11319
11320 //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());
11321
11322 /* call user function if successful */
11323 if (status == BM_SUCCESS) {
11324 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11325 dispatched_something = TRUE;
11326 }
11327
11328 /* break if no more events */
11329 if (status == BM_ASYNC_RETURN)
11330 break;
11331
11332 /* break if corrupted event buffer */
11333 if (status == BM_TRUNCATED) {
11334 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());
11335 }
11336
11337 /* break if corrupted event buffer */
11338 if (status == BM_CORRUPTED)
11339 return SS_ABORT;
11340
11341 /* break if server died */
11342 if (status == RPC_NET_ERROR) {
11343 return SS_ABORT;
11344 }
11345
11346 /* stop after one second */
11347 if (ss_millitime() - start_time > 1000) {
11348 break;
11349 }
11350
11351 } while (TRUE);
11352 }
11353
11354 if (locked)
11355 _request_list_mutex.unlock();
11356
11357 if (dispatched_something)
11358 return BM_SUCCESS;
11359 else
11360 return BM_ASYNC_RETURN;
11361}
11362
11363/********************************************************************/
11389 if (rpc_is_remote())
11391
11392#ifdef LOCAL_ROUTINES
11393 {
11394 std::vector<BUFFER*> mybuffers;
11395
11396 gBuffersMutex.lock();
11397 mybuffers = gBuffers;
11398 gBuffersMutex.unlock();
11399
11400 /* go through all buffers */
11401 for (BUFFER* pbuf : mybuffers) {
11402 if (!pbuf)
11403 continue;
11404 if (!pbuf->attached)
11405 continue;
11406
11407 int status = bm_skip_event(pbuf);
11408 if (status != BM_SUCCESS)
11409 return status;
11410 }
11411 }
11412#endif /* LOCAL_ROUTINES */
11413
11414 return BM_SUCCESS;
11415}
11416
11418#ifndef DOXYGEN_SHOULD_SKIP_THIS
11419
11420#define MAX_DEFRAG_EVENTS 10
11421
11428
11430
11431/********************************************************************/
11432static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11433 EVENT_HEADER *pevent, void *pdata,
11434 EVENT_HANDLER *dispatcher)
11435/********************************************************************\
11436
11437 Routine: bm_defragment_event
11438
11439 Purpose: Called internally from the event receiving routines
11440 bm_push_event and bm_poll_event to recombine event
11441 fragments and call the user callback routine upon
11442 completion.
11443
11444 Input:
11445 HNDLE buffer_handle Handle for the buffer containing event
11446 HNDLE request_id Handle for event request
11447 EVENT_HEADER *pevent Pointer to event header
11448 void *pata Pointer to event data
11449 dispatcher() User callback routine
11450
11451 Output:
11452 <calls dispatcher() after successfull recombination of event>
11453
11454 Function value:
11455 void
11456
11457\********************************************************************/
11458{
11459 INT i;
11460
11461 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11462 /*---- start new event ----*/
11463
11464 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11465
11466 /* check if fragments already stored */
11467 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11468 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11469 break;
11470
11471 if (i < MAX_DEFRAG_EVENTS) {
11472 free(defrag_buffer[i].pevent);
11473 defrag_buffer[i].pevent = NULL;
11474 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11475 cm_msg(MERROR, "bm_defragement_event",
11476 "Received new event with ID %d while old fragments were not completed",
11477 (pevent->event_id & 0x0FFF));
11478 }
11479
11480 /* search new slot */
11481 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11482 if (defrag_buffer[i].event_id == 0)
11483 break;
11484
11485 if (i == MAX_DEFRAG_EVENTS) {
11486 cm_msg(MERROR, "bm_defragment_event",
11487 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11488 return;
11489 }
11490
11491 /* check event size */
11492 if (pevent->data_size != sizeof(DWORD)) {
11493 cm_msg(MERROR, "bm_defragment_event",
11494 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11495 pevent->data_size, (int) sizeof(DWORD));
11496 return;
11497 }
11498
11499 /* setup defragment buffer */
11500 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11501 defrag_buffer[i].data_size = *(DWORD *) pdata;
11504
11505 if (defrag_buffer[i].pevent == NULL) {
11506 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11507 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11508 return;
11509 }
11510
11511 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11514
11515 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11516 // pevent->serial_number, defrag_buffer[i].data_size);
11517
11518 return;
11519 }
11520
11521 /* search buffer for that event */
11522 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11523 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11524 break;
11525
11526 if (i == MAX_DEFRAG_EVENTS) {
11527 /* no buffer available -> no first fragment received */
11528 cm_msg(MERROR, "bm_defragement_event",
11529 "Received fragment without first fragment (ID %d) Ser#:%d",
11530 pevent->event_id & 0x0FFF, pevent->serial_number);
11531 return;
11532 }
11533
11534 /* add fragment to buffer */
11536 free(defrag_buffer[i].pevent);
11537 defrag_buffer[i].pevent = NULL;
11538 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11539 cm_msg(MERROR, "bm_defragement_event",
11540 "Received fragments with more data (%d) than event size (%d)",
11541 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11542 return;
11543 }
11544
11545 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11546 defrag_buffer[i].received, pdata, pevent->data_size);
11547
11548 defrag_buffer[i].received += pevent->data_size;
11549
11550 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11551 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11552
11553 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11554 /* event complete */
11555 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11556 free(defrag_buffer[i].pevent);
11557 defrag_buffer[i].pevent = NULL;
11558 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11559 }
11560}
11561
11563#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11564
/* end of bmfunctionc */
11567
11573/********************************************************************\
11574* *
11575* RPC functions *
11576* *
11577\********************************************************************/
11578
11580{
11581public:
11582 std::atomic_bool connected{false}; /* socket is connected */
11583 std::string host_name; /* server name */
11584 int port = 0; /* server port */
11585 std::mutex mutex; /* connection lock */
11586 int index = 0; /* index in the connection array */
11587 std::string client_name; /* name of remote client */
11588 int send_sock = 0; /* tcp socket */
11589 int remote_hw_type = 0; /* remote hardware type */
11590 int rpc_timeout = 0; /* timeout in milliseconds */
11591
11592 void print() {
11593 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11594 index,
11595 client_name.c_str(),
11596 host_name.c_str(),
11597 port,
11598 send_sock,
11599 int(connected),
11600 rpc_timeout);
11601 }
11602
11604 if (send_sock > 0) {
11606 }
11607 connected = false;
11608 }
11609};
11610
11611/* globals */
11612
11613//
11614// locking rules for client connections:
11615//
11616// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11617// 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
11618// lock individual connection, check c->connected, work on the connection, unlock connection
11619//
11620// ok to access without locking client connection:
11621//
11622// - c->connected (std::atomic, but must recheck it after taking the lock)
11623// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11624//
11625// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11626// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11627//
11628// lifetime of client connections:
11629//
11630// - client connection slots are allocated by rpc_client_connect()
11631// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11632// - client slots marked NULL are free and will be reused by rpc_client_connect()
11633// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11634// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11635// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11636// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11637// client call will see an empty slot and return an error
11638// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11639// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11640// only when one is sure no other threads are running concurrent rpc client calls.
11641//
11642
11644static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11645
11646static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11647static bool _rpc_is_remote = false;
11648
11649//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11650static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11651static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11652
11657
11659{
11660 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11661 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11662 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11663 return _server_acceptions[idx];
11664 }
11665 }
11666
11668
11669 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11670 if (_server_acceptions[idx] == NULL) {
11671 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11672 _server_acceptions[idx] = sa;
11673 return _server_acceptions[idx];
11674 }
11675 }
11676
11677 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11678 _server_acceptions.push_back(sa);
11679
11680 return sa;
11681}
11682
11684{
11685 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11686
11687 if (is_mserver) {
11688 assert(_mserver_acception == this);
11689 _mserver_acception = NULL;
11690 is_mserver = false;
11691 }
11692
11693 /* close server connection */
11694 if (recv_sock)
11696 if (send_sock)
11698 if (event_sock)
11700
11701 /* free TCP cache */
11702 if (net_buffer) {
11703 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11704 free(net_buffer);
11705 net_buffer = NULL;
11706 net_buffer_size = 0;
11707 }
11708
11709 /* mark this entry as invalid */
11710 clear();
11711}
11712
11713static std::vector<RPC_LIST> rpc_list;
11714static std::mutex rpc_list_mutex;
11715
11717
11718
11719/********************************************************************\
11720* conversion functions *
11721\********************************************************************/
11722
11723void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11724 *convert_flags = 0;
11725
11726 /* big/little endian conversion */
11727 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11728 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11729 && (hw_type & DRI_BIG_ENDIAN)))
11730 *convert_flags |= CF_ENDIAN;
11731
11732 /* float conversion between IEEE and VAX G */
11733 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11734 *convert_flags |= CF_VAX2IEEE;
11735
11736 /* float conversion between VAX G and IEEE */
11737 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11738 *convert_flags |= CF_IEEE2VAX;
11739
11741 //if (remote_hw_type & DR_ASCII)
11742 // *convert_flags |= CF_ASCII;
11743}
11744
11745/********************************************************************/
11749
11750/********************************************************************/
11751void rpc_ieee2vax_float(float *var) {
11752 unsigned short int lo, hi;
11753
11754 /* swap hi and lo word */
11755 lo = *((short int *) (var) + 1);
11756 hi = *((short int *) (var));
11757
11758 /* correct exponent */
11759 if (lo != 0)
11760 lo += 0x100;
11761
11762 *((short int *) (var) + 1) = hi;
11763 *((short int *) (var)) = lo;
11764}
11765
11766void rpc_vax2ieee_float(float *var) {
11767 unsigned short int lo, hi;
11768
11769 /* swap hi and lo word */
11770 lo = *((short int *) (var) + 1);
11771 hi = *((short int *) (var));
11772
11773 /* correct exponent */
11774 if (hi != 0)
11775 hi -= 0x100;
11776
11777 *((short int *) (var) + 1) = hi;
11778 *((short int *) (var)) = lo;
11779
11780}
11781
11782void rpc_vax2ieee_double(double *var) {
11783 unsigned short int i1, i2, i3, i4;
11784
11785 /* swap words */
11786 i1 = *((short int *) (var) + 3);
11787 i2 = *((short int *) (var) + 2);
11788 i3 = *((short int *) (var) + 1);
11789 i4 = *((short int *) (var));
11790
11791 /* correct exponent */
11792 if (i4 != 0)
11793 i4 -= 0x20;
11794
11795 *((short int *) (var) + 3) = i4;
11796 *((short int *) (var) + 2) = i3;
11797 *((short int *) (var) + 1) = i2;
11798 *((short int *) (var)) = i1;
11799}
11800
11801void rpc_ieee2vax_double(double *var) {
11802 unsigned short int i1, i2, i3, i4;
11803
11804 /* swap words */
11805 i1 = *((short int *) (var) + 3);
11806 i2 = *((short int *) (var) + 2);
11807 i3 = *((short int *) (var) + 1);
11808 i4 = *((short int *) (var));
11809
11810 /* correct exponent */
11811 if (i1 != 0)
11812 i1 += 0x20;
11813
11814 *((short int *) (var) + 3) = i4;
11815 *((short int *) (var) + 2) = i3;
11816 *((short int *) (var) + 1) = i2;
11817 *((short int *) (var)) = i1;
11818}
11819
11820/********************************************************************/
11821void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11822
11823 if (convert_flags & CF_ENDIAN) {
11824 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11825 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11826 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11827 }
11828
11829 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11830 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11831 if (tid == TID_FLOAT)
11832 rpc_ieee2vax_float((float *) data);
11833 if (tid == TID_DOUBLE)
11834 rpc_ieee2vax_double((double *) data);
11835 }
11836
11837 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11838 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11839 if (tid == TID_FLOAT)
11840 rpc_vax2ieee_float((float *) data);
11841 if (tid == TID_DOUBLE)
11842 rpc_vax2ieee_double((double *) data);
11843 }
11844}
11845
11846void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11847/********************************************************************\
11848
11849 Routine: rpc_convert_data
11850
11851 Purpose: Convert data format between differenct computers
11852
11853 Input:
11854 void *data Pointer to data
11855 INT tid Type ID of data, one of TID_xxx
11856 INT flags Combination of following flags:
11857 RPC_IN: data is input parameter
11858 RPC_OUT: data is output variable
11859 RPC_FIXARRAY, RPC_VARARRAY: data is array
11860 of "size" bytes (see next param.)
11861 RPC_OUTGOING: data is outgoing
11862 INT total_size Size of bytes of data. Used for variable
11863 length arrays.
11864 INT convert_flags Flags for data conversion
11865
11866 Output:
11867 void *data Is converted according to _convert_flag
11868 value
11869
11870 Function value:
11871 RPC_SUCCESS Successful completion
11872
11873\********************************************************************/
11874{
11875 /* convert array */
11876 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11877 int single_size = rpc_tid_size(tid);
11878 /* don't convert TID_ARRAY & TID_STRUCT */
11879 if (single_size == 0)
11880 return;
11881
11882 int n = total_size / single_size;
11883
11884 for (int i = 0; i < n; i++) {
11885 char* p = (char *) data + (i * single_size);
11886 rpc_convert_single(p, tid, flags, convert_flags);
11887 }
11888 } else {
11889 rpc_convert_single(data, tid, flags, convert_flags);
11890 }
11891}
11892
11893/********************************************************************\
11894* type ID functions *
11895\********************************************************************/
11896
11898 if (id >= 0 && id < TID_LAST)
11899 return tid_size[id];
11900
11901 return 0;
11902}
11903
11904const char *rpc_tid_name(INT id) {
11905 if (id >= 0 && id < TID_LAST)
11906 return tid_name[id];
11907 else
11908 return "<unknown>";
11909}
11910
11911const char *rpc_tid_name_old(INT id) {
11912 if (id >= 0 && id < TID_LAST)
11913 return tid_name_old[id];
11914 else
11915 return "<unknown>";
11916}
11917
11918int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11919{
11920 for (int i=0; i<TID_LAST; i++) {
11921 if (strcmp(name, tid_name[i]) == 0)
11922 return i;
11923 }
11924
11925 for (int i=0; i<TID_LAST; i++) {
11926 if (strcmp(name, tid_name_old[i]) == 0)
11927 return i;
11928 }
11929
11930 return 0;
11931}
11932
11933/********************************************************************\
11934* client functions *
11935\********************************************************************/
11936
11937/********************************************************************/
11952
11953 return RPC_SUCCESS;
11954}
11955
11956/********************************************************************/
11968{
11969 for (int i = 0; new_list[i].id != 0; i++) {
11970 /* check valid ID for user functions */
11971 if (new_list != rpc_get_internal_list(0) &&
11972 new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
11973 || new_list[i].id > RPC_MAX_ID)) {
11974 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11975 }
11976 }
11977
11978 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11979
11980 /* check double defined functions */
11981 for (int i = 0; new_list[i].id != 0; i++) {
11982 for (size_t j = 0; j < rpc_list.size(); j++) {
11983 if (rpc_list[j].id == new_list[i].id) {
11984 return RPC_DOUBLE_DEFINED;
11985 }
11986 }
11987 }
11988
11989 /* append new functions */
11990 for (int i = 0; new_list[i].id != 0; i++) {
11991 RPC_LIST e = new_list[i];
11992
11993 /* set default dispatcher */
11994 if (e.dispatch == NULL) {
11995 e.dispatch = func;
11996 }
11997
11998 rpc_list.push_back(e);
11999 }
12000
12001 return RPC_SUCCESS;
12002}
12003
12004
12005
12007#ifndef DOXYGEN_SHOULD_SKIP_THIS
12008
12009/********************************************************************/
12011/********************************************************************\
12012
12013 Routine: rpc_deregister_functions
12014
12015 Purpose: Free memory of previously registered functions
12016
12017 Input:
12018 none
12019
12020 Output:
12021 none
12022
12023 Function value:
12024 RPC_SUCCESS Successful completion
12025
12026\********************************************************************/
12027{
12028 rpc_list_mutex.lock();
12029 rpc_list.clear();
12030 rpc_list_mutex.unlock();
12031
12032 return RPC_SUCCESS;
12033}
12034
12035
12036/********************************************************************/
12037INT rpc_register_function(INT id, INT(*func)(INT, void **))
12038/********************************************************************\
12039
12040 Routine: rpc_register_function
12041
12042 Purpose: Replace a dispatch function for a specific rpc routine
12043
12044 Input:
12045 INT id RPC ID
12046 INT *func New dispatch function
12047
12048 Output:
12049 <implicit: func gets copied to rpc_list>
12050
12051 Function value:
12052 RPC_SUCCESS Successful completion
12053 RPC_INVALID_ID RPC ID not found
12054
12055\********************************************************************/
12056{
12057 std::lock_guard<std::mutex> guard(rpc_list_mutex);
12058
12059 for (size_t i = 0; i < rpc_list.size(); i++) {
12060 if (rpc_list[i].id == id) {
12061 rpc_list[i].dispatch = func;
12062 return RPC_SUCCESS;
12063 }
12064 }
12065
12066 return RPC_INVALID_ID;
12067}
12068
12069/********************************************************************/
12070
12071static int handle_msg_odb(int n, const NET_COMMAND *nc) {
12072 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
12073 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
12074 /* update a changed record */
12075 HNDLE hDB = *((INT *) nc->param);
12076 HNDLE hKeyRoot = *((INT *) nc->param + 1);
12077 HNDLE hKey = *((INT *) nc->param + 2);
12078 int index = *((INT *) nc->param + 3);
12079 return db_update_record_local(hDB, hKeyRoot, hKey, index);
12080 }
12081 return CM_VERSION_MISMATCH;
12082}
12083
12084/********************************************************************/
12086/********************************************************************\
12087
12088 Routine: rpc_client_dispatch
12089
12090 Purpose: Receive data from the mserver: watchdog and buffer notification messages
12091
12092\********************************************************************/
12093{
12094 INT status = 0;
12095 char net_buffer[256];
12096
12097 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12098 if (n <= 0)
12099 return SS_ABORT;
12100
12101 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
12102
12103 if (nc->header.routine_id == MSG_ODB) {
12104 status = handle_msg_odb(n, nc);
12105 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12106 nc->header.routine_id = 1;
12107 nc->header.param_size = 0;
12108 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12110 } else if (nc->header.routine_id == MSG_BM) {
12111 fd_set readfds;
12112 struct timeval timeout;
12113
12114 //printf("rpc_client_dispatch: received MSG_BM!\n");
12115
12116 /* receive further messages to empty TCP queue */
12117 do {
12118 FD_ZERO(&readfds);
12119 FD_SET(sock, &readfds);
12120
12121 timeout.tv_sec = 0;
12122 timeout.tv_usec = 0;
12123
12124 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12125
12126 if (FD_ISSET(sock, &readfds)) {
12127 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12128 if (n <= 0)
12129 return SS_ABORT;
12130
12131 if (nc->header.routine_id == MSG_ODB) {
12132 status = handle_msg_odb(n, nc);
12133 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12134 nc->header.routine_id = 1;
12135 nc->header.param_size = 0;
12136 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12138 }
12139 }
12140
12141 } while (FD_ISSET(sock, &readfds));
12142
12143 /* poll event from server */
12145 }
12146
12147 return status;
12148}
12149
12150
12151/********************************************************************/
12152INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12153/********************************************************************\
12154
12155 Routine: rpc_client_connect
12156
12157 Purpose: Establish a network connection to a remote client
12158
12159 Input:
12160 char *host_name IP address of host to connect to.
12161 INT port TPC port to connect to.
12162 char *clinet_name Client program name
12163
12164 Output:
12165 HNDLE *hConnection Handle for new connection which can be used
12166 in future rpc_call(hConnection....) calls
12167
12168 Function value:
12169 RPC_SUCCESS Successful completion
12170 RPC_NET_ERROR Error in socket call
12171 RPC_NO_CONNECTION Maximum number of connections reached
12172 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12173
12174\********************************************************************/
12175{
12176 INT i, status;
12177 bool debug = false;
12178
12179 /* check if cm_connect_experiment was called */
12180 if (_client_name.length() == 0) {
12181 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12182 return RPC_NOT_REGISTERED;
12183 }
12184
12185 /* refuse connection to port 0 */
12186 if (port == 0) {
12187 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12188 return RPC_NET_ERROR;
12189 }
12190
12191 RPC_CLIENT_CONNECTION* c = NULL;
12192
12193 static std::mutex gHostnameMutex;
12194
12195 {
12196 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12197
12198 if (debug) {
12199 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12200 for (size_t i = 0; i < _client_connections.size(); i++) {
12201 if (_client_connections[i]) {
12202 printf("client connection %d: ", (int)i);
12203 _client_connections[i]->print();
12204 printf("\n");
12205 }
12206 }
12207 }
12208
12209 // slot with index 0 is not used, fill it with a NULL
12210
12211 if (_client_connections.empty()) {
12212 _client_connections.push_back(NULL);
12213 }
12214
12215 bool hostname_locked = false;
12216
12217 /* check if connection already exists */
12218 for (size_t i = 1; i < _client_connections.size(); i++) {
12220 if (c && c->connected) {
12221
12222 if (!hostname_locked) {
12223 gHostnameMutex.lock();
12224 hostname_locked = true;
12225 }
12226
12227 if ((c->host_name == host_name) && (c->port == port)) {
12228 // NB: we must release the hostname lock before taking
12229 // c->mutex to avoid a locking order inversion deadlock:
12230 // later on we lock the hostname mutex while holding the c->mutex
12231 gHostnameMutex.unlock();
12232 hostname_locked = false;
12233 std::lock_guard<std::mutex> cguard(c->mutex);
12234 // check if socket is still connected
12235 if (c->connected) {
12236 // found connection slot with matching hostname and port number
12237 status = ss_socket_wait(c->send_sock, 0);
12238 if (status == SS_TIMEOUT) { // yes, still connected and empty
12239 // so reuse it connection
12240 *hConnection = c->index;
12241 if (debug) {
12242 printf("already connected: ");
12243 c->print();
12244 printf("\n");
12245 }
12246 // implicit unlock of c->mutex
12247 // gHostnameLock is not locked here
12248 return RPC_SUCCESS;
12249 }
12250 //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);
12251 c->close_locked();
12252 }
12253 // implicit unlock of c->mutex
12254 }
12255 }
12256 }
12257
12258 if (hostname_locked) {
12259 gHostnameMutex.unlock();
12260 hostname_locked = false;
12261 }
12262
12263 // only start reusing connections once we have
12264 // a good number of slots allocated.
12265 if (_client_connections.size() > 10) {
12266 static int last_reused = 0;
12267
12268 int size = _client_connections.size();
12269 for (int j = 1; j < size; j++) {
12270 int i = (last_reused + j) % size;
12271 if (_client_connections[i] && !_client_connections[i]->connected) {
12273 if (debug) {
12274 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12275 c->print();
12276 printf("\n");
12277 }
12278 last_reused = i;
12279 break;
12280 }
12281 }
12282 }
12283
12284 // no slots to reuse, allocate a new slot.
12285 if (!c) {
12287
12288 // if empty slot not found, add to end of array
12289 c->index = _client_connections.size();
12290 _client_connections.push_back(c);
12291
12292 if (debug) {
12293 printf("new connection appended to array: ");
12294 c->print();
12295 printf("\n");
12296 }
12297 }
12298
12299 c->mutex.lock();
12300 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12301
12302 // done with the array of connections
12303 // implicit unlock of _client_connections_mutex
12304 }
12305
12306 // locked connection slot for new connection
12307 assert(c != NULL);
12308
12309 std::string errmsg;
12310
12311 /* create a new socket for connecting to remote server */
12312 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12313 if (status != SS_SUCCESS) {
12314 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12315 c->mutex.unlock();
12316 return RPC_NET_ERROR;
12317 }
12318
12319 gHostnameMutex.lock();
12320
12321 c->host_name = host_name;
12322 c->port = port;
12323
12324 gHostnameMutex.unlock();
12325
12326 c->client_name = client_name;
12327 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12328
12329 /* set TCP_NODELAY option for better performance */
12330 i = 1;
12331 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12332
12333 /* send local computer info */
12334 std::string local_prog_name = rpc_get_name();
12335 std::string local_host_name = ss_gethostname();
12336
12337 int hw_type = rpc_get_hw_type();
12338
12339 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12340
12341 int size = cstr.length() + 1;
12342 i = send(c->send_sock, cstr.c_str(), size, 0);
12343 if (i < 0 || i != size) {
12344 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12345 c->mutex.unlock();
12346 return RPC_NET_ERROR;
12347 }
12348
12349 bool restore_watchdog_timeout = false;
12350 BOOL watchdog_call;
12351 DWORD watchdog_timeout;
12352 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12353
12354 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12355
12356 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12357 restore_watchdog_timeout = true;
12358 cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
12359 }
12360
12361 char str[256];
12362
12363 /* receive remote computer info */
12364 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12365
12366 if (restore_watchdog_timeout) {
12367 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12368 }
12369
12370 if (i <= 0) {
12371 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12372 c->close_locked();
12373 c->mutex.unlock();
12374 return RPC_NET_ERROR;
12375 }
12376
12377 int remote_hw_type = 0;
12378 char remote_version[32];
12379 remote_version[0] = 0;
12380 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12381
12382 c->remote_hw_type = remote_hw_type;
12383
12384 /* print warning if version patch level doesn't agree */
12385 char v1[32];
12386 mstrlcpy(v1, remote_version, sizeof(v1));
12387 if (strchr(v1, '.'))
12388 if (strchr(strchr(v1, '.') + 1, '.'))
12389 *strchr(strchr(v1, '.') + 1, '.') = 0;
12390
12391 mstrlcpy(str, cm_get_version(), sizeof(str));
12392 if (strchr(str, '.'))
12393 if (strchr(strchr(str, '.') + 1, '.'))
12394 *strchr(strchr(str, '.') + 1, '.') = 0;
12395
12396 if (strcmp(v1, str) != 0) {
12397 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12398 }
12399
12400 c->connected = true;
12401
12402 *hConnection = c->index;
12403
12404 c->mutex.unlock();
12405
12406 return RPC_SUCCESS;
12407}
12408
12409/********************************************************************/
12411/********************************************************************\
12412
12413 Routine: rpc_client_check
12414
12415 Purpose: Check all client connections if remote client closed link
12416
12417\********************************************************************/
12418{
12419#if 0
12420 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12421 if (_client_connection[i].send_sock != 0)
12422 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);
12423#endif
12424
12425 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12426
12427 /* check for broken connections */
12428 for (unsigned i = 0; i < _client_connections.size(); i++) {
12430 if (c && c->connected) {
12431 std::lock_guard<std::mutex> cguard(c->mutex);
12432
12433 if (!c->connected) {
12434 // implicit unlock
12435 continue;
12436 }
12437
12438 //printf("rpc_client_check: connection %d: ", i);
12439 //c->print();
12440 //printf("\n");
12441
12442 int ok = 0;
12443
12444 fd_set readfds;
12445 FD_ZERO(&readfds);
12446 FD_SET(c->send_sock, &readfds);
12447
12448 struct timeval timeout;
12449 timeout.tv_sec = 0;
12450 timeout.tv_usec = 0;
12451
12452 int status;
12453
12454#ifdef OS_WINNT
12455 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12456#else
12457 do {
12458 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12459 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12460#endif
12461
12462 if (!FD_ISSET(c->send_sock, &readfds)) {
12463 // implicit unlock
12464 continue;
12465 }
12466
12467 char buffer[64];
12468
12469 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12470 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12471
12472 if (status < 0) {
12473#ifndef OS_WINNT
12474 if (errno == EAGAIN) { // still connected
12475 ok = 1;
12476 } else
12477#endif
12478 {
12479 // connection error
12480 cm_msg(MERROR, "rpc_client_check",
12481 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12482 c->client_name.c_str(),
12483 c->host_name.c_str(),
12484 errno, strerror(errno));
12485 ok = 0;
12486 }
12487 } else if (status == 0) {
12488 // connection closed by remote end without sending an EXIT message
12489 // this can happen if the remote end has crashed, so this message
12490 // is still necessary as a useful diagnostic for unexpected crashes
12491 // of midas programs. K.O.
12492 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());
12493 ok = 0;
12494 } else {
12495 // read some data
12496 ok = 1;
12497 if (equal_ustring(buffer, "EXIT")) {
12498 /* normal exit */
12499 ok = 0;
12500 }
12501 }
12502
12503 if (!ok) {
12504 //printf("rpc_client_check: closing connection %d: ", i);
12505 //c->print();
12506 //printf("\n");
12507
12508 // connection lost, close the socket
12509 c->close_locked();
12510 }
12511
12512 // implicit unlock
12513 }
12514 }
12515
12516 // implicit unlock of _client_connections_mutex
12517}
12518
12519
12520/********************************************************************/
12521INT rpc_server_connect(const char *host_name, const char *exp_name)
12522/********************************************************************\
12523
12524 Routine: rpc_server_connect
12525
12526 Purpose: Extablish a network connection to a remote MIDAS
12527 server using a callback scheme.
12528
12529 Input:
12530 char *host_name IP address of host to connect to.
12531
12532 INT port TPC port to connect to.
12533
12534 char *exp_name Name of experiment to connect to. By using
12535 this name, several experiments (e.g. online
12536 DAQ and offline analysis) can run simultan-
12537 eously on the same host.
12538
12539 Output:
12540 none
12541
12542 Function value:
12543 RPC_SUCCESS Successful completion
12544 RPC_NET_ERROR Error in socket call
12545 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12546 CM_UNDEF_EXP Undefined experiment on server
12547
12548\********************************************************************/
12549{
12550 INT i, status;
12551 INT remote_hw_type, hw_type;
12552 char str[200], version[32], v1[32];
12553 fd_set readfds;
12554 struct timeval timeout;
12555 int port = MIDAS_TCP_PORT;
12556 char *s;
12557
12558#ifdef OS_WINNT
12559 {
12560 WSADATA WSAData;
12561
12562 /* Start windows sockets */
12563 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12564 return RPC_NET_ERROR;
12565 }
12566#endif
12567
12568 /* check if local connection */
12569 if (host_name[0] == 0)
12570 return RPC_SUCCESS;
12571
12572 /* register system functions */
12574
12575 /* check if cm_connect_experiment was called */
12576 if (_client_name.length() == 0) {
12577 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12578 return RPC_NOT_REGISTERED;
12579 }
12580
12581 /* check if connection already exists */
12583 return RPC_SUCCESS;
12584
12588
12589 bool listen_localhost = false;
12590
12591 if (strcmp(host_name, "localhost") == 0)
12592 listen_localhost = true;
12593
12594 int lsock1, lport1;
12595 int lsock2, lport2;
12596 int lsock3, lport3;
12597
12598 std::string errmsg;
12599
12600 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
12601
12602 if (status != SS_SUCCESS) {
12603 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12604 return RPC_NET_ERROR;
12605 }
12606
12607 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
12608
12609 if (status != SS_SUCCESS) {
12610 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12611 return RPC_NET_ERROR;
12612 }
12613
12614 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
12615
12616 if (status != SS_SUCCESS) {
12617 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12618 return RPC_NET_ERROR;
12619 }
12620
12621 /* extract port number from host_name */
12622 mstrlcpy(str, host_name, sizeof(str));
12623 s = strchr(str, ':');
12624 if (s) {
12625 *s = 0;
12626 port = strtoul(s + 1, NULL, 0);
12627 }
12628
12629 int sock;
12630
12631 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12632
12633 if (status != SS_SUCCESS) {
12634 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12635 return RPC_NET_ERROR;
12636 }
12637
12638 /* connect to experiment */
12639 if (exp_name[0] == 0)
12640 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12641 else
12642 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12643
12644 send(sock, str, strlen(str) + 1, 0);
12645 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12646 ss_socket_close(&sock);
12647 if (i <= 0) {
12648 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12649 return RPC_NET_ERROR;
12650 }
12651
12652 status = version[0] = 0;
12653 sscanf(str, "%d %s", &status, version);
12654
12655 if (status == 2) {
12656/* message "undefined experiment" should be displayed by application */
12657 return CM_UNDEF_EXP;
12658 }
12659
12660 /* print warning if version patch level doesn't agree */
12661 strcpy(v1, version);
12662 if (strchr(v1, '.'))
12663 if (strchr(strchr(v1, '.') + 1, '.'))
12664 *strchr(strchr(v1, '.') + 1, '.') = 0;
12665
12666 strcpy(str, cm_get_version());
12667 if (strchr(str, '.'))
12668 if (strchr(strchr(str, '.') + 1, '.'))
12669 *strchr(strchr(str, '.') + 1, '.') = 0;
12670
12671 if (strcmp(v1, str) != 0) {
12672 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12673 cm_get_version());
12674 }
12675
12676 /* wait for callback on send and recv socket with timeout */
12677 FD_ZERO(&readfds);
12678 FD_SET(lsock1, &readfds);
12679 FD_SET(lsock2, &readfds);
12680 FD_SET(lsock3, &readfds);
12681
12682 timeout.tv_sec = _rpc_connect_timeout / 1000;
12683 timeout.tv_usec = 0;
12684
12685 do {
12686 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12687
12688 /* if an alarm signal was cought, restart select with reduced timeout */
12689 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12690 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12691
12692 } while (status == -1); /* dont return if an alarm signal was cought */
12693
12694 if (!FD_ISSET(lsock1, &readfds)) {
12695 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12696 ss_socket_close(&lsock1);
12697 ss_socket_close(&lsock2);
12698 ss_socket_close(&lsock3);
12699 return RPC_NET_ERROR;
12700 }
12701
12702 _server_connection.send_sock = accept(lsock1, NULL, NULL);
12703 _server_connection.recv_sock = accept(lsock2, NULL, NULL);
12704 _server_connection.event_sock = accept(lsock3, NULL, NULL);
12705
12707 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12708 return RPC_NET_ERROR;
12709 }
12710
12711 ss_socket_close(&lsock1);
12712 ss_socket_close(&lsock2);
12713 ss_socket_close(&lsock3);
12714
12715 /* set TCP_NODELAY option for better performance */
12716 int flag = 1;
12717 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12718 setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12719
12720 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12721 flag = 2 * 1024 * 1024;
12722 status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
12723 if (status != 0)
12724 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12725
12726 /* send local computer info */
12727 std::string local_prog_name = rpc_get_name();
12728 hw_type = rpc_get_hw_type();
12729 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12730
12731 send(_server_connection.send_sock, str, strlen(str) + 1, 0);
12732
12733 /* receive remote computer info */
12735 if (i <= 0) {
12736 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12737 return RPC_NET_ERROR;
12738 }
12739
12740 sscanf(str, "%d", &remote_hw_type);
12741 _server_connection.remote_hw_type = remote_hw_type;
12742
12744
12745 _rpc_is_remote = true;
12746
12747 return RPC_SUCCESS;
12748}
12749
12750/********************************************************************/
12751
12753{
12755 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12757 if (c && c->connected) {
12759 c->mutex.lock();
12760 if (!c->connected) {
12761 // disconnected while we were waiting for the lock
12762 c->mutex.unlock();
12763 return NULL;
12764 }
12765 return c;
12766 }
12767 }
12769 return NULL;
12770}
12771
12773{
12774 /* close all open connections */
12775
12777
12778 for (unsigned i = 0; i < _client_connections.size(); i++) {
12780 if (c && c->connected) {
12781 int index = c->index;
12782 // must unlock the array, otherwise we hang -
12783 // rpc_client_disconnect() will do rpc_call_client()
12784 // which needs to lock the array to convert handle
12785 // to connection pointer. Ouch! K.O. Dec 2020.
12789 }
12790 }
12791
12792 for (unsigned i = 0; i < _client_connections.size(); i++) {
12794 //printf("client connection %d %p\n", i, c);
12795 if (c) {
12796 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12797 if (!c->connected) {
12798 delete c;
12799 _client_connections[i] = NULL;
12800 }
12801 }
12802 }
12803
12805
12806 /* close server connection from other clients */
12807 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12808 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12809 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12810 _server_acceptions[i]->close();
12811 }
12812 }
12813}
12814
12815/********************************************************************/
12817/********************************************************************\
12818
12819 Routine: rpc_client_disconnect
12820
12821 Purpose: Close a rpc connection to a MIDAS client
12822
12823 Input:
12824 HNDLE hConn Handle of connection
12825 BOOL bShutdown Shut down remote server if TRUE
12826
12827 Output:
12828 none
12829
12830 Function value:
12831 RPC_SUCCESS Successful completion
12832
12833\********************************************************************/
12834{
12835 /* notify server about exit */
12836
12837 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12838
12840
12841 return RPC_SUCCESS;
12842}
12843
12844/********************************************************************/
12846/********************************************************************\
12847
12848 Routine: rpc_server_disconnect
12849
12850 Purpose: Close a rpc connection to a MIDAS server and close all
12851 server connections from other clients
12852
12853 Input:
12854 none
12855
12856 Output:
12857 none
12858
12859 Function value:
12860 RPC_SUCCESS Successful completion
12861 RPC_NET_ERROR Error in socket call
12862 RPC_NO_CONNECTION Maximum number of connections reached
12863
12864\********************************************************************/
12865{
12866 static int rpc_server_disconnect_recursion_level = 0;
12867
12868 if (rpc_server_disconnect_recursion_level)
12869 return RPC_SUCCESS;
12870
12871 rpc_server_disconnect_recursion_level = 1;
12872
12873 /* flush remaining events */
12875
12876 /* notify server about exit */
12877 if (rpc_is_connected()) {
12879 }
12880
12881 /* close sockets */
12888
12890
12891 /* remove semaphore */
12892 if (_mutex_rpc)
12894 _mutex_rpc = NULL;
12895
12896 rpc_server_disconnect_recursion_level = 0;
12897 return RPC_SUCCESS;
12898}
12899
12900/********************************************************************/
12902/********************************************************************\
12903
12904 Routine: rpc_is_remote
12905
12906 Purpose: Return true if program is connected to a remote server
12907
12908 Input:
12909 none
12910
12911 Output:
12912 none
12913
12914 Function value:
12915 INT true is remote client connected to mserver, false if local connection
12916
12917\********************************************************************/
12918{
12919 return _rpc_is_remote;
12920}
12921
12922/********************************************************************/
12924/********************************************************************\
12925
12926 Routine: rpc_is_connected
12927
12928 Purpose: Return true if connection to mserver is still open
12929
12930 Input:
12931 none
12932
12933 Output:
12934 none
12935
12936 Function value:
12937 INT true if connection to mserver is still open, false if connection to mserver is already closed
12938
12939\********************************************************************/
12940{
12941 return _server_connection.send_sock != 0;
12942}
12943
12944/********************************************************************/
12946/********************************************************************\
12947
12948 Routine: rpc_get_mserver_hostname
12949
12950 Purpose: Return the hostname of the mserver connection (host:port format)
12951
12952\********************************************************************/
12953{
12955}
12956
12957/********************************************************************/
12959/********************************************************************\
12960
12961 Routine: rpc_is_mserver
12962
12963 Purpose: Return true if we are the mserver
12964
12965 Function value:
12966 INT "true" if we are the mserver
12967
12968\********************************************************************/
12969{
12970 return _mserver_acception != NULL;
12971}
12972
12973/********************************************************************/
12975/********************************************************************\
12976
12977 Routine: rpc_get_hw_type
12978
12979 Purpose: get hardware information
12980
12981 Function value:
12982 INT combination of DRI_xxx bits
12983
12984\********************************************************************/
12985{
12986 {
12987 {
12988 INT tmp_type, size;
12989 DWORD dummy;
12990 unsigned char *p;
12991 float f;
12992 double d;
12993
12994 tmp_type = 0;
12995
12996 /* test pointer size */
12997 size = sizeof(p);
12998 if (size == 2)
12999 tmp_type |= DRI_16;
13000 if (size == 4)
13001 tmp_type |= DRI_32;
13002 if (size == 8)
13003 tmp_type |= DRI_64;
13004
13005 /* test if little or big endian machine */
13006 dummy = 0x12345678;
13007 p = (unsigned char *) &dummy;
13008 if (*p == 0x78)
13009 tmp_type |= DRI_LITTLE_ENDIAN;
13010 else if (*p == 0x12)
13011 tmp_type |= DRI_BIG_ENDIAN;
13012 else
13013 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
13014
13015 /* floating point format */
13016 f = (float) 1.2345;
13017 dummy = 0;
13018 memcpy(&dummy, &f, sizeof(f));
13019 if ((dummy & 0xFF) == 0x19 &&
13020 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
13021 && ((dummy >> 24) & 0xFF) == 0x3F)
13022 tmp_type |= DRF_IEEE;
13023 else if ((dummy & 0xFF) == 0x9E &&
13024 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
13025 && ((dummy >> 24) & 0xFF) == 0x04)
13026 tmp_type |= DRF_G_FLOAT;
13027 else
13028 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13029
13030 d = (double) 1.2345;
13031 dummy = 0;
13032 memcpy(&dummy, &d, sizeof(f));
13033 if ((dummy & 0xFF) == 0x8D && /* little endian */
13034 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
13035 && ((dummy >> 24) & 0xFF) == 0x12)
13036 tmp_type |= DRF_IEEE;
13037 else if ((dummy & 0xFF) == 0x83 && /* big endian */
13038 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
13039 && ((dummy >> 24) & 0xFF) == 0x3F)
13040 tmp_type |= DRF_IEEE;
13041 else if ((dummy & 0xFF) == 0x13 &&
13042 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
13043 && ((dummy >> 24) & 0xFF) == 0xC0)
13044 tmp_type |= DRF_G_FLOAT;
13045 else if ((dummy & 0xFF) == 0x9E &&
13046 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
13047 && ((dummy >> 24) & 0xFF) == 0x04)
13048 cm_msg(MERROR, "rpc_get_option",
13049 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
13050 else
13051 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13052
13053 return tmp_type;
13054 }
13055 }
13056}
13057
13059#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13060
13061/********************************************************************/
13069#if 0
13070INT rpc_set_option(HNDLE hConn, INT item, INT value) {
13071 switch (item) {
13072 case RPC_OTIMEOUT:
13073 if (hConn == -1)
13075 else if (hConn == -2)
13077 else {
13079 if (c) {
13080 c->rpc_timeout = value;
13081 c->mutex.unlock();
13082 }
13083 }
13084 break;
13085
13086 case RPC_NODELAY:
13087 if (hConn == -1) {
13088 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13089 } else {
13091 if (c) {
13092 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13093 c->mutex.unlock();
13094 }
13095 }
13096 break;
13097
13098 default:
13099 cm_msg(MERROR, "rpc_set_option", "invalid argument");
13100 break;
13101 }
13102
13103 return 0;
13104}
13105#endif
13106
13107/********************************************************************/
13114{
13115 if (hConn == RPC_HNDLE_MSERVER) {
13117 } else if (hConn == RPC_HNDLE_CONNECT) {
13118 return _rpc_connect_timeout;
13119 } else {
13121 if (c) {
13122 int timeout = c->rpc_timeout;
13123 c->mutex.unlock();
13124 return timeout;
13125 }
13126 }
13127 return 0;
13128}
13129
13130/********************************************************************/
13138INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
13139{
13140 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13141
13142 if (hConn == RPC_HNDLE_MSERVER) {
13143 if (old_timeout_msec)
13144 *old_timeout_msec = _server_connection.rpc_timeout;
13145 _server_connection.rpc_timeout = timeout_msec;
13146 } else if (hConn == RPC_HNDLE_CONNECT) {
13147 if (old_timeout_msec)
13148 *old_timeout_msec = _rpc_connect_timeout;
13149 _rpc_connect_timeout = timeout_msec;
13150 } else {
13152 if (c) {
13153 if (old_timeout_msec)
13154 *old_timeout_msec = c->rpc_timeout;
13155 c->rpc_timeout = timeout_msec;
13156 c->mutex.unlock();
13157 } else {
13158 if (old_timeout_msec)
13159 *old_timeout_msec = 0;
13160 }
13161 }
13162 return RPC_SUCCESS;
13163}
13164
13165
13167#ifndef DOXYGEN_SHOULD_SKIP_THIS
13168
13169/********************************************************************/
13171/********************************************************************\
13172
13173 Routine: rpc_get_convert_flags
13174
13175 Purpose: Get RPC convert_flags for the mserver connection
13176
13177 Function value:
13178 INT Actual option
13179
13180\********************************************************************/
13181{
13184 else
13185 return 0;
13186}
13187
13188static std::string _mserver_path;
13189
13190/********************************************************************/
13192/********************************************************************\
13193
13194 Routine: rpc_get_mserver_path()
13195
13196 Purpose: Get path of the mserver executable
13197
13198\********************************************************************/
13199{
13200 return _mserver_path.c_str();
13201}
13202
13203/********************************************************************/
13204INT rpc_set_mserver_path(const char *path)
13205/********************************************************************\
13206
13207 Routine: rpc_set_mserver_path
13208
13209 Purpose: Remember the path of the mserver executable
13210
13211 Input:
13212 char *path Full path of the mserver executable
13213
13214 Function value:
13215 RPC_SUCCESS Successful completion
13216
13217\********************************************************************/
13218{
13219 _mserver_path = path;
13220 return RPC_SUCCESS;
13221}
13222
13223/********************************************************************/
13224std::string rpc_get_name()
13225/********************************************************************\
13226
13227 Routine: rpc_get_name
13228
13229 Purpose: Get name set by rpc_set_name
13230
13231 Input:
13232 none
13233
13234 Output:
13235 char* name The location pointed by *name receives a
13236 copy of the _prog_name
13237
13238 Function value:
13239 RPC_SUCCESS Successful completion
13240
13241\********************************************************************/
13242{
13243 return _client_name;
13244}
13245
13246
13247/********************************************************************/
13249/********************************************************************\
13250
13251 Routine: rpc_set_name
13252
13253 Purpose: Set name of actual program for further rpc connections
13254
13255 Input:
13256 char *name Program name, up to NAME_LENGTH chars,
13257 no blanks
13258
13259 Output:
13260 none
13261
13262 Function value:
13263 RPC_SUCCESS Successful completion
13264
13265\********************************************************************/
13266{
13268
13269 return RPC_SUCCESS;
13270}
13271
13272
13273/********************************************************************/
13274INT rpc_set_debug(void (*func)(const char *), INT mode)
13275/********************************************************************\
13276
13277 Routine: rpc_set_debug
13278
13279 Purpose: Set a function which is called on every RPC call to
13280 display the function name and parameters of the RPC
13281 call.
13282
13283 Input:
13284 void *func(char*) Pointer to function.
13285 INT mode Debug mode
13286
13287 Output:
13288 none
13289
13290 Function value:
13291 RPC_SUCCESS Successful completion
13292
13293\********************************************************************/
13294{
13295 _debug_print = func;
13296 _debug_mode = mode;
13297 return RPC_SUCCESS;
13298}
13299
13300/********************************************************************/
13301void rpc_debug_printf(const char *format, ...)
13302/********************************************************************\
13303
13304 Routine: rpc_debug_print
13305
13306 Purpose: Calls function set via rpc_set_debug to output a string.
13307
13308 Input:
13309 char *str Debug string
13310
13311 Output:
13312 none
13313
13314\********************************************************************/
13315{
13316 va_list argptr;
13317 char str[1000];
13318
13319 if (_debug_mode) {
13320 va_start(argptr, format);
13321 vsprintf(str, (char *) format, argptr);
13322 va_end(argptr);
13323
13324 if (_debug_print) {
13325 strcat(str, "\n");
13327 } else
13328 puts(str);
13329 }
13330}
13331
13332/********************************************************************/
13333void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
13334 switch (arg_type) {
13335 /* On the stack, the minimum parameter size is sizeof(int).
13336 To avoid problems on little endian systems, treat all
13337 smaller parameters as int's */
13338 case TID_UINT8:
13339 case TID_INT8:
13340 case TID_CHAR:
13341 case TID_UINT16:
13342 case TID_INT16:
13343 *((int *) arg) = va_arg(*arg_ptr, int);
13344 break;
13345
13346 case TID_INT32:
13347 case TID_BOOL:
13348 *((INT *) arg) = va_arg(*arg_ptr, INT);
13349 break;
13350
13351 case TID_UINT32:
13352 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13353 break;
13354
13355 /* float variables are passed as double by the compiler */
13356 case TID_FLOAT:
13357 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13358 break;
13359
13360 case TID_DOUBLE:
13361 *((double *) arg) = va_arg(*arg_ptr, double);
13362 break;
13363
13364 case TID_ARRAY:
13365 *((char **) arg) = va_arg(*arg_ptr, char *);
13366 break;
13367 }
13368}
13369
13370/********************************************************************/
13371static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13372{
13373 //printf("rpc_call_encode!\n");
13374
13375 bool debug = false;
13376
13377 if (debug) {
13378 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13379 for (int i=0; rl.param[i].tid != 0; i++) {
13380 int tid = rl.param[i].tid;
13381 int flags = rl.param[i].flags;
13382 int n = rl.param[i].n;
13383 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13384 }
13385 }
13386
13387 char args[MAX_RPC_PARAMS][8];
13388
13389 for (int i=0; rl.param[i].tid != 0; i++) {
13390 int tid = rl.param[i].tid;
13391 int flags = rl.param[i].flags;
13392 int arg_type = 0;
13393
13394 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13395 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13396 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13397
13398 if (bpointer)
13399 arg_type = TID_ARRAY;
13400 else
13401 arg_type = tid;
13402
13403 /* floats are passed as doubles, at least under NT */
13404 if (tid == TID_FLOAT && !bpointer)
13405 arg_type = TID_DOUBLE;
13406
13407 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13408
13409 rpc_va_arg(&ap, arg_type, args[i]);
13410 }
13411
13412 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13413 char* buf = (char *)malloc(buf_size);
13414 assert(buf);
13415
13416 (*nc) = (NET_COMMAND*) buf;
13417
13418 /* find out if we are on a big endian system */
13419 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13420
13421 char* param_ptr = (*nc)->param;
13422
13423 for (int i=0; rl.param[i].tid != 0; i++) {
13424 int tid = rl.param[i].tid;
13425 int flags = rl.param[i].flags;
13426 int arg_type = 0;
13427
13428 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13429 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13430 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13431
13432 if (bpointer)
13433 arg_type = TID_ARRAY;
13434 else
13435 arg_type = tid;
13436
13437 /* floats are passed as doubles, at least under NT */
13438 if (tid == TID_FLOAT && !bpointer)
13439 arg_type = TID_DOUBLE;
13440
13441 /* get pointer to argument */
13442 //char arg[8];
13443 //rpc_va_arg(&ap, arg_type, arg);
13444
13445 char* arg = args[i];
13446
13447 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13448 if (bbig) {
13449 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13450 arg[0] = arg[3];
13451 }
13452 if (tid == TID_UINT16 || tid == TID_INT16) {
13453 arg[0] = arg[2];
13454 arg[1] = arg[3];
13455 }
13456 }
13457
13458 if (flags & RPC_IN) {
13459 int arg_size = 0;
13460
13461 if (bpointer)
13462 arg_size = rpc_tid_size(tid);
13463 else
13464 arg_size = rpc_tid_size(arg_type);
13465
13466 /* for strings, the argument size depends on the string length */
13467 if (tid == TID_STRING || tid == TID_LINK) {
13468 arg_size = 1 + strlen((char *) *((char **) arg));
13469 }
13470
13471 /* for varibale length arrays, the size is given by
13472 the next parameter on the stack */
13473 if (flags & RPC_VARARRAY) {
13474 //va_list aptmp;
13476 //va_copy(aptmp, ap);
13477
13478 //char arg_tmp[8];
13479 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13480
13481 const char* arg_tmp = args[i+1];
13482
13483 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13484 if (flags & RPC_OUT)
13485 arg_size = *((INT *) *((void **) arg_tmp));
13486 else
13487 arg_size = *((INT *) arg_tmp);
13488
13489 *((INT *) param_ptr) = ALIGN8(arg_size);
13490 param_ptr += ALIGN8(sizeof(INT));
13491
13492 //va_end(aptmp);
13493 }
13494
13495 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13496 arg_size = rl.param[i].n;
13497
13498 /* always align parameter size */
13499 int param_size = ALIGN8(arg_size);
13500
13501 {
13502 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13503
13504 if (param_offset + param_size + 16 > buf_size) {
13505 size_t new_size = param_offset + param_size + 1024;
13506 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13507 buf = (char *) realloc(buf, new_size);
13508 assert(buf);
13509 buf_size = new_size;
13510 (*nc) = (NET_COMMAND*) buf;
13511 param_ptr = buf + param_offset;
13512 }
13513 }
13514
13515 if (bpointer) {
13516 if (debug) {
13517 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);
13518 }
13519 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13520 } else if (tid == TID_FLOAT) {
13521 if (debug) {
13522 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);
13523 }
13524 /* floats are passed as doubles on most systems */
13525 *((float *) param_ptr) = (float) *((double *) arg);
13526 } else {
13527 if (debug) {
13528 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);
13529 }
13530 memcpy(param_ptr, arg, arg_size);
13531 }
13532
13533 param_ptr += param_size;
13534 }
13535 }
13536
13537 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13538
13539 if (debug)
13540 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13541}
13542
13543/********************************************************************/
13544static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13545{
13546 //printf("rpc_call_decode!\n");
13547
13548 bool debug = false;
13549
13550 if (debug)
13551 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13552
13553 /* extract result variables and place it to argument list */
13554
13555 const char* param_ptr = buf;
13556
13557 for (int i = 0; rl.param[i].tid != 0; i++) {
13558 int tid = rl.param[i].tid;
13559 int flags = rl.param[i].flags;
13560 int arg_type = 0;
13561
13562 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13563 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13564 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13565
13566 if (bpointer)
13567 arg_type = TID_ARRAY;
13568 else
13569 arg_type = rl.param[i].tid;
13570
13571 if (tid == TID_FLOAT && !bpointer)
13572 arg_type = TID_DOUBLE;
13573
13574 char arg[8];
13575 rpc_va_arg(&ap, arg_type, arg);
13576
13577 if (rl.param[i].flags & RPC_OUT) {
13578
13579 if (param_ptr == NULL) {
13580 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);
13581 return RPC_NET_ERROR;
13582 }
13583
13584 tid = rl.param[i].tid;
13585 int arg_size = rpc_tid_size(tid);
13586
13587 if (tid == TID_STRING || tid == TID_LINK)
13588 arg_size = strlen((char *) (param_ptr)) + 1;
13589
13590 if (flags & RPC_VARARRAY) {
13591 arg_size = *((INT *) param_ptr);
13592 param_ptr += ALIGN8(sizeof(INT));
13593 }
13594
13595 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13596 arg_size = rl.param[i].n;
13597
13598 /* parameter size is always aligned */
13599 int param_size = ALIGN8(arg_size);
13600
13601 /* return parameters are always pointers */
13602 if (*((char **) arg)) {
13603 if (debug)
13604 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);
13605 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13606 }
13607
13608 param_ptr += param_size;
13609 }
13610 }
13611
13612 return RPC_SUCCESS;
13613}
13614
13615/********************************************************************/
13616static void rpc_call_encode_cxx(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13617{
13618 //printf("rpc_call_encode_cxx!\n");
13619
13620 bool debug = false;
13621
13622 //if (rl.id == RPC_TEST2)
13623 // debug = true;
13624
13625 //if (rl.id == RPC_TEST2_CXX)
13626 // debug = true;
13627
13628 if (debug) {
13629 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13630 for (int i=0; rl.param[i].tid != 0; i++) {
13631 int tid = rl.param[i].tid;
13632 int flags = rl.param[i].flags;
13633 int n = rl.param[i].n;
13634 printf("param %2d, tid %2d, flags 0x%02x, n %3d\n", i, tid, flags, n);
13635 }
13636 }
13637
13638 char args[MAX_RPC_PARAMS][sizeof(char*)];
13639
13640 for (int i=0; rl.param[i].tid != 0; i++) {
13641 int tid = rl.param[i].tid;
13642 int flags = rl.param[i].flags;
13643 int arg_type = 0;
13644
13645 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13646 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13647 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13648
13649 if (bpointer)
13650 arg_type = TID_ARRAY;
13651 else
13652 arg_type = tid;
13653
13654 /* floats are passed as doubles, at least under NT */
13655 if (tid == TID_FLOAT && !bpointer)
13656 arg_type = TID_DOUBLE;
13657
13658 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13659
13660 rpc_va_arg(&ap, arg_type, args[i]);
13661 }
13662
13663 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13664 char* buf = (char *)malloc(buf_size);
13665 assert(buf);
13666
13667 (*nc) = (NET_COMMAND*) buf;
13668
13669 /* find out if we are on a big endian system */
13670 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13671
13672 char* param_ptr = (*nc)->param;
13673
13674 for (int i=0; rl.param[i].tid != 0; i++) {
13675 int tid = rl.param[i].tid;
13676 int flags = rl.param[i].flags;
13677 int arg_type = 0;
13678
13679 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13680 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13681 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13682
13683 if (bpointer)
13684 arg_type = TID_ARRAY;
13685 else
13686 arg_type = tid;
13687
13688 /* floats are passed as doubles, at least under NT */
13689 if (tid == TID_FLOAT && !bpointer)
13690 arg_type = TID_DOUBLE;
13691
13692 /* get pointer to argument */
13693 //char arg[8];
13694 //rpc_va_arg(&ap, arg_type, arg);
13695
13696 char* arg = args[i];
13697
13698 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13699 if (bbig) {
13700 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13701 arg[0] = arg[3];
13702 }
13703 if (tid == TID_UINT16 || tid == TID_INT16) {
13704 arg[0] = arg[2];
13705 arg[1] = arg[3];
13706 }
13707 }
13708
13709 if (flags & RPC_IN) {
13710 int arg_size = 0;
13711
13712 if (bpointer)
13713 arg_size = rpc_tid_size(tid);
13714 else
13715 arg_size = rpc_tid_size(arg_type);
13716
13717 void* parg = (void *) *((void **) arg);
13718
13719 /* for strings, the argument size depends on the string length */
13720 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13721 std::string* s = (std::string*)parg;
13722 arg_size = 1 + s->length();
13723 parg = (void*)s->c_str();
13724 bpointer = TRUE;
13725 //printf("STRING %p, len %zu, [%s]\n", s, s->length(), s->c_str());
13726 } else if (tid == TID_STRING || tid == TID_LINK) {
13727 arg_size = 1 + strlen((char *) *((char **) arg));
13728 }
13729
13730 /* for varibale length arrays, the size is given by
13731 the next parameter on the stack */
13732 if (flags & RPC_VARARRAY) {
13733 //va_list aptmp;
13735 //va_copy(aptmp, ap);
13736
13737 //char arg_tmp[8];
13738 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13739
13740 if (flags & RPC_CXX) {
13741 std::vector<char>* pv = (std::vector<char>*)parg;
13742 arg_size = pv->size();
13743 parg = (void*)pv->data();
13744 bpointer = TRUE;
13745 //printf("VECTOR %p, size %zu\n", pv, pv->size());
13746
13747 *((INT *) param_ptr) = arg_size; // NB: for std:vector<char> data, pass true data size. it is safe because decoder always aligns it themselves.
13748
13749 } else {
13750 const char* arg_tmp = args[i+1];
13751
13752 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13753 if (flags & RPC_OUT) {
13754 arg_size = *((INT *) *((void **) arg_tmp));
13755 } else {
13756 arg_size = *((INT *) arg_tmp);
13757 }
13758
13759 *((INT *) param_ptr) = ALIGN8(arg_size);
13760 }
13761
13762 param_ptr += ALIGN8(sizeof(INT));
13763
13764 //va_end(aptmp);
13765 }
13766
13767 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13768 arg_size = rl.param[i].n;
13769
13770 /* always align parameter size */
13771 int param_size = ALIGN8(arg_size);
13772
13773 {
13774 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13775
13776 if (param_offset + param_size + 16 > buf_size) {
13777 size_t new_size = param_offset + param_size + 1024;
13778 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13779 buf = (char *) realloc(buf, new_size);
13780 assert(buf);
13781 buf_size = new_size;
13782 (*nc) = (NET_COMMAND*) buf;
13783 param_ptr = buf + param_offset;
13784 }
13785 }
13786
13787 if (bpointer) {
13788 if (debug) {
13789 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);
13790 }
13791 memcpy(param_ptr, parg, arg_size);
13792 } else if (tid == TID_FLOAT) {
13793 if (debug) {
13794 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);
13795 }
13796 /* floats are passed as doubles on most systems */
13797 *((float *) param_ptr) = (float) *((double *) arg);
13798 } else {
13799 if (debug) {
13800 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);
13801 }
13802 memcpy(param_ptr, arg, arg_size);
13803 }
13804
13805 param_ptr += param_size;
13806 }
13807 }
13808
13809 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13810
13811 if (debug)
13812 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13813}
13814
13815/********************************************************************/
13816static int rpc_call_decode_cxx(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13817{
13818 //printf("rpc_call_decode_cxx!\n");
13819
13820 bool debug = false;
13821
13822 //if (rl.id == RPC_TEST2)
13823 // debug = true;
13824
13825 //if (rl.id == RPC_TEST2_CXX)
13826 // debug = true;
13827
13828 if (debug)
13829 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13830
13831 /* extract result variables and place it to argument list */
13832
13833 const char* param_ptr = buf;
13834
13835 for (int i = 0; rl.param[i].tid != 0; i++) {
13836 const int tid = rl.param[i].tid;
13837 const int flags = rl.param[i].flags;
13838 int arg_type = 0;
13839
13840 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13841 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13842 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13843
13844 if (bpointer)
13845 arg_type = TID_ARRAY;
13846 else
13847 arg_type = tid;
13848
13849 if (tid == TID_FLOAT && !bpointer)
13850 arg_type = TID_DOUBLE;
13851
13852 char arg[sizeof(double)+sizeof(uint64_t)+sizeof(char*)];
13853 rpc_va_arg(&ap, arg_type, arg);
13854
13855 if (flags & RPC_OUT) {
13856
13857 if (param_ptr == NULL) {
13858 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);
13859 return RPC_NET_ERROR;
13860 }
13861
13862 int arg_size = rpc_tid_size(tid);
13863
13864 if (tid == TID_STRING || tid == TID_LINK) {
13865 arg_size = strlen((char *) (param_ptr)) + 1;
13866
13867 if (debug)
13868 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));
13869 }
13870
13871 if (flags & RPC_VARARRAY) {
13872 arg_size = *((INT *) param_ptr);
13873 param_ptr += ALIGN8(sizeof(INT));
13874 }
13875
13876 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13877 arg_size = rl.param[i].n;
13878
13879 /* parameter size is always aligned */
13880 int param_size = ALIGN8(arg_size);
13881
13882 /* return parameters are always pointers */
13883 void* parg = *(char**) arg;
13884 if (parg) {
13885 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13886 if (debug)
13887 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);
13888 *(std::string*)parg = param_ptr;
13889 } else if ((tid == TID_ARRAY) && (flags & RPC_CXX)) {
13890 if (debug)
13891 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);
13892 std::vector<char>* pvec = (std::vector<char>*)parg;
13893 pvec->clear();
13894 pvec->insert(pvec->end(), param_ptr, param_ptr + arg_size);
13895 } else {
13896 if (debug)
13897 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);
13898 memcpy(parg, param_ptr, arg_size);
13899 }
13900 }
13901
13902 param_ptr += param_size;
13903 }
13904 }
13905
13906 return RPC_SUCCESS;
13907}
13908
13909static int rpc_find_rpc(int routine_id, RPC_LIST* pentry, bool* prpc_cxx)
13910{
13911 rpc_list_mutex.lock();
13912
13913 for (size_t i = 0; i < rpc_list.size(); i++) {
13914 if (rpc_list[i].id == routine_id) {
13915 *pentry = rpc_list[i];
13916
13917 rpc_list_mutex.unlock();
13918
13919 *prpc_cxx = false;
13920
13921 for (int j=0; j<MAX_RPC_PARAMS; j++)
13922 if (pentry->param[j].flags & RPC_CXX)
13923 *prpc_cxx = true;
13924
13925 return RPC_SUCCESS;
13926 }
13927 }
13928
13929 rpc_list_mutex.unlock();
13930
13931 return RPC_INVALID_ID;
13932}
13933
13934/********************************************************************/
13935INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
13936/********************************************************************\
13937
13938 Routine: rpc_client_call
13939
13940 Purpose: Call a function on a MIDAS client
13941
13942 Input:
13943 INT hConn Client connection
13944 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13945
13946 ... variable argument list
13947
13948 Output:
13949 (depends on argument list)
13950
13951 Function value:
13952 RPC_SUCCESS Successful completion
13953 RPC_NET_ERROR Error in socket call
13954 RPC_NO_CONNECTION No active connection
13955 RPC_TIMEOUT Timeout in RPC call
13956 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13957 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13958
13959\********************************************************************/
13960{
13962
13963 if (!c) {
13964 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13965 return RPC_NO_CONNECTION;
13966 }
13967
13968 //printf("rpc_client_call: handle %d, connection: ", hConn);
13969 //c->print();
13970 //printf("\n");
13971
13972 INT i, status;
13973
13974 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13975 routine_id &= ~RPC_NO_REPLY;
13976
13977 //if (rpc_no_reply)
13978 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13979
13980 // make local copy of the client name just in case _client_connection is erased by another thread
13981
13982 /* find rpc_index */
13983
13984 RPC_LIST rpc_entry;
13985 bool rpc_cxx = false;
13986
13987 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
13988
13989 if (status != RPC_SUCCESS) {
13990 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);
13991 c->mutex.unlock();
13992 return RPC_INVALID_ID;
13993 }
13994
13995 const char *rpc_name = rpc_entry.name;
13996
13997 NET_COMMAND *nc = NULL;
13998
13999 /* examine variable argument list and convert it to parameter array */
14000 va_list ap;
14001 va_start(ap, routine_id);
14002
14003 if (rpc_cxx)
14004 rpc_call_encode_cxx(ap, rpc_entry, &nc);
14005 else
14006 rpc_call_encode(ap, rpc_entry, &nc);
14007
14008 va_end(ap);
14009
14010 nc->header.routine_id = routine_id;
14011
14012 if (rpc_no_reply)
14014
14015 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14016
14017 /* in FAST TCP mode, only send call and return immediately */
14018 if (rpc_no_reply) {
14019 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14020
14021 if (i != send_size) {
14022 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);
14023 free(nc);
14024 c->mutex.unlock();
14025 return RPC_NET_ERROR;
14026 }
14027
14028 free(nc);
14029
14030 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
14031 //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);
14032 //c->print();
14033 //printf("\n");
14034 c->close_locked();
14035 }
14036
14037 c->mutex.unlock();
14038 return RPC_SUCCESS;
14039 }
14040
14041 /* in TCP mode, send and wait for reply on send socket */
14042 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14043 if (i != send_size) {
14044 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);
14045 c->mutex.unlock();
14046 return RPC_NET_ERROR;
14047 }
14048
14049 free(nc);
14050 nc = NULL;
14051
14052 bool restore_watchdog_timeout = false;
14053 BOOL watchdog_call;
14054 DWORD watchdog_timeout;
14055 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14056
14057 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
14058
14059 if (c->rpc_timeout >= (int) watchdog_timeout) {
14060 restore_watchdog_timeout = true;
14061 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
14062 }
14063
14064 DWORD rpc_status = 0;
14065 DWORD buf_size = 0;
14066 char* buf = NULL;
14067
14068 /* receive result on send socket */
14069 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
14070
14071 if (restore_watchdog_timeout) {
14072 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
14073 }
14074
14075 if (status == SS_TIMEOUT) {
14076 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);
14077 if (buf)
14078 free(buf);
14079 c->mutex.unlock();
14080 return RPC_TIMEOUT;
14081 }
14082
14083 if (status != SS_SUCCESS) {
14084 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);
14085 if (buf)
14086 free(buf);
14087 c->mutex.unlock();
14088 return RPC_NET_ERROR;
14089 }
14090
14091 c->mutex.unlock();
14092
14093 if (rpc_status == RPC_INVALID_ID) {
14094 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);
14095 if (buf)
14096 free(buf);
14097 return rpc_status;
14098 }
14099
14100 /* extract result variables and place it to argument list */
14101
14102 va_start(ap, routine_id);
14103
14104 if (rpc_cxx)
14105 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14106 else
14107 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14108
14109 if (status != RPC_SUCCESS) {
14110 rpc_status = status;
14111 }
14112
14113 va_end(ap);
14114
14115 if (buf)
14116 free(buf);
14117 buf = NULL;
14118 buf_size = 0;
14119
14120 return rpc_status;
14121}
14122
14123/********************************************************************/
14124INT rpc_call(DWORD routine_id, ...)
14125/********************************************************************\
14126
14127 Routine: rpc_call
14128
14129 Purpose: Call a function on a MIDAS server
14130
14131 Input:
14132 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
14133
14134 ... variable argument list
14135
14136 Output:
14137 (depends on argument list)
14138
14139 Function value:
14140 RPC_SUCCESS Successful completion
14141 RPC_NET_ERROR Error in socket call
14142 RPC_NO_CONNECTION No active connection
14143 RPC_TIMEOUT Timeout in RPC call
14144 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
14145 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
14146
14147\********************************************************************/
14148{
14149 va_list ap;
14150 INT i, status;
14151
14152 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
14153 routine_id &= ~RPC_NO_REPLY;
14154
14155 //if (rpc_no_reply)
14156 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
14157
14158 int send_sock = _server_connection.send_sock;
14159 int rpc_timeout = _server_connection.rpc_timeout;
14160
14161 if (!send_sock) {
14162 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
14163 return RPC_NET_ERROR;
14164 }
14165
14166 if (!_mutex_rpc) {
14167 /* create a local mutex for multi-threaded applications */
14169 }
14170
14171 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
14172 if (status != SS_SUCCESS) {
14173 cm_msg(MERROR, "rpc_call", "Mutex timeout");
14174 return RPC_MUTEX_TIMEOUT;
14175 }
14176
14177 /* find rpc definition */
14178
14179 RPC_LIST rpc_entry;
14180 bool rpc_cxx = false;
14181
14182 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
14183
14184 if (status != RPC_SUCCESS) {
14186 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
14187 return RPC_INVALID_ID;
14188 }
14189
14190 const char* rpc_name = rpc_entry.name;
14191
14192 /* prepare output buffer */
14193
14194 NET_COMMAND* nc = NULL;
14195
14196 /* examine variable argument list and convert it to parameter array */
14197 va_start(ap, routine_id);
14198
14199 if (rpc_cxx)
14200 rpc_call_encode_cxx(ap, rpc_entry, &nc);
14201 else
14202 rpc_call_encode(ap, rpc_entry, &nc);
14203
14204 va_end(ap);
14205
14206 nc->header.routine_id = routine_id;
14207
14208 if (rpc_no_reply)
14210
14211 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14212
14213 /* do not wait for reply if requested RPC_NO_REPLY */
14214 if (rpc_no_reply) {
14215 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14216
14217 if (i != send_size) {
14219 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14220 free(nc);
14221 return RPC_NET_ERROR;
14222 }
14223
14225 free(nc);
14226 return RPC_SUCCESS;
14227 }
14228
14229 /* in TCP mode, send and wait for reply on send socket */
14230 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14231 if (i != send_size) {
14233 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14234 free(nc);
14235 return RPC_NET_ERROR;
14236 }
14237
14238 free(nc);
14239 nc = NULL;
14240
14241 bool restore_watchdog_timeout = false;
14242 BOOL watchdog_call;
14243 DWORD watchdog_timeout;
14244 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14245
14246 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
14247
14248 if (!rpc_is_remote()) {
14249 // if RPC is remote, we are connected to an mserver,
14250 // the mserver takes care of watchdog timeouts.
14251 // otherwise we should make sure the watchdog timeout
14252 // is longer than the RPC timeout. K.O.
14253 if (rpc_timeout >= (int) watchdog_timeout) {
14254 restore_watchdog_timeout = true;
14255 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
14256 }
14257 }
14258
14259 DWORD rpc_status = 0;
14260 DWORD buf_size = 0;
14261 char* buf = NULL;
14262
14263 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
14264
14265 if (restore_watchdog_timeout) {
14266 cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
14267 }
14268
14269 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
14270
14272
14273 /* check for reply errors */
14274
14275 if (status == SS_TIMEOUT) {
14276 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
14277 if (buf)
14278 free(buf);
14279 abort(); // cannot continue - our mserver is not talking to us!
14280 return RPC_TIMEOUT;
14281 }
14282
14283 if (status != SS_SUCCESS) {
14284 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
14285 if (buf)
14286 free(buf);
14287 abort(); // cannot continue - something is wrong with our mserver connection
14288 return RPC_NET_ERROR;
14289 }
14290
14291 if (rpc_status == RPC_INVALID_ID) {
14292 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
14293 if (buf)
14294 free(buf);
14295 return rpc_status;
14296 }
14297
14298 /* extract result variables and place it to argument list */
14299
14300 va_start(ap, routine_id);
14301
14302 if (rpc_cxx)
14303 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14304 else
14305 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14306
14307 if (status != RPC_SUCCESS) {
14308 rpc_status = status;
14309 }
14310
14311 va_end(ap);
14312
14313 if (buf)
14314 free(buf);
14315
14316 return rpc_status;
14317}
14318
14319
14320/********************************************************************/
14322 INT old;
14323
14324 old = _opt_tcp_size;
14325 _opt_tcp_size = tcp_size;
14326 return old;
14327}
14328
14330 return _opt_tcp_size;
14331}
14332
14334#endif /* DOXYGEN_SHOULD_SKIP_THIS */
14335
14336/********************************************************************/
14358INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
14359{
14360 if (rpc_is_remote()) {
14361 return rpc_send_event1(buffer_handle, pevent);
14362 } else {
14363 return bm_send_event(buffer_handle, pevent, unused, async_flag);
14364 }
14365}
14366
14367/********************************************************************/
14376INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
14377{
14378 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
14379 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
14380}
14381
14382INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
14383{
14384 if (sg_n < 1) {
14385 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
14386 return BM_INVALID_SIZE;
14387 }
14388
14389 if (sg_ptr[0] == NULL) {
14390 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
14391 return BM_INVALID_SIZE;
14392 }
14393
14394 if (sg_len[0] < sizeof(EVENT_HEADER)) {
14395 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));
14396 return BM_INVALID_SIZE;
14397 }
14398
14399 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
14400
14401 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
14402 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
14403
14404 if (data_size == 0) {
14405 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
14406 return BM_INVALID_SIZE;
14407 }
14408
14409 if (data_size > MAX_DATA_SIZE) {
14410 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);
14411 return BM_INVALID_SIZE;
14412 }
14413
14414 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
14415 const size_t total_size = ALIGN8(event_size);
14416
14417 size_t count = 0;
14418 for (int i=0; i<sg_n; i++) {
14419 count += sg_len[i];
14420 }
14421
14422 if (count != event_size) {
14423 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);
14424 return BM_INVALID_SIZE;
14425 }
14426
14427 // protect non-atomic access to _server_connection.event_sock. K.O.
14428
14429 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
14430
14431 //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);
14432
14433 if (_server_connection.event_sock == 0) {
14434 return RPC_NO_CONNECTION;
14435 }
14436
14437 //
14438 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14439 //
14440 // 4 bytes of buffer handle
14441 // 16 bytes of event header, includes data_size
14442 // ALIGN8(data_size) bytes of event data
14443 //
14444
14445 int status;
14446
14447 /* send buffer handle */
14448
14449 assert(sizeof(DWORD) == 4);
14450 DWORD bh_buf = buffer_handle;
14451
14452 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14453 if (status != SS_SUCCESS) {
14455 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14456 return RPC_NET_ERROR;
14457 }
14458
14459 /* send data */
14460
14461 for (int i=0; i<sg_n; i++) {
14462 status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
14463 if (status != SS_SUCCESS) {
14465 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14466 return RPC_NET_ERROR;
14467 }
14468 }
14469
14470 /* send padding */
14471
14472 if (count < total_size) {
14473 char padding[8] = { 0,0,0,0,0,0,0,0 };
14474 size_t padlen = total_size - count;
14475 assert(padlen < 8);
14477 if (status != SS_SUCCESS) {
14479 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14480 return RPC_NET_ERROR;
14481 }
14482 }
14483
14484 return RPC_SUCCESS;
14485}
14486
14487/********************************************************************/
14496 return RPC_SUCCESS;
14497}
14498
14499/********************************************************************/
14500
14501struct TR_FIFO {
14502 int transition = 0;
14503 int run_number = 0;
14504 time_t trans_time = 0;
14506};
14507
14508static std::mutex _tr_fifo_mutex;
14510static int _tr_fifo_wp = 0;
14511static int _tr_fifo_rp = 0;
14512
14513static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
14514/********************************************************************\
14515
14516 Routine: rpc_transition_dispatch
14517
14518 Purpose: Gets called when a transition function was registered and
14519 a transition occured. Internal use only.
14520
14521 Input:
14522 INT idx RPC function ID
14523 void *prpc_param RPC parameters
14524
14525 Output:
14526 none
14527
14528 Function value:
14529 INT return value from called user routine
14530
14531\********************************************************************/
14532{
14533 /* erase error string */
14534 *(CSTRING(2)) = 0;
14535
14536 if (idx == RPC_RC_TRANSITION) {
14537 // find registered handler
14538 // NB: this code should match same code in cm_transition_call_direct()
14539 // NB: only use the first handler, this is how MIDAS always worked
14540 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14541 _trans_table_mutex.lock();
14542 size_t n = _trans_table.size();
14543 _trans_table_mutex.unlock();
14544
14545 for (size_t i = 0; i < n; i++) {
14546 _trans_table_mutex.lock();
14548 _trans_table_mutex.unlock();
14549
14550 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14551 if (tt.func) {
14552 /* execute callback if defined */
14553 return tt.func(CINT(1), CSTRING(2));
14554 } else {
14555 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14556 /* store transition in FIFO */
14559 _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
14561 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14562 // implicit unlock
14563 return RPC_SUCCESS;
14564 }
14565 }
14566 }
14567 // no handler for this transition
14568 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14569 return CM_SUCCESS;
14570 } else {
14571 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14572 return RPC_INVALID_ID;
14573 }
14574}
14575
14576/********************************************************************/
14577int cm_query_transition(int *transition, int *run_number, int *trans_time)
14578/********************************************************************\
14579
14580 Routine: cm_query_transition
14581
14582 Purpose: Query system if transition has occured. Normally, one
14583 registers callbacks for transitions via
14584 cm_register_transition. In some environments however,
14585 callbacks are not possible. In that case one spciefies
14586 a NULL pointer as the callback routine and can query
14587 transitions "manually" by calling this functions. A small
14588 FIFO takes care that no transition is lost if this functions
14589 did not get called between some transitions.
14590
14591 Output:
14592 INT *transition Type of transition, one of TR_xxx
14593 INT *run_nuber Run number for transition
14594 time_t *trans_time Time (in UNIX time) of transition
14595
14596 Function value:
14597 FALSE No transition occured since last call
14598 TRUE Transition occured
14599
14600\********************************************************************/
14601{
14602 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14603
14604 if (_tr_fifo_wp == _tr_fifo_rp)
14605 return FALSE;
14606
14607 if (transition)
14609
14610 if (run_number)
14612
14613 if (trans_time)
14614 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14615
14616 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14617
14618 // implicit unlock
14619 return TRUE;
14620}
14621
14622/********************************************************************\
14623* server functions *
14624\********************************************************************/
14625
14626#if 0
14627void debug_dump(unsigned char *p, int size)
14628{
14629 int i, j;
14630 unsigned char c;
14631
14632 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14633 printf("%p ", p + i * 16);
14634 for (j = 0; j < 16; j++)
14635 if (i * 16 + j < size)
14636 printf("%02X ", p[i * 16 + j]);
14637 else
14638 printf(" ");
14639 printf(" ");
14640
14641 for (j = 0; j < 16; j++) {
14642 c = p[i * 16 + j];
14643 if (i * 16 + j < size)
14644 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14645 }
14646 printf("\n");
14647 }
14648
14649 printf("\n");
14650}
14651#endif
14652
14653/********************************************************************/
14654static int recv_net_command_realloc(RPC_SERVER_ACCEPTION* sa, char **pbuf, int *pbufsize, INT *remaining)
14655/********************************************************************\
14656
14657 Routine: recv_net_command
14658
14659 Purpose: TCP receive routine with local cache. To speed up network
14660 performance, a 64k buffer is read in at once and split into
14661 several RPC command on successive calls to recv_net_command.
14662 Therefore, the number of recv() calls is minimized.
14663
14664 This routine is ment to be called by the server process.
14665 Clients should call recv_tcp instead.
14666
14667 Input:
14668 INT idx Index of server connection
14669 DWORD buffer_size Size of the buffer in bytes.
14670 INT flags Flags passed to recv()
14671 INT convert_flags Convert flags needed for big/little
14672 endian conversion
14673
14674 Output:
14675 char *buffer Network receive buffer.
14676 INT *remaining Remaining data in cache
14677
14678 Function value:
14679 INT Same as recv()
14680
14681\********************************************************************/
14682{
14683 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14684
14685 int sock = sa->recv_sock;
14686
14687 if (!sa->net_buffer) {
14688 if (sa->is_mserver)
14690 else
14692
14693 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14694 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14695 sa->write_ptr = 0;
14696 sa->read_ptr = 0;
14697 sa->misalign = 0;
14698 }
14699 if (!sa->net_buffer) {
14700 cm_msg(MERROR, "recv_net_command_realloc", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14701 return -1;
14702 }
14703
14704 int copied = 0;
14705 int param_size = -1;
14706
14707 int write_ptr = sa->write_ptr;
14708 int read_ptr = sa->read_ptr;
14709 int misalign = sa->misalign;
14710 char *net_buffer = sa->net_buffer;
14711
14712 do {
14713 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14714 if (param_size == -1) {
14715 if (copied > 0) {
14716 /* assemble split header */
14717 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14718 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14719 param_size = (INT) nc->header.param_size;
14720 } else {
14721 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14722 param_size = (INT) nc->header.param_size;
14723 }
14724
14725 if (sa->convert_flags)
14726 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14727 }
14728
14729 //printf("recv_net_command_realloc: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14730
14731 /* check if parameters fit in buffer */
14732 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14733 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14734 char *p = (char *) realloc(*pbuf, new_size);
14735 //printf("recv_net_command_realloc: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14736 if (p == NULL) {
14737 cm_msg(MERROR, "recv_net_command_realloc", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14738 sa->read_ptr = 0;
14739 sa->write_ptr = 0;
14740 return -1;
14741 }
14742 *pbuf = p;
14743 *pbufsize = new_size;
14744 }
14745
14746 buffer = *pbuf;
14747
14748 /* check if we have all parameters in buffer */
14749 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14750 break;
14751 }
14752
14753 /* not enough data, so copy partially and get new */
14754 int size = write_ptr - read_ptr;
14755
14756 if (size > 0) {
14757 memcpy(buffer + copied, net_buffer + read_ptr, size);
14758 copied += size;
14759 read_ptr = write_ptr;
14760 }
14761#ifdef OS_UNIX
14762 do {
14763 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14764
14765 /* don't return if an alarm signal was cought */
14766 } while (write_ptr == -1 && errno == EINTR);
14767#else
14768 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14769#endif
14770
14771 /* abort if connection broken */
14772 if (write_ptr <= 0) {
14773 if (write_ptr == 0)
14774 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());
14775 else
14776 cm_msg(MERROR, "recv_net_command_realloc", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14777
14778 if (remaining)
14779 *remaining = 0;
14780
14781 return write_ptr;
14782 }
14783
14784 read_ptr = misalign;
14785 write_ptr += misalign;
14786
14787 misalign = write_ptr % 8;
14788 } while (TRUE);
14789
14790 /* copy rest of parameters */
14791 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14792 memcpy(buffer + copied, net_buffer + read_ptr, size);
14793 read_ptr += size;
14794
14795 if (remaining) {
14796 /* don't keep rpc_server_receive in an infinite loop */
14797 if (write_ptr - read_ptr < param_size)
14798 *remaining = 0;
14799 else
14800 *remaining = write_ptr - read_ptr;
14801 }
14802
14803 sa->write_ptr = write_ptr;
14804 sa->read_ptr = read_ptr;
14805 sa->misalign = misalign;
14806
14807 return size + copied;
14808}
14809
14810
14811/********************************************************************/
14813/********************************************************************\
14814
14815 Routine: recv_tcp_check
14816
14817 Purpose: Check if in TCP receive buffer associated with sock is
14818 some data. Called by ss_suspend.
14819
14820 Input:
14821 INT sock TCP receive socket
14822
14823 Output:
14824 none
14825
14826 Function value:
14827 INT count Number of bytes remaining in TCP buffer
14828
14829\********************************************************************/
14830{
14831 /* figure out to which connection socket belongs */
14832 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14833 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14834 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14835 }
14836
14837 return 0;
14838}
14839
14840
14841/********************************************************************/
14842static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
14843/********************************************************************\
14844
14845 Routine: recv_event_server_realloc
14846
14847 Purpose: receive events sent by rpc_send_event()
14848
14849 Input:
14850 INT idx Index of server connection
14851 DWORD buffer_size Size of the buffer in bytes.
14852 INT flags Flags passed to recv()
14853 INT convert_flags Convert flags needed for big/little
14854 endian conversion
14855
14856 Output:
14857 char *buffer Network receive buffer.
14858 INT *remaining Remaining data in cache
14859
14860 Function value:
14861 INT Same as recv()
14862
14863\********************************************************************/
14864{
14865 int sock = psa->event_sock;
14866
14867 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14868
14869 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14870
14871 char header_buf[header_size];
14872
14873 // First read the header.
14874 //
14875 // Data format is:
14876 // INT buffer handle (4 bytes)
14877 // EVENT_HEADER (16 bytes)
14878 // event data
14879 // ALIGN8() padding
14880 // ...next event
14881
14882 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14883
14884 if (hrd == 0) {
14885 // timeout waiting for data
14886 return 0;
14887 }
14888
14889 /* abort if connection broken */
14890 if (hrd < 0) {
14891 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14892 return -1;
14893 }
14894
14895 if (hrd < (int) header_size) {
14896 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14897
14898 /* abort if connection broken */
14899 if (hrd1 <= 0) {
14900 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14901 return -1;
14902 }
14903
14904 hrd += hrd1;
14905 }
14906
14907 /* abort if connection broken */
14908 if (hrd != (int) header_size) {
14909 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14910 return -1;
14911 }
14912
14913 INT *pbh = (INT *) header_buf;
14914 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14915
14916 /* convert header little endian/big endian */
14917 if (psa->convert_flags) {
14924 }
14925
14926 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14927 int total_size = ALIGN8(event_size);
14928
14929 //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);
14930
14931 if (pevent->data_size == 0) {
14932 for (int i=0; i<5; i++) {
14933 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14934 }
14935 abort();
14936 }
14937
14938 /* check for sane event size */
14939 if (event_size <= 0 || total_size <= 0) {
14940 cm_msg(MERROR, "recv_event_server",
14941 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14942 event_size, total_size);
14943 return -1;
14944 }
14945
14946 //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);
14947
14948
14949 int bufsize = sizeof(INT) + total_size;
14950
14951 // Second, check that output buffer is big enough
14952
14953 /* check if data part fits in buffer */
14954 if (*pbuffer_size < bufsize) {
14955 int newsize = 1024 + ALIGN8(bufsize);
14956
14957 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14958
14959 char *newbuf = (char *) realloc(*pbuffer, newsize);
14960 if (newbuf == NULL) {
14961 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14962 newsize);
14963 return -1;
14964 }
14965 *pbuffer = newbuf;
14966 *pbuffer_size = newsize;
14967 }
14968
14969 // Third, copy header into output buffer
14970
14971 memcpy(*pbuffer, header_buf, header_size);
14972
14973 // Forth, read the event data
14974
14975 int to_read = sizeof(INT) + total_size - header_size;
14976 int rptr = header_size;
14977
14978 if (to_read > 0) {
14979 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14980
14981 /* abort if connection broken */
14982 if (drd <= 0) {
14983 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14984 return -1;
14985 }
14986 }
14987
14988 return bufsize;
14989}
14990
14991
14992/********************************************************************/
14993INT rpc_register_server(int port, int *plsock, int *pport)
14994/********************************************************************\
14995
14996 Routine: rpc_register_listener
14997
14998 Purpose: Register the calling process as a MIDAS RPC server. Note
14999 that cm_connnect_experiment must be called prior to any call of
15000 rpc_register_server.
15001
15002 Input:
15003 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
15004
15005 Output:
15006 int *plsock Listener socket, can be NULL
15007 int *pport Port under which server is listening, can be NULL
15008
15009 Function value:
15010 RPC_SUCCESS Successful completion
15011 RPC_NET_ERROR Error in socket call
15012 RPC_NOT_REGISTERED cm_connect_experiment was not called
15013
15014\********************************************************************/
15015{
15016 int status;
15017 int lsock;
15018
15019 status = rpc_register_listener(port, NULL, &lsock, pport);
15020 if (status != RPC_SUCCESS)
15021 return status;
15022
15024 if (status != SS_SUCCESS)
15025 return status;
15026
15027 if (plsock)
15028 *plsock = lsock;
15029
15030 return RPC_SUCCESS;
15031}
15032
15033/********************************************************************/
15034INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
15035/********************************************************************\
15036
15037 Routine: rpc_register_listener
15038
15039 Purpose: Register the calling process as a MIDAS RPC server. Note
15040 that cm_connnect_experiment must be called prior to any call of
15041 rpc_register_listener.
15042
15043 Input:
15044 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
15045 INT *func Default dispatch function
15046
15047 Output:
15048 int *plsock Listener socket, should not be NULL
15049 int *pport Port under which server is listening, can be NULL
15050
15051 Function value:
15052 RPC_SUCCESS Successful completion
15053 RPC_NET_ERROR Error in socket call
15054 RPC_NOT_REGISTERED cm_connect_experiment was not called
15055
15056\********************************************************************/
15057{
15058 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
15060
15061 /* create a socket for listening */
15062 int lsock = 0;
15063 int lport = 0;
15064 std::string errmsg;
15065
15066 int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
15067
15068 if (status != SS_SUCCESS) {
15069 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
15070 return RPC_NET_ERROR;
15071 }
15072
15073 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
15074#if defined(F_SETFD) && defined(FD_CLOEXEC)
15075 status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
15076 if (status < 0) {
15077 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
15078 return RPC_NET_ERROR;
15079 }
15080#endif
15081
15082 /* return port wich OS has choosen */
15083 if (pport) {
15084 *pport = lport;
15085 }
15086
15087 if (plsock)
15088 *plsock = lsock;
15089
15090 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
15091
15092 return RPC_SUCCESS;
15093}
15094
15100
15102static int tls_size = 0;
15103
15104/********************************************************************/
15105static INT rpc_execute_old(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15106/********************************************************************\
15107
15108 Routine: rpc_execute
15109
15110 Purpose: Execute a RPC command received over the network
15111
15112 Input:
15113 INT sock TCP socket to which the result should be
15114 send back
15115
15116 char *buffer Command buffer
15117 INT convert_flags Flags for data conversion
15118
15119 Output:
15120 none
15121
15122 Function value:
15123 RPC_SUCCESS Successful completion
15124 RPC_INVALID_ID Invalid routine_id received
15125 RPC_NET_ERROR Error in socket call
15126 RPC_EXCEED_BUFFER Not enough memory for network buffer
15127 RPC_SHUTDOWN Shutdown requested
15128 SS_ABORT TCP connection broken
15129 SS_EXIT TCP connection closed
15130
15131\********************************************************************/
15132{
15133 INT i, routine_id, status;
15134 char *in_param_ptr, *out_param_ptr, *last_param_ptr;
15135 INT tid, flags;
15136 NET_COMMAND *nc_in, *nc_out;
15137 INT param_size, max_size;
15138 void *prpc_param[20];
15139 char debug_line[1024], *return_buffer;
15140 int return_buffer_size;
15141 int return_buffer_tls;
15142#ifdef FIXED_BUFFER
15143 int initial_buffer_size = NET_BUFFER_SIZE;
15144#else
15145 int initial_buffer_size = 1024;
15146#endif
15147
15148 /* return buffer must must use thread local storage multi-thread servers */
15149 if (!tls_size) {
15150 tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
15152 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15153 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15154 tls_size = 1;
15155 }
15156 for (i = 0; i < tls_size; i++)
15157 if (tls_buffer[i].thread_id == ss_gettid())
15158 break;
15159 if (i == tls_size) {
15160 /* new thread -> allocate new buffer */
15161 tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
15163 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15164 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15165 tls_size++;
15166 }
15167
15168 return_buffer_tls = i;
15169 return_buffer_size = tls_buffer[i].buffer_size;
15170 return_buffer = tls_buffer[i].buffer;
15171 assert(return_buffer);
15172
15173 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
15174 // and valgrind complains that we transmit uninitialized data
15175 //memset(return_buffer, 0, return_buffer_size);
15176
15177 /* extract pointer array to parameters */
15178 nc_in = (NET_COMMAND *) buffer;
15179
15180 /* convert header format (byte swapping) */
15181 if (convert_flags) {
15182 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15183 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15184 }
15185
15186 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15187 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15188 //}
15189
15190 /* no result return as requested */
15191 if (nc_in->header.routine_id & RPC_NO_REPLY)
15192 sock = 0;
15193
15194 /* find entry in rpc_list */
15195 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15196
15197 assert(xroutine_id == routine_id);
15198
15199 again:
15200
15201 in_param_ptr = nc_in->param;
15202
15203 nc_out = (NET_COMMAND *) return_buffer;
15204 out_param_ptr = nc_out->param;
15205
15206 sprintf(debug_line, "%s(", rl.name);
15207
15208 for (i = 0; rl.param[i].tid != 0; i++) {
15209 tid = rl.param[i].tid;
15210 flags = rl.param[i].flags;
15211
15212 if (flags & RPC_IN) {
15213 param_size = ALIGN8(rpc_tid_size(tid));
15214
15215 if (tid == TID_STRING || tid == TID_LINK)
15216 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
15217
15218 if (flags & RPC_VARARRAY) {
15219 /* for arrays, the size is stored as a INT in front of the array */
15220 param_size = *((INT *) in_param_ptr);
15221 if (convert_flags)
15222 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
15223 param_size = ALIGN8(param_size);
15224
15225 in_param_ptr += ALIGN8(sizeof(INT));
15226 }
15227
15228 if (tid == TID_STRUCT)
15229 param_size = ALIGN8(rl.param[i].n);
15230
15231 prpc_param[i] = in_param_ptr;
15232
15233 /* convert data format */
15234 if (convert_flags) {
15235 if (flags & RPC_VARARRAY)
15236 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15237 else
15238 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
15239 convert_flags);
15240 }
15241
15242 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
15243 if (rl.param[i].tid == TID_STRING) {
15244 /* check for long strings (db_create_record...) */
15245 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
15246 strcat(debug_line, "\"");
15247 strcat(debug_line, str.c_str());
15248 strcat(debug_line, "\"");
15249 } else
15250 strcat(debug_line, "...");
15251 } else
15252 strcat(debug_line, str.c_str());
15253
15254 in_param_ptr += param_size;
15255 }
15256
15257 if (flags & RPC_OUT) {
15258 param_size = ALIGN8(rpc_tid_size(tid));
15259
15260 if (flags & RPC_VARARRAY || tid == TID_STRING) {
15261
15262 /* save maximum array length from the value of the next argument.
15263 * this means RPC_OUT arrays and strings should always be passed like this:
15264 * rpc_call(..., array_ptr, array_max_size, ...); */
15265
15266 max_size = *((INT *) in_param_ptr);
15267
15268 if (convert_flags)
15269 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15270 max_size = ALIGN8(max_size);
15271
15272 *((INT *) out_param_ptr) = max_size;
15273
15274 /* save space for return array length */
15275 out_param_ptr += ALIGN8(sizeof(INT));
15276
15277 /* use maximum array length from input */
15278 param_size = max_size;
15279 }
15280
15281 if (rl.param[i].tid == TID_STRUCT)
15282 param_size = ALIGN8(rl.param[i].n);
15283
15284 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
15285#ifdef FIXED_BUFFER
15286 cm_msg(MERROR, "rpc_execute",
15287 "return parameters (%d) too large for network buffer (%d)",
15288 (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
15289
15290 return RPC_EXCEED_BUFFER;
15291#else
15292 int itls;
15293 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
15294
15295#if 0
15296 cm_msg(MINFO, "rpc_execute",
15297 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
15298 (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
15299#endif
15300
15301 itls = return_buffer_tls;
15302
15303 tls_buffer[itls].buffer_size = new_size;
15304 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
15305
15306 if (!tls_buffer[itls].buffer) {
15307 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
15308 return RPC_EXCEED_BUFFER;
15309 }
15310
15311 return_buffer_size = tls_buffer[itls].buffer_size;
15312 return_buffer = tls_buffer[itls].buffer;
15313 assert(return_buffer);
15314
15315 goto again;
15316#endif
15317 }
15318
15319 /* if parameter goes both directions, copy input to output */
15320 if (rl.param[i].flags & RPC_IN)
15321 memcpy(out_param_ptr, prpc_param[i], param_size);
15322
15323 if (_debug_print && !(flags & RPC_IN))
15324 strcat(debug_line, "-");
15325
15326 prpc_param[i] = out_param_ptr;
15327 out_param_ptr += param_size;
15328 }
15329
15330 if (rl.param[i + 1].tid)
15331 strcat(debug_line, ", ");
15332 }
15333
15334 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
15335
15336 strcat(debug_line, ")");
15337 rpc_debug_printf(debug_line);
15338
15339 last_param_ptr = out_param_ptr;
15340
15341 /*********************************\
15342 * call dispatch function *
15343 \*********************************/
15344 if (rl.dispatch)
15345 status = rl.dispatch(routine_id, prpc_param);
15346 else
15348
15349 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15351
15352 /* return immediately for closed down client connections */
15353 if (!sock && routine_id == RPC_ID_EXIT)
15354 return SS_EXIT;
15355
15356 if (!sock && routine_id == RPC_ID_SHUTDOWN)
15357 return RPC_SHUTDOWN;
15358
15359 /* Return if TCP connection broken */
15360 if (status == SS_ABORT)
15361 return SS_ABORT;
15362
15363 /* if sock == 0, we are in FTCP mode and may not sent results */
15364 if (!sock)
15365 return RPC_SUCCESS;
15366
15367 /* compress variable length arrays */
15368 out_param_ptr = nc_out->param;
15369 for (i = 0; rl.param[i].tid != 0; i++)
15370 if (rl.param[i].flags & RPC_OUT) {
15371 tid = rl.param[i].tid;
15372 flags = rl.param[i].flags;
15373 param_size = ALIGN8(rpc_tid_size(tid));
15374
15375 if (tid == TID_STRING) {
15376 max_size = *((INT *) out_param_ptr);
15377 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15378 // and prpc_param() is now pointing to the wrong place. here we know our string data
15379 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
15380 //const char* param_ptr = (char *) prpc_param[i];
15381 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
15382 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
15383 param_size = strlen(param_ptr) + 1;
15384 param_size = ALIGN8(param_size);
15385
15386 /* move string ALIGN8(sizeof(INT)) left */
15387 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
15388
15389 /* move remaining parameters to end of string */
15390 memmove(out_param_ptr + param_size,
15391 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
15392 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
15393 }
15394
15395 if (flags & RPC_VARARRAY) {
15396 /* store array length at current out_param_ptr */
15397 max_size = *((INT *) out_param_ptr);
15398 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15399 // and prpc_param() is now pointing to the wrong place. instead, compute location
15400 // of next parameter using max_size. K.O.
15401 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
15402 if (rl.param[i+1].flags & RPC_OUT)
15403 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
15404 else
15405 param_size = *((INT *) prpc_param[i + 1]);
15406 *((INT *) out_param_ptr) = param_size; // store new array size
15407 if (convert_flags)
15408 rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
15409
15410 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
15411
15412 param_size = ALIGN8(param_size);
15413
15414 /* move remaining parameters to end of array */
15415 memmove(out_param_ptr + param_size,
15416 out_param_ptr + max_size,
15417 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
15418 }
15419
15420 if (tid == TID_STRUCT)
15421 param_size = ALIGN8(rl.param[i].n);
15422
15423 /* convert data format */
15424 if (convert_flags) {
15425 if (flags & RPC_VARARRAY)
15426 rpc_convert_data(out_param_ptr, tid,
15427 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15428 else
15429 rpc_convert_data(out_param_ptr, tid,
15430 rl.param[i].flags | RPC_OUTGOING,
15431 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15432 }
15433
15434 out_param_ptr += param_size;
15435 }
15436
15437 /* send return parameters */
15438 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15439 nc_out->header.routine_id = status;
15440 nc_out->header.param_size = param_size;
15441
15442 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15443
15444 /* convert header format (byte swapping) if necessary */
15445 if (convert_flags) {
15446 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15447 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15448 }
15449
15450 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15451 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15452
15453 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15454
15455 if (status < 0) {
15456 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15457 return RPC_NET_ERROR;
15458 }
15459
15460 /* print return buffer */
15461/*
15462 printf("Return buffer, ID %d:\n", routine_id);
15463 for (i=0; i<param_size ; i++)
15464 {
15465 status = (char) nc_out->param[i];
15466 printf("%02X ", status);
15467 if (i%8 == 7)
15468 printf("\n");
15469 }
15470*/
15471 /* return SS_EXIT if RPC_EXIT is called */
15472 if (routine_id == RPC_ID_EXIT)
15473 return SS_EXIT;
15474
15475 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15476 if (routine_id == RPC_ID_SHUTDOWN)
15477 return RPC_SHUTDOWN;
15478
15479 return RPC_SUCCESS;
15480}
15481
15482
15483
15484class RPE
15485{
15486public:
15487 size_t offset = 0;
15488 size_t arg_size = 0;
15489 size_t param_size = 0;
15491 size_t out_max_size = 0;
15492 std::string* ps = NULL;
15493 std::vector<char>* pv = NULL;
15494 void* pparam = NULL;
15495
15496 RPE() // ctor
15497 {
15498 }
15499 ~RPE() // dtor
15500 {
15501 if (ps)
15502 delete ps;
15503 if (pv)
15504 delete pv;
15505 ps = NULL; // poison the pointer
15506 pv = NULL; // poison the pointer
15507 pparam = NULL; // poison the pointer
15508 }
15509};
15510
15511/********************************************************************/
15512static INT rpc_execute_cxx(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15513/********************************************************************\
15514
15515 Routine: rpc_execute
15516
15517 Purpose: Execute a RPC command received over the network
15518
15519 Input:
15520 INT sock TCP socket to which the result should be
15521 send back
15522
15523 char *buffer Command buffer
15524 INT convert_flags Flags for data conversion
15525
15526 Output:
15527 none
15528
15529 Function value:
15530 RPC_SUCCESS Successful completion
15531 RPC_INVALID_ID Invalid routine_id received
15532 RPC_NET_ERROR Error in socket call
15533 RPC_EXCEED_BUFFER Not enough memory for network buffer
15534 RPC_SHUTDOWN Shutdown requested
15535 SS_ABORT TCP connection broken
15536 SS_EXIT TCP connection closed
15537
15538\********************************************************************/
15539{
15540 INT status;
15541
15542 bool debug = false;
15543
15544 /* extract pointer array to parameters */
15545 NET_COMMAND* nc_in = (NET_COMMAND *) buffer;
15546
15547 /* convert header format (byte swapping) */
15548 if (convert_flags) {
15549 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15550 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15551 }
15552
15553 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15554 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15555 //}
15556
15557 /* no result return as requested */
15558 if (nc_in->header.routine_id & RPC_NO_REPLY)
15559 sock = 0;
15560
15561 int routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15562
15563 assert(xroutine_id == routine_id);
15564
15565#if 0
15566 if (routine_id == RPC_TEST2)
15567 debug = true;
15568
15569 if (routine_id == RPC_TEST2_CXX)
15570 debug = true;
15571
15572 if (routine_id == RPC_TEST3_CXX)
15573 debug = true;
15574
15575 if (routine_id == RPC_TEST4_CXX)
15576 debug = true;
15577#endif
15578
15579 /* find entry in rpc_list */
15580
15581 char* in_param_ptr = (char*)nc_in->param;
15582
15583 if (debug)
15584 printf("rpc_execute_cxx: routine_id %d, name \"%s\"\n", routine_id, rl.name);
15585
15586 void *prpc_param[MAX_RPC_PARAMS];
15587
15588 size_t in_param_size[MAX_RPC_PARAMS];
15589 size_t in_param_offset[MAX_RPC_PARAMS];
15590
15591 std::vector<RPE> params;
15592
15593 int nparams = 0;
15594 for (int i = 0; rl.param[i].tid != 0; i++) {
15595 nparams++;
15596 }
15597
15598 params.resize(nparams);
15599
15600 size_t in_offset = 0;
15601
15602 for (int i = 0; i < nparams; i++) {
15603 in_param_size[i] = 0;
15604 in_param_offset[i] = 0;
15605
15606 int tid = rl.param[i].tid;
15607 int flags = rl.param[i].flags;
15608
15609 if (flags & RPC_IN) {
15610 int arg_size = rpc_tid_size(tid);
15611
15612 if (tid == TID_STRING || tid == TID_LINK) {
15613 arg_size = 1 + strlen((char *) (in_param_ptr));
15614 }
15615
15616 if (flags & RPC_VARARRAY) {
15617 /* for arrays, the size is stored as a INT in front of the array */
15618 int arg_size_align8 = *((INT *) in_param_ptr);
15619 if (convert_flags)
15620 rpc_convert_single(&arg_size_align8, TID_INT32, 0, convert_flags);
15621 in_param_ptr += ALIGN8(sizeof(INT));
15622 in_offset += ALIGN8(sizeof(INT));
15623 if (flags & RPC_CXX) {
15624 /* for std::vector<char> data, it is the true length of the array */
15625 arg_size = arg_size_align8;
15626 } else {
15627 /* true size is stored in the next parameter */
15628 arg_size = *((INT *) (((char*)in_param_ptr) + ALIGN8(arg_size_align8))); // NB: this ALIGN8() is redundant with ALIGN8() in the RPC client *encoder*
15629 if (convert_flags)
15630 rpc_convert_single(&arg_size, TID_INT32, 0, convert_flags);
15631 }
15632 //printf("RPC_VARARRAY: arg_size %d %d\n", arg_size_align8, arg_size);
15633
15634 if (ALIGN8(arg_size_align8) != ALIGN8(arg_size)) {
15635 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);
15636 return RPC_INVALID_ID;
15637 }
15638 }
15639
15640 if (tid == TID_STRUCT) {
15641 arg_size = rl.param[i].n;
15642 }
15643
15644 int param_size = ALIGN8(arg_size);
15645
15646 in_param_size[i] = param_size;
15647 in_param_offset[i] = in_offset;
15648
15649 params[i].offset = in_offset;
15650 params[i].arg_size = arg_size;
15651 params[i].param_size = param_size;
15652
15653 /* convert data format */
15654 if (convert_flags) {
15655 if (flags & RPC_VARARRAY) {
15656 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15657 } else {
15658 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid), convert_flags);
15659 }
15660 }
15661
15662 in_param_ptr += param_size;
15663 in_offset += param_size;
15664 }
15665
15666 if (flags & RPC_OUT) {
15667 params[i].out_max_size = rpc_tid_size(tid);
15668
15669 if (flags & RPC_CXX) {
15670 params[i].out_max_size = 0; // no max size!
15671 } else if (flags & RPC_VARARRAY || tid == TID_STRING) {
15672
15673 /* save maximum array length from the value of the next argument.
15674 * this means RPC_OUT arrays and strings should always be passed like this:
15675 * rpc_call(..., array_ptr, array_max_size, ...); */
15676
15677 params[i].out_max_size_offset = in_offset;
15678
15679 INT max_size = *((INT *) in_param_ptr);
15680
15681 if (convert_flags)
15682 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15683
15684 params[i].out_max_size = max_size;
15685 }
15686
15687 if (rl.param[i].tid == TID_STRUCT) {
15688 params[i].out_max_size = rl.param[i].n;
15689 }
15690 }
15691
15692 if (flags & RPC_CXX) {
15693 if (tid == TID_STRING) {
15694 params[i].ps = new std::string;
15695 if (flags & RPC_IN) {
15696 *(params[i].ps) = (char*)nc_in->param + in_param_offset[i];
15697 //printf("STRING %d decode [%s]\n", i, (char*)nc_in->param + in_param_offset[i]);
15698 }
15699 prpc_param[i] = (void*) params[i].ps;
15700 } else if (tid == TID_ARRAY) {
15701 params[i].pv = new std::vector<char>;
15702 if (flags & RPC_IN) {
15703 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);
15704 //printf("VECTOR %d decode %zu bytes\n", i, params[i].pv->size());
15705 }
15706 prpc_param[i] = (void*) params[i].pv;
15707 } else {
15708 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);
15709 return RPC_INVALID_ID;
15710 }
15711 } else {
15712 if ((flags & RPC_IN) && (flags & RPC_OUT)) {
15713 params[i].pv = new std::vector<char>;
15714 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);
15715 size_t want_size = params[i].out_max_size;
15716 //printf("param %d size %zu want %zu\n", i, params[i].pv->size(), want_size);
15717 if (params[i].pv->size() < want_size)
15718 params[i].pv->resize(want_size);
15719 prpc_param[i] = params[i].pv->data();
15720 } else if (flags & RPC_IN) {
15721 prpc_param[i] = (char*)nc_in->param + in_param_offset[i];
15722 //printf("param %d input value %d [%s]\n", i, *(int*)prpc_param[i], (char*)prpc_param[i]);
15723 } else if (flags & RPC_OUT) {
15724 params[i].pv = new std::vector<char>;
15725 params[i].pv->resize(params[i].out_max_size);
15726 prpc_param[i] = params[i].pv->data();
15727 //printf("param %d size %zu\n", i, params[i].pv->size());
15728 }
15729 }
15730
15731 if (debug)
15732 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]);
15733 }
15734
15735 if (debug)
15736 printf("rpc_execute_cxx: nc_in size %d, in_offset %zu\n", nc_in->header.param_size, in_offset);
15737
15738 if (routine_id == RPC_TEST2) {
15739 bool ok = true;
15740
15741 ok &= in_param_offset[ 0] == 0; ok &= in_param_size[ 0] == 8; // int_in
15742 ok &= in_param_offset[ 1] == 0; ok &= in_param_size[ 1] == 0; // int_out
15743 ok &= in_param_offset[ 2] == 8; ok &= in_param_size[ 2] == 8; // &int_inout
15744 ok &= in_param_offset[ 3] == 16; ok &= in_param_size[ 3] == 16; // string_in
15745 ok &= in_param_offset[ 4] == 0; ok &= in_param_size[ 4] == 0; // string_out
15746 ok &= in_param_offset[ 5] == 32; ok &= in_param_size[ 5] == 8; // string_out size
15747 ok &= in_param_offset[ 6] == 0; ok &= in_param_size[ 6] == 0; // string2_out
15748 ok &= in_param_offset[ 7] == 40; ok &= in_param_size[ 7] == 8; // string2_out size
15749 ok &= in_param_offset[ 8] == 48; ok &= in_param_size[ 8] == 16; // string_inout
15750 ok &= in_param_offset[ 9] == 64; ok &= in_param_size[ 9] == 8; // string_inout size
15751 ok &= in_param_offset[10] == 72; ok &= in_param_size[10] == 72; // struct_in
15752 ok &= in_param_offset[11] == 0; ok &= in_param_size[11] == 0; // struct_out
15753 ok &= in_param_offset[12] == 144; ok &= in_param_size[12] == 72; // struct_inout
15754 ok &= in_param_offset[13] == 224; ok &= in_param_size[13] == 40; // uint32_t[10] array inout
15755 ok &= in_param_offset[14] == 264; ok &= in_param_size[14] == 8; // &size
15756 ok &= in_param_offset[15] == 280; ok &= in_param_size[15] == 16; // char[10] array in
15757 ok &= in_param_offset[16] == 296; ok &= in_param_size[16] == 8; // size
15758 ok &= in_param_offset[17] == 0; ok &= in_param_size[17] == 0; // char[16] array out
15759 ok &= in_param_offset[18] == 304; ok &= in_param_size[18] == 8; // &size
15760 ok &= in_offset == 312;
15761
15762 if (!ok) {
15763 cm_msg(MERROR, "rpc_execute_cxx", "RPC_TEST2 parameters encoding error!");
15764 return RPC_INVALID_ID;
15765 }
15766 }
15767
15768 if (debug) {
15769 printf("rpc_execute_cxx: calling dispatch()\n");
15770 }
15771
15772 /*********************************\
15773 * call dispatch function *
15774 \*********************************/
15775 if (rl.dispatch)
15776 status = rl.dispatch(routine_id, prpc_param);
15777 else
15779
15780 if (debug) {
15781 printf("rpc_execute_cxx: dispatch() status %d\n", status);
15782 }
15783
15784 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15786
15787 /* return immediately for closed down client connections */
15788 if (!sock && routine_id == RPC_ID_EXIT) {
15789 return SS_EXIT;
15790 }
15791
15792 if (!sock && routine_id == RPC_ID_SHUTDOWN) {
15793 return RPC_SHUTDOWN;
15794 }
15795
15796 /* Return if TCP connection broken */
15797 if (status == SS_ABORT) {
15798 return SS_ABORT;
15799 }
15800
15801 /* if sock == 0, we are in FTCP mode and may not sent results */
15802 if (!sock) {
15803 return RPC_SUCCESS;
15804 }
15805
15806 std::vector<char> v_out;
15807
15808 v_out.resize(sizeof(NET_COMMAND_HEADER));
15809
15810 for (int i = 0; i < nparams; i++) {
15811 if (rl.param[i].flags & RPC_OUT) {
15812 int tid = rl.param[i].tid;
15813 int flags = rl.param[i].flags;
15814
15815 if (flags & RPC_CXX) {
15816 if (tid == TID_STRING) {
15817 size_t arg_size = 1 + params[i].ps->length();
15818 size_t param_size = ALIGN8(arg_size);
15819
15820 if (debug)
15821 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());
15822
15823 v_out.insert(v_out.end(), params[i].ps->c_str(), params[i].ps->c_str() + arg_size);
15824 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15825 } else if (tid == TID_ARRAY) {
15826 size_t arg_size = params[i].pv->size();
15827 size_t param_size = ALIGN8(arg_size);
15828
15829 if (debug)
15830 printf("rpc_execute_cxx: param %2d, std::vector arg_size %zu, param_size %zu\n", i, arg_size, param_size);
15831
15832 char buf[ALIGN8(sizeof(INT))];
15833 *((INT *) buf) = arg_size; // store new array size
15834 if (convert_flags)
15835 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15836 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15837 v_out.insert(v_out.end(), params[i].pv->data(), params[i].pv->data() + arg_size); // data
15838 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15839 } else {
15840 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);
15841 return RPC_INVALID_ID;
15842 }
15843 } else {
15844 size_t convert_offset = 0;
15845 size_t convert_size = 0;
15846
15847 if (tid == TID_STRING) {
15848 size_t max_size = params[i].out_max_size;
15849 char* param_ptr = (char *) prpc_param[i];
15850 //printf("param %d string param [%s] max_size %zu\n", i, param_ptr, max_size);
15851 size_t arg_size = 1 + strlen(param_ptr);
15852 if (arg_size > max_size) {
15853 param_ptr[max_size] = 0; // truncate!
15854 size_t arg_size = 1 + strlen(param_ptr);
15855 assert(arg_size == max_size);
15856 }
15857 size_t param_size = ALIGN8(arg_size);
15858
15859 if (debug)
15860 printf("rpc_execute_cxx: param %2d, string max_size %zu, string_size %zu, param_size %zu\n", i, max_size, arg_size, param_size);
15861
15862 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size);
15863 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15864 } else if (flags & RPC_VARARRAY) {
15865 size_t max_size = params[i].out_max_size;
15866 size_t arg_size = *((INT *) prpc_param[i + 1]);
15867 char* param_ptr = (char*)prpc_param[i];
15868 size_t param_size = ALIGN8(arg_size);
15869
15870 if (debug)
15871 printf("rpc_execute_cxx: param %2d, array max_size %zu, param_size %zu\n", i, max_size, param_size);
15872
15873 char buf[ALIGN8(sizeof(INT))];
15874 *((INT *) buf) = arg_size; // store new array size
15875 if (convert_flags)
15876 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15877 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15878 convert_offset = v_out.size();
15879 convert_size = arg_size;
15880 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
15881 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15882 } else {
15883 char* param_ptr = (char*)prpc_param[i];
15884 size_t arg_size = rpc_tid_size(tid);
15885 if (tid == TID_STRUCT)
15886 arg_size = rl.param[i].n;
15887 size_t param_size = ALIGN8(arg_size);
15888
15889 if (debug) {
15890 if (tid == TID_INT) {
15891 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);
15892 } else {
15893 printf("rpc_execute_cxx: param %2d, tid %2d, arg_size %zu, param_size %zu\n", i, tid, arg_size, param_size);
15894 }
15895 }
15896
15897 convert_offset = v_out.size();
15898 convert_size = arg_size;
15899 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
15900 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15901 }
15902
15903 /* convert data format */
15904 if (convert_flags) {
15905 if (flags & RPC_VARARRAY)
15906 rpc_convert_data(v_out.data()+convert_offset, tid, rl.param[i].flags | RPC_OUTGOING, convert_size, convert_flags);
15907 else
15908 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);
15909 }
15910 }
15911 }
15912 }
15913
15914 NET_COMMAND* nc_out = (NET_COMMAND*)v_out.data();
15915
15916 /* send return parameters */
15917 nc_out->header.routine_id = status;
15918 nc_out->header.param_size = v_out.size() - sizeof(NET_COMMAND_HEADER);
15919
15920
15921 /* convert header format (byte swapping) if necessary */
15922 if (convert_flags) {
15923 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15924 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15925 }
15926
15927 status = send_tcp(sock, v_out.data(), v_out.size(), 0);
15928
15929 if (status < 0) {
15930 cm_msg(MERROR, "rpc_execute_cxx", "send_tcp() failed, status %d", status);
15931 return RPC_NET_ERROR;
15932 }
15933
15934 if (debug)
15935 printf("rpc_execute_cxx: send_tcp() sent %d bytes\n", status);
15936
15937 /* return SS_EXIT if RPC_EXIT is called */
15938 if (routine_id == RPC_ID_EXIT)
15939 return SS_EXIT;
15940
15941 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15942 if (routine_id == RPC_ID_SHUTDOWN)
15943 return RPC_SHUTDOWN;
15944
15945 return RPC_SUCCESS;
15946}
15947
15948/********************************************************************/
15950/********************************************************************\
15951 Routine: rpc_test_rpc
15952
15953 Purpose: Test RPC parameters encoding and decoding
15954
15955 Input:
15956 none
15957
15958 Output:
15959 none
15960
15961 Function value:
15962 RPC_SUCCESS Successful completion
15963
15964\********************************************************************/
15965{
15966 int status = RPC_SUCCESS;
15967
15968 printf("rpc_test_rpc_test2!\n");
15969
15970 int int_out = 0;
15971 int int_inout = 456;
15972
15973 char string_out[33];
15974 char string2_out[49];
15975
15976 char string_inout[25];
15977 strcpy(string_inout, "string_inout");
15978
15979 KEY struct_in;
15980
15981 struct_in.type = 111;
15982 struct_in.num_values = 222;
15983 strcpy(struct_in.name, "name");
15984 struct_in.last_written = 333;
15985
15986 KEY struct_out;
15987 KEY struct_inout;
15988
15989 struct_inout.type = 111111;
15990 struct_inout.num_values = 222222;
15991 strcpy(struct_inout.name, "name_name");
15992 struct_inout.last_written = 333333;
15993
15994 uint32_t dwordarray_inout[9];
15995 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15996
15997 for (int i=0; i<9; i++) {
15998 dwordarray_inout[i] = i*10;
15999 }
16000
16001 char array_in[10];
16002
16003 for (size_t i=0; i<sizeof(array_in); i++) {
16004 array_in[i] = 'a' + i;
16005 }
16006
16007 char array_out[16];
16008 size_t array_out_size = sizeof(array_out);
16009
16010 for (size_t i=0; i<sizeof(array_out); i++) {
16011 array_out[i] = 'Z';
16012 }
16013
16015 123,
16016 &int_out,
16017 &int_inout,
16018 "test string",
16019 string_out, sizeof(string_out),
16020 string2_out, sizeof(string2_out),
16021 string_inout, sizeof(string_inout),
16022 &struct_in,
16023 &struct_out,
16024 &struct_inout,
16025 dwordarray_inout, &dwordarray_inout_size,
16026 array_in, sizeof(array_in),
16027 array_out, &array_out_size
16028 );
16029
16030 if (status != RPC_SUCCESS) {
16031 printf("rpc_call(RPC_TEST2) status %d\n", status);
16032 return status;
16033 }
16034
16035 if (int_out != 789) {
16036 printf("int_out mismatch!\n");
16037 status = 0;
16038 }
16039
16040 if (int_inout != 456*2) {
16041 printf("int_inout mismatch!\n");
16042 status = 0;
16043 }
16044
16045 if (strcmp(string_out, "string_out") != 0) {
16046 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16047 status = 0;
16048 }
16049
16050 if (strcmp(string2_out, "second string_out") != 0) {
16051 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
16052 status = 0;
16053 }
16054
16055 if (strcmp(string_inout, "return string_inout") != 0) {
16056 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
16057 status = 0;
16058 }
16059
16060 KEY* pkey;
16061
16062 pkey = &struct_in;
16063
16064 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16065
16066 pkey = &struct_out;
16067
16068 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16069 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);
16070 status = 0;
16071 }
16072
16073 pkey = &struct_inout;
16074
16075 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16076 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);
16077 status = 0;
16078 }
16079
16080 if (dwordarray_inout_size != 4*5) {
16081 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16082 status = 0;
16083 } else {
16084 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16085 if (dwordarray_inout[i] != i*10+i) {
16086 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16087 status = 0;
16088 }
16089 }
16090 }
16091
16092 //printf("array_out_size %d\n", array_out_size);
16093 //for (int i=0; i<array_out_size; i++) {
16094 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16095 //}
16096
16097 if (array_out_size != 15) {
16098 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16099 status = 0;
16100 } else {
16101 if (strcmp(array_out, "test test test") != 0) {
16102 printf("array_out data mismatch\n");
16103 status = 0;
16104 }
16105 }
16106
16107 return status;
16108}
16109
16110/********************************************************************/
16112/********************************************************************\
16113 Routine: rpc_test_rpc
16114
16115 Purpose: Test RPC parameters encoding and decoding
16116
16117 Input:
16118 none
16119
16120 Output:
16121 none
16122
16123 Function value:
16124 RPC_SUCCESS Successful completion
16125
16126\********************************************************************/
16127{
16128 int status = RPC_SUCCESS;
16129
16130 printf("rpc_test_rpc_test2_cxx!\n");
16131
16132 int int_out = 0;
16133 int int_inout = 456;
16134
16135 char string_out[33];
16136 std::string string2_out;
16137 std::string string_inout = "string_inout";
16138
16139 KEY struct_in;
16140
16141 struct_in.type = 111;
16142 struct_in.num_values = 222;
16143 strcpy(struct_in.name, "name");
16144 struct_in.last_written = 333;
16145
16146 KEY struct_out;
16147 KEY struct_inout;
16148
16149 struct_inout.type = 111111;
16150 struct_inout.num_values = 222222;
16151 strcpy(struct_inout.name, "name_name");
16152 struct_inout.last_written = 333333;
16153
16154 uint32_t dwordarray_inout[9];
16155 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16156
16157 for (int i=0; i<9; i++) {
16158 dwordarray_inout[i] = i*10;
16159 }
16160
16161 std::vector<char> array_in;
16162 int array_in_size = 10;
16163
16164 for (int i=0; i<array_in_size; i++) {
16165 array_in.push_back('a' + i);
16166 }
16167
16168 std::vector<char> array_out;
16169 size_t array_out_size = 16;
16170
16172 123,
16173 &int_out,
16174 &int_inout,
16175 "test string",
16176 string_out, sizeof(string_out),
16177 &string2_out, 48,
16178 &string_inout, 25,
16179 &struct_in,
16180 &struct_out,
16181 &struct_inout,
16182 dwordarray_inout, &dwordarray_inout_size,
16183 &array_in, array_in_size,
16184 &array_out, &array_out_size
16185 );
16186
16187 if (status != RPC_SUCCESS) {
16188 printf("rpc_call(RPC_TEST2_CXX) status %d\n", status);
16189 return status;
16190 }
16191
16192 if (int_out != 789) {
16193 printf("int_out mismatch!\n");
16194 status = 0;
16195 }
16196
16197 if (int_inout != 456*2) {
16198 printf("int_inout mismatch!\n");
16199 status = 0;
16200 }
16201
16202 if (strcmp(string_out, "string_out") != 0) {
16203 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16204 status = 0;
16205 }
16206
16207 if (string2_out != "second string_out") {
16208 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16209 status = 0;
16210 }
16211
16212 if (string_inout != "return string_inout") {
16213 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16214 status = 0;
16215 }
16216
16217 KEY* pkey;
16218
16219 pkey = &struct_in;
16220
16221 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16222
16223 pkey = &struct_out;
16224
16225 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16226 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);
16227 status = 0;
16228 }
16229
16230 pkey = &struct_inout;
16231
16232 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16233 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);
16234 status = 0;
16235 }
16236
16237 if (dwordarray_inout_size != 4*5) {
16238 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16239 status = 0;
16240 } else {
16241 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16242 if (dwordarray_inout[i] != i*10+i) {
16243 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16244 status = 0;
16245 }
16246 }
16247 }
16248
16249 //printf("array_out_size %d\n", array_out_size);
16250 //for (int i=0; i<array_out_size; i++) {
16251 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16252 //}
16253
16254 if (array_out_size != 15) {
16255 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16256 status = 0;
16257 } else if (array_out.size() != 15) {
16258 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16259 status = 0;
16260 } else {
16261 if (strcmp(array_out.data(), "test test test") != 0) {
16262 printf("array_out data mismatch\n");
16263 status = 0;
16264 }
16265 }
16266
16267 return status;
16268}
16269
16270/********************************************************************/
16272/********************************************************************\
16273 Routine: rpc_test_rpc
16274
16275 Purpose: Test RPC parameters encoding and decoding
16276
16277 Input:
16278 none
16279
16280 Output:
16281 none
16282
16283 Function value:
16284 RPC_SUCCESS Successful completion
16285
16286\********************************************************************/
16287{
16288 int status = RPC_SUCCESS;
16289
16290 printf("rpc_test_rpc_test3_cxx!\n");
16291
16292 int int_out = 0;
16293 int int_inout = 456;
16294
16295 char string_out[33];
16296 std::string string2_out;
16297 std::string string_inout = "string_inout";
16298
16299 KEY struct_in;
16300
16301 struct_in.type = 111;
16302 struct_in.num_values = 222;
16303 strcpy(struct_in.name, "name");
16304 struct_in.last_written = 333;
16305
16306 KEY struct_out;
16307 KEY struct_inout;
16308
16309 struct_inout.type = 111111;
16310 struct_inout.num_values = 222222;
16311 strcpy(struct_inout.name, "name_name");
16312 struct_inout.last_written = 333333;
16313
16314 uint32_t dwordarray_inout[9];
16315 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16316
16317 for (int i=0; i<9; i++) {
16318 dwordarray_inout[i] = i*10;
16319 }
16320
16321 std::vector<char> array_in;
16322 int array_in_size = 10;
16323
16324 for (int i=0; i<array_in_size; i++) {
16325 array_in.push_back('a' + i);
16326 }
16327
16328 std::vector<char> array_out;
16329
16331 123,
16332 &int_out,
16333 &int_inout,
16334 "test string",
16335 string_out, sizeof(string_out),
16336 &string2_out,
16337 &string_inout,
16338 &struct_in,
16339 &struct_out,
16340 &struct_inout,
16341 dwordarray_inout, &dwordarray_inout_size,
16342 &array_in,
16343 &array_out
16344 );
16345
16346 if (status != RPC_SUCCESS) {
16347 printf("rpc_call(RPC_TEST3_CXX) status %d\n", status);
16348 return status;
16349 }
16350
16351 if (int_out != 789) {
16352 printf("int_out mismatch!\n");
16353 status = 0;
16354 }
16355
16356 if (int_inout != 456*2) {
16357 printf("int_inout mismatch!\n");
16358 status = 0;
16359 }
16360
16361 if (strcmp(string_out, "string_out") != 0) {
16362 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16363 status = 0;
16364 }
16365
16366 if (string2_out != "second string_out") {
16367 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16368 status = 0;
16369 }
16370
16371 if (string_inout != "return string_inout") {
16372 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16373 status = 0;
16374 }
16375
16376 KEY* pkey;
16377
16378 pkey = &struct_in;
16379
16380 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16381
16382 pkey = &struct_out;
16383
16384 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16385 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);
16386 status = 0;
16387 }
16388
16389 pkey = &struct_inout;
16390
16391 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16392 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);
16393 status = 0;
16394 }
16395
16396 if (dwordarray_inout_size != 4*5) {
16397 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16398 status = 0;
16399 } else {
16400 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16401 if (dwordarray_inout[i] != i*10+i) {
16402 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16403 status = 0;
16404 }
16405 }
16406 }
16407
16408 //printf("array_out_size %d\n", array_out_size);
16409 //for (int i=0; i<array_out_size; i++) {
16410 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16411 //}
16412
16413 if (array_out.size() != 15) {
16414 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16415 status = 0;
16416 } else {
16417 if (strcmp(array_out.data(), "test test test") != 0) {
16418 printf("array_out data mismatch\n");
16419 status = 0;
16420 }
16421 }
16422
16423 return status;
16424}
16425
16426/********************************************************************/
16428/********************************************************************\
16429 Routine: rpc_test_rpc
16430
16431 Purpose: Test RPC parameters encoding and decoding
16432
16433 Input:
16434 none
16435
16436 Output:
16437 none
16438
16439 Function value:
16440 RPC_SUCCESS Successful completion
16441
16442\********************************************************************/
16443{
16444 int status = RPC_SUCCESS;
16445
16446 printf("rpc_test_rpc_test4_cxx!\n");
16447
16448 int int_out = 0;
16449 int int_inout = 456;
16450
16451 std::string string_in = "test string";
16452 std::string string_out;
16453 std::string string_inout = "string_inout";
16454
16455 std::vector<char> array_in;
16456 int array_in_size = 10;
16457
16458 for (int i=0; i<array_in_size; i++) {
16459 array_in.push_back('a' + i);
16460 }
16461
16462 std::vector<char> array_out;
16463
16464 std::vector<char> array_inout;
16465 int array_inout_size = 6;
16466
16467 for (int i=0; i<array_inout_size; i++) {
16468 array_inout.push_back('0' + i);
16469 }
16470
16472 123,
16473 &int_out,
16474 &int_inout,
16475 &string_in,
16476 &string_out,
16477 &string_inout,
16478 &array_in,
16479 &array_out,
16480 &array_inout
16481 );
16482
16483 if (status != RPC_SUCCESS) {
16484 printf("rpc_call(RPC_TEST4_CXX) status %d\n", status);
16485 return status;
16486 }
16487
16488 if (int_out != 789) {
16489 printf("int_out mismatch!\n");
16490 status = 0;
16491 }
16492
16493 if (int_inout != 456*2) {
16494 printf("int_inout mismatch!\n");
16495 status = 0;
16496 }
16497
16498 if (string_out != "return string_out") {
16499 printf("string_out mismatch [%s] vs [%s]\n", string_out.c_str(), "return string_out");
16500 status = 0;
16501 }
16502
16503 if (string_inout != "return string_inout") {
16504 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16505 status = 0;
16506 }
16507
16508 if (array_out.size() != 15) {
16509 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16510 status = 0;
16511 } else {
16512 if (strcmp(array_out.data(), "test test test") != 0) {
16513 printf("array_out data mismatch\n");
16514 status = 0;
16515 }
16516 }
16517
16518 if (array_inout.size() != 12) {
16519 printf("array_inout.size() mismatch %d vs %d\n", (int)array_inout.size(), 12);
16520 status = 0;
16521 } else {
16522 for (int i=0; i<6; i++) {
16523 if (array_inout[i] != '0' + i) {
16524 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], ('0'+i));
16525 status = 0;
16526 }
16527 }
16528 for (int i=6; i<12; i++) {
16529 if (array_inout[i] != 2*('0' + (i-6))) {
16530 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], 2*('0'+i));
16531 status = 0;
16532 }
16533 }
16534 }
16535
16536 return status;
16537}
16538
16539/********************************************************************/
16541/********************************************************************\
16542 Routine: rpc_test_rpc
16543
16544 Purpose: Test RPC parameters encoding and decoding
16545
16546 Input:
16547 none
16548
16549 Output:
16550 none
16551
16552 Function value:
16553 RPC_SUCCESS Successful completion
16554
16555\********************************************************************/
16556{
16557 int status;
16559 if (status != RPC_SUCCESS)
16560 return status;
16562 if (status != RPC_SUCCESS)
16563 return status;
16565 if (status != RPC_SUCCESS)
16566 return status;
16568 if (status != RPC_SUCCESS)
16569 return status;
16570 return RPC_SUCCESS;
16571}
16572
16573static std::atomic_bool gAllowedHostsEnabled(false);
16574static std::vector<std::string> gAllowedHosts;
16575static std::mutex gAllowedHostsMutex;
16576
16577/********************************************************************/
16579/********************************************************************\
16580 Routine: rpc_clear_allowed_hosts
16581
16582 Purpose: Clear list of allowed hosts and permit connections from anybody
16583
16584 Input:
16585 none
16586
16587 Output:
16588 none
16589
16590 Function value:
16591 RPC_SUCCESS Successful completion
16592
16593\********************************************************************/
16594{
16595 gAllowedHostsMutex.lock();
16596 gAllowedHosts.clear();
16597 gAllowedHostsEnabled = false;
16598 gAllowedHostsMutex.unlock();
16599 return RPC_SUCCESS;
16600}
16601
16602/********************************************************************/
16603INT rpc_add_allowed_host(const char *hostname)
16604/********************************************************************\
16605 Routine: rpc_add_allowed_host
16606
16607 Purpose: Permit connections from listed hosts only
16608
16609 Input:
16610 none
16611
16612 Output:
16613 none
16614
16615 Function value:
16616 RPC_SUCCESS Successful completion
16617 RPC_NO_MEMORY Too many allowed hosts
16618
16619\********************************************************************/
16620{
16621 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
16622
16623 gAllowedHostsMutex.lock();
16624 gAllowedHosts.push_back(hostname);
16625 gAllowedHostsEnabled = true;
16626 gAllowedHostsMutex.unlock();
16627
16628 return RPC_SUCCESS;
16629}
16630
16631/********************************************************************/
16632INT rpc_check_allowed_host(const char *hostname)
16633/********************************************************************\
16634 Routine: rpc_check_allowed_host
16635
16636 Purpose: Check if hostname is permitted to connect
16637
16638 Function value:
16639 RPC_SUCCESS hostname is permitted to connect
16640 RPC_NOT_REGISTERED hostname not permitted to connect
16641
16642\********************************************************************/
16643{
16644 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
16645
16647 return RPC_SUCCESS;
16648
16649 if (strcmp(hostname, "localhost") == 0)
16650 return RPC_SUCCESS;
16651
16652 if (strcmp(hostname, "localhost.localdomain") == 0)
16653 return RPC_SUCCESS;
16654
16655 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
16656 return RPC_SUCCESS;
16657
16658 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
16659 return RPC_SUCCESS;
16660
16662
16663 gAllowedHostsMutex.lock();
16664
16665 for (const auto& h: gAllowedHosts) {
16666 if (h == hostname) {
16668 break;
16669 }
16670 }
16671
16672 gAllowedHostsMutex.unlock();
16673
16674 //if (status != RPC_SUCCESS)
16675 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
16676
16677 return status;
16678}
16679
16680/*------------------------------------------------------------------*/
16682{
16683 std::string hostname;
16684
16685 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
16686
16687 if (status != SS_SUCCESS)
16688 return status;
16689
16690 status = rpc_check_allowed_host(hostname.c_str());
16691
16692 if (status == RPC_SUCCESS)
16693 return RPC_SUCCESS;
16694
16695 static std::atomic_int max_report(10);
16696 if (max_report > 0) {
16697 max_report--;
16698 if (max_report == 0) {
16699 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
16700 } else {
16701 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());
16702 }
16703 }
16704
16705 return RPC_NET_ERROR;
16706}
16707
16708/********************************************************************/
16710/********************************************************************\
16711
16712 Routine: rpc_server_accept
16713
16714 Purpose: Accept new incoming connections
16715
16716 Input:
16717 INT lscok Listen socket
16718
16719 Output:
16720 none
16721
16722 Function value:
16723 RPC_SUCCESS Successful completion
16724 RPC_NET_ERROR Error in socket call
16725 RPC_CONNCLOSED Connection was closed
16726 RPC_SHUTDOWN Listener shutdown
16727 RPC_EXCEED_BUFFER Not enough memory for network buffer
16728
16729\********************************************************************/
16730{
16731 INT i;
16732 INT sock;
16733 char version[NAME_LENGTH], v1[32];
16734 char experiment[NAME_LENGTH];
16735 INT port1, port2, port3;
16736 char *ptr;
16737 char net_buffer[256];
16738 struct linger ling;
16739
16740 static struct callback_addr callback;
16741
16742 if (lsock > 0) {
16743 sock = accept(lsock, NULL, NULL);
16744
16745 if (sock == -1)
16746 return RPC_NET_ERROR;
16747 } else {
16748 /* lsock is stdin -> already connected from inetd */
16749
16750 sock = lsock;
16751 }
16752
16753 /* check access control list */
16756
16757 if (status != RPC_SUCCESS) {
16758 ss_socket_close(&sock);
16759 return RPC_NET_ERROR;
16760 }
16761 }
16762
16763 /* receive string with timeout */
16764 i = recv_string(sock, net_buffer, 256, 10000);
16765 rpc_debug_printf("Received command: %s", net_buffer);
16766
16767 if (i > 0) {
16768 char command = (char) toupper(net_buffer[0]);
16769
16770 //printf("rpc_server_accept: command [%c]\n", command);
16771
16772 switch (command) {
16773 case 'S': {
16774
16775 /*----------- shutdown listener ----------------------*/
16776 ss_socket_close(&sock);
16777 return RPC_SHUTDOWN;
16778 }
16779 case 'I': {
16780
16781 /*----------- return available experiments -----------*/
16782#ifdef LOCAL_ROUTINES
16783 exptab_struct exptab;
16784 cm_read_exptab(&exptab); // thread safe!
16785 for (unsigned i=0; i<exptab.exptab.size(); i++) {
16786 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
16787 const char* str = exptab.exptab[i].name.c_str();
16788 send(sock, str, strlen(str) + 1, 0);
16789 }
16790 send(sock, "", 1, 0);
16791#endif
16792 ss_socket_close(&sock);
16793 break;
16794 }
16795 case 'C': {
16796
16797 /*----------- connect to experiment -----------*/
16798
16799 /* get callback information */
16800 callback.experiment[0] = 0;
16801 port1 = port2 = version[0] = 0;
16802
16803 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
16804
16805 /* parse string in format "C port1 port2 port3 version expt" */
16806 /* example: C 51046 45838 56832 2.0.0 alpha */
16807
16808 port1 = strtoul(net_buffer + 2, &ptr, 0);
16809 port2 = strtoul(ptr, &ptr, 0);
16810 port3 = strtoul(ptr, &ptr, 0);
16811
16812 while (*ptr == ' ')
16813 ptr++;
16814
16815 i = 0;
16816 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
16817 version[i++] = *ptr++;
16818
16819 // ensure that we do not overwrite buffer "version"
16820 assert(i < (int) sizeof(version));
16821 version[i] = 0;
16822
16823 // skip wjatever is left from the "version" string
16824 for (; *ptr != 0 && *ptr != ' ';)
16825 ptr++;
16826
16827 while (*ptr == ' ')
16828 ptr++;
16829
16830 i = 0;
16831 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
16832 experiment[i++] = *ptr++;
16833
16834 // ensure that we do not overwrite buffer "experiment"
16835 assert(i < (int) sizeof(experiment));
16836 experiment[i] = 0;
16837
16839
16840 /* print warning if version patch level doesn't agree */
16841 mstrlcpy(v1, version, sizeof(v1));
16842 if (strchr(v1, '.'))
16843 if (strchr(strchr(v1, '.') + 1, '.'))
16844 *strchr(strchr(v1, '.') + 1, '.') = 0;
16845
16846 char str[100];
16847 mstrlcpy(str, cm_get_version(), sizeof(str));
16848 if (strchr(str, '.'))
16849 if (strchr(strchr(str, '.') + 1, '.'))
16850 *strchr(strchr(str, '.') + 1, '.') = 0;
16851
16852 if (strcmp(v1, str) != 0) {
16853 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
16854 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
16855 }
16856
16857 callback.host_port1 = (short) port1;
16858 callback.host_port2 = (short) port2;
16859 callback.host_port3 = (short) port3;
16861
16863
16864 if (status != SS_SUCCESS) {
16865 ss_socket_close(&sock);
16866 break;
16867 }
16868
16869#ifdef LOCAL_ROUTINES
16870 /* update experiment definition */
16871 exptab_struct exptab;
16872 cm_read_exptab(&exptab); // thread safe!
16873
16874 unsigned idx = 0;
16875 bool found = false;
16876 /* lookup experiment */
16877 if (equal_ustring(callback.experiment.c_str(), "Default")) {
16878 found = true;
16879 idx = 0;
16880 } else {
16881 for (idx = 0; idx < exptab.exptab.size(); idx++) {
16882 if (exptab.exptab[idx].name == callback.experiment) {
16883 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
16884 found = true;
16885 break;
16886 }
16887 }
16888 }
16889 }
16890
16891 if (!found) {
16892 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
16893
16894 send(sock, "2", 2, 0); /* 2 means exp. not found */
16895 ss_socket_close(&sock);
16896 break;
16897 }
16898
16899 callback.directory = exptab.exptab[idx].directory;
16900 callback.user = exptab.exptab[idx].user;
16901
16902 /* create a new process */
16903 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
16904 char debug_str[30];
16905
16906 sprintf(host_port1_str, "%d", callback.host_port1);
16907 sprintf(host_port2_str, "%d", callback.host_port2);
16908 sprintf(host_port3_str, "%d", callback.host_port3);
16909 sprintf(debug_str, "%d", callback.debug);
16910
16911 const char *mserver_path = rpc_get_mserver_path();
16912
16913 const char *argv[10];
16914 argv[0] = mserver_path;
16915 argv[1] = callback.host_name.c_str();
16916 argv[2] = host_port1_str;
16917 argv[3] = host_port2_str;
16918 argv[4] = host_port3_str;
16919 argv[5] = debug_str;
16920 argv[6] = callback.experiment.c_str();
16921 argv[7] = callback.directory.c_str();
16922 argv[8] = callback.user.c_str();
16923 argv[9] = NULL;
16924
16925 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
16926 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
16927 argv[9]);
16928
16929 status = ss_spawnv(P_NOWAIT, mserver_path, argv);
16930
16931 if (status != SS_SUCCESS) {
16932 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
16933
16934 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
16935 send(sock, str, strlen(str) + 1, 0);
16936 ss_socket_close(&sock);
16937 break;
16938 }
16939
16940 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
16941 send(sock, str, strlen(str) + 1, 0);
16942#endif // LOCAL_ROUTINES
16943 ss_socket_close(&sock);
16944
16945 break;
16946 }
16947 default: {
16948 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
16949 ss_socket_close(&sock);
16950 break;
16951 }
16952 }
16953 } else { /* if i>0 */
16954
16955 /* lingering needed for PCTCP */
16956 ling.l_onoff = 1;
16957 ling.l_linger = 0;
16958 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16959 ss_socket_close(&sock);
16960 }
16961
16962 return RPC_SUCCESS;
16963}
16964
16965/********************************************************************/
16967/********************************************************************\
16968
16969 Routine: rpc_client_accept
16970
16971 Purpose: midas program accept new RPC connection (run transitions, etc)
16972
16973 Input:
16974 INT lsock Listen socket
16975
16976 Output:
16977 none
16978
16979 Function value:
16980 RPC_SUCCESS Successful completion
16981 RPC_NET_ERROR Error in socket call
16982 RPC_CONNCLOSED Connection was closed
16983 RPC_SHUTDOWN Listener shutdown
16984 RPC_EXCEED_BUFFER Not enough memory for network buffer
16985
16986\********************************************************************/
16987{
16988 INT i, status;
16989 INT client_hw_type = 0, hw_type;
16990 std::string client_program;
16991 std::string host_name;
16992 INT convert_flags;
16993 char net_buffer[256], *p;
16994
16995 int sock = accept(lsock, NULL, NULL);
16996
16997 if (sock == -1)
16998 return RPC_NET_ERROR;
16999
17000 /* check access control list */
17003
17004 if (status != RPC_SUCCESS) {
17005 ss_socket_close(&sock);
17006 return RPC_NET_ERROR;
17007 }
17008 }
17009
17010 host_name = "(unknown)";
17011 client_program = "(unknown)";
17012
17013 /* receive string with timeout */
17014 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
17015 if (i <= 0) {
17016 ss_socket_close(&sock);
17017 return RPC_NET_ERROR;
17018 }
17019
17020 /* get remote computer info */
17021 p = strtok(net_buffer, " ");
17022 if (p != NULL) {
17023 client_hw_type = atoi(p);
17024 p = strtok(NULL, " ");
17025 }
17026 if (p != NULL) {
17027 //version = atoi(p);
17028 p = strtok(NULL, " ");
17029 }
17030 if (p != NULL) {
17031 client_program = p;
17032 p = strtok(NULL, " ");
17033 }
17034 if (p != NULL) {
17035 host_name = p;
17036 p = strtok(NULL, " ");
17037 }
17038
17039 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
17040
17042
17043 /* save information in _server_acception structure */
17044 sa->recv_sock = sock;
17045 sa->send_sock = 0;
17046 sa->event_sock = 0;
17047 sa->remote_hw_type = client_hw_type;
17048 sa->host_name = host_name;
17049 sa->prog_name = client_program;
17051 sa->watchdog_timeout = 0;
17052 sa->is_mserver = FALSE;
17053
17054 /* send my own computer id */
17055 hw_type = rpc_get_hw_type();
17056 std::string str = msprintf("%d %s", hw_type, cm_get_version());
17057 status = send(sock, str.c_str(), str.length() + 1, 0);
17058 if (status != (INT) str.length() + 1)
17059 return RPC_NET_ERROR;
17060
17061 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17062 sa->convert_flags = convert_flags;
17063
17065
17066 return RPC_SUCCESS;
17067}
17068
17069/********************************************************************/
17071/********************************************************************\
17072
17073 Routine: rpc_server_callback
17074
17075 Purpose: Callback a remote client. Setup _server_acception entry
17076 with optional conversion flags and establish two-way
17077 TCP connection.
17078
17079 Input:
17080 callback_addr pcallback Pointer to a callback structure
17081
17082 Output:
17083 none
17084
17085 Function value:
17086 RPC_SUCCESS Successful completion
17087
17088\********************************************************************/
17089{
17090 INT status;
17091 int recv_sock, send_sock, event_sock;
17092 char str[100];
17093 std::string client_program;
17094 INT client_hw_type, hw_type;
17095 INT convert_flags;
17096 char net_buffer[256];
17097 char *p;
17098 int flag;
17099
17100 /* copy callback information */
17101 struct callback_addr callback = *pcallback;
17102 //idx = callback.index;
17103
17104 std::string errmsg;
17105
17106 /* create new sockets for TCP */
17107 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
17108
17109 if (status != SS_SUCCESS) {
17110 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());
17111 ss_socket_close(&recv_sock);
17112 //ss_socket_close(&send_sock);
17113 //ss_socket_close(&event_sock);
17114 return RPC_NET_ERROR;
17115 }
17116
17117 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
17118
17119 if (status != SS_SUCCESS) {
17120 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());
17121 ss_socket_close(&recv_sock);
17122 ss_socket_close(&send_sock);
17123 //ss_socket_close(&event_sock);
17124 return RPC_NET_ERROR;
17125 }
17126
17127 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
17128
17129 if (status != SS_SUCCESS) {
17130 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());
17131 ss_socket_close(&recv_sock);
17132 ss_socket_close(&send_sock);
17133 ss_socket_close(&event_sock);
17134 return RPC_NET_ERROR;
17135 }
17136#ifndef OS_ULTRIX /* crashes ULTRIX... */
17137 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
17138 flag = 2 * 1024 * 1024;
17139 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
17140 if (status != 0)
17141 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
17142 strerror(errno));
17143#endif
17144
17145 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
17146 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
17147 ss_socket_close(&recv_sock);
17148 ss_socket_close(&send_sock);
17149 ss_socket_close(&event_sock);
17150 return RPC_NET_ERROR;
17151 }
17152 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
17153
17154 /* get remote computer info */
17155 client_hw_type = strtoul(net_buffer, &p, 0);
17156
17157 while (*p == ' ')
17158 p++;
17159
17160 client_program = p;
17161
17162 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
17163
17164 std::string host_name;
17165
17166 status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
17167
17168 if (status != SS_SUCCESS)
17169 host_name = "unknown";
17170
17171 //printf("rpc_server_callback: mserver acception\n");
17172
17174
17175 /* save information in _server_acception structure */
17176 sa->recv_sock = recv_sock;
17177 sa->send_sock = send_sock;
17178 sa->event_sock = event_sock;
17179 sa->remote_hw_type = client_hw_type;
17180 sa->host_name = host_name;
17181 sa->prog_name = client_program;
17183 sa->watchdog_timeout = 0;
17184 sa->is_mserver = TRUE;
17185
17186 assert(_mserver_acception == NULL);
17187
17188 _mserver_acception = sa;
17189
17190 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
17191
17192 /* send my own computer id */
17193 hw_type = rpc_get_hw_type();
17194 sprintf(str, "%d", hw_type);
17195 send(recv_sock, str, strlen(str) + 1, 0);
17196
17197 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17198 sa->convert_flags = convert_flags;
17199
17201
17202 if (rpc_is_mserver()) {
17203 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
17204 }
17205
17206 return RPC_SUCCESS;
17207}
17208
17209
17210/********************************************************************/
17212/********************************************************************\
17213
17214 Routine: rpc_server_loop
17215
17216 Purpose: mserver main event loop
17217
17218\********************************************************************/
17219{
17220 while (1) {
17221 int status = ss_suspend(1000, 0);
17222
17223 if (status == SS_ABORT || status == SS_EXIT)
17224 break;
17225
17227 break;
17228
17229 /* check alarms, etc */
17231
17233 }
17234
17235 return RPC_SUCCESS;
17236}
17237
17238/********************************************************************/
17240/********************************************************************\
17241
17242 Routine: rpc_server_receive_rpc
17243
17244 Purpose: Receive rpc commands and execute them. Close the connection
17245 if client has broken TCP pipe.
17246
17247 Function value:
17248 RPC_SUCCESS Successful completion
17249 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17250 SS_EXIT Server connection was closed
17251 SS_ABORT Server connection was broken
17252
17253\********************************************************************/
17254{
17255 int status = 0;
17256 int remaining = 0;
17257
17258 char *buf = NULL;
17259 int bufsize = 0;
17260
17261 do {
17262 int n_received = recv_net_command_realloc(sa, &buf, &bufsize, &remaining);
17263
17264 if (n_received <= 0) {
17265 status = SS_ABORT;
17266 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d", n_received);
17267 goto error;
17268 }
17269
17270 /* extract pointer array to parameters */
17271 NET_COMMAND nc_in;
17272
17273 memcpy(&nc_in, buf, sizeof(nc_in));
17274
17275 /* convert header format (byte swapping) */
17276 if (sa->convert_flags) {
17279 }
17280
17281 int routine_id = nc_in.header.routine_id & ~RPC_NO_REPLY;
17282
17283 RPC_LIST rpc_entry;
17284 bool rpc_cxx = false;
17285
17286 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
17287
17288 if (status != RPC_SUCCESS) {
17289 cm_msg(MERROR, "rpc_server_receive_rpc", "Unknown RPC routine_id %d", routine_id);
17290 goto error;
17291 }
17292
17293 if (rpc_cxx)
17294 status = rpc_execute_cxx(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17295 else
17296 status = rpc_execute_old(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17297
17298 if (status == SS_ABORT) {
17299 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
17300 goto error;
17301 }
17302
17303 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
17304 if (rpc_is_mserver())
17305 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
17306 goto exit;
17307 }
17308
17309 } while (remaining);
17310
17311 if (buf) {
17312 free(buf);
17313 buf = NULL;
17314 bufsize = 0;
17315 }
17316
17317 return RPC_SUCCESS;
17318
17319 error:
17320
17321 {
17322 char str[80];
17323 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17324 if (strchr(str, '.'))
17325 *strchr(str, '.') = 0;
17326 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17327 }
17328
17329 exit:
17330
17332
17333 if (buf) {
17334 free(buf);
17335 buf = NULL;
17336 bufsize = 0;
17337 }
17338
17339 /* disconnect from experiment as MIDAS server */
17340 if (rpc_is_mserver()) {
17341
17342 if (status != SS_EXIT)
17343 cm_msg(MERROR, "rpc_server_receive_rpc", "mserver unexpected shutdown, status %d", status);
17344
17345 HNDLE hDB, hKey;
17346
17348
17349 /* only disconnect from experiment if previously connected.
17350 Necessary for pure RPC servers (RPC_SRVR) */
17351 if (hDB) {
17355
17357
17359 }
17360 }
17361
17362 bool is_mserver = sa->is_mserver;
17363
17364 sa->close();
17365
17366 /* signal caller a shutdonw */
17367 if (status == RPC_SHUTDOWN)
17368 return status;
17369
17370 /* only the mserver should stop on server connection closure */
17371 if (!is_mserver) {
17372 return SS_SUCCESS;
17373 }
17374
17375 return status;
17376}
17377
17378/********************************************************************/
17380/********************************************************************\
17381
17382 Routine: rpc_server_receive_event
17383
17384 Purpose: Receive event and dispatch it
17385
17386 Function value:
17387 RPC_SUCCESS Successful completion
17388 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17389 SS_EXIT Server connection was closed
17390 SS_ABORT Server connection was broken
17391
17392\********************************************************************/
17393{
17394 int status = 0;
17395
17396 DWORD start_time = ss_millitime();
17397
17398 //
17399 // THIS IS NOT THREAD SAFE!!!
17400 //
17401 // IT IS ONLY USED BY THE MSERVER
17402 // MSERVER IS SINGLE-THREADED!!!
17403 //
17404
17405 static char *xbuf = NULL;
17406 static int xbufsize = 0;
17407 static bool xbufempty = true;
17408
17409 // short cut
17410 if (sa == NULL && xbufempty)
17411 return RPC_SUCCESS;
17412
17413 static bool recurse = false;
17414
17415 if (recurse) {
17416 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
17417 // do not do anything if we are called recursively
17418 // via recursive ss_suspend() or otherwise. K.O.
17419 if (xbufempty)
17420 return RPC_SUCCESS;
17421 else
17422 return BM_ASYNC_RETURN;
17423 }
17424
17425 recurse = true;
17426
17427 do {
17428 if (xbufempty && sa) {
17429 int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
17430
17431 if (n_received < 0) {
17432 status = SS_ABORT;
17433 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
17434 goto error;
17435 }
17436
17437 if (n_received == 0) {
17438 // no more data in the tcp socket
17439 recurse = false;
17440 return RPC_SUCCESS;
17441 }
17442
17443 xbufempty = false;
17444 }
17445
17446 if (xbufempty) {
17447 // no event in xbuf buffer
17448 recurse = false;
17449 return RPC_SUCCESS;
17450 }
17451
17452 /* send event to buffer */
17453 INT *pbh = (INT *) xbuf;
17454 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
17455
17456 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
17457
17458 //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);
17459
17460 if (status == SS_ABORT) {
17461 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
17462 goto error;
17463 }
17464
17465 if (status == BM_ASYNC_RETURN) {
17466 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
17467 recurse = false;
17468 return status;
17469 }
17470
17471 if (status != BM_SUCCESS) {
17472 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
17473 }
17474
17475 xbufempty = true;
17476
17477 /* repeat for maximum 0.5 sec */
17478 } while (ss_millitime() - start_time < 500);
17479
17480 recurse = false;
17481 return RPC_SUCCESS;
17482
17483 error:
17484
17485 {
17486 char str[80];
17487 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17488 if (strchr(str, '.'))
17489 *strchr(str, '.') = 0;
17490 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17491 }
17492
17493 //exit:
17494
17496
17497 /* disconnect from experiment as MIDAS server */
17498 if (rpc_is_mserver()) {
17499 HNDLE hDB, hKey;
17500
17502
17503 /* only disconnect from experiment if previously connected.
17504 Necessary for pure RPC servers (RPC_SRVR) */
17505 if (hDB) {
17509
17511
17513 }
17514 }
17515
17516 bool is_mserver = sa->is_mserver;
17517
17518 sa->close();
17519
17520 /* signal caller a shutdonw */
17521 if (status == RPC_SHUTDOWN)
17522 return status;
17523
17524 /* only the mserver should stop on server connection closure */
17525 if (!is_mserver) {
17526 return SS_SUCCESS;
17527 }
17528
17529 return status;
17530}
17531
17532
17533/********************************************************************/
17534int rpc_flush_event_socket(int timeout_msec)
17535/********************************************************************\
17536
17537 Routine: rpc_flush_event_socket
17538
17539 Purpose: Receive and en-buffer events from the mserver event socket
17540
17541 Function value:
17542 BM_SUCCESS Event socket is empty, all data was read an en-buffered
17543 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
17544 SS_EXIT Server connection was closed
17545 SS_ABORT Server connection was broken
17546
17547\********************************************************************/
17548{
17549 bool has_data = ss_event_socket_has_data();
17550
17551 //printf("ss_event_socket_has_data() returned %d\n", has_data);
17552
17553 if (has_data) {
17554 if (timeout_msec == BM_NO_WAIT) {
17555 return BM_ASYNC_RETURN;
17556 } else if (timeout_msec == BM_WAIT) {
17557 return BM_ASYNC_RETURN;
17558 } else {
17559 int status = ss_suspend(timeout_msec, MSG_BM);
17560 if (status == SS_ABORT || status == SS_EXIT)
17561 return status;
17562 return BM_ASYNC_RETURN;
17563 }
17564 }
17565
17566 int status = rpc_server_receive_event(0, NULL, timeout_msec);
17567
17568 //printf("rpc_server_receive_event() status %d\n", status);
17569
17570 if (status == BM_ASYNC_RETURN) {
17571 return BM_ASYNC_RETURN;
17572 }
17573
17574 if (status == SS_ABORT || status == SS_EXIT)
17575 return status;
17576
17577 return BM_SUCCESS;
17578}
17579
17580/********************************************************************/
17582/********************************************************************\
17583
17584 Routine: rpc_server_shutdown
17585
17586 Purpose: Shutdown RPC server, abort all connections
17587
17588 Input:
17589 none
17590
17591 Output:
17592 none
17593
17594 Function value:
17595 RPC_SUCCESS Successful completion
17596
17597\********************************************************************/
17598{
17599 //printf("rpc_server_shutdown!\n");
17600
17601 struct linger ling;
17602
17603 /* close all open connections */
17604 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17605 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
17607 /* lingering needed for PCTCP */
17608 ling.l_onoff = 1;
17609 ling.l_linger = 0;
17610 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17612
17613 if (sa->send_sock) {
17614 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17616 }
17617
17618 if (sa->event_sock) {
17619 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17621 }
17622 }
17623 }
17624
17625 /* avoid memory leak */
17626 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17628 if (sa) {
17629 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
17630 if (sa == _mserver_acception) {
17631 // do not leave behind a stale pointer!
17632 _mserver_acception = NULL;
17633 }
17634 delete sa;
17635 _server_acceptions[idx] = NULL;
17636 }
17637 }
17638
17639 if (_rpc_registered) {
17642 }
17643
17644 /* free suspend structures */
17646
17647 return RPC_SUCCESS;
17648}
17649
17650
17651/********************************************************************/
17653/********************************************************************\
17654
17655 Routine: rpc_check_channels
17656
17657 Purpose: Check open rpc channels by sending watchdog messages
17658
17659 Input:
17660 none
17661
17662 Output:
17663 none
17664
17665 Function value:
17666 RPC_SUCCESS Channel is still alive
17667 RPC_NET_ERROR Connection is broken
17668
17669\********************************************************************/
17670{
17671 INT status;
17672 NET_COMMAND nc;
17673 fd_set readfds;
17674 struct timeval timeout;
17675
17676 //printf("rpc_check_channels!\n");
17677
17678 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17679 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
17681 if (sa == NULL)
17682 continue;
17683
17684 if (sa->watchdog_timeout == 0) {
17685 continue;
17686 }
17687
17688 DWORD elapsed = ss_millitime() - sa->last_activity;
17689
17690 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
17691
17692 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
17693
17694 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
17695
17696 /* send a watchdog message */
17698 nc.header.param_size = 0;
17699
17700 int convert_flags = sa->convert_flags;
17701 if (convert_flags) {
17704 }
17705
17706 /* send the header to the client */
17707 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
17708
17709 if (i < 0) {
17710 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
17711 sa->prog_name.c_str(),
17712 sa->host_name.c_str(),
17713 sa->watchdog_timeout / 1000,
17714 i);
17715
17716 /* disconnect from experiment */
17717 if (rpc_is_mserver()) {
17719 return RPC_NET_ERROR;
17720 }
17721
17722 sa->close();
17723 return RPC_NET_ERROR;
17724 }
17725
17726 DWORD timeout_end_ms = ss_millitime() + sa->watchdog_timeout;
17727
17728 while (1) {
17729 FD_ZERO(&readfds);
17730 FD_SET(sa->send_sock, &readfds);
17731 FD_SET(sa->recv_sock, &readfds);
17732
17733 timeout.tv_sec = 1;
17734 timeout.tv_usec = 0;
17735
17736 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
17737
17738 DWORD now = ss_millitime();
17739
17740 //printf("waiting for reply: %d %d, diff %d, select() status %d\n", now, timeout_end_ms, timeout_end_ms - now, status);
17741
17742 if (now > timeout_end_ms) // timeout
17743 break;
17744
17745 if (status > 0) // select has something to read
17746 break;
17747
17748 // select() returned 0, timeout
17749 // select() returned -1, error, likely EAGAIN or EINTR
17750
17753 }
17754
17755 if (!FD_ISSET(sa->send_sock, &readfds) &&
17756 !FD_ISSET(sa->recv_sock, &readfds)) {
17757
17758 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
17759 sa->prog_name.c_str(),
17760 sa->host_name.c_str(),
17761 sa->watchdog_timeout / 1000);
17762
17763 /* disconnect from experiment */
17764 if (rpc_is_mserver()) {
17766 return RPC_NET_ERROR;
17767 }
17768
17769 sa->close();
17770 return RPC_NET_ERROR;
17771 }
17772
17773 /* receive result on send socket */
17774 if (FD_ISSET(sa->send_sock, &readfds)) {
17775 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
17776 if (i <= 0) {
17777 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
17778 sa->prog_name.c_str(),
17779 sa->host_name.c_str(),
17780 sa->watchdog_timeout / 1000,
17781 i);
17782
17783 /* disconnect from experiment */
17784 if (rpc_is_mserver()) {
17786 return RPC_NET_ERROR;
17787 }
17788
17789 sa->close();
17790 return RPC_NET_ERROR;
17791 }
17792 }
17793 }
17794 }
17795 }
17796
17797 return RPC_SUCCESS;
17798}
17799
17807/********************************************************************\
17808* *
17809* Bank functions *
17810* *
17811\********************************************************************/
17812
17813/********************************************************************/
17819void bk_init(void *event) {
17820 ((BANK_HEADER *) event)->data_size = 0;
17821 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
17822}
17823
17825#ifndef DOXYGEN_SHOULD_SKIP_THIS
17826
17827/********************************************************************/
17828BOOL bk_is32(const void *event)
17829/********************************************************************\
17830
17831 Routine: bk_is32
17832
17833 Purpose: Return true if banks inside event are 32-bit banks
17834
17835 Input:
17836 void *event pointer to the event
17837
17838 Output:
17839 none
17840
17841 Function value:
17842 none
17843
17844\********************************************************************/
17845{
17846 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
17847}
17848
17849/********************************************************************/
17850BOOL bk_is32a(const void *event)
17851/********************************************************************\
17852
17853 Routine: bk_is32a
17854
17855 Purpose: Return true if banks inside event are 32-bit banks
17856 and banks are 64-bit aligned
17857
17858 Input:
17859 void *event pointer to the event
17860
17861 Output:
17862 none
17863
17864 Function value:
17865 none
17866
17867\********************************************************************/
17868{
17869 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
17870}
17871
17873#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17874
17875/********************************************************************/
17882void bk_init32(void *event) {
17883 ((BANK_HEADER *) event)->data_size = 0;
17884 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
17885}
17886
17887/********************************************************************/
17895void bk_init32a(void *event) {
17896 ((BANK_HEADER *) event)->data_size = 0;
17898}
17899
17900/********************************************************************/
17908INT bk_size(const void *event) {
17909 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
17910}
17911
17912static void copy_bk_name(char* dst, const char* src)
17913{
17914 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
17915
17916 if (src[0] == 0) {
17917 // invalid empty name
17918 dst[0] = 0;
17919 dst[1] = 0;
17920 dst[2] = 0;
17921 dst[3] = 0;
17922 return;
17923 }
17924
17925 dst[0] = src[0];
17926
17927 if (src[1] == 0) {
17928 dst[1] = 0;
17929 dst[2] = 0;
17930 dst[3] = 0;
17931 return;
17932 }
17933
17934 dst[1] = src[1];
17935
17936 if (src[2] == 0) {
17937 dst[2] = 0;
17938 dst[3] = 0;
17939 return;
17940 }
17941
17942 dst[2] = src[2];
17943
17944 if (src[3] == 0) {
17945 dst[3] = 0;
17946 return;
17947 }
17948
17949 dst[3] = src[3];
17950}
17951
17952/********************************************************************/
17974void bk_create(void *event, const char *name, WORD type, void **pdata) {
17975 if (bk_is32a((BANK_HEADER *) event)) {
17976 if (((PTYPE) event & 0x07) != 0) {
17977 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
17978 return;
17979 }
17980 BANK32A *pbk32a;
17981
17982 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17983 copy_bk_name(pbk32a->name, name);
17984 pbk32a->type = type;
17985 pbk32a->data_size = 0;
17986 *pdata = pbk32a + 1;
17987 } else if (bk_is32((BANK_HEADER *) event)) {
17988 BANK32 *pbk32;
17989
17990 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17991 copy_bk_name(pbk32->name, name);
17992 pbk32->type = type;
17993 pbk32->data_size = 0;
17994 *pdata = pbk32 + 1;
17995 } else {
17996 BANK *pbk;
17997
17998 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17999 copy_bk_name(pbk->name, name);
18000 pbk->type = type;
18001 pbk->data_size = 0;
18002 *pdata = pbk + 1;
18003 }
18004}
18005
18007#ifndef DOXYGEN_SHOULD_SKIP_THIS
18008
18009/********************************************************************/
18017INT bk_copy(char *pevent, char *psrce, const char *bkname) {
18018
18019 INT status;
18020 DWORD bklen, bktype, bksze;
18021 BANK_HEADER *psBkh;
18022 BANK *psbkh;
18023 char *pdest;
18024 void *psdata;
18025
18026 // source pointing on the BANKxx
18027 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
18028 // Find requested bank
18029 status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
18030 // Return 0 if not found
18031 if (status != SUCCESS) return 0;
18032
18033 // Check bank type...
18034 // You cannot mix BANK and BANK32 so make sure all the FE use either
18035 // bk_init(pevent) or bk_init32(pevent).
18036 if (bk_is32a(psBkh)) {
18037
18038 // pointer to the source bank header
18039 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
18040 // Data size in the bank
18041 bksze = psbkh32a->data_size;
18042
18043 // Get to the end of the event
18044 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18045 // Copy from BANK32 to end of Data
18046 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
18047 // Bring pointer to the next free location
18048 pdest += ALIGN8(bksze) + sizeof(BANK32A);
18049
18050 } else if (bk_is32(psBkh)) {
18051
18052 // pointer to the source bank header
18053 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
18054 // Data size in the bank
18055 bksze = psbkh32->data_size;
18056
18057 // Get to the end of the event
18058 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18059 // Copy from BANK32 to end of Data
18060 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
18061 // Bring pointer to the next free location
18062 pdest += ALIGN8(bksze) + sizeof(BANK32);
18063
18064 } else {
18065
18066 // pointer to the source bank header
18067 psbkh = ((BANK *) psdata - 1);
18068 // Data size in the bank
18069 bksze = psbkh->data_size;
18070
18071 // Get to the end of the event
18072 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18073 // Copy from BANK to end of Data
18074 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
18075 // Bring pointer to the next free location
18076 pdest += ALIGN8(bksze) + sizeof(BANK);
18077 }
18078
18079 // Close bank (adjust BANK_HEADER size)
18080 bk_close(pevent, pdest);
18081 // Adjust EVENT_HEADER size
18082 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
18083 return SUCCESS;
18084}
18085
18086/********************************************************************/
18087int bk_delete(void *event, const char *name)
18088/********************************************************************\
18089
18090 Routine: bk_delete
18091
18092 Purpose: Delete a MIDAS bank inside an event
18093
18094 Input:
18095 void *event pointer to the event
18096 char *name Name of bank (exactly four letters)
18097
18098 Function value:
18099 CM_SUCCESS Bank has been deleted
18100 0 Bank has not been found
18101
18102\********************************************************************/
18103{
18104 BANK *pbk;
18105 DWORD dname;
18106 int remaining;
18107
18108 if (bk_is32a((BANK_HEADER *) event)) {
18109 /* locate bank */
18110 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18111 copy_bk_name((char *) &dname, name);
18112 do {
18113 if (*((DWORD *) pbk32a->name) == dname) {
18114 /* bank found, delete it */
18115 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18116 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18117
18118 /* reduce total event size */
18119 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
18120
18121 /* copy remaining bytes */
18122 if (remaining > 0)
18123 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
18124 return CM_SUCCESS;
18125 }
18126
18127 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18128 } while ((DWORD) ((char *) pbk32a - (char *) event) <
18129 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18130 } else if (bk_is32((BANK_HEADER *) event)) {
18131 /* locate bank */
18132 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18133 copy_bk_name((char *) &dname, name);
18134 do {
18135 if (*((DWORD *) pbk32->name) == dname) {
18136 /* bank found, delete it */
18137 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18138 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18139
18140 /* reduce total event size */
18141 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
18142
18143 /* copy remaining bytes */
18144 if (remaining > 0)
18145 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
18146 return CM_SUCCESS;
18147 }
18148
18149 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18150 } while ((DWORD) ((char *) pbk32 - (char *) event) <
18151 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18152 } else {
18153 /* locate bank */
18154 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18155 copy_bk_name((char *) &dname, name);
18156 do {
18157 if (*((DWORD *) pbk->name) == dname) {
18158 /* bank found, delete it */
18159 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18160 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18161
18162 /* reduce total event size */
18163 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
18164
18165 /* copy remaining bytes */
18166 if (remaining > 0)
18167 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
18168 return CM_SUCCESS;
18169 }
18170
18171 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18172 } while ((DWORD) ((char *) pbk - (char *) event) <
18173 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18174 }
18175
18176 return 0;
18177}
18178
18180#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18181
18182/********************************************************************/
18193INT bk_close(void *event, void *pdata) {
18194 if (bk_is32a((BANK_HEADER *) event)) {
18195 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18196 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
18197 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
18198 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]);
18199 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
18200 return pbk32a->data_size;
18201 } else if (bk_is32((BANK_HEADER *) event)) {
18202 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18203 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
18204 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
18205 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]);
18206 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
18207 return pbk32->data_size;
18208 } else {
18209 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18210 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
18211 if (size > 0xFFFF) {
18212 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18213 size = 0;
18214 }
18215 pbk->data_size = (WORD) (size);
18216 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
18217 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]);
18218 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
18219 if (size > 0xFFFF) {
18220 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18221 size = 0;
18222 }
18223 ((BANK_HEADER *) event)->data_size = size;
18224 return pbk->data_size;
18225 }
18226}
18227
18228/********************************************************************/
18253INT bk_list(const void *event, char *bklist) { /* Full event */
18254 INT nbk;
18255 BANK *pmbk = NULL;
18256 BANK32 *pmbk32 = NULL;
18257 BANK32A *pmbk32a = NULL;
18258 char *pdata;
18259
18260 /* compose bank list */
18261 bklist[0] = 0;
18262 nbk = 0;
18263 do {
18264 /* scan all banks for bank name only */
18265 if (bk_is32a(event)) {
18266 bk_iterate32a(event, &pmbk32a, &pdata);
18267 if (pmbk32a == NULL)
18268 break;
18269 } else if (bk_is32(event)) {
18270 bk_iterate32(event, &pmbk32, &pdata);
18271 if (pmbk32 == NULL)
18272 break;
18273 } else {
18274 bk_iterate(event, &pmbk, &pdata);
18275 if (pmbk == NULL)
18276 break;
18277 }
18278 nbk++;
18279
18280 if (nbk > BANKLIST_MAX) {
18281 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
18282 return (nbk - 1);
18283 }
18284 if (bk_is32a(event))
18285 strncat(bklist, (char *) pmbk32a->name, 4);
18286 else if (bk_is32(event))
18287 strncat(bklist, (char *) pmbk32->name, 4);
18288 else
18289 strncat(bklist, (char *) pmbk->name, 4);
18290 } while (1);
18291 return (nbk);
18292}
18293
18294/********************************************************************/
18302INT bk_locate(const void *event, const char *name, void *pdata) {
18303 BANK *pbk;
18304 BANK32 *pbk32;
18305 BANK32A *pbk32a;
18306 DWORD dname;
18307
18308 if (bk_is32a(event)) {
18309 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18310 copy_bk_name((char *) &dname, name);
18311 while ((DWORD) ((char *) pbk32a - (char *) event) <
18312 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18313 if (*((DWORD *) pbk32a->name) == dname) {
18314 *((void **) pdata) = pbk32a + 1;
18315 if (tid_size[pbk32a->type & 0xFF] == 0)
18316 return pbk32a->data_size;
18317 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
18318 }
18319 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18320 }
18321 } else if (bk_is32(event)) {
18322 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18323 copy_bk_name((char *) &dname, name);
18324 while ((DWORD) ((char *) pbk32 - (char *) event) <
18325 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18326 if (*((DWORD *) pbk32->name) == dname) {
18327 *((void **) pdata) = pbk32 + 1;
18328 if (tid_size[pbk32->type & 0xFF] == 0)
18329 return pbk32->data_size;
18330 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
18331 }
18332 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18333 }
18334 } else {
18335 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18336 copy_bk_name((char *) &dname, name);
18337 while ((DWORD) ((char *) pbk - (char *) event) <
18338 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18339 if (*((DWORD *) pbk->name) == dname) {
18340 *((void **) pdata) = pbk + 1;
18341 if (tid_size[pbk->type & 0xFF] == 0)
18342 return pbk->data_size;
18343 return pbk->data_size / tid_size[pbk->type & 0xFF];
18344 }
18345 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18346 }
18347
18348 }
18349
18350 /* bank not found */
18351 *((void **) pdata) = NULL;
18352 return 0;
18353}
18354
18355/********************************************************************/
18365INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
18366 DWORD dname;
18367
18368 if (bk_is32a(pbkh)) {
18369 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
18370 copy_bk_name((char *) &dname, name);
18371 do {
18372 if (*((DWORD *) pbk32a->name) == dname) {
18373 *((void **) pdata) = pbk32a + 1;
18374 if (tid_size[pbk32a->type & 0xFF] == 0)
18375 *bklen = pbk32a->data_size;
18376 else
18377 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
18378
18379 *bktype = pbk32a->type;
18380 return 1;
18381 }
18382 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18383 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18384 } else if (bk_is32(pbkh)) {
18385 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
18386 copy_bk_name((char *) &dname, name);
18387 do {
18388 if (*((DWORD *) pbk32->name) == dname) {
18389 *((void **) pdata) = pbk32 + 1;
18390 if (tid_size[pbk32->type & 0xFF] == 0)
18391 *bklen = pbk32->data_size;
18392 else
18393 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
18394
18395 *bktype = pbk32->type;
18396 return 1;
18397 }
18398 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18399 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18400 } else {
18401 BANK *pbk = (BANK *) (pbkh + 1);
18402 copy_bk_name((char *) &dname, name);
18403 do {
18404 if (*((DWORD *) pbk->name) == dname) {
18405 *((void **) pdata) = pbk + 1;
18406 if (tid_size[pbk->type & 0xFF] == 0)
18407 *bklen = pbk->data_size;
18408 else
18409 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
18410
18411 *bktype = pbk->type;
18412 return 1;
18413 }
18414 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18415 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18416 }
18417
18418 /* bank not found */
18419 *((void **) pdata) = NULL;
18420 return 0;
18421}
18422
18423/********************************************************************/
18459INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
18460 if (*pbk == NULL)
18461 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18462 else
18463 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18464
18465 *((void **) pdata) = (*pbk) + 1;
18466
18467 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18468 *pbk = *((BANK **) pdata) = NULL;
18469 return 0;
18470 }
18471
18472 return (*pbk)->data_size;
18473}
18474
18475
18477#ifndef DOXYGEN_SHOULD_SKIP_THIS
18478
18479/********************************************************************/
18480INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
18481/********************************************************************\
18482
18483 Routine: bk_iterate32
18484
18485 Purpose: Iterate through 32 bit MIDAS banks inside an event
18486
18487 Input:
18488 void *event pointer to the event
18489 BANK32 **pbk32 must be NULL for the first call to bk_iterate
18490
18491 Output:
18492 BANK32 **pbk32 pointer to the bank header
18493 void *pdata pointer to data area of the bank
18494
18495 Function value:
18496 INT size of the bank in bytes
18497
18498\********************************************************************/
18499{
18500 if (*pbk == NULL)
18501 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
18502 else
18503 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18504
18505 *((void **) pdata) = (*pbk) + 1;
18506
18507 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18508 *pbk = NULL;
18509 pdata = NULL;
18510 return 0;
18511 }
18512
18513 return (*pbk)->data_size;
18514}
18515
18516INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
18517/********************************************************************\
18518
18519 Routine: bk_iterate32a
18520
18521 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
18522
18523 Input:
18524 void *event pointer to the event
18525 BANK32A **pbk32a must be NULL for the first call to bk_iterate
18526
18527 Output:
18528 BANK32A **pbk32 pointer to the bank header
18529 void *pdata pointer to data area of the bank
18530
18531 Function value:
18532 INT size of the bank in bytes
18533
18534\********************************************************************/
18535{
18536 if (*pbk32a == NULL)
18537 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18538 else
18539 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
18540
18541 *((void **) pdata) = (*pbk32a) + 1;
18542
18543 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18544 *pbk32a = NULL;
18545 pdata = NULL;
18546 return 0;
18547 }
18548
18549 return (*pbk32a)->data_size;
18550}
18551
18553#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18554
18555/********************************************************************/
18570INT bk_swap(void *event, BOOL force) {
18571 BANK_HEADER *pbh;
18572 BANK *pbk;
18573 BANK32 *pbk32;
18574 BANK32A *pbk32a;
18575 void *pdata;
18576 WORD type;
18577
18578 pbh = (BANK_HEADER *) event;
18579
18580 /* only swap if flags in high 16-bit */
18581 if (pbh->flags < 0x10000 && !force)
18582 return 0;
18583
18584 /* swap bank header */
18585 DWORD_SWAP(&pbh->data_size);
18586 DWORD_SWAP(&pbh->flags);
18587
18588 pbk = (BANK *) (pbh + 1);
18589 pbk32 = (BANK32 *) pbk;
18590 pbk32a = (BANK32A *) pbk;
18591
18592 /* scan event */
18593 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
18594 /* swap bank header */
18595 if (bk_is32a(event)) {
18596 DWORD_SWAP(&pbk32a->type);
18597 DWORD_SWAP(&pbk32a->data_size);
18598 pdata = pbk32a + 1;
18599 type = (WORD) pbk32a->type;
18600 } else if (bk_is32(event)) {
18601 DWORD_SWAP(&pbk32->type);
18602 DWORD_SWAP(&pbk32->data_size);
18603 pdata = pbk32 + 1;
18604 type = (WORD) pbk32->type;
18605 } else {
18606 WORD_SWAP(&pbk->type);
18607 WORD_SWAP(&pbk->data_size);
18608 pdata = pbk + 1;
18609 type = pbk->type;
18610 }
18611
18612 /* pbk points to next bank */
18613 if (bk_is32a(event)) {
18614 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18615 pbk = (BANK *) pbk32a;
18616 } else if (bk_is32(event)) {
18617 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18618 pbk = (BANK *) pbk32;
18619 } else {
18620 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18621 pbk32 = (BANK32 *) pbk;
18622 }
18623
18624 switch (type) {
18625 case TID_UINT16:
18626 case TID_INT16:
18627 while ((char *) pdata < (char *) pbk) {
18628 WORD_SWAP(pdata);
18629 pdata = (void *) (((WORD *) pdata) + 1);
18630 }
18631 break;
18632
18633 case TID_UINT32:
18634 case TID_INT32:
18635 case TID_BOOL:
18636 case TID_FLOAT:
18637 while ((char *) pdata < (char *) pbk) {
18638 DWORD_SWAP(pdata);
18639 pdata = (void *) (((DWORD *) pdata) + 1);
18640 }
18641 break;
18642
18643 case TID_DOUBLE:
18644 case TID_INT64:
18645 case TID_UINT64:
18646 while ((char *) pdata < (char *) pbk) {
18647 QWORD_SWAP(pdata);
18648 pdata = (void *) (((double *) pdata) + 1);
18649 }
18650 break;
18651 }
18652 }
18653
18654 return CM_SUCCESS;
18655}
18656
/* end of bkfunctionc */
18660
18661
18668#ifndef DOXYGEN_SHOULD_SKIP_THIS
18669/********************************************************************/
18670
18671/********************************************************************\
18672* *
18673* Ring buffer functions *
18674* *
18675* Provide an inter-thread buffer scheme for handling front-end *
18676* events. This code allows concurrent data acquisition, calibration *
18677* and network transfer on a multi-CPU machine. One thread reads *
18678* out the data, passes it vis the ring buffer functions *
18679* to another thread running on the other CPU, which can then *
18680* calibrate and/or send the data over the network. *
18681* *
18682\********************************************************************/
18683
18684typedef struct {
18685 unsigned char *buffer;
18686 unsigned int size;
18687 unsigned int max_event_size;
18688 unsigned char *rp;
18689 unsigned char *wp;
18690 unsigned char *ep;
18691} RING_BUFFER;
18692
18693#define MAX_RING_BUFFER 100
18694
18696
18697static volatile int _rb_nonblocking = 0;
18698
18700#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18701
18702/********************************************************************/
18709/********************************************************************\
18710
18711 Routine: rb_set_nonblocking
18712
18713 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
18714 environments for stopping all theads without deadlock
18715
18716 Input:
18717 NONE
18718
18719 Output:
18720 NONE
18721
18722 Function value:
18723 DB_SUCCESS Successful completion
18724
18725\********************************************************************/
18726{
18727 _rb_nonblocking = 1;
18728
18729 return DB_SUCCESS;
18730}
18731
18732/********************************************************************/
18749int rb_create(int size, int max_event_size, int *handle)
18750/********************************************************************\
18751
18752 Routine: rb_create
18753
18754 Purpose: Create a ring buffer with a given size
18755
18756 Input:
18757 int size Size of ring buffer, must be larger than
18758 2*max_event_size
18759 int max_event_size Maximum event size to be placed into
18760 ring buffer
18761 Output:
18762 int *handle Handle to ring buffer
18763
18764 Function value:
18765 DB_SUCCESS Successful completion
18766 DB_NO_MEMORY Maximum number of ring buffers exceeded
18767 DB_INVALID_PARAM Invalid event size specified
18768
18769\********************************************************************/
18770{
18771 int i;
18772
18773 for (i = 0; i < MAX_RING_BUFFER; i++)
18774 if (rb[i].buffer == NULL)
18775 break;
18776
18777 if (i == MAX_RING_BUFFER)
18778 return DB_NO_MEMORY;
18779
18780 if (size < max_event_size * 2)
18781 return DB_INVALID_PARAM;
18782
18783 memset(&rb[i], 0, sizeof(RING_BUFFER));
18784 rb[i].buffer = (unsigned char *) M_MALLOC(size);
18785 assert(rb[i].buffer);
18786 rb[i].size = size;
18788 rb[i].rp = rb[i].buffer;
18789 rb[i].wp = rb[i].buffer;
18790 rb[i].ep = rb[i].buffer;
18791
18792 *handle = i + 1;
18793
18794 return DB_SUCCESS;
18795}
18796
18797/********************************************************************/
18803int rb_delete(int handle)
18804/********************************************************************\
18805
18806 Routine: rb_delete
18807
18808 Purpose: Delete a ring buffer
18809
18810 Input:
18811 none
18812 Output:
18813 int handle Handle to ring buffer
18814
18815 Function value:
18816 DB_SUCCESS Successful completion
18817
18818\********************************************************************/
18819{
18820 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18821 return DB_INVALID_HANDLE;
18822
18823 M_FREE(rb[handle - 1].buffer);
18824 rb[handle - 1].buffer = NULL;
18825 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
18826
18827 return DB_SUCCESS;
18828}
18829
18830/********************************************************************/
18840int rb_get_wp(int handle, void **p, int millisec)
18841/********************************************************************\
18842
18843Routine: rb_get_wp
18844
18845 Purpose: Retrieve write pointer where new data can be written
18846
18847 Input:
18848 int handle Ring buffer handle
18849 int millisec Optional timeout in milliseconds if
18850 buffer is full. Zero to not wait at
18851 all (non-blocking)
18852
18853 Output:
18854 char **p Write pointer
18855
18856 Function value:
18857 DB_SUCCESS Successful completion
18858
18859\********************************************************************/
18860{
18861 int h, i;
18862 unsigned char *rp;
18863
18864 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18865 return DB_INVALID_HANDLE;
18866
18867 h = handle - 1;
18868
18869 for (i = 0; i <= millisec / 10; i++) {
18870
18871 rp = rb[h].rp; // keep local copy for convenience
18872
18873 /* check if enough size for wp >= rp without wrap-around */
18874 if (rb[h].wp >= rp
18875 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
18876 *p = rb[h].wp;
18877 return DB_SUCCESS;
18878 }
18879
18880 /* check if enough size for wp >= rp with wrap-around */
18881 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
18882 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
18883 *p = rb[h].wp;
18884 return DB_SUCCESS;
18885 }
18886
18887 /* check if enough size for wp < rp */
18888 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
18889 *p = rb[h].wp;
18890 return DB_SUCCESS;
18891 }
18892
18893 if (millisec == 0)
18894 return DB_TIMEOUT;
18895
18896 if (_rb_nonblocking)
18897 return DB_TIMEOUT;
18898
18899 /* wait one time slice */
18900 ss_sleep(10);
18901 }
18902
18903 return DB_TIMEOUT;
18904}
18905
18906/********************************************************************/
18915int rb_increment_wp(int handle, int size)
18916/********************************************************************\
18917
18918 Routine: rb_increment_wp
18919
18920 Purpose: Increment current write pointer, making the data at
18921 the write pointer available to the receiving thread
18922
18923 Input:
18924 int handle Ring buffer handle
18925 int size Number of bytes placed at the WP
18926
18927 Output:
18928 NONE
18929
18930 Function value:
18931 DB_SUCCESS Successful completion
18932 DB_INVALID_PARAM Event size too large or invalid handle
18933\********************************************************************/
18934{
18935 int h;
18936 unsigned char *new_wp;
18937
18938 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18939 return DB_INVALID_HANDLE;
18940
18941 h = handle - 1;
18942
18943 if ((DWORD) size > rb[h].max_event_size) {
18944 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
18945 size/1024/1024, rb[h].max_event_size/1024/1024);
18946 abort();
18947 }
18948
18949 new_wp = rb[h].wp + size;
18950
18951 /* wrap around wp if not enough space */
18952 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
18953 rb[h].ep = new_wp;
18954 new_wp = rb[h].buffer;
18955 assert(rb[h].rp != rb[h].buffer);
18956 } else
18957 if (new_wp > rb[h].ep)
18958 rb[h].ep = new_wp;
18959
18960 rb[h].wp = new_wp;
18961
18962 return DB_SUCCESS;
18963}
18964
18965/********************************************************************/
18981int rb_get_rp(int handle, void **p, int millisec)
18982/********************************************************************\
18983
18984 Routine: rb_get_rp
18985
18986 Purpose: Obtain the current read pointer at which new data is
18987 available with optional timeout
18988
18989 Input:
18990 int handle Ring buffer handle
18991 int millisec Optional timeout in milliseconds if
18992 buffer is full. Zero to not wait at
18993 all (non-blocking)
18994
18995 Output:
18996 char **p Address of pointer pointing to newly
18997 available data. If p == NULL, only
18998 return status.
18999
19000 Function value:
19001 DB_SUCCESS Successful completion
19002
19003\********************************************************************/
19004{
19005 int i, h;
19006
19007 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19008 return DB_INVALID_HANDLE;
19009
19010 h = handle - 1;
19011
19012 for (i = 0; i <= millisec / 10; i++) {
19013
19014 if (rb[h].wp != rb[h].rp) {
19015 if (p != NULL)
19016 *p = rb[handle - 1].rp;
19017 return DB_SUCCESS;
19018 }
19019
19020 if (millisec == 0)
19021 return DB_TIMEOUT;
19022
19023 if (_rb_nonblocking)
19024 return DB_TIMEOUT;
19025
19026 /* wait one time slice */
19027 ss_sleep(10);
19028 }
19029
19030 return DB_TIMEOUT;
19031}
19032
19033/********************************************************************/
19043int rb_increment_rp(int handle, int size)
19044/********************************************************************\
19045
19046 Routine: rb_increment_rp
19047
19048 Purpose: Increment current read pointer, freeing up space for
19049 the writing thread.
19050
19051 Input:
19052 int handle Ring buffer handle
19053 int size Number of bytes to free up at current
19054 read pointer
19055
19056 Output:
19057 NONE
19058
19059 Function value:
19060 DB_SUCCESS Successful completion
19061 DB_INVALID_PARAM Event size too large or invalid handle
19062
19063\********************************************************************/
19064{
19065 int h;
19066
19067 unsigned char *new_rp;
19068 unsigned char *ep;
19069
19070 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19071 return DB_INVALID_HANDLE;
19072
19073 h = handle - 1;
19074
19075 if ((DWORD) size > rb[h].max_event_size)
19076 return DB_INVALID_PARAM;
19077
19078 new_rp = rb[h].rp + size;
19079 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
19080
19081 /* wrap around if end pointer reached */
19082 if (new_rp >= ep && rb[h].wp < ep)
19083 new_rp = rb[h].buffer;
19084
19085 rb[handle - 1].rp = new_rp;
19086
19087 return DB_SUCCESS;
19088}
19089
19090/********************************************************************/
19098int rb_get_buffer_level(int handle, int *n_bytes)
19099/********************************************************************\
19100
19101 Routine: rb_get_buffer_level
19102
19103 Purpose: Return number of bytes in a ring buffer
19104
19105 Input:
19106 int handle Handle of the buffer to get the info
19107
19108 Output:
19109 int *n_bytes Number of bytes in buffer
19110
19111 Function value:
19112 DB_SUCCESS Successful completion
19113 DB_INVALID_HANDLE Buffer handle is invalid
19114
19115\********************************************************************/
19116{
19117 int h;
19118
19119 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19120 return DB_INVALID_HANDLE;
19121
19122 h = handle - 1;
19123
19124 if (rb[h].wp >= rb[h].rp)
19125 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
19126 else
19127 *n_bytes =
19128 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
19129
19130 return DB_SUCCESS;
19131}
19132
/* end of rbfunctionc */
19134
19135
19137{
19138 if (format == FORMAT_FIXED) {
19139 int status;
19140 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
19141 if (status != DB_SUCCESS) {
19142 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
19143 return status;
19144 }
19145 return SUCCESS;
19146 } else if (format == FORMAT_MIDAS) {
19147 INT size, i, status, n_data;
19148 int n;
19149 char *pdata, *pdata0;
19150
19151 char name[5];
19152 BANK_HEADER *pbh;
19153 BANK *pbk;
19154 BANK32 *pbk32;
19155 BANK32A *pbk32a;
19156 DWORD bkname;
19157 WORD bktype;
19158 HNDLE hKeyRoot, hKeyl, *hKeys;
19159 KEY key;
19160
19161 pbh = (BANK_HEADER *) (pevent + 1);
19162 pbk = NULL;
19163 pbk32 = NULL;
19164 pbk32a = NULL;
19165
19166 /* count number of banks */
19167 for (n=0 ; ; n++) {
19168 if (bk_is32a(pbh)) {
19169 bk_iterate32a(pbh, &pbk32a, &pdata);
19170 if (pbk32a == NULL)
19171 break;
19172 } else if (bk_is32(pbh)) {
19173 bk_iterate32(pbh, &pbk32, &pdata);
19174 if (pbk32 == NULL)
19175 break;
19176 } else {
19177 bk_iterate(pbh, &pbk, &pdata);
19178 if (pbk == NULL)
19179 break;
19180 }
19181 }
19182
19183 /* build array of keys */
19184 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
19185
19186 pbk = NULL;
19187 pbk32 = NULL;
19188 n = 0;
19189 do {
19190 /* scan all banks */
19191 if (bk_is32a(pbh)) {
19192 size = bk_iterate32a(pbh, &pbk32a, &pdata);
19193 if (pbk32a == NULL)
19194 break;
19195 bkname = *((DWORD *) pbk32a->name);
19196 bktype = (WORD) pbk32a->type;
19197 } else if (bk_is32(pbh)) {
19198 size = bk_iterate32(pbh, &pbk32, &pdata);
19199 if (pbk32 == NULL)
19200 break;
19201 bkname = *((DWORD *) pbk32->name);
19202 bktype = (WORD) pbk32->type;
19203 } else {
19204 size = bk_iterate(pbh, &pbk, &pdata);
19205 if (pbk == NULL)
19206 break;
19207 bkname = *((DWORD *) pbk->name);
19208 bktype = (WORD) pbk->type;
19209 }
19210
19211 n_data = size;
19212 if (rpc_tid_size(bktype & 0xFF))
19213 n_data /= rpc_tid_size(bktype & 0xFF);
19214
19215 /* get bank key */
19216 *((DWORD *) name) = bkname; // NB: "name" is only byte-aligned, and may have wrong alignement for DWORD, flagged by UBSAN. K.O.
19217
19218 //name[0] = bkname>>0 & 0xFF;
19219 //name[1] = bkname>>8 & 0xFF;
19220 //name[2] = bkname>>16 & 0xFF;
19221 //name[3] = bkname>>24 & 0xFF;
19222 name[4] = 0;
19223
19224 //printf("bkname 0x%08x, name [%s]\n", bkname, name);
19225
19226 /* record the start of the data in case it is struct */
19227 pdata0 = pdata;
19228 if (bktype == TID_STRUCT) {
19229 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19230 if (status != DB_SUCCESS) {
19231 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
19232 continue;
19233 }
19234
19235 /* write structured bank */
19236 for (i = 0;; i++) {
19237 status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
19239 break;
19240
19241 db_get_key(hDB, hKeyl, &key);
19242
19243 /* adjust for alignment */
19244 if (key.type != TID_STRING && key.type != TID_LINK)
19245 pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
19246
19248 if (status != DB_SUCCESS) {
19249 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19250 continue;
19251 }
19252 hKeys[n++] = hKeyl;
19253
19254 /* shift data pointer to next item */
19255 pdata += key.item_size * key.num_values;
19256 }
19257 } else {
19258 /* write variable length bank */
19259 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19260 if (status != DB_SUCCESS) {
19261 status = db_create_key(hDB, hKey, name, bktype);
19262 if (status != DB_SUCCESS) {
19263 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);
19264 continue;
19265 }
19266 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19267 if (status != DB_SUCCESS) {
19268 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);
19269 continue;
19270 }
19271 }
19272 if (n_data > 0) {
19273 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
19274 if (status != DB_SUCCESS) {
19275 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19276 }
19277 hKeys[n++] = hKeyRoot;
19278 }
19279 }
19280 } while (1);
19281
19282 /* notify all hot-lined clients in one go */
19283 db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
19284
19285 free(hKeys);
19286
19287 return SUCCESS;
19288 } else {
19289 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
19290 return CM_DB_ERROR;
19291 }
19292}
19293
19294/* emacs
19295 * Local Variables:
19296 * tab-width: 8
19297 * c-basic-offset: 3
19298 * indent-tabs-mode: nil
19299 * End:
19300 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11583
std::atomic_bool connected
Definition midas.cxx:11582
std::string client_name
Definition midas.cxx:11587
void * pparam
Definition midas.cxx:15494
size_t param_size
Definition midas.cxx:15489
size_t offset
Definition midas.cxx:15487
size_t out_max_size
Definition midas.cxx:15491
size_t out_max_size_offset
Definition midas.cxx:15490
std::string * ps
Definition midas.cxx:15492
std::vector< char > * pv
Definition midas.cxx:15493
size_t arg_size
Definition midas.cxx:15488
BUFFER * get_pbuf() const
Definition midas.cxx:3197
bool is_error() const
Definition midas.cxx:3187
int get_status() const
Definition midas.cxx:3192
bool is_locked() const
Definition midas.cxx:3182
bm_lock_buffer_guard(BUFFER *pbuf, bool do_not_lock=false)
Definition midas.cxx:3101
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:1616
static bool exists(const std::string &name)
Definition odbxx.cxx:76
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:866
INT al_check()
Definition alarm.cxx:612
void bk_init32a(void *event)
Definition midas.cxx:17895
INT bk_close(void *event, void *pdata)
Definition midas.cxx:18193
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:18516
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:17912
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:18570
BOOL bk_is32a(const void *event)
Definition midas.cxx:17850
int bk_delete(void *event, const char *name)
Definition midas.cxx:18087
BOOL bk_is32(const void *event)
Definition midas.cxx:17828
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:18480
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:18302
void bk_init(void *event)
Definition midas.cxx:17819
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:18253
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:18017
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:18459
void bk_init32(void *event)
Definition midas.cxx:17882
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:17974
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:18365
INT bk_size(const void *event)
Definition midas.cxx:17908
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8807
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:10518
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6728
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6207
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9698
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:10007
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:6475
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:10981
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10098
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:10626
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7300
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11420
INT bm_delete_request(INT request_id)
Definition midas.cxx:8604
INT bm_close_all_buffers(void)
Definition midas.cxx:7263
INT bm_poll_event()
Definition midas.cxx:11274
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:8334
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6597
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6241
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10957
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9623
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9609
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8986
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6084
INT bm_empty_buffers()
Definition midas.cxx:11388
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8740
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9089
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:8485
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5940
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8160
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6325
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10798
INT bm_check_buffers()
Definition midas.cxx:11102
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9012
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:8312
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8538
static double _bm_mutex_timeout_sec
Definition midas.cxx:5938
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7116
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:9798
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8301
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:9524
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11429
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6274
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6033
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:8956
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8885
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6135
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8875
static DWORD _bm_max_event_size
Definition midas.cxx:5932
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6426
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10242
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8843
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8642
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:10299
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:9100
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7095
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9725
static int _bm_lock_timeout
Definition midas.cxx:5937
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:8911
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10879
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6409
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6053
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:11050
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:9412
INT cm_set_path(const char *path)
Definition midas.cxx:1513
INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number)
Definition midas.cxx:3617
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7420
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4189
INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:2849
static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition midas.cxx:3377
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7343
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4106
INT cm_yield(INT millisec)
Definition midas.cxx:5660
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3027
INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names)
Definition midas.cxx:2628
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3341
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11432
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2782
INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void(*func)(char *))
Definition midas.cxx:2294
static BUFFER_CLIENT * bm_get_my_client_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:6017
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2602
INT cm_start_watchdog_thread()
Definition midas.cxx:7375
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:11066
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
Definition midas.cxx:3049
static int tr_finish(HNDLE hDB, TrState *tr, int transition, int status, const char *errorstr)
Definition midas.cxx:4020
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3807
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:7924
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4053
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5304
INT cm_stop_watchdog_thread()
Definition midas.cxx:7390
std::string cm_asctime()
Definition midas.cxx:1428
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5808
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:2313
INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:1885
static int xbm_lock_buffer(BUFFER *pbuf)
Definition midas.cxx:7995
INT cm_periodic_tasks()
Definition midas.cxx:5597
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8057
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5403
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5256
INT cm_select_experiment_remote(const char *host_name, std::string *exp_name)
Definition midas.cxx:2735
INT cm_register_server(void)
Definition midas.cxx:3476
static BOOL _ctrlc_pressed
Definition midas.cxx:5457
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3428
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5474
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5741
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3360
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7630
void cm_check_connect(void)
Definition midas.cxx:2217
static BUFFER * bm_get_buffer(const char *who, INT buffer_handle, int *pstatus)
Definition midas.cxx:6633
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2686
std::string cm_expand_env(const char *str)
Definition midas.cxx:7730
std::string cm_get_client_name()
Definition midas.cxx:2075
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7966
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5479
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1561
static void rpc_client_shutdown()
Definition midas.cxx:12772
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7342
static exptab_struct _exptab
Definition midas.cxx:1621
INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name, char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout)
Definition midas.cxx:1909
INT cm_disconnect_experiment(void)
Definition midas.cxx:2862
static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition midas.cxx:6170
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11196
int cm_get_exptab(const char *expname, std::string *dir, std::string *user)
Definition midas.cxx:1815
static void xcm_watchdog_thread()
Definition midas.cxx:7369
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7762
INT cm_synchronize(DWORD *seconds)
Definition midas.cxx:1385
std::string cm_get_exptab_filename()
Definition midas.cxx:1804
std::string cm_get_path()
Definition midas.cxx:1553
static DWORD _deferred_transition_mask
Definition midas.cxx:3843
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5861
void cm_test_expand_env()
Definition midas.cxx:7777
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3861
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2182
static INT _requested_transition
Definition midas.cxx:3842
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2150
const char * cm_get_version()
Definition midas.cxx:1492
INT cm_read_exptab(exptab_struct *exptab)
Definition midas.cxx:1630
static int bm_lock_buffer_write_cache(BUFFER *pbuf)
Definition midas.cxx:7945
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3693
INT cm_check_deferred_transition()
Definition midas.cxx:3913
std::string cm_get_experiment_name()
Definition midas.cxx:1596
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3747
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:4000
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1868
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7341
const char * cm_get_revision()
Definition midas.cxx:1500
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7349
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:2955
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5470
static INT tr_main_thread(void *param)
Definition midas.cxx:5272
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5459
INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3260
INT cm_time(DWORD *t)
Definition midas.cxx:1450
INT cm_transition_cleanup()
Definition midas.cxx:5285
INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3299
static int cm_transition_call_direct(TrClient *tr_client)
Definition midas.cxx:4424
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7540
INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
Definition midas.cxx:2974
static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4547
INT cm_set_experiment_name(const char *name)
Definition midas.cxx:1574
#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:1112
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:989
int cm_msg_early_init(void)
Definition midas.cxx:481
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:518
int cm_msg_open_buffer(void)
Definition midas.cxx:488
int cm_msg_close_buffer(void)
Definition midas.cxx:501
static std::mutex gMsgBufMutex
Definition midas.cxx:873
static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message)
Definition midas.cxx:1081
INT cm_msg_register(EVENT_HANDLER *func)
Definition midas.cxx:1067
INT cm_msg_log(INT message_type, const char *facility, const char *message)
Definition midas.cxx:678
INT cm_msg_flush_buffer()
Definition midas.cxx:881
static std::deque< msg_buffer_entry > gMsgBuf
Definition midas.cxx:872
static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message)
Definition midas.cxx:840
std::string cm_get_error(INT code)
Definition midas.cxx:469
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:931
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:765
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1350
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1280
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:553
INT cm_set_msg_print(INT system_mask, INT user_mask, int(*func)(const char *))
Definition midas.cxx:661
#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
std::string strcomb1(const char **list)
Definition odb.cxx:684
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 db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:13392
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:18981
#define MAX_RING_BUFFER
Definition midas.cxx:18693
int rb_delete(int handle)
Definition midas.cxx:18803
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:18840
int rb_increment_rp(int handle, int size)
Definition midas.cxx:19043
int rb_set_nonblocking()
Definition midas.cxx:18708
static volatile int _rb_nonblocking
Definition midas.cxx:18697
int rb_increment_wp(int handle, int size)
Definition midas.cxx:18915
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:18749
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:19098
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:18695
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:16603
#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:11846
#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:12152
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14993
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:15102
#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:14842
INT rpc_get_opt_tcp_size()
Definition midas.cxx:14329
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12816
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11716
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13935
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11967
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:17070
static std::mutex _client_connections_mutex
Definition midas.cxx:11643
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13138
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
#define RPC_TEST4_CXX
Definition mrpc.h:127
static std::mutex gAllowedHostsMutex
Definition midas.cxx:16575
INT recv_tcp_check(int sock)
Definition midas.cxx:14812
#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:13191
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11653
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11723
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12521
std::string rpc_get_name()
Definition midas.cxx:13224
int rpc_test_rpc_test2_cxx()
Definition midas.cxx:16111
#define RPC_ID_WATCHDOG
Definition mrpc.h:139
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11658
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
INT rpc_server_receive_rpc(RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:17239
bool rpc_is_remote(void)
Definition midas.cxx:12901
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13301
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11911
#define RPC_TEST2_CXX
Definition mrpc.h:125
static int _tr_fifo_rp
Definition midas.cxx:14511
int rpc_test_rpc_test2()
Definition midas.cxx:15949
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14577
#define RPC_RC_TRANSITION
Definition mrpc.h:117
int rpc_test_rpc_test3_cxx()
Definition midas.cxx:16271
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13333
#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:15105
INT rpc_server_loop(void)
Definition midas.cxx:17211
INT rpc_clear_allowed_hosts()
Definition midas.cxx:16578
int rpc_test_rpc()
Definition midas.cxx:16540
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12945
INT rpc_get_hw_type()
Definition midas.cxx:12974
INT rpc_deregister_functions()
Definition midas.cxx:12010
bool rpc_is_connected(void)
Definition midas.cxx:12923
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13204
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11713
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:15101
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11644
#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:13371
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12752
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:16574
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:14124
static std::mutex rpc_list_mutex
Definition midas.cxx:11714
void rpc_client_check()
Definition midas.cxx:12410
const char * rpc_tid_name(INT id)
Definition midas.cxx:11904
static void rpc_call_encode_cxx(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13616
INT rpc_flush_event()
Definition midas.cxx:14495
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11948
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11650
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:16681
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:17581
static int _tr_fifo_wp
Definition midas.cxx:14510
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:17534
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:14358
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14509
static std::string _mserver_path
Definition midas.cxx:13188
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:13113
INT rpc_server_disconnect()
Definition midas.cxx:12845
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13274
INT rpc_client_accept(int lsock)
Definition midas.cxx:16966
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11766
static int rpc_find_rpc(int routine_id, RPC_LIST *pentry, bool *prpc_cxx)
Definition midas.cxx:13909
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:14321
#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:11918
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:15034
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:17379
#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:16709
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14508
bool rpc_is_mserver(void)
Definition midas.cxx:12958
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11651
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11751
#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:14376
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11647
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13544
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:14382
INT rpc_set_name(const char *name)
Definition midas.cxx:13248
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:12037
#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:15512
INT rpc_client_dispatch(int sock)
Definition midas.cxx:12085
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11646
INT rpc_check_channels(void)
Definition midas.cxx:17652
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14513
int rpc_test_rpc_test4_cxx()
Definition midas.cxx:16427
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:12071
#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:11801
static int rpc_call_decode_cxx(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13816
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11782
#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:14654
INT rpc_get_convert_flags(void)
Definition midas.cxx:13170
INT rpc_tid_size(INT id)
Definition midas.cxx:11897
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:16632
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11821
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:449
INT _semaphore_alarm
Definition midas.cxx:1478
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:1472
int(* MessagePrintCallback)(const char *)
Definition midas.cxx:445
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:1474
static std::string _path_name
Definition midas.cxx:1476
INT _database_entries
static std::string _client_name
Definition midas.cxx:1475
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:1477
INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
Definition midas.cxx:7811
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:447
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:450
static std::mutex _request_list_mutex
Definition midas.cxx:219
INT _semaphore_elog
Definition midas.cxx:1479
INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
Definition midas.cxx:7858
INT _semaphore_history
Definition midas.cxx:1480
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:1473
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:19136
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8083
#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:1595
#define M_MALLOC(x)
Definition midas.h:1535
#define CINT(_i)
Definition midas.h:1606
#define RPC_OUT
Definition midas.h:1564
#define RPC_CXX
Definition midas.h:1569
#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:1563
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1590
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1591
#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:1579
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1447
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:908
#define RPC_OUTGOING
Definition midas.h:1568
#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:1630
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1565
#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:1537
#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:1596
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1567
#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:1597
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:1566
#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:6438
int last_count_lock
Definition midas.cxx:6442
BOOL get_all_flag
Definition midas.cxx:6434
int count_lock
Definition midas.cxx:6437
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6451
int count_write_wait
Definition midas.cxx:6440
double bytes_read
Definition midas.cxx:6447
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6448
int wait_client_index
Definition midas.cxx:6444
DWORD time_write_wait
Definition midas.cxx:6441
int count_read
Definition midas.cxx:6446
double bytes_sent
Definition midas.cxx:6439
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6449
DWORD wait_start_time
Definition midas.cxx:6443
int max_requested_space
Definition midas.cxx:6445
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:11426
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:18689
unsigned char * buffer
Definition midas.cxx:18685
unsigned int max_event_size
Definition midas.cxx:18687
unsigned int size
Definition midas.cxx:18686
unsigned char * rp
Definition midas.cxx:18688
unsigned char * ep
Definition midas.cxx:18690
RPC_PARAM param[MAX_RPC_PARAMS]
Definition midas.h:1584
INT id
Definition midas.h:1582
RPC_HANDLER * dispatch
Definition midas.h:1585
const char * name
Definition midas.h:1583
WORD flags
Definition midas.h:1575
WORD tid
Definition midas.h:1574
INT n
Definition midas.h:1576
midas_thread_t thread_id
Definition midas.cxx:15096
int buffer_size
Definition midas.cxx:15097
char * buffer
Definition midas.cxx:15098
int transition
Definition midas.cxx:14502
time_t trans_time
Definition midas.cxx:14504
int run_number
Definition midas.cxx:14503
int sequence_number
Definition midas.cxx:14505
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:3969
int transition
Definition midas.cxx:3954
std::string host_name
Definition midas.cxx:3960
DWORD connect_end_time
Definition midas.cxx:3971
std::atomic_int status
Definition midas.cxx:3964
~TrClient()
Definition midas.cxx:3982
std::vector< int > wait_for_index
Definition midas.cxx:3959
DWORD init_time
Definition midas.cxx:3967
std::string key_name
Definition midas.cxx:3963
DWORD rpc_end_time
Definition midas.cxx:3974
std::string waiting_for_client
Definition midas.cxx:3968
DWORD rpc_timeout
Definition midas.cxx:3972
int async_flag
Definition midas.cxx:3956
int port
Definition midas.cxx:3962
DWORD rpc_start_time
Definition midas.cxx:3973
int sequence_number
Definition midas.cxx:3958
int debug_flag
Definition midas.cxx:3957
int run_number
Definition midas.cxx:3955
DWORD end_time
Definition midas.cxx:3975
DWORD connect_start_time
Definition midas.cxx:3970
std::string client_name
Definition midas.cxx:3961
std::string errorstr
Definition midas.cxx:3966
void Print() const
Definition midas.cxx:3988
std::thread * thread
Definition midas.cxx:3965
int transition
Definition midas.cxx:4007
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:4015
int async_flag
Definition midas.cxx:4009
DWORD end_time
Definition midas.cxx:4014
int run_number
Definition midas.cxx:4008
int status
Definition midas.cxx:4011
DWORD start_time
Definition midas.cxx:4013
std::string errorstr
Definition midas.cxx:4012
int debug_flag
Definition midas.cxx:4010
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:1610
std::string name
Definition midas.cxx:1611
std::string directory
Definition midas.cxx:1612
std::string user
Definition midas.cxx:1613
std::string filename
Definition midas.cxx:1617
std::vector< exptab_entry > exptab
Definition midas.cxx:1618
Definition midas.cxx:866
DWORD ts
Definition midas.cxx:867
int message_type
Definition midas.cxx:868
std::string message
Definition midas.cxx:869
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