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), "%G/%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 return BM_INVALID_PARAM;
6793 }
6794
6796
6797 size = sizeof(INT);
6798 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6799
6800 if (status != DB_SUCCESS) {
6801 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6802 status);
6803 return status;
6804 }
6805
6806 /* check if buffer already is open */
6807 gBuffersMutex.lock();
6808 for (size_t i = 0; i < gBuffers.size(); i++) {
6809 BUFFER* pbuf = gBuffers[i];
6810 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6811 *buffer_handle = i + 1;
6812 gBuffersMutex.unlock();
6813 return BM_SUCCESS;
6814 }
6815 }
6816 gBuffersMutex.unlock();
6817
6818 // only one thread at a time should create new buffers
6819
6820 static std::mutex gNewBufferMutex;
6821 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6822
6823 // if we had a race against another thread
6824 // and while we were waiting for gNewBufferMutex
6825 // the other thread created this buffer, we return it.
6826
6827 gBuffersMutex.lock();
6828 for (size_t i = 0; i < gBuffers.size(); i++) {
6829 BUFFER* pbuf = gBuffers[i];
6830 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6831 *buffer_handle = i + 1;
6832 gBuffersMutex.unlock();
6833 return BM_SUCCESS;
6834 }
6835 }
6836 gBuffersMutex.unlock();
6837
6838 /* allocate new BUFFER object */
6839
6840 BUFFER* pbuf = new BUFFER;
6841
6842 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6843
6844 for (int i=0; i<MAX_CLIENTS; i++) {
6845 pbuf->client_count_write_wait[i] = 0;
6846 pbuf->client_time_write_wait[i] = 0;
6847 }
6848
6849 /* create buffer semaphore */
6850
6852
6853 if (status != SS_CREATED && status != SS_SUCCESS) {
6854 *buffer_handle = 0;
6855 delete pbuf;
6856 return BM_NO_SEMAPHORE;
6857 }
6858
6859 std::string client_name = cm_get_client_name();
6860
6861 /* store client name */
6862 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6863
6864 /* store buffer name */
6865 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6866
6867 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6868
6869 pbuf->attached = true; // required by bm_lock_buffer()
6870
6871 bm_lock_buffer_guard pbuf_guard(pbuf);
6872
6873 if (!pbuf_guard.is_locked()) {
6874 // cannot happen, no other thread can see this pbuf
6875 abort();
6876 return BM_NO_SEMAPHORE;
6877 }
6878
6879 /* open shared memory */
6880
6881 void *p = NULL;
6882 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6883
6884 if (status != SS_SUCCESS && status != SS_CREATED) {
6885 *buffer_handle = 0;
6886 pbuf_guard.unlock();
6887 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6888 delete pbuf;
6889 return BM_NO_SHM;
6890 }
6891
6892 pbuf->buffer_header = (BUFFER_HEADER *) p;
6893
6894 BUFFER_HEADER *pheader = pbuf->buffer_header;
6895
6896 bool shm_created = (status == SS_CREATED);
6897
6898 if (shm_created) {
6899 /* initialize newly created shared memory */
6900
6901 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6902
6903 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6904 pheader->size = buffer_size;
6905
6906 } else {
6907 /* validate existing shared memory */
6908
6909 if (!equal_ustring(pheader->name, buffer_name)) {
6910 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6911 pbuf_guard.unlock();
6912 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6913 cm_msg(MERROR, "bm_open_buffer",
6914 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6915 pheader->name);
6916 *buffer_handle = 0;
6917 delete pbuf;
6918 return BM_CORRUPTED;
6919 }
6920
6921 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
6922 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6923 pbuf_guard.unlock();
6924 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6925 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6927 *buffer_handle = 0;
6928 delete pbuf;
6929 return BM_CORRUPTED;
6930 }
6931
6932 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6933 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6934 pbuf_guard.unlock();
6935 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6936 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6938 *buffer_handle = 0;
6939 delete pbuf;
6940 return BM_CORRUPTED;
6941 }
6942
6943 /* check if buffer size is identical */
6944 if (pheader->size != buffer_size) {
6945 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6946 buffer_name, buffer_size, pheader->size);
6947
6948 buffer_size = pheader->size;
6949
6950 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6951
6952 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6953
6954 if (status != SS_SUCCESS) {
6955 *buffer_handle = 0;
6956 pbuf_guard.unlock();
6957 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6958 delete pbuf;
6959 return BM_NO_SHM;
6960 }
6961
6962 pbuf->buffer_header = (BUFFER_HEADER *) p;
6963 pheader = pbuf->buffer_header;
6964 }
6965 }
6966
6967 /* shared memory is good from here down */
6968
6969 pbuf->attached = true;
6970
6971 pbuf->shm_handle = shm_handle;
6972 pbuf->shm_size = shm_size;
6973 pbuf->callback = FALSE;
6974
6975 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6976
6978 if (status != BM_SUCCESS) {
6979 cm_msg(MERROR, "bm_open_buffer",
6980 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6981 status);
6983 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6984 }
6985
6986 /* add our client BUFFER_HEADER */
6987
6988 int iclient = 0;
6989 for (; iclient < MAX_CLIENTS; iclient++)
6990 if (pheader->client[iclient].pid == 0)
6991 break;
6992
6993 if (iclient == MAX_CLIENTS) {
6994 *buffer_handle = 0;
6995 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6996 pbuf_guard.unlock();
6997 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6998 delete pbuf;
6999 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
7000 return BM_NO_SLOT;
7001 }
7002
7003 /* store slot index in _buffer structure */
7004 pbuf->client_index = iclient;
7005
7006 /*
7007 Save the index of the last client of that buffer so that later only
7008 the clients 0..max_client_index-1 have to be searched through.
7009 */
7010 pheader->num_clients++;
7011 if (iclient + 1 > pheader->max_client_index)
7012 pheader->max_client_index = iclient + 1;
7013
7014 /* setup buffer header and client structure */
7015 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7016
7017 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7018
7019 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7020
7021 pclient->pid = ss_getpid();
7022
7024
7025 pclient->read_pointer = pheader->write_pointer;
7026 pclient->last_activity = ss_millitime();
7027
7029
7030 pbuf_guard.unlock();
7031
7032 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7033
7034 pheader = NULL;
7035
7036 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7037
7040
7041 /* add pbuf to buffer list */
7042
7043 gBuffersMutex.lock();
7044
7045 bool added = false;
7046 for (size_t i=0; i<gBuffers.size(); i++) {
7047 if (gBuffers[i] == NULL) {
7048 gBuffers[i] = pbuf;
7049 added = true;
7050 *buffer_handle = i+1;
7051 break;
7052 }
7053 }
7054 if (!added) {
7055 *buffer_handle = gBuffers.size() + 1;
7056 gBuffers.push_back(pbuf);
7057 }
7058
7059 /* from here down we should not touch pbuf without locking it */
7060
7061 pbuf = NULL;
7062
7063 gBuffersMutex.unlock();
7064
7065 /* new buffer is now ready for use */
7066
7067 /* initialize buffer counters */
7068 bm_init_buffer_counters(*buffer_handle);
7069
7070 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7071
7072 if (shm_created)
7073 return BM_CREATED;
7074 }
7075#endif /* LOCAL_ROUTINES */
7076
7077 return BM_SUCCESS;
7078}
7079
7080/********************************************************************/
7086INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7087{
7088 gBuffersMutex.lock();
7089 for (size_t i = 0; i < gBuffers.size(); i++) {
7090 BUFFER* pbuf = gBuffers[i];
7091 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7092 *buffer_handle = i + 1;
7093 gBuffersMutex.unlock();
7094 return BM_SUCCESS;
7095 }
7096 }
7097 gBuffersMutex.unlock();
7098 return BM_NOT_FOUND;
7099}
7100
7101/********************************************************************/
7107INT bm_close_buffer(INT buffer_handle) {
7108 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7109
7110 if (rpc_is_remote())
7111 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7112
7113#ifdef LOCAL_ROUTINES
7114 {
7115 int status = 0;
7116
7117 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7118
7119 if (!pbuf)
7120 return status;
7121
7122 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7123
7124 int i;
7125
7126 { /* delete all requests for this buffer */
7127 _request_list_mutex.lock();
7128 std::vector<EventRequest> request_list_copy = _request_list;
7129 _request_list_mutex.unlock();
7130 for (size_t i = 0; i < request_list_copy.size(); i++) {
7131 if (request_list_copy[i].buffer_handle == buffer_handle) {
7133 }
7134 }
7135 }
7136
7137 HNDLE hDB;
7139
7140 if (hDB) {
7141 /* write statistics to odb */
7143 }
7144
7145 /* lock buffer in correct order */
7146
7148
7149 if (status != BM_SUCCESS) {
7150 return status;
7151 }
7152
7154
7155 if (status != BM_SUCCESS) {
7156 pbuf->read_cache_mutex.unlock();
7157 return status;
7158 }
7159
7160 bm_lock_buffer_guard pbuf_guard(pbuf);
7161
7162 if (!pbuf_guard.is_locked()) {
7163 pbuf->write_cache_mutex.unlock();
7164 pbuf->read_cache_mutex.unlock();
7165 return pbuf_guard.get_status();
7166 }
7167
7168 BUFFER_HEADER *pheader = pbuf->buffer_header;
7169
7170 /* mark entry in _buffer as empty */
7171 pbuf->attached = false;
7172
7173 BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
7174
7175 if (pclient) {
7176 /* clear entry from client structure in buffer header */
7177 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7178 }
7179
7180 /* calculate new max_client_index entry */
7181 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7182 if (pheader->client[i].pid != 0)
7183 break;
7184 pheader->max_client_index = i + 1;
7185
7186 /* count new number of clients */
7187 int j = 0;
7188 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7189 if (pheader->client[i].pid != 0)
7190 j++;
7191 pheader->num_clients = j;
7192
7193 int destroy_flag = (pheader->num_clients == 0);
7194
7195 // we hold the locks on the read cache and the write cache.
7196
7197 /* free cache */
7198 if (pbuf->read_cache_size > 0) {
7199 free(pbuf->read_cache);
7200 pbuf->read_cache = NULL;
7201 pbuf->read_cache_size = 0;
7202 pbuf->read_cache_rp = 0;
7203 pbuf->read_cache_wp = 0;
7204 }
7205
7206 if (pbuf->write_cache_size > 0) {
7207 free(pbuf->write_cache);
7208 pbuf->write_cache = NULL;
7209 pbuf->write_cache_size = 0;
7210 pbuf->write_cache_rp = 0;
7211 pbuf->write_cache_wp = 0;
7212 }
7213
7214 /* check if anyone is waiting and wake him up */
7215
7216 for (int i = 0; i < pheader->max_client_index; i++) {
7217 BUFFER_CLIENT *pclient = pheader->client + i;
7218 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7219 ss_resume(pclient->port, "B ");
7220 }
7221
7222 /* unmap shared memory, delete it if we are the last */
7223
7224 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7225
7226 /* after ss_shm_close() these are invalid: */
7227
7228 pheader = NULL;
7229 pbuf->buffer_header = NULL;
7230 pbuf->shm_size = 0;
7231 pbuf->shm_handle = 0;
7232
7233 /* unlock buffer in correct order */
7234
7235 pbuf_guard.unlock();
7236
7237 pbuf->write_cache_mutex.unlock();
7238 pbuf->read_cache_mutex.unlock();
7239
7240 /* delete semaphore */
7241
7242 ss_semaphore_delete(pbuf->semaphore, destroy_flag);
7243 }
7244#endif /* LOCAL_ROUTINES */
7245
7246 return BM_SUCCESS;
7247}
7248
7249/********************************************************************/
7255 if (rpc_is_remote())
7257
7258#ifdef LOCAL_ROUTINES
7259 {
7261
7262 gBuffersMutex.lock();
7263 size_t nbuf = gBuffers.size();
7264 gBuffersMutex.unlock();
7265
7266 for (size_t i = nbuf; i > 0; i--) {
7268 }
7269
7270 gBuffersMutex.lock();
7271 for (size_t i=0; i< gBuffers.size(); i++) {
7272 BUFFER* pbuf = gBuffers[i];
7273 if (!pbuf)
7274 continue;
7275 delete pbuf;
7276 pbuf = NULL;
7277 gBuffers[i] = NULL;
7278 }
7279 gBuffersMutex.unlock();
7280 }
7281#endif /* LOCAL_ROUTINES */
7282
7283 return BM_SUCCESS;
7284}
7285
7286/********************************************************************/
7292#ifdef LOCAL_ROUTINES
7293 {
7294 int status;
7295 HNDLE hDB;
7296
7298
7299 if (status != CM_SUCCESS) {
7300 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7301 return BM_SUCCESS;
7302 }
7303
7304 std::vector<BUFFER*> mybuffers;
7305
7306 gBuffersMutex.lock();
7307 mybuffers = gBuffers;
7308 gBuffersMutex.unlock();
7309
7310 for (BUFFER* pbuf : mybuffers) {
7311 if (!pbuf || !pbuf->attached)
7312 continue;
7314 }
7315 }
7316#endif /* LOCAL_ROUTINES */
7317
7318 return BM_SUCCESS;
7319}
7320
/* end of bmfunctionc */
7323
7329/*-- Watchdog routines ---------------------------------------------*/
7330#ifdef LOCAL_ROUTINES
7331
7332static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7333static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7334static std::atomic<std::thread*> _watchdog_thread{NULL};
7335
7336/********************************************************************/
7342 //printf("cm_watchdog_thread started!\n");
7343 while (_watchdog_thread_run) {
7344 //printf("cm_watchdog_thread runs!\n");
7345 DWORD now = ss_millitime();
7348 int i;
7349 for (i = 0; i < 20; i++) {
7350 ss_sleep(100);
7352 break;
7353 }
7354 }
7355 //printf("cm_watchdog_thread stopped!\n");
7357 return 0;
7358}
7359
7360static void xcm_watchdog_thread() {
7361 cm_watchdog_thread(NULL);
7362}
7363
7364#endif
7365
7367 /* watchdog does not run inside remote clients.
7368 * watchdog timeout timers are maintained by the mserver */
7369 if (rpc_is_remote())
7370 return CM_SUCCESS;
7371#ifdef LOCAL_ROUTINES
7372 /* only start once */
7373 if (_watchdog_thread)
7374 return CM_SUCCESS;
7375 _watchdog_thread_run = true;
7376 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7377#endif
7378 return CM_SUCCESS;
7379}
7380
7382 /* watchdog does not run inside remote clients.
7383 * watchdog timeout timers are maintained by the mserver */
7384 if (rpc_is_remote())
7385 return CM_SUCCESS;
7386#ifdef LOCAL_ROUTINES
7387 _watchdog_thread_run = false;
7389 //printf("waiting for watchdog thread to shut down\n");
7390 ss_sleep(10);
7391 }
7392 if (_watchdog_thread != NULL) {
7393 _watchdog_thread.load()->join();
7394 delete static_cast<std::thread *>(_watchdog_thread);
7395 _watchdog_thread = NULL;
7396 }
7397#endif
7398 return CM_SUCCESS;
7399}
7400
7401/********************************************************************/
7411INT cm_shutdown(const char *name, BOOL bUnique) {
7412 INT status, return_status, i, size;
7413 HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
7414 KEY key;
7415 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7416 INT port;
7417 DWORD start_time;
7418 DWORD timeout;
7419 DWORD last;
7420
7421 cm_get_experiment_database(&hDB, &hKeyClient);
7422
7423 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7424 if (status != DB_SUCCESS)
7425 return DB_NO_KEY;
7426
7427 return_status = CM_NO_CLIENT;
7428
7429 /* loop over all clients */
7430 for (i = 0;; i++) {
7433 break;
7434
7435 /* don't shutdown ourselves */
7436 if (hSubkey == hKeyClient)
7437 continue;
7438
7439 if (status == DB_SUCCESS) {
7441
7442 /* contact client */
7443 size = sizeof(client_name);
7444 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7445 if (status != DB_SUCCESS)
7446 continue;
7447
7448 if (!bUnique)
7449 client_name[strlen(name)] = 0; /* strip number */
7450
7451 /* check if individual client */
7452 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7453 continue;
7454
7455 size = sizeof(port);
7456 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7457
7458 size = sizeof(remote_host);
7459 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7460
7461 cm_get_watchdog_info(hDB, name, &timeout, &last);
7462 if (timeout == 0)
7463 timeout = 5000;
7464
7465 /* client found -> connect to its server port */
7466 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7467 if (status != RPC_SUCCESS) {
7468 int client_pid = atoi(key.name);
7469 return_status = CM_NO_CLIENT;
7470 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7471 client_name, remote_host, port);
7472#ifdef SIGKILL
7473 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7474 client_pid);
7475 kill(client_pid, SIGKILL);
7476 return_status = CM_SUCCESS;
7477 status = cm_delete_client_info(hDB, client_pid);
7478 if (status != CM_SUCCESS)
7479 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7480 name, client_pid, status);
7481#endif
7482 } else {
7483 /* call disconnect with shutdown=TRUE */
7485
7486 /* wait until client has shut down */
7487 start_time = ss_millitime();
7488 do {
7489 ss_sleep(100);
7490 status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
7491 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7492
7493 if (status == DB_SUCCESS) {
7494 int client_pid = atoi(key.name);
7495 return_status = CM_NO_CLIENT;
7496 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7497#ifdef SIGKILL
7498 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7499 client_pid);
7500 kill(client_pid, SIGKILL);
7501 status = cm_delete_client_info(hDB, client_pid);
7502 if (status != CM_SUCCESS)
7503 cm_msg(MERROR, "cm_shutdown",
7504 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7505 status);
7506#endif
7507 return_status = CM_NO_CLIENT;
7508 } else {
7509 return_status = CM_SUCCESS;
7510 i--;
7511 }
7512 }
7513 }
7514
7515 /* display any message created during each shutdown */
7517 }
7518
7519 return return_status;
7520}
7521
7522/********************************************************************/
7531INT cm_exist(const char *name, BOOL bUnique) {
7532 INT status, i, size;
7533 HNDLE hDB, hKeyClient, hKey, hSubkey;
7534 char client_name[NAME_LENGTH];
7535
7536 if (rpc_is_remote())
7537 return rpc_call(RPC_CM_EXIST, name, bUnique);
7538
7539 cm_get_experiment_database(&hDB, &hKeyClient);
7540
7541 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7542 if (status != DB_SUCCESS)
7543 return DB_NO_KEY;
7544
7546
7547 /* loop over all clients */
7548 for (i = 0;; i++) {
7551 break;
7552
7553 if (hSubkey == hKeyClient)
7554 continue;
7555
7556 if (status == DB_SUCCESS) {
7557 /* get client name */
7558 size = sizeof(client_name);
7559 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7560
7561 if (status != DB_SUCCESS) {
7562 //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);
7563 continue;
7564 }
7565
7566 if (equal_ustring(client_name, name)) {
7568 return CM_SUCCESS;
7569 }
7570
7571 if (!bUnique) {
7572 client_name[strlen(name)] = 0; /* strip number */
7573 if (equal_ustring(client_name, name)) {
7575 return CM_SUCCESS;
7576 }
7577 }
7578 }
7579 }
7580
7582
7583 return CM_NO_CLIENT;
7584}
7585
7586/********************************************************************/
7621INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7622 if (rpc_is_remote())
7623 return rpc_call(RPC_CM_CLEANUP, client_name);
7624
7625#ifdef LOCAL_ROUTINES
7626 {
7627 DWORD interval;
7628 DWORD now = ss_millitime();
7629
7630 std::vector<BUFFER*> mybuffers;
7631
7632 gBuffersMutex.lock();
7633 mybuffers = gBuffers;
7634 gBuffersMutex.unlock();
7635
7636 /* check buffers */
7637 for (BUFFER* pbuf : mybuffers) {
7638 if (!pbuf)
7639 continue;
7640 if (pbuf->attached) {
7641 std::string msg;
7642
7643 bm_lock_buffer_guard pbuf_guard(pbuf);
7644
7645 if (!pbuf_guard.is_locked())
7646 continue;
7647
7648 /* update the last_activity entry to show that we are alive */
7649 BUFFER_HEADER *pheader = pbuf->buffer_header;
7650 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7651 pclient->last_activity = ss_millitime();
7652
7653 /* now check other clients */
7654 for (int j = 0; j < pheader->max_client_index; j++) {
7655 BUFFER_CLIENT *pbclient = &pheader->client[j];
7656 if (j != pbuf->client_index && pbclient->pid &&
7657 (client_name == NULL || client_name[0] == 0
7658 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7659 if (ignore_timeout)
7660 interval = 2 * WATCHDOG_INTERVAL;
7661 else
7662 interval = pbclient->watchdog_timeout;
7663
7664 /* If client process has no activity, clear its buffer entry. */
7665 if (interval > 0
7666 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7667
7668 /* now make again the check with the buffer locked */
7669 if (interval > 0
7670 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7671 msg = msprintf(
7672 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7673 pbclient->name, pheader->name,
7674 (ss_millitime() - pbclient->last_activity) / 1000.0,
7675 interval / 1000.0);
7676
7677 bm_remove_client_locked(pheader, j);
7678 }
7679
7680 /* go again through whole list */
7681 j = 0;
7682 }
7683 }
7684 }
7685
7686 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7687 pbuf_guard.unlock();
7688
7689 /* display info message after unlocking buffer */
7690 if (!msg.empty())
7691 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7692 }
7693 }
7694
7695 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7696 }
7697#endif /* LOCAL_ROUTINES */
7698
7699 return CM_SUCCESS;
7700}
7701
7702/********************************************************************/
7721std::string cm_expand_env(const char *str) {
7722 const char *s = str;
7723 std::string r;
7724 for (; *s;) {
7725 if (*s == '$') {
7726 s++;
7727 std::string envname;
7728 for (; *s;) {
7729 if (*s == DIR_SEPARATOR)
7730 break;
7731 envname += *s;
7732 s++;
7733 }
7734 const char *e = getenv(envname.c_str());
7735 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7736 if (!e) {
7737 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7738 r += '$';
7739 r += envname;
7740 } else {
7741 r += e;
7742 //if (r[r.length()-1] != DIR_SEPARATOR)
7743 //r += DIR_SEPARATOR_STR;
7744 }
7745 } else {
7746 r += *s;
7747 s++;
7748 }
7749 }
7750 return r;
7751}
7752
7753static bool test_cm_expand_env1(const char *str, const char *expected) {
7754 std::string s = cm_expand_env(str);
7755 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7756 str,
7757 s.c_str(),
7758 expected);
7759 if (s != expected) {
7760 printf(", MISMATCH!\n");
7761 return false;
7762 }
7763
7764 printf("\n");
7765 return true;
7766}
7767
7769 printf("Test expand_end()\n");
7770 setenv("FOO", "foo", 1);
7771 setenv("BAR", "bar", 1);
7772 setenv("EMPTY", "", 1);
7773 unsetenv("UNDEF");
7774
7775 bool ok = true;
7776
7777 ok &= test_cm_expand_env1("aaa", "aaa");
7778 ok &= test_cm_expand_env1("$FOO", "foo");
7779 ok &= test_cm_expand_env1("/$FOO", "/foo");
7780 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7781 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7782 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7783 ok &= test_cm_expand_env1("1$FOO", "1foo");
7784 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7785 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7786
7787 if (ok) {
7788 printf("test_expand_env: all tests passed!\n");
7789 } else {
7790 printf("test_expand_env: test FAILED!\n");
7791 }
7792}
7793
/* end of cmfunctionc */
7797
7799#ifndef DOXYGEN_SHOULD_SKIP_THIS
7800
7801/********************************************************************/
7802INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7803/********************************************************************\
7804
7805 Routine: bm_buffer_info
7806
7807 Purpose: Copies the current buffer header referenced by buffer_handle
7808 into the *buffer_header structure which must be supplied
7809 by the calling routine.
7810
7811 Input:
7812 INT buffer_handle Handle of the buffer to get the header from
7813
7814 Output:
7815 BUFFER_HEADER *buffer_header Destination address which gets a copy
7816 of the buffer header structure.
7817
7818 Function value:
7819 BM_SUCCESS Successful completion
7820 BM_INVALID_HANDLE Buffer handle is invalid
7821 RPC_NET_ERROR Network error
7822
7823\********************************************************************/
7824{
7825 if (rpc_is_remote())
7826 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7827
7828#ifdef LOCAL_ROUTINES
7829
7830 int status = 0;
7831 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7832
7833 if (!pbuf)
7834 return status;
7835
7836 bm_lock_buffer_guard pbuf_guard(pbuf);
7837
7838 if (!pbuf_guard.is_locked())
7839 return pbuf_guard.get_status();
7840
7841 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7842
7843#endif /* LOCAL_ROUTINES */
7844
7845 return BM_SUCCESS;
7846}
7847
7848/********************************************************************/
7849INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7850/********************************************************************\
7851
7852 Routine: bm_get_buffer_level
7853
7854 Purpose: Return number of bytes in buffer or in cache
7855
7856 Input:
7857 INT buffer_handle Handle of the buffer to get the info
7858
7859 Output:
7860 INT *n_bytes Number of bytes in buffer
7861
7862 Function value:
7863 BM_SUCCESS Successful completion
7864 BM_INVALID_HANDLE Buffer handle is invalid
7865 RPC_NET_ERROR Network error
7866
7867\********************************************************************/
7868{
7869 if (rpc_is_remote())
7870 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7871
7872#ifdef LOCAL_ROUTINES
7873 {
7874 int status = 0;
7875
7876 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7877
7878 if (!pbuf)
7879 return status;
7880
7881 bm_lock_buffer_guard pbuf_guard(pbuf);
7882
7883 if (!pbuf_guard.is_locked())
7884 return pbuf_guard.get_status();
7885
7886 BUFFER_HEADER *pheader = pbuf->buffer_header;
7887
7888 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7889
7890 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7891 if (*n_bytes < 0)
7892 *n_bytes += pheader->size;
7893
7894 pbuf_guard.unlock();
7895
7896 if (pbuf->read_cache_size) {
7898 if (status == BM_SUCCESS) {
7899 /* add bytes in cache */
7900 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7901 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7902 pbuf->read_cache_mutex.unlock();
7903 }
7904 }
7905 }
7906#endif /* LOCAL_ROUTINES */
7907
7908 return BM_SUCCESS;
7909}
7910
7911
7912#ifdef LOCAL_ROUTINES
7913
7914/********************************************************************/
7916{
7917 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7918
7919 if (!locked) {
7920 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);
7921 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);
7922 abort();
7923 /* DOES NOT RETURN */
7924 }
7925
7926 if (!pbuf->attached) {
7927 pbuf->read_cache_mutex.unlock();
7928 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);
7929 return BM_INVALID_HANDLE;
7930 }
7931
7932 return BM_SUCCESS;
7933}
7934
7935/********************************************************************/
7937{
7938 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7939
7940 if (!locked) {
7941 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);
7942 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);
7943 abort();
7944 /* DOES NOT RETURN */
7945 }
7946
7947 if (!pbuf->attached) {
7948 pbuf->write_cache_mutex.unlock();
7949 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);
7950 return BM_INVALID_HANDLE;
7951 }
7952
7953 return BM_SUCCESS;
7954}
7955
7956/********************************************************************/
7958{
7959 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7960
7961 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7962
7963 if (!locked) {
7964 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7965 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7966 abort();
7967 /* DOES NOT RETURN */
7968 }
7969
7970 if (!pbuf->attached) {
7971 pbuf->buffer_mutex.unlock();
7972 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);
7973 return BM_INVALID_HANDLE;
7974 }
7975
7976 //static int counter = 0;
7977 //counter++;
7978 //printf("locked %d!\n", counter);
7979 //if (counter > 50)
7980 // ::sleep(3);
7981
7982 return BM_SUCCESS;
7983}
7984
7985/********************************************************************/
7986static int xbm_lock_buffer(BUFFER *pbuf)
7987{
7988 int status;
7989
7990 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
7991
7992 //if (pbuf->locked) {
7993 // fprintf(stderr, "double lock, abort!\n");
7994 // abort();
7995 //}
7996
7998
7999 if (status != BM_SUCCESS)
8000 return status;
8001
8002 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8003
8004 if (status != SS_SUCCESS) {
8005 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8006
8007 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8008
8009 if (status != SS_SUCCESS) {
8010 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);
8011
8012 if (pbuf->buffer_header) {
8013 for (int i=0; i<MAX_CLIENTS; i++) {
8014 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);
8015 }
8016 }
8017
8019
8020 if (status != SS_SUCCESS) {
8021 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8022 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8023 abort();
8024 /* DOES NOT RETURN */
8025 }
8026 }
8027 }
8028
8029 // protect against double lock
8030 assert(!pbuf->locked);
8031 pbuf->locked = TRUE;
8032
8033#if 0
8034 int x = MAX_CLIENTS - 1;
8035 if (pbuf->buffer_header->client[x].unused1 != 0) {
8036 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8037 }
8038 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8039 pbuf->buffer_header->client[x].unused1 = getpid();
8040#endif
8041
8042 pbuf->count_lock++;
8043
8044 return BM_SUCCESS;
8045}
8046
8047/********************************************************************/
8048static void xbm_unlock_buffer(BUFFER *pbuf) {
8049 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8050
8051#if 0
8052 int x = MAX_CLIENTS-1;
8053 if (pbuf->attached) {
8054 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8055 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8056 }
8057 pbuf->buffer_header->client[x].unused1 = 0;
8058 } else {
8059 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8060 }
8061#endif
8062
8063 // protect against double unlock
8064 assert(pbuf->locked);
8065 pbuf->locked = FALSE;
8066
8068 pbuf->buffer_mutex.unlock();
8069}
8070
8071#endif /* LOCAL_ROUTINES */
8072
8073/********************************************************************/
8075/********************************************************************\
8076
8077 Routine: bm_init_event_counters
8078
8079 Purpose: Initialize counters for a specific buffer. This routine
8080 should be called at the beginning of a run.
8081
8082 Input:
8083 INT buffer_handle Handle to the buffer to be
8084 initialized.
8085 Output:
8086 none
8087
8088 Function value:
8089 BM_SUCCESS Successful completion
8090 BM_INVALID_HANDLE Buffer handle is invalid
8091
8092\********************************************************************/
8093{
8094 if (rpc_is_remote())
8095 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8096
8097#ifdef LOCAL_ROUTINES
8098
8099 int status = 0;
8100
8101 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8102
8103 if (!pbuf)
8104 return status;
8105
8106 bm_lock_buffer_guard pbuf_guard(pbuf);
8107
8108 if (!pbuf_guard.is_locked())
8109 return pbuf_guard.get_status();
8110
8111 pbuf->buffer_header->num_in_events = 0;
8112 pbuf->buffer_header->num_out_events = 0;
8113
8114#endif /* LOCAL_ROUTINES */
8115
8116 return BM_SUCCESS;
8117}
8118
8120#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8121
8127/********************************************************************/
8151INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8152/*------------------------------------------------------------------*/
8153{
8154 if (rpc_is_remote())
8155 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8156
8157#ifdef LOCAL_ROUTINES
8158 {
8159 int status = 0;
8160
8161 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8162
8163 if (!pbuf)
8164 return status;
8165
8166 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8167
8169
8170 if (status != BM_SUCCESS)
8171 return status;
8172
8173 if (write_size < 0)
8174 write_size = 0;
8175
8176 if (write_size > 0) {
8177 if (write_size < MIN_WRITE_CACHE_SIZE) {
8178 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);
8179 write_size = MIN_WRITE_CACHE_SIZE;
8180 }
8181 }
8182
8183 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8184
8185 if (write_size > max_write_size) {
8186 size_t new_write_size = max_write_size;
8187 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);
8188 write_size = new_write_size;
8189 }
8190
8191 pbuf->buffer_mutex.unlock();
8192
8193 /* resize read cache */
8194
8196
8197 if (status != BM_SUCCESS) {
8198 return status;
8199 }
8200
8201 if (pbuf->read_cache_size > 0) {
8202 free(pbuf->read_cache);
8203 pbuf->read_cache = NULL;
8204 }
8205
8206 if (read_size > 0) {
8207 pbuf->read_cache = (char *) malloc(read_size);
8208 if (pbuf->read_cache == NULL) {
8209 pbuf->read_cache_size = 0;
8210 pbuf->read_cache_rp = 0;
8211 pbuf->read_cache_wp = 0;
8212 pbuf->read_cache_mutex.unlock();
8213 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);
8214 return BM_NO_MEMORY;
8215 }
8216 }
8217
8218 pbuf->read_cache_size = read_size;
8219 pbuf->read_cache_rp = 0;
8220 pbuf->read_cache_wp = 0;
8221
8222 pbuf->read_cache_mutex.unlock();
8223
8224 /* resize the write cache */
8225
8227
8228 if (status != BM_SUCCESS)
8229 return status;
8230
8231 // FIXME: should flush the write cache!
8232 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8233 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8234 }
8235
8236 /* manage write cache */
8237 if (pbuf->write_cache_size > 0) {
8238 free(pbuf->write_cache);
8239 pbuf->write_cache = NULL;
8240 }
8241
8242 if (write_size > 0) {
8243 pbuf->write_cache = (char *) M_MALLOC(write_size);
8244 if (pbuf->write_cache == NULL) {
8245 pbuf->write_cache_size = 0;
8246 pbuf->write_cache_rp = 0;
8247 pbuf->write_cache_wp = 0;
8248 pbuf->write_cache_mutex.unlock();
8249 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);
8250 return BM_NO_MEMORY;
8251 }
8252 }
8253
8254 pbuf->write_cache_size = write_size;
8255 pbuf->write_cache_rp = 0;
8256 pbuf->write_cache_wp = 0;
8257
8258 pbuf->write_cache_mutex.unlock();
8259 }
8260#endif /* LOCAL_ROUTINES */
8261
8262 return BM_SUCCESS;
8263}
8264
8265/********************************************************************/
8292INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
8293{
8294 event_header->event_id = event_id;
8295 event_header->trigger_mask = trigger_mask;
8296 event_header->data_size = data_size;
8297 event_header->time_stamp = ss_time();
8298 event_header->serial_number = serial;
8299
8300 return BM_SUCCESS;
8301}
8302
8303INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
8304{
8305 static std::mutex mutex;
8306
8307 event_header->event_id = event_id;
8308 event_header->trigger_mask = trigger_mask;
8309 event_header->data_size = data_size;
8310 event_header->time_stamp = ss_time();
8311 {
8312 std::lock_guard<std::mutex> lock(mutex);
8313 event_header->serial_number = *serial;
8314 *serial = *serial + 1;
8315 // implicit unlock
8316 }
8317
8318 return BM_SUCCESS;
8319}
8320
8322#ifndef DOXYGEN_SHOULD_SKIP_THIS
8323
8324/********************************************************************/
8325INT bm_add_event_request(INT buffer_handle, short int event_id,
8326 short int trigger_mask,
8327 INT sampling_type,
8328 EVENT_HANDLER *func,
8329 INT request_id)
8330/********************************************************************\
8331
8332 Routine: bm_add_event_request
8333
8334 Purpose: Place a request for a specific event type in the client
8335 structure of the buffer refereced by buffer_handle.
8336
8337 Input:
8338 INT buffer_handle Handle to the buffer where the re-
8339 quest should be placed in
8340
8341 short int event_id Event ID \
8342 short int trigger_mask Trigger mask / Event specification
8343
8344 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8345
8346
8347 Note: to request all types of events, use
8348 event_id = 0 (all others should be !=0 !)
8349 trigger_mask = TRIGGER_ALL
8350 sampling_typ = GET_ALL
8351
8352
8353 void *func Callback function
8354 INT request_id Request id (unique number assigned
8355 by bm_request_event)
8356
8357 Output:
8358 none
8359
8360 Function value:
8361 BM_SUCCESS Successful completion
8362 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8363 MIDAS.H should be increased.
8364 BM_INVALID_HANDLE Buffer handle is invalid
8365 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8366 RPC_NET_ERROR Network error
8367
8368\********************************************************************/
8369{
8370 if (rpc_is_remote())
8371 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8372 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8373
8374#ifdef LOCAL_ROUTINES
8375 {
8376 int status = 0;
8377
8378 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8379
8380 if (!pbuf)
8381 return status;
8382
8383 /* lock buffer */
8384 bm_lock_buffer_guard pbuf_guard(pbuf);
8385
8386 if (!pbuf_guard.is_locked())
8387 return pbuf_guard.get_status();
8388
8389 /* avoid callback/non callback requests */
8390 if (func == NULL && pbuf->callback) {
8391 pbuf_guard.unlock(); // unlock before cm_msg()
8392 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8393 return BM_INVALID_MIXING;
8394 }
8395
8396 /* do not allow GET_RECENT with nonzero cache size */
8397 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8398 pbuf_guard.unlock(); // unlock before cm_msg()
8399 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8400 return BM_INVALID_PARAM;
8401 }
8402
8403 /* get a pointer to the proper client structure */
8404 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8405
8406 /* look for a empty request entry */
8407 int i;
8408 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8409 if (!pclient->event_request[i].valid)
8410 break;
8411
8412 if (i == MAX_EVENT_REQUESTS) {
8413 // implicit unlock
8414 return BM_NO_MEMORY;
8415 }
8416
8417 /* setup event_request structure */
8418 pclient->event_request[i].id = request_id;
8419 pclient->event_request[i].valid = TRUE;
8420 pclient->event_request[i].event_id = event_id;
8422 pclient->event_request[i].sampling_type = sampling_type;
8423
8424 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8425
8426 pbuf->get_all_flag = pclient->all_flag;
8427
8428 /* set callback flag in buffer structure */
8429 if (func != NULL)
8430 pbuf->callback = TRUE;
8431
8432 /*
8433 Save the index of the last request in the list so that later only the
8434 requests 0..max_request_index-1 have to be searched through.
8435 */
8436
8437 if (i + 1 > pclient->max_request_index)
8438 pclient->max_request_index = i + 1;
8439 }
8440#endif /* LOCAL_ROUTINES */
8441
8442 return BM_SUCCESS;
8443}
8444
8446#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8447
8448/********************************************************************/
8476INT bm_request_event(HNDLE buffer_handle, short int event_id,
8477 short int trigger_mask,
8478 INT sampling_type, HNDLE *request_id,
8479 EVENT_HANDLER *func)
8480{
8481 assert(request_id != NULL);
8482
8483 EventRequest r;
8484 r.buffer_handle = buffer_handle;
8485 r.event_id = event_id;
8487 r.dispatcher = func;
8488
8489 {
8490 std::lock_guard<std::mutex> guard(_request_list_mutex);
8491
8492 bool found = false;
8493
8494 // find deleted entry
8495 for (size_t i = 0; i < _request_list.size(); i++) {
8496 if (_request_list[i].buffer_handle == 0) {
8497 _request_list[i] = r;
8498 *request_id = i;
8499 found = true;
8500 break;
8501 }
8502 }
8503
8504 if (!found) { // not found
8505 *request_id = _request_list.size();
8506 _request_list.push_back(r);
8507 }
8508
8509 // implicit unlock()
8510 }
8511
8512 /* add request in buffer structure */
8513 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8514 if (status != BM_SUCCESS)
8515 return status;
8516
8517 return BM_SUCCESS;
8518}
8519
8520/********************************************************************/
8529INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8530 if (rpc_is_remote())
8531 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8532
8533#ifdef LOCAL_ROUTINES
8534 {
8535 int status = 0;
8536
8537 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8538
8539 if (!pbuf)
8540 return status;
8541
8542 /* lock buffer */
8543 bm_lock_buffer_guard pbuf_guard(pbuf);
8544
8545 if (!pbuf_guard.is_locked())
8546 return pbuf_guard.get_status();
8547
8548 INT i, deleted;
8549
8550 /* get a pointer to the proper client structure */
8551 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8552
8553 /* check all requests and set to zero if matching */
8554 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8555 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8556 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8557 deleted++;
8558 }
8559
8560 /* calculate new max_request_index entry */
8561 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8562 if (pclient->event_request[i].valid)
8563 break;
8564
8565 pclient->max_request_index = i + 1;
8566
8567 /* calculate new all_flag */
8568 pclient->all_flag = FALSE;
8569
8570 for (i = 0; i < pclient->max_request_index; i++)
8571 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8572 pclient->all_flag = TRUE;
8573 break;
8574 }
8575
8576 pbuf->get_all_flag = pclient->all_flag;
8577
8578 if (!deleted)
8579 return BM_NOT_FOUND;
8580 }
8581#endif /* LOCAL_ROUTINES */
8582
8583 return BM_SUCCESS;
8584}
8585
8586/********************************************************************/
8596{
8597 _request_list_mutex.lock();
8598
8599 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8600 _request_list_mutex.unlock();
8601 return BM_INVALID_HANDLE;
8602 }
8603
8604 int buffer_handle = _request_list[request_id].buffer_handle;
8605
8606 _request_list[request_id].clear();
8607
8608 _request_list_mutex.unlock();
8609
8610 /* remove request entry from buffer */
8611 return bm_remove_event_request(buffer_handle, request_id);
8612}
8613
8614#if 0 // currently not used
8615static void bm_show_pointers(const BUFFER_HEADER * pheader)
8616{
8617 int i;
8618 const BUFFER_CLIENT *pclient;
8619
8620 pclient = pheader->client;
8621
8622 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8623 pheader->write_pointer, pheader->size);
8624 for (i = 0; i < pheader->max_client_index; i++)
8625 if (pclient[i].pid) {
8626 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8627 }
8628
8629 printf("done\n");
8630}
8631#endif
8632
8634 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8635 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8636
8637 if (pheader->read_pointer <= pheader->write_pointer) {
8638
8639 if (pclient->read_pointer < pheader->read_pointer) {
8640 cm_msg(MINFO, "bm_validate_client_pointers",
8641 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8642 pclient->name,
8643 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8644
8645 pclient->read_pointer = pheader->read_pointer;
8646 }
8647
8648 if (pclient->read_pointer > pheader->write_pointer) {
8649 cm_msg(MINFO, "bm_validate_client_pointers",
8650 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8651 pclient->name,
8652 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8653
8654 pclient->read_pointer = pheader->write_pointer;
8655 }
8656
8657 } else {
8658
8659 if (pclient->read_pointer < 0) {
8660 cm_msg(MINFO, "bm_validate_client_pointers",
8661 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8662 pclient->name,
8663 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8664
8665 pclient->read_pointer = pheader->read_pointer;
8666 }
8667
8668 if (pclient->read_pointer >= pheader->size) {
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->write_pointer && pclient->read_pointer < pheader->read_pointer) {
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}
8687
8688#if 0 // currently not used
8689static void bm_validate_pointers(BUFFER_HEADER * pheader)
8690{
8691 BUFFER_CLIENT *pclient = pheader->client;
8692 int i;
8693
8694 for (i = 0; i < pheader->max_client_index; i++)
8695 if (pclient[i].pid) {
8696 bm_validate_client_pointers(pheader, &pclient[i]);
8697 }
8698}
8699#endif
8700
8701//
8702// Buffer pointers
8703//
8704// normal:
8705//
8706// zero -->
8707// ... free space
8708// read_pointer -->
8709// client1 rp -->
8710// client2 rp -->
8711// ... buffered data
8712// write_pointer -->
8713// ... free space
8714// pheader->size -->
8715//
8716// inverted:
8717//
8718// zero -->
8719// client3 rp -->
8720// ... buffered data
8721// client4 rp -->
8722// write_pointer -->
8723// ... free space
8724// read_pointer -->
8725// client1 rp -->
8726// client2 rp -->
8727// ... buffered data
8728// pheader->size -->
8729//
8730
8731static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
8732 assert(caller_name);
8733
8734 /* calculate global read pointer as "minimum" of client read pointers */
8735 int min_rp = pheader->write_pointer;
8736
8737 int i;
8738 for (i = 0; i < pheader->max_client_index; i++) {
8739 BUFFER_CLIENT *pc = pheader->client + i;
8740 if (pc->pid) {
8742
8743#if 0
8744 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8745 pheader->name,
8746 pheader->read_pointer,
8747 pheader->write_pointer,
8748 pheader->size,
8749 min_rp,
8750 pc->name,
8751 pc->read_pointer);
8752#endif
8753
8754 if (pheader->read_pointer <= pheader->write_pointer) {
8755 // normal pointers
8756 if (pc->read_pointer < min_rp)
8757 min_rp = pc->read_pointer;
8758 } else {
8759 // inverted pointers
8760 if (pc->read_pointer <= pheader->write_pointer) {
8761 // clients 3 and 4
8762 if (pc->read_pointer < min_rp)
8763 min_rp = pc->read_pointer;
8764 } else {
8765 // clients 1 and 2
8766 int xptr = pc->read_pointer - pheader->size;
8767 if (xptr < min_rp)
8768 min_rp = xptr;
8769 }
8770 }
8771 }
8772 }
8773
8774 if (min_rp < 0)
8775 min_rp += pheader->size;
8776
8777 assert(min_rp >= 0);
8778 assert(min_rp < pheader->size);
8779
8780 if (min_rp == pheader->read_pointer) {
8781 return FALSE;
8782 }
8783
8784#if 0
8785 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8786 pheader->name,
8787 pheader->read_pointer,
8788 pheader->write_pointer,
8789 pheader->size,
8790 min_rp);
8791#endif
8792
8793 pheader->read_pointer = min_rp;
8794
8795 return TRUE;
8796}
8797
8798static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8799 int i;
8800 int have_get_all_requests = 0;
8801
8802 for (i = 0; i < pc->max_request_index; i++)
8803 if (pc->event_request[i].valid)
8804 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8805
8806 /* only GET_ALL requests actually free space in the event buffer */
8807 if (!have_get_all_requests)
8808 return;
8809
8810 /*
8811 If read pointer has been changed, it may have freed up some space
8812 for waiting producers. So check if free space is now more than 50%
8813 of the buffer size and wake waiting producers.
8814 */
8815
8816 int free_space = pc->read_pointer - pheader->write_pointer;
8817 if (free_space <= 0)
8818 free_space += pheader->size;
8819
8820 if (free_space >= pheader->size * 0.5) {
8821 for (i = 0; i < pheader->max_client_index; i++) {
8822 const BUFFER_CLIENT *pc = pheader->client + i;
8823 if (pc->pid && pc->write_wait) {
8824 BOOL send_wakeup = (pc->write_wait < free_space);
8825 //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);
8826 if (send_wakeup) {
8827 ss_resume(pc->port, "B ");
8828 }
8829 }
8830 }
8831 }
8832}
8833
8834static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8835{
8836 _request_list_mutex.lock();
8837 bool locked = true;
8838 size_t n = _request_list.size();
8839 /* call dispatcher */
8840 for (size_t i = 0; i < n; i++) {
8841 if (!locked) {
8842 _request_list_mutex.lock();
8843 locked = true;
8844 }
8846 if (r.buffer_handle != buffer_handle)
8847 continue;
8848 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8849 continue;
8850 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8851 _request_list_mutex.unlock();
8852 locked = false;
8853 /* if event is fragmented, call defragmenter */
8854 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))) {
8855 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8856 } else {
8857 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8858 }
8859 }
8860 if (locked)
8861 _request_list_mutex.unlock();
8862}
8863
8864#ifdef LOCAL_ROUTINES
8865
8866static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8867 /* increment read cache read pointer */
8868 pbuf->read_cache_rp += total_size;
8869
8870 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8871 pbuf->read_cache_rp = 0;
8872 pbuf->read_cache_wp = 0;
8873 }
8874}
8875
8876static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8877{
8878 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8879 return FALSE;
8880
8881 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8882 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8883 int total_size = ALIGN8(event_size);
8884
8885 if (ppevent)
8886 *ppevent = pevent;
8887 if (pevent_size)
8888 *pevent_size = event_size;
8889 if (ptotal_size)
8890 *ptotal_size = total_size;
8891
8892 return TRUE;
8893}
8894
8895//
8896// return values:
8897// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8898// BM_ASYNC_RETURN - buffer is empty
8899// BM_CORRUPTED - buffer is corrupted
8900//
8901
8902static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8903{
8904 if (pc->read_pointer == pheader->write_pointer) {
8905 /* no more events buffered for this client */
8906 if (!pc->read_wait) {
8907 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8908 pc->read_wait = TRUE;
8909 }
8910 return BM_ASYNC_RETURN;
8911 }
8912
8913 if (pc->read_wait) {
8914 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8915 pc->read_wait = FALSE;
8916 }
8917
8918 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8919 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);
8920 return BM_CORRUPTED;
8921 }
8922
8923 char *pdata = (char *) (pheader + 1);
8924
8925 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8926 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8927 int total_size = ALIGN8(event_size);
8928
8929 if ((total_size <= 0) || (total_size > pheader->size)) {
8930 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);
8931 return BM_CORRUPTED;
8932 }
8933
8934 assert(total_size > 0);
8935 assert(total_size <= pheader->size);
8936
8937 if (ppevent)
8938 *ppevent = pevent;
8939 if (pevent_size)
8940 *pevent_size = event_size;
8941 if (ptotal_size)
8942 *ptotal_size = total_size;
8943
8944 return BM_SUCCESS;
8945}
8946
8947static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8948{
8949 const char *pdata = (const char *) (pheader + 1);
8950
8951 if (rp + event_size <= pheader->size) {
8952 /* copy event to cache */
8953 memcpy(buf, pdata + rp, event_size);
8954 } else {
8955 /* event is splitted */
8956 int size = pheader->size - rp;
8957 memcpy(buf, pdata + rp, size);
8958 memcpy(buf + size, pdata, event_size - size);
8959 }
8960}
8961
8962static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8963{
8964 const char *pdata = (const char *) (pheader + 1);
8965
8966 if (rp + event_size <= pheader->size) {
8967 /* copy event to cache */
8968 vecptr->assign(pdata + rp, pdata + rp + event_size);
8969 } else {
8970 /* event is splitted */
8971 int size = pheader->size - rp;
8972 vecptr->assign(pdata + rp, pdata + rp + size);
8973 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8974 }
8975}
8976
8977static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8978
8979 BOOL is_requested = FALSE;
8980 int i;
8981 for (i = 0; i < pc->max_request_index; i++) {
8982 const EVENT_REQUEST *prequest = pc->event_request + i;
8983 if (prequest->valid) {
8984 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8985 /* check if this is a recent event */
8986 if (prequest->sampling_type == GET_RECENT) {
8987 if (ss_time() - pevent->time_stamp > 1) {
8988 /* skip that event */
8989 continue;
8990 }
8991 }
8992
8993 is_requested = TRUE;
8994 break;
8995 }
8996 }
8997 }
8998 return is_requested;
8999}
9000
9001static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
9002
9003static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
9004{
9005 BUFFER* pbuf = pbuf_guard.get_pbuf();
9006 BUFFER_HEADER* pheader = pbuf->buffer_header;
9007 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9008 BOOL need_wakeup = FALSE;
9009
9010 //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);
9011
9012 /* loop over all events in the buffer */
9013
9014 while (1) {
9015 EVENT_HEADER *pevent = NULL;
9016 int event_size = 3; // poison value
9017 int total_size = 3; // poison value
9018
9019 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9020 if (status == BM_CORRUPTED) {
9021 return status;
9022 } else if (status != BM_SUCCESS) {
9023 /* event buffer is empty */
9024 if (timeout_msec == BM_NO_WAIT) {
9025 if (need_wakeup)
9026 bm_wakeup_producers_locked(pheader, pc);
9027 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9028 // read cache is empty
9029 return BM_ASYNC_RETURN;
9030 }
9031 return BM_SUCCESS;
9032 }
9033
9034 int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
9035
9036 if (status != BM_SUCCESS) {
9037 // we only come here with SS_ABORT & co
9038 return status;
9039 }
9040
9041 // make sure we wait for new event only once
9042 timeout_msec = BM_NO_WAIT;
9043 // go back to bm_peek_buffer_locked
9044 continue;
9045 }
9046
9047 /* loop over all requests: if this event matches a request,
9048 * copy it to the read cache */
9049
9050 BOOL is_requested = bm_check_requests(pc, pevent);
9051
9052 if (is_requested) {
9053 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9054 /* read cache is full */
9055 if (need_wakeup)
9056 bm_wakeup_producers_locked(pheader, pc);
9057 return BM_SUCCESS;
9058 }
9059
9061
9062 pbuf->read_cache_wp += total_size;
9063
9064 /* update statistics */
9065 pheader->num_out_events++;
9066 pbuf->count_read++;
9067 pbuf->bytes_read += event_size;
9068 }
9069
9070 /* shift read pointer */
9071
9072 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9073 pc->read_pointer = new_read_pointer;
9074
9075 need_wakeup = TRUE;
9076 }
9077 /* NOT REACHED */
9078}
9079
9080static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9081 /* now convert event header */
9082 if (convert_flags) {
9083 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9084 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9085 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9086 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9087 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9088 }
9089}
9090
9091static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
9092{
9093 // return values:
9094 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9095 // BM_CORRUPTED - shared memory is corrupted
9096 // BM_NO_MEMORY - asked for more than buffer size
9097 // BM_ASYNC_RETURN - timeout waiting for free space
9098 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9099 // SS_ABORT - we are told to shutdown (locks releases)
9100
9101 int status;
9102 BUFFER* pbuf = pbuf_guard.get_pbuf();
9103 BUFFER_HEADER *pheader = pbuf->buffer_header;
9104 char *pdata = (char *) (pheader + 1);
9105
9106 /* make sure the buffer never completely full:
9107 * read pointer and write pointer would coincide
9108 * and the code cannot tell if it means the
9109 * buffer is 100% full or 100% empty. It will explode
9110 * or lose events */
9111 requested_space += 100;
9112
9113 if (requested_space >= pheader->size)
9114 return BM_NO_MEMORY;
9115
9116 DWORD time_start = ss_millitime();
9117 DWORD time_end = time_start + timeout_msec;
9118
9119 //DWORD blocking_time = 0;
9120 //int blocking_loops = 0;
9121 int blocking_client_index = -1;
9122 char blocking_client_name[NAME_LENGTH];
9123 blocking_client_name[0] = 0;
9124
9125 while (1) {
9126 while (1) {
9127 /* check if enough space in buffer */
9128
9129 int free = pheader->read_pointer - pheader->write_pointer;
9130 if (free <= 0)
9131 free += pheader->size;
9132
9133 //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);
9134
9135 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9136 //if (blocking_loops) {
9137 // DWORD wait_time = ss_millitime() - blocking_time;
9138 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9139 //}
9140
9141 if (pbuf->wait_start_time != 0) {
9142 DWORD now = ss_millitime();
9143 DWORD wait_time = now - pbuf->wait_start_time;
9144 pbuf->time_write_wait += wait_time;
9145 pbuf->wait_start_time = 0;
9146 int iclient = pbuf->wait_client_index;
9147 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9148 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9149 pbuf->client_count_write_wait[iclient] += 1;
9150 pbuf->client_time_write_wait[iclient] += wait_time;
9151 }
9152 }
9153
9154 //if (blocking_loops > 0) {
9155 // 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);
9156 //}
9157
9158 return BM_SUCCESS;
9159 }
9160
9161 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9162 cm_msg(MERROR, "bm_wait_for_free_space",
9163 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9164 pheader->name,
9165 pheader->read_pointer,
9166 pheader->write_pointer,
9167 pheader->size,
9168 free,
9169 requested_space);
9170 return BM_CORRUPTED;
9171 }
9172
9173 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9174 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9175 int total_size = ALIGN8(event_size);
9176
9177#if 0
9178 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);
9179#endif
9180
9181 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9182 cm_msg(MERROR, "bm_wait_for_free_space",
9183 "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",
9184 pheader->name,
9185 pheader->read_pointer,
9186 pheader->write_pointer,
9187 pheader->size,
9188 free,
9189 requested_space,
9190 pevent->data_size,
9191 event_size,
9192 total_size);
9193 return BM_CORRUPTED;
9194 }
9195
9196 int blocking_client = -1;
9197
9198 int i;
9199 for (i = 0; i < pheader->max_client_index; i++) {
9200 BUFFER_CLIENT *pc = pheader->client + i;
9201 if (pc->pid) {
9202 if (pc->read_pointer == pheader->read_pointer) {
9203 /*
9204 First assume that the client with the "minimum" read pointer
9205 is not really blocking due to a GET_ALL request.
9206 */
9207 BOOL blocking = FALSE;
9208 //int blocking_request_id = -1;
9209
9210 int j;
9211 for (j = 0; j < pc->max_request_index; j++) {
9212 const EVENT_REQUEST *prequest = pc->event_request + j;
9213 if (prequest->valid
9214 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9215 if (prequest->sampling_type & GET_ALL) {
9216 blocking = TRUE;
9217 //blocking_request_id = prequest->id;
9218 break;
9219 }
9220 }
9221 }
9222
9223 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9224
9225 if (blocking) {
9226 blocking_client = i;
9227 break;
9228 }
9229
9230 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9231 }
9232 }
9233 } /* client loop */
9234
9235 if (blocking_client >= 0) {
9236 blocking_client_index = blocking_client;
9237 mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
9238 //if (!blocking_time) {
9239 // blocking_time = ss_millitime();
9240 //}
9241
9242 //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);
9243
9244 // from this "break" we go into timeout check and sleep/wait.
9245 break;
9246 }
9247
9248 /* no blocking clients. move the read pointer and again check for free space */
9249
9250 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9251
9252 if (!moved) {
9253 cm_msg(MERROR, "bm_wait_for_free_space",
9254 "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",
9255 pheader->name,
9256 pheader->read_pointer,
9257 pheader->write_pointer,
9258 pheader->size,
9259 free,
9260 requested_space);
9261 return BM_CORRUPTED;
9262 }
9263
9264 /* we freed one event, loop back to the check for free space */
9265 }
9266
9267 //blocking_loops++;
9268
9269 /* at least one client is blocking */
9270
9271 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9272 pc->write_wait = requested_space;
9273
9274 if (pbuf->wait_start_time == 0) {
9275 pbuf->wait_start_time = ss_millitime();
9276 pbuf->count_write_wait++;
9277 if (requested_space > pbuf->max_requested_space)
9278 pbuf->max_requested_space = requested_space;
9279 pbuf->wait_client_index = blocking_client_index;
9280 }
9281
9282 DWORD now = ss_millitime();
9283
9284 //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);
9285
9286 int sleep_time_msec = 1000;
9287
9288 if (timeout_msec == BM_WAIT) {
9289 // wait forever
9290 } else if (timeout_msec == BM_NO_WAIT) {
9291 // no wait
9292 return BM_ASYNC_RETURN;
9293 } else {
9294 // check timeout
9295 if (now >= time_end) {
9296 // timeout!
9297 return BM_ASYNC_RETURN;
9298 }
9299
9300 sleep_time_msec = time_end - now;
9301
9302 if (sleep_time_msec <= 0) {
9303 sleep_time_msec = 10;
9304 } else if (sleep_time_msec > 1000) {
9305 sleep_time_msec = 1000;
9306 }
9307 }
9308
9310
9311 /* before waiting, unlock everything in the correct order */
9312
9313 pbuf_guard.unlock();
9314
9315 if (unlock_write_cache)
9316 pbuf->write_cache_mutex.unlock();
9317
9318 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9319
9320#ifdef DEBUG_MSG
9321 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9322#endif
9323
9325 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9326 //if (idx >= 0)
9327 // pheader->client[idx].write_wait = requested_space;
9328
9329 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9330
9331 status = ss_suspend(sleep_time_msec, MSG_BM);
9332
9333 /* we are told to shutdown */
9334 if (status == SS_ABORT) {
9335 // NB: buffer is locked!
9336 return SS_ABORT;
9337 }
9338
9339 /* make sure we do sleep in this loop:
9340 * if we are the mserver receiving data on the event
9341 * socket and the data buffer is full, ss_suspend() will
9342 * never sleep: it will detect data on the event channel,
9343 * call rpc_server_receive() (recursively, we already *are* in
9344 * rpc_server_receive()) and return without sleeping. Result
9345 * is a busy loop waiting for free space in data buffer */
9346
9347 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9348 * the event socket, and should sleep now, so this sleep below
9349 * maybe is not needed now. but for safety, I keep it. K.O. */
9350
9351 if (status != SS_TIMEOUT) {
9352 //printf("ss_suspend: status %d\n", status);
9353 ss_sleep(1);
9354 }
9355
9356 /* we may be stuck in this loop for an arbitrary long time,
9357 * depending on how other buffer clients read the accumulated data
9358 * so we should update all the timeouts & etc. K.O. */
9359
9361
9362 /* lock things again in the correct order */
9363
9364 if (unlock_write_cache) {
9366
9367 if (status != BM_SUCCESS) {
9368 // bail out with all locks released
9369 return status;
9370 }
9371 }
9372
9373 if (!pbuf_guard.relock()) {
9374 if (unlock_write_cache) {
9375 pbuf->write_cache_mutex.unlock();
9376 }
9377
9378 // bail out with all locks released
9379 return pbuf_guard.get_status();
9380 }
9381
9382 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9383 pc = bm_get_my_client_locked(pbuf_guard);
9384
9385 pc->write_wait = 0;
9386
9388 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9389 //if (idx >= 0)
9390 // pheader->client[idx].write_wait = 0;
9391 //else {
9392 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9393 // status = SS_ABORT;
9394 //}
9395
9396#ifdef DEBUG_MSG
9397 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);
9398#endif
9399
9400 }
9401}
9402
9403static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
9404{
9405 BUFFER* pbuf = pbuf_guard.get_pbuf();
9406 BUFFER_HEADER* pheader = pbuf->buffer_header;
9407
9408 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9409
9410 if (pc->read_pointer != pheader->write_pointer) {
9411 // buffer has data
9412 return BM_SUCCESS;
9413 }
9414
9415 if (timeout_msec == BM_NO_WAIT) {
9416 /* event buffer is empty and we are told to not wait */
9417 if (!pc->read_wait) {
9418 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9419 pc->read_wait = TRUE;
9420 }
9421 return BM_ASYNC_RETURN;
9422 }
9423
9424 DWORD time_start = ss_millitime();
9425 DWORD time_wait = time_start + timeout_msec;
9426 DWORD sleep_time = 1000;
9427 if (timeout_msec == BM_NO_WAIT) {
9428 // default sleep time
9429 } else if (timeout_msec == BM_WAIT) {
9430 // default sleep time
9431 } else {
9432 if (sleep_time > (DWORD)timeout_msec)
9433 sleep_time = timeout_msec;
9434 }
9435
9436 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9437
9438 while (pc->read_pointer == pheader->write_pointer) {
9439 /* wait until there is data in the buffer (write pointer moves) */
9440
9441 if (!pc->read_wait) {
9442 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9443 pc->read_wait = TRUE;
9444 }
9445
9447
9449
9450 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9451
9452 pbuf_guard.unlock();
9453
9454 if (unlock_read_cache)
9455 pbuf->read_cache_mutex.unlock();
9456
9457 int status = ss_suspend(sleep_time, MSG_BM);
9458
9459 if (timeout_msec == BM_NO_WAIT) {
9460 // return immediately
9461 } else if (timeout_msec == BM_WAIT) {
9462 // wait forever
9463 } else {
9464 DWORD now = ss_millitime();
9465 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9466 if (now >= time_wait) {
9467 timeout_msec = BM_NO_WAIT; // cause immediate return
9468 } else {
9469 sleep_time = time_wait - now;
9470 if (sleep_time > 1000)
9471 sleep_time = 1000;
9472 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9473 }
9474 }
9475
9476 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9477
9478 if (unlock_read_cache) {
9480 if (status != BM_SUCCESS) {
9481 // bail out with all locks released
9482 return status;
9483 }
9484 }
9485
9486 if (!pbuf_guard.relock()) {
9487 if (unlock_read_cache) {
9488 pbuf->read_cache_mutex.unlock();
9489 }
9490 // bail out with all locks released
9491 return pbuf_guard.get_status();
9492 }
9493
9494 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9495 * because we may have been removed from the buffer by bm_cleanup() & co
9496 * due to a timeout or whatever. */
9497 pc = bm_get_my_client_locked(pbuf_guard);
9498
9499 /* return if TCP connection broken */
9500 if (status == SS_ABORT)
9501 return SS_ABORT;
9502
9503 if (timeout_msec == BM_NO_WAIT)
9504 return BM_ASYNC_RETURN;
9505 }
9506
9507 if (pc->read_wait) {
9508 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9509 pc->read_wait = FALSE;
9510 }
9511
9512 return BM_SUCCESS;
9513}
9514
9515static 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)
9516{
9517 char *pdata = (char *) (pheader + 1);
9518
9519 //int old_write_pointer = pheader->write_pointer;
9520
9521 /* new event fits into the remaining space? */
9522 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9523 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9524 char* wptr = pdata + pheader->write_pointer;
9525 for (int i=0; i<sg_n; i++) {
9526 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9527 memcpy(wptr, sg_ptr[i], sg_len[i]);
9528 wptr += sg_len[i];
9529 }
9530 pheader->write_pointer = pheader->write_pointer + total_size;
9531 assert(pheader->write_pointer <= pheader->size);
9532 /* remaining space is smaller than size of an event header? */
9533 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9534 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9535 // equal to the event header size, we will write the next event header here,
9536 // then wrap the pointer and write the event data at the beginning of the buffer.
9537 //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);
9538 pheader->write_pointer = 0;
9539 }
9540 } else {
9541 /* split event */
9542 size_t size = pheader->size - pheader->write_pointer;
9543
9544 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9545
9546 //memcpy(pdata + pheader->write_pointer, pevent, size);
9547 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9548
9549 char* wptr = pdata + pheader->write_pointer;
9550 size_t count = 0;
9551
9552 // copy first part
9553
9554 int i = 0;
9555 for (; i<sg_n; i++) {
9556 if (count + sg_len[i] > size)
9557 break;
9558 memcpy(wptr, sg_ptr[i], sg_len[i]);
9559 wptr += sg_len[i];
9560 count += sg_len[i];
9561 }
9562
9563 //printf("wptr %d, count %d\n", wptr-pdata, count);
9564
9565 // split segment
9566
9567 size_t first = size - count;
9568 size_t second = sg_len[i] - first;
9569 assert(first + second == sg_len[i]);
9570 assert(count + first == size);
9571
9572 //printf("first %d, second %d\n", first, second);
9573
9574 memcpy(wptr, sg_ptr[i], first);
9575 wptr = pdata + 0;
9576 count += first;
9577 memcpy(wptr, sg_ptr[i] + first, second);
9578 wptr += second;
9579 count += second;
9580 i++;
9581
9582 // copy remaining
9583
9584 for (; i<sg_n; i++) {
9585 memcpy(wptr, sg_ptr[i], sg_len[i]);
9586 wptr += sg_len[i];
9587 count += sg_len[i];
9588 }
9589
9590 //printf("wptr %d, count %d\n", wptr-pdata, count);
9591
9592 //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);
9593
9594 pheader->write_pointer = total_size - size;
9595 }
9596
9597 //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);
9598}
9599
9601 if (pc->pid) {
9602 int j;
9603 for (j = 0; j < pc->max_request_index; j++) {
9604 const EVENT_REQUEST *prequest = pc->event_request + j;
9605 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9606 return prequest->id;
9607 }
9608 }
9609 }
9610
9611 return -1;
9612}
9613
9614static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9615 if (request_id >= 0) {
9616 /* if that client has a request and is suspended, wake it up */
9617 if (pc->read_wait) {
9618 char str[80];
9619 sprintf(str, "B %s %d", pheader->name, request_id);
9620 ss_resume(pc->port, str);
9621 //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);
9622 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9623 pc->read_wait = FALSE;
9624 }
9625 }
9626}
9627
9628#endif // LOCAL_ROUTINES
9629
9630#if 0
9631INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9632{
9633 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9634
9635 DWORD time_start = ss_millitime();
9636 DWORD time_end = time_start + timeout_msec;
9637
9638 int xtimeout_msec = timeout_msec;
9639
9640 while (1) {
9641 if (timeout_msec == BM_WAIT) {
9642 xtimeout_msec = 1000;
9643 } else if (timeout_msec == BM_NO_WAIT) {
9644 xtimeout_msec = BM_NO_WAIT;
9645 } else {
9646 if (xtimeout_msec > 1000) {
9647 xtimeout_msec = 1000;
9648 }
9649 }
9650
9651 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9652
9653 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9654
9655 if (status == BM_ASYNC_RETURN) {
9656 if (timeout_msec == BM_WAIT) {
9657 // BM_WAIT means wait forever
9658 continue;
9659 } else if (timeout_msec == BM_NO_WAIT) {
9660 // BM_NO_WAIT means do not wait
9661 return status;
9662 } else {
9663 DWORD now = ss_millitime();
9664 if (now >= time_end) {
9665 // timeout, return BM_ASYNC_RETURN
9666 return status;
9667 }
9668
9669 DWORD remain = time_end - now;
9670
9671 if (remain < xtimeout_msec) {
9672 xtimeout_msec = remain;
9673 }
9674
9675 // keep asking for event...
9676 continue;
9677 }
9678 } else if (status == BM_SUCCESS) {
9679 // success, return BM_SUCCESS
9680 return status;
9681 } else {
9682 // error
9683 return status;
9684 }
9685 }
9686}
9687#endif
9688
9689INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9690{
9691 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9692 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9693
9694 if (data_size == 0) {
9695 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9696 return BM_INVALID_SIZE;
9697 }
9698
9699 if (data_size > MAX_DATA_SIZE) {
9700 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);
9701 return BM_INVALID_SIZE;
9702 }
9703
9704 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9705
9706 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9707
9708 if (rpc_is_remote()) {
9709 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9710 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9711 } else {
9712 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9713 }
9714}
9715
9716int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9717{
9718 const char* cptr = event.data();
9719 size_t clen = event.size();
9720 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9721}
9722
9723int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9724{
9725 int sg_n = event.size();
9726 const char* sg_ptr[sg_n];
9727 size_t sg_len[sg_n];
9728 for (int i=0; i<sg_n; i++) {
9729 sg_ptr[i] = event[i].data();
9730 sg_len[i] = event[i].size();
9731 }
9732 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9733}
9734
9735#ifdef LOCAL_ROUTINES
9736static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
9737#endif
9738
9739/********************************************************************/
9789int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9790{
9791 if (rpc_is_remote())
9792 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9793
9794 if (sg_n < 1) {
9795 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9796 return BM_INVALID_SIZE;
9797 }
9798
9799 if (sg_ptr[0] == NULL) {
9800 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9801 return BM_INVALID_SIZE;
9802 }
9803
9804 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9805 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));
9806 return BM_INVALID_SIZE;
9807 }
9808
9809 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9810
9811 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9812 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9813
9814 if (data_size == 0) {
9815 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9816 return BM_INVALID_SIZE;
9817 }
9818
9819 if (data_size > MAX_DATA_SIZE) {
9820 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);
9821 return BM_INVALID_SIZE;
9822 }
9823
9824 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9825
9826 size_t count = 0;
9827 for (int i=0; i<sg_n; i++) {
9828 count += sg_len[i];
9829 }
9830
9831 if (count != event_size) {
9832 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);
9833 return BM_INVALID_SIZE;
9834 }
9835
9836 //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);
9837
9838#ifdef LOCAL_ROUTINES
9839 {
9840 int status = 0;
9841 const size_t total_size = ALIGN8(event_size);
9842
9843 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9844
9845 if (!pbuf)
9846 return status;
9847
9848 /* round up total_size to next DWORD boundary */
9849 //int total_size = ALIGN8(event_size);
9850
9851 /* check if write cache is enabled */
9852 if (pbuf->write_cache_size) {
9854
9855 if (status != BM_SUCCESS)
9856 return status;
9857
9858 /* check if write cache is enabled */
9859 if (pbuf->write_cache_size) {
9861 bool too_big = event_size > max_event_size;
9862
9863 //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);
9864
9865 /* if this event does not fit into the write cache, flush the write cache */
9866 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9867 //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);
9868
9869 bm_lock_buffer_guard pbuf_guard(pbuf);
9870
9871 if (!pbuf_guard.is_locked()) {
9872 pbuf->write_cache_mutex.unlock();
9873 return pbuf_guard.get_status();
9874 }
9875
9876 int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
9877
9878 if (pbuf_guard.is_locked()) {
9879 // check if bm_wait_for_free_space() failed to relock the buffer
9880 pbuf_guard.unlock();
9881 }
9882
9883 if (status != BM_SUCCESS) {
9884 pbuf->write_cache_mutex.unlock();
9885 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9886 if (status == BM_NO_MEMORY)
9887 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9888 return status;
9889 }
9890
9891 // write cache must be empty here
9892 assert(pbuf->write_cache_wp == 0);
9893 }
9894
9895 /* write this event into the write cache, if it is not too big and if it fits */
9896 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9897 //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);
9898
9899 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9900
9901 for (int i=0; i<sg_n; i++) {
9902 memcpy(wptr, sg_ptr[i], sg_len[i]);
9903 wptr += sg_len[i];
9904 }
9905
9906 pbuf->write_cache_wp += total_size;
9907
9908 pbuf->write_cache_mutex.unlock();
9909 return BM_SUCCESS;
9910 }
9911 }
9912
9913 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9914 pbuf->write_cache_mutex.unlock();
9915 }
9916
9917 /* we come here only for events that are too big to fit into the cache */
9918
9919 /* lock the buffer */
9920 bm_lock_buffer_guard pbuf_guard(pbuf);
9921
9922 if (!pbuf_guard.is_locked()) {
9923 return pbuf_guard.get_status();
9924 }
9925
9926 /* calculate some shorthands */
9927 BUFFER_HEADER *pheader = pbuf->buffer_header;
9928
9929#if 0
9931 if (status != BM_SUCCESS) {
9932 printf("bm_send_event: corrupted 111!\n");
9933 abort();
9934 }
9935#endif
9936
9937 /* check if buffer is large enough */
9938 if (total_size >= (size_t)pheader->size) {
9939 pbuf_guard.unlock(); // unlock before cm_msg()
9940 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9941 return BM_NO_MEMORY;
9942 }
9943
9944 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
9945
9946 if (status != BM_SUCCESS) {
9947 // implicit unlock
9948 return status;
9949 }
9950
9951#if 0
9953 if (status != BM_SUCCESS) {
9954 printf("bm_send_event: corrupted 222!\n");
9955 abort();
9956 }
9957#endif
9958
9959 int old_write_pointer = pheader->write_pointer;
9960
9961 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9962
9963 /* write pointer was incremented, but there should
9964 * always be some free space in the buffer and the
9965 * write pointer should never cacth up to the read pointer:
9966 * the rest of the code gets confused this happens (buffer 100% full)
9967 * as it is write_pointer == read_pointer can be either
9968 * 100% full or 100% empty. My solution: never fill
9969 * the buffer to 100% */
9970 assert(pheader->write_pointer != pheader->read_pointer);
9971
9972 /* send wake up messages to all clients that want this event */
9973 int i;
9974 for (i = 0; i < pheader->max_client_index; i++) {
9975 BUFFER_CLIENT *pc = pheader->client + i;
9976 int request_id = bm_find_first_request_locked(pc, pevent);
9977 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9978 }
9979
9980#if 0
9982 if (status != BM_SUCCESS) {
9983 printf("bm_send_event: corrupted 333!\n");
9984 abort();
9985 }
9986#endif
9987
9988 /* update statistics */
9989 pheader->num_in_events++;
9990 pbuf->count_sent += 1;
9991 pbuf->bytes_sent += total_size;
9992 }
9993#endif /* LOCAL_ROUTINES */
9994
9995 return BM_SUCCESS;
9996}
9997
9998static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
9999{
10000 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10001
10002 DWORD time_start = ss_millitime();
10003 DWORD time_end = time_start + timeout_msec;
10004 DWORD time_bombout = time_end;
10005
10006 if (timeout_msec < 10000)
10007 time_bombout = time_start + 10000; // 10 seconds
10008
10009 int xtimeout_msec = timeout_msec;
10010
10011 while (1) {
10012 if (timeout_msec == BM_WAIT) {
10013 xtimeout_msec = 1000;
10014 } else if (timeout_msec == BM_NO_WAIT) {
10015 xtimeout_msec = BM_NO_WAIT;
10016 } else {
10017 if (xtimeout_msec > 1000) {
10018 xtimeout_msec = 1000;
10019 }
10020 }
10021
10022 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10023
10024 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10025
10026 if (status == BM_ASYNC_RETURN) {
10027 if (timeout_msec == BM_WAIT) {
10028 DWORD now = ss_millitime();
10029 if (now >= time_bombout) {
10030 // timeout
10031 return BM_TIMEOUT;
10032 }
10033
10034 // BM_WAIT means wait forever
10035 continue;
10036 } else if (timeout_msec == BM_NO_WAIT) {
10037 // BM_NO_WAIT means do not wait
10038 return status;
10039 } else {
10040 DWORD now = ss_millitime();
10041 if (now >= time_end) {
10042 // timeout, return BM_ASYNC_RETURN
10043 return status;
10044 }
10045
10046 DWORD remain = time_end - now;
10047
10048 if (remain < (DWORD)xtimeout_msec) {
10049 xtimeout_msec = remain;
10050 }
10051
10052 if (now >= time_bombout) {
10053 // timeout
10054 return BM_TIMEOUT;
10055 }
10056
10057 // keep asking for event...
10058 continue;
10059 }
10060 } else if (status == BM_SUCCESS) {
10061 // success, return BM_SUCCESS
10062 return status;
10063 } else {
10064 // error
10065 return status;
10066 }
10067 }
10068}
10069
10070/********************************************************************/
10088#ifdef LOCAL_ROUTINES
10089static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
10090{
10091 // NB we come here with write cache locked and buffer locked.
10092
10093 {
10094 INT status = 0;
10095
10096 //printf("bm_flush_cache_locked!\n");
10097
10098 BUFFER* pbuf = pbuf_guard.get_pbuf();
10099 BUFFER_HEADER* pheader = pbuf->buffer_header;
10100
10101 //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);
10102
10103 int old_write_pointer = pheader->write_pointer;
10104
10105 int request_id[MAX_CLIENTS];
10106 for (int i = 0; i < pheader->max_client_index; i++) {
10107 request_id[i] = -1;
10108 }
10109
10110 size_t ask_rp = pbuf->write_cache_rp;
10111 size_t ask_wp = pbuf->write_cache_wp;
10112
10113 if (ask_wp == 0) { // nothing to do
10114 return BM_SUCCESS;
10115 }
10116
10117 if (ask_rp == ask_wp) { // nothing to do
10118 return BM_SUCCESS;
10119 }
10120
10121 assert(ask_rp < ask_wp);
10122
10123 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10124
10125 if (ask_free == 0) { // nothing to do
10126 return BM_SUCCESS;
10127 }
10128
10129#if 0
10131 if (status != BM_SUCCESS) {
10132 printf("bm_flush_cache: corrupted 111!\n");
10133 abort();
10134 }
10135#endif
10136
10137 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
10138
10139 if (status != BM_SUCCESS) {
10140 return status;
10141 }
10142
10143 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10144 //
10145 // wait_for_free_space() will sleep with all locks released,
10146 // during this time, another thread may call bm_send_event() that will
10147 // add one or more events to the write cache and after wait_for_free_space()
10148 // returns, size of data in cache will be bigger than the amount
10149 // of free space we requested. so we need to keep track of how
10150 // much data we write to the buffer and ask for more data
10151 // if we run short. This is the reason for the big loop
10152 // around wait_for_free_space(). We ask for slightly too little free
10153 // space to make sure all this code is always used and does work. K.O.
10154
10155 if (pbuf->write_cache_wp == 0) {
10156 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10157 return BM_SUCCESS;
10158 }
10159
10160 //size_t written = 0;
10161 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10162 /* loop over all events in cache */
10163
10164 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10165 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10166 size_t total_size = ALIGN8(event_size);
10167
10168#if 0
10169 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",
10170 int(pbuf->write_cache_size),
10171 int(pbuf->write_cache_wp),
10172 int(pbuf->write_cache_rp),
10173 int(pevent->data_size),
10174 int(event_size),
10175 int(total_size),
10176 int(ask_free),
10177 int(written));
10178#endif
10179
10180 // check for crazy event size
10181 assert(total_size >= sizeof(EVENT_HEADER));
10182 assert(total_size <= (size_t)pheader->size);
10183
10184 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10185
10186 /* update statistics */
10187 pheader->num_in_events++;
10188 pbuf->count_sent += 1;
10189 pbuf->bytes_sent += total_size;
10190
10191 /* see comment for the same code in bm_send_event().
10192 * We make sure the buffer is never 100% full */
10193 assert(pheader->write_pointer != pheader->read_pointer);
10194
10195 /* check if anybody has a request for this event */
10196 for (int i = 0; i < pheader->max_client_index; i++) {
10197 BUFFER_CLIENT *pc = pheader->client + i;
10198 int r = bm_find_first_request_locked(pc, pevent);
10199 if (r >= 0) {
10200 request_id[i] = r;
10201 }
10202 }
10203
10204 /* this loop does not loop forever because rp
10205 * is monotonously incremented here. write_cache_wp does
10206 * not change */
10207
10208 pbuf->write_cache_rp += total_size;
10209 //written += total_size;
10210
10211 assert(pbuf->write_cache_rp > 0);
10212 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10213 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10214 }
10215
10216 /* the write cache is now empty */
10217 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10218 pbuf->write_cache_wp = 0;
10219 pbuf->write_cache_rp = 0;
10220
10221 /* check which clients are waiting */
10222 for (int i = 0; i < pheader->max_client_index; i++) {
10223 BUFFER_CLIENT *pc = pheader->client + i;
10224 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10225 }
10226 }
10227
10228 return BM_SUCCESS;
10229}
10230
10231#endif /* LOCAL_ROUTINES */
10232
10233INT bm_flush_cache(int buffer_handle, int timeout_msec)
10234{
10235 if (rpc_is_remote()) {
10236 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10237 }
10238
10239#ifdef LOCAL_ROUTINES
10240 {
10241 INT status = 0;
10242
10243 //printf("bm_flush_cache!\n");
10244
10245 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10246
10247 if (!pbuf)
10248 return status;
10249
10250 if (pbuf->write_cache_size == 0)
10251 return BM_SUCCESS;
10252
10254
10255 if (status != BM_SUCCESS)
10256 return status;
10257
10258 /* check if anything needs to be flushed */
10259 if (pbuf->write_cache_wp == 0) {
10260 pbuf->write_cache_mutex.unlock();
10261 return BM_SUCCESS;
10262 }
10263
10264 /* lock the buffer */
10265 bm_lock_buffer_guard pbuf_guard(pbuf);
10266
10267 if (!pbuf_guard.is_locked())
10268 return pbuf_guard.get_status();
10269
10270 status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
10271
10272 /* unlock in correct order */
10273
10274 if (pbuf_guard.is_locked()) {
10275 // check if bm_wait_for_free_space() failed to relock the buffer
10276 pbuf_guard.unlock();
10277 }
10278
10279 pbuf->write_cache_mutex.unlock();
10280
10281 return status;
10282 }
10283#endif /* LOCAL_ROUTINES */
10284
10285 return BM_SUCCESS;
10286}
10287
10288#ifdef LOCAL_ROUTINES
10289
10290static 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) {
10292
10293 int max_size = 0;
10294 if (buf_size) {
10295 max_size = *buf_size;
10296 *buf_size = 0;
10297 }
10298
10299 //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);
10300
10301 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10302
10303 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10304
10305 /* look if there is anything in the cache */
10306 if (pbuf->read_cache_size > 0) {
10307
10309
10310 if (status != BM_SUCCESS)
10311 return status;
10312
10313 if (pbuf->read_cache_wp == 0) {
10314
10315 // lock buffer for the first time
10316
10317 if (!pbuf_guard.relock()) {
10318 pbuf->read_cache_mutex.unlock();
10319 return pbuf_guard.get_status();
10320 }
10321
10322 status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
10323 if (status != BM_SUCCESS) {
10324 // unlock in correct order
10325 if (pbuf_guard.is_locked()) {
10326 // check if bm_wait_for_more_events() failed to relock the buffer
10327 pbuf_guard.unlock();
10328 }
10329 pbuf->read_cache_mutex.unlock();
10330 return status;
10331 }
10332
10333 // buffer remains locked here
10334 }
10335 EVENT_HEADER *pevent;
10336 int event_size;
10337 int total_size;
10338 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10339 if (pbuf_guard.is_locked()) {
10340 // do not need to keep the event buffer locked
10341 // when reading from the read cache
10342 pbuf_guard.unlock();
10343 }
10344 //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);
10346 if (buf) {
10347 if (event_size > max_size) {
10348 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);
10349 event_size = max_size;
10351 }
10352
10353 memcpy(buf, pevent, event_size);
10354
10355 if (buf_size) {
10356 *buf_size = event_size;
10357 }
10358 if (convert_flags) {
10359 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10360 }
10361 } else if (bufptr) {
10362 *bufptr = malloc(event_size);
10363 memcpy(*bufptr, pevent, event_size);
10365 } else if (vecptr) {
10366 vecptr->resize(0);
10367 char* cptr = (char*)pevent;
10368 vecptr->assign(cptr, cptr+event_size);
10369 }
10370 bm_incr_read_cache_locked(pbuf, total_size);
10371 pbuf->read_cache_mutex.unlock();
10372 if (dispatch) {
10373 // FIXME need to protect currently dispatched event against
10374 // another thread overwriting it by refilling the read cache
10375 bm_dispatch_event(buffer_handle, pevent);
10376 return BM_MORE_EVENTS;
10377 }
10378 // buffer is unlocked here
10379 return status;
10380 }
10381 pbuf->read_cache_mutex.unlock();
10382 }
10383
10384 /* we come here if the read cache is disabled */
10385 /* we come here if the next event is too big to fit into the read cache */
10386
10387 if (!pbuf_guard.is_locked()) {
10388 if (!pbuf_guard.relock())
10389 return pbuf_guard.get_status();
10390 }
10391
10392 EVENT_HEADER *event_buffer = NULL;
10393
10394 BUFFER_HEADER *pheader = pbuf->buffer_header;
10395
10396 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
10397
10398 while (1) {
10399 /* loop over events in the event buffer */
10400
10401 status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
10402
10403 if (status != BM_SUCCESS) {
10404 // implicit unlock
10405 return status;
10406 }
10407
10408 /* check if event at current read pointer matches a request */
10409
10410 EVENT_HEADER *pevent;
10411 int event_size;
10412 int total_size;
10413
10414 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10415 if (status == BM_CORRUPTED) {
10416 // implicit unlock
10417 return status;
10418 } else if (status != BM_SUCCESS) {
10419 /* event buffer is empty */
10420 break;
10421 }
10422
10423 BOOL is_requested = bm_check_requests(pc, pevent);
10424
10425 if (is_requested) {
10426 //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);
10427
10429
10430 if (buf) {
10431 if (event_size > max_size) {
10432 cm_msg(MERROR, "bm_read_buffer",
10433 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10434 event_size, pheader->name);
10435 event_size = max_size;
10437 }
10438
10439 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10440
10441 if (buf_size) {
10442 *buf_size = event_size;
10443 }
10444
10445 if (convert_flags) {
10446 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10447 }
10448
10449 pbuf->count_read++;
10450 pbuf->bytes_read += event_size;
10451 } else if (dispatch || bufptr) {
10452 assert(event_buffer == NULL); // make sure we only come here once
10453 event_buffer = (EVENT_HEADER *) malloc(event_size);
10455 pbuf->count_read++;
10456 pbuf->bytes_read += event_size;
10457 } else if (vecptr) {
10458 bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
10459 pbuf->count_read++;
10460 pbuf->bytes_read += event_size;
10461 }
10462
10463 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10464 pc->read_pointer = new_read_pointer;
10465
10466 pheader->num_out_events++;
10467 /* exit loop over events */
10468 break;
10469 }
10470
10471 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10472 pc->read_pointer = new_read_pointer;
10473 pheader->num_out_events++;
10474 }
10475
10476 /*
10477 If read pointer has been changed, it may have freed up some space
10478 for waiting producers. So check if free space is now more than 50%
10479 of the buffer size and wake waiting producers.
10480 */
10481
10482 bm_wakeup_producers_locked(pheader, pc);
10483
10484 pbuf_guard.unlock();
10485
10486 if (dispatch && event_buffer) {
10487 bm_dispatch_event(buffer_handle, event_buffer);
10488 free(event_buffer);
10489 event_buffer = NULL;
10490 return BM_MORE_EVENTS;
10491 }
10492
10493 if (bufptr && event_buffer) {
10494 *bufptr = event_buffer;
10495 event_buffer = NULL;
10497 }
10498
10499 if (event_buffer) {
10500 free(event_buffer);
10501 event_buffer = NULL;
10502 }
10503
10504 return status;
10505}
10506
10507#endif
10508
10509static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10510{
10511 //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);
10512
10513 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10514
10515 void *xbuf = NULL;
10516 int xbuf_size = 0;
10517
10518 if (buf) {
10519 xbuf = buf;
10520 xbuf_size = *buf_size;
10521 } else if (ppevent) {
10522 *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
10523 xbuf_size = _bm_max_event_size;
10524 } else if (pvec) {
10525 pvec->resize(_bm_max_event_size);
10526 xbuf = pvec->data();
10527 xbuf_size = pvec->size();
10528 } else {
10529 assert(!"incorrect call to bm_receivent_event_rpc()");
10530 }
10531
10532 int status;
10533 DWORD time_start = ss_millitime();
10534 DWORD time_end = time_start + timeout_msec;
10535
10536 int xtimeout_msec = timeout_msec;
10537
10538 int zbuf_size = xbuf_size;
10539
10540 while (1) {
10541 if (timeout_msec == BM_WAIT) {
10542 xtimeout_msec = 1000;
10543 } else if (timeout_msec == BM_NO_WAIT) {
10544 xtimeout_msec = BM_NO_WAIT;
10545 } else {
10546 if (xtimeout_msec > 1000) {
10547 xtimeout_msec = 1000;
10548 }
10549 }
10550
10551 zbuf_size = xbuf_size;
10552
10553 status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
10554
10555 //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);
10556
10557 if (status == BM_ASYNC_RETURN) {
10558 if (timeout_msec == BM_WAIT) {
10559 // BM_WAIT means wait forever
10560 continue;
10561 } else if (timeout_msec == BM_NO_WAIT) {
10562 // BM_NO_WAIT means do not wait
10563 break;
10564 } else {
10565 DWORD now = ss_millitime();
10566 if (now >= time_end) {
10567 // timeout, return BM_ASYNC_RETURN
10568 break;
10569 }
10570
10571 DWORD remain = time_end - now;
10572
10573 if (remain < (DWORD)xtimeout_msec) {
10574 xtimeout_msec = remain;
10575 }
10576
10577 // keep asking for event...
10578 continue;
10579 }
10580 } else if (status == BM_SUCCESS) {
10581 // success, return BM_SUCCESS
10582 break;
10583 }
10584
10585 // RPC error
10586
10587 if (buf) {
10588 *buf_size = 0;
10589 } else if (ppevent) {
10590 free(*ppevent);
10591 *ppevent = NULL;
10592 } else if (pvec) {
10593 pvec->resize(0);
10594 } else {
10595 assert(!"incorrect call to bm_receivent_event_rpc()");
10596 }
10597
10598 return status;
10599 }
10600
10601 // status is BM_SUCCESS or BM_ASYNC_RETURN
10602
10603 if (buf) {
10604 *buf_size = zbuf_size;
10605 } else if (ppevent) {
10606 // nothing to do
10607 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10608 } else if (pvec) {
10609 pvec->resize(zbuf_size);
10610 } else {
10611 assert(!"incorrect call to bm_receivent_event_rpc()");
10612 }
10613
10614 return status;
10615}
10616
10617static INT bm_receive_event_rpc_cxx(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10618{
10619 //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);
10620
10621 std::vector<char> *pv;
10622
10623 if (pvec == NULL)
10624 pv = new std::vector<char>;
10625 else
10626 pv = pvec;
10627
10628 pv->clear();
10629
10630 int status;
10631 DWORD time_start = ss_millitime();
10632 DWORD time_end = time_start + timeout_msec;
10633
10634 int xtimeout_msec = timeout_msec;
10635
10636 while (1) {
10637 if (timeout_msec == BM_WAIT) {
10638 xtimeout_msec = 1000;
10639 } else if (timeout_msec == BM_NO_WAIT) {
10640 xtimeout_msec = BM_NO_WAIT;
10641 } else {
10642 if (xtimeout_msec > 1000) {
10643 xtimeout_msec = 1000;
10644 }
10645 }
10646
10647 status = rpc_call(RPC_BM_RECEIVE_EVENT_CXX, buffer_handle, pv, xtimeout_msec);
10648
10649 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());
10650
10651 if (status == BM_ASYNC_RETURN) {
10652 if (timeout_msec == BM_WAIT) {
10653 // BM_WAIT means wait forever
10654 continue;
10655 } else if (timeout_msec == BM_NO_WAIT) {
10656 // BM_NO_WAIT means do not wait
10657 break;
10658 } else {
10659 DWORD now = ss_millitime();
10660 if (now >= time_end) {
10661 // timeout, return BM_ASYNC_RETURN
10662 break;
10663 }
10664
10665 DWORD remain = time_end - now;
10666
10667 if (remain < (DWORD)xtimeout_msec) {
10668 xtimeout_msec = remain;
10669 }
10670
10671 // keep asking for event...
10672 continue;
10673 }
10674 } else if (status == BM_SUCCESS) {
10675 // success, return BM_SUCCESS
10676 break;
10677 }
10678
10679 // RPC error
10680
10681 if (buf) {
10682 *buf_size = 0;
10683 } else if (ppevent) {
10684 free(*ppevent);
10685 *ppevent = NULL;
10686 } else if (pvec) {
10687 pvec->clear();
10688 } else {
10689 assert(!"incorrect call to bm_receivent_event_rpc_cxx()");
10690 }
10691
10692 if (pvec == NULL)
10693 delete pv;
10694
10695 return status;
10696 }
10697
10698 // status is BM_SUCCESS or BM_ASYNC_RETURN
10699
10700 if (buf) {
10701 if (pv->size() > (size_t)*buf_size) {
10703 memcpy(buf, pv->data(), *buf_size);
10704 } else {
10705 *buf_size = pv->size();
10706 memcpy(buf, pv->data(), *buf_size);
10707 }
10708 } else if (ppevent) {
10709 if (*ppevent == NULL) {
10710 *ppevent = (EVENT_HEADER*)malloc(pv->size());
10711 assert(*ppevent != NULL);
10712 memcpy(*ppevent, pv->data(), pv->size());
10713 } else {
10714 *ppevent = (EVENT_HEADER*)realloc(*ppevent, pv->size()); // shrink memory allocation
10715 assert(*ppevent != NULL);
10716 memcpy(*ppevent, pv->data(), pv->size());
10717 }
10718 } else if (pvec) {
10719 // nothing to do
10720 } else {
10721 assert(!"incorrect call to bm_receivent_event_rpc()");
10722 }
10723
10724 if (!pvec)
10725 delete pv;
10726
10727 return status;
10728}
10729
10730/********************************************************************/
10789INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10790 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10791 if (rpc_is_remote()) {
10792 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10793 }
10794#ifdef LOCAL_ROUTINES
10795 {
10797
10798 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10799
10800 if (!pbuf)
10801 return status;
10802
10803 int convert_flags = rpc_get_convert_flags();
10804
10805 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10806 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10807 return status;
10808 }
10809#else /* LOCAL_ROUTINES */
10810
10811 return BM_SUCCESS;
10812#endif
10813}
10814
10815/********************************************************************/
10870INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
10871 if (rpc_is_remote()) {
10872 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10873 }
10874#ifdef LOCAL_ROUTINES
10875 {
10877
10878 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10879
10880 if (!pbuf)
10881 return status;
10882
10883 int convert_flags = rpc_get_convert_flags();
10884
10885 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10886 }
10887#else /* LOCAL_ROUTINES */
10888
10889 return BM_SUCCESS;
10890#endif
10891}
10892
10893/********************************************************************/
10948INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10949 if (rpc_is_remote()) {
10950 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10951 }
10952#ifdef LOCAL_ROUTINES
10953 {
10955
10956 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10957
10958 if (!pbuf)
10959 return status;
10960
10961 int convert_flags = rpc_get_convert_flags();
10962
10963 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10964 }
10965#else /* LOCAL_ROUTINES */
10966 return BM_SUCCESS;
10967#endif
10968}
10969
10970#ifdef LOCAL_ROUTINES
10971
10972static int bm_skip_event(BUFFER* pbuf)
10973{
10974 /* clear read cache */
10975 if (pbuf->read_cache_size > 0) {
10976
10978
10979 if (status != BM_SUCCESS)
10980 return status;
10981
10982 pbuf->read_cache_rp = 0;
10983 pbuf->read_cache_wp = 0;
10984
10985 pbuf->read_cache_mutex.unlock();
10986 }
10987
10988 bm_lock_buffer_guard pbuf_guard(pbuf);
10989
10990 if (!pbuf_guard.is_locked())
10991 return pbuf_guard.get_status();
10992
10993 BUFFER_HEADER *pheader = pbuf->buffer_header;
10994
10995 /* forward read pointer to global write pointer */
10996 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
10997 pclient->read_pointer = pheader->write_pointer;
10998
10999 return BM_SUCCESS;
11000}
11001
11002#endif /* LOCAL_ROUTINES */
11003
11004/********************************************************************/
11013INT bm_skip_event(INT buffer_handle) {
11014 if (rpc_is_remote())
11015 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
11016
11017#ifdef LOCAL_ROUTINES
11018 {
11019 int status = 0;
11020
11021 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
11022
11023 if (!pbuf)
11024 return status;
11025
11026 return bm_skip_event(pbuf);
11027 }
11028#endif
11029
11030 return BM_SUCCESS;
11031}
11032
11033#ifdef LOCAL_ROUTINES
11034/********************************************************************/
11041static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
11042 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
11043
11044 /* return immediately if no callback routine is defined */
11045 if (!pbuf->callback)
11046 return BM_SUCCESS;
11047
11048 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
11049}
11050
11051/********************************************************************/
11057static INT bm_push_event(const char *buffer_name)
11058{
11059 std::vector<BUFFER*> mybuffers;
11060
11061 gBuffersMutex.lock();
11062 mybuffers = gBuffers;
11063 gBuffersMutex.unlock();
11064
11065 for (size_t i = 0; i < mybuffers.size(); i++) {
11066 BUFFER *pbuf = mybuffers[i];
11067 if (!pbuf || !pbuf->attached)
11068 continue;
11069 // FIXME: unlocked read access to pbuf->buffer_name!
11070 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
11071 return bm_push_buffer(pbuf, i + 1);
11072 }
11073 }
11074
11075 return BM_INVALID_HANDLE;
11076}
11077
11078#else
11079
11080static INT bm_push_event(const char *buffer_name)
11081{
11082 return BM_SUCCESS;
11083}
11084
11085#endif /* LOCAL_ROUTINES */
11086
11087/********************************************************************/
11094#ifdef LOCAL_ROUTINES
11095 {
11096 INT status = 0;
11097 BOOL bMore;
11098 DWORD start_time;
11099 //static DWORD last_time = 0;
11100
11101 /* if running as a server, buffer checking is done by client
11102 via ASYNC bm_receive_event */
11103 if (rpc_is_mserver()) {
11104 return FALSE;
11105 }
11106
11107 bMore = FALSE;
11108 start_time = ss_millitime();
11109
11110 std::vector<BUFFER*> mybuffers;
11111
11112 gBuffersMutex.lock();
11113 mybuffers = gBuffers;
11114 gBuffersMutex.unlock();
11115
11116 /* go through all buffers */
11117 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11118 BUFFER* pbuf = mybuffers[idx];
11119
11120 if (!pbuf || !pbuf->attached)
11121 continue;
11122
11123 //int count_loops = 0;
11124 while (1) {
11125 if (pbuf->attached) {
11126 /* one bm_push_event could cause a run stop and a buffer close, which
11127 * would crash the next call to bm_push_event(). So check for valid
11128 * buffer on each call */
11129
11130 /* this is what happens:
11131 * bm_push_buffer() may call a user callback function
11132 * user callback function may indirectly call bm_close() of this buffer,
11133 * i.e. if it stops the run,
11134 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11135 * here we will see pbuf->attched is false and quit this loop
11136 */
11137
11138 status = bm_push_buffer(pbuf, idx + 1);
11139
11140 if (status == BM_CORRUPTED) {
11141 return status;
11142 }
11143
11144 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11145
11146 if (status != BM_MORE_EVENTS) {
11147 //DWORD t = ss_millitime() - start_time;
11148 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11149 break;
11150 }
11151
11152 // count_loops++;
11153 }
11154
11155 // NB: this code has a logic error: if 2 buffers always have data,
11156 // this timeout will cause us to exit reading the 1st buffer
11157 // after 1000 msec, then we read the 2nd buffer exactly once,
11158 // and exit the loop because the timeout is still active -
11159 // we did not reset "start_time" when we started reading
11160 // from the 2nd buffer. Result is that we always read all
11161 // the data in a loop from the 1st buffer, but read just
11162 // one event from the 2nd buffer, resulting in severe unfairness.
11163
11164 /* stop after one second */
11165 DWORD t = ss_millitime() - start_time;
11166 if (t > 1000) {
11167 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11168 bMore = TRUE;
11169 break;
11170 }
11171 }
11172 }
11173
11174 //last_time = start_time;
11175
11176 return bMore;
11177
11178 }
11179#else /* LOCAL_ROUTINES */
11180
11181 return FALSE;
11182
11183#endif
11184}
11185
11186/********************************************************************/
11187static INT bm_notify_client(const char *buffer_name, int client_socket)
11188/********************************************************************\
11189
11190 Routine: bm_notify_client
11191
11192 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11193 the connected client. Used by mserver to relay the BM_MSG
11194 buffer message from local UDP socket to the remote connected client.
11195
11196 Input:
11197 char *buffer_name Name of buffer
11198 int client_socket Network socket to client
11199
11200 Output:
11201 none
11202
11203 Function value:
11204 BM_SUCCESS Successful completion
11205
11206\********************************************************************/
11207{
11208 static DWORD last_time = 0;
11209 DWORD now = ss_millitime();
11210
11211 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11212
11213 BUFFER* fbuf = NULL;
11214
11215 gBuffersMutex.lock();
11216
11217 for (size_t i = 0; i < gBuffers.size(); i++) {
11218 BUFFER* pbuf = gBuffers[i];
11219 if (!pbuf || !pbuf->attached)
11220 continue;
11221 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11222 fbuf = pbuf;
11223 break;
11224 }
11225 }
11226
11227 gBuffersMutex.unlock();
11228
11229 if (!fbuf)
11230 return BM_INVALID_HANDLE;
11231
11232 /* don't send notification if client has no callback defined
11233 to receive events -> client calls bm_receive_event manually */
11234 if (!fbuf->callback)
11235 return DB_SUCCESS;
11236
11237 int convert_flags = rpc_get_convert_flags();
11238
11239 /* only send notification once each 500ms */
11240 if (now - last_time < 500)
11241 return DB_SUCCESS;
11242
11243 last_time = now;
11244
11245 char buffer[32];
11246 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11247
11248 nc->header.routine_id = MSG_BM;
11249 nc->header.param_size = 0;
11250
11251 if (convert_flags) {
11254 }
11255
11256 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11257
11258 /* send the update notification to the client */
11259 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11260
11261 return BM_SUCCESS;
11262}
11263
11264/********************************************************************/
11266/********************************************************************\
11267
11268 Routine: bm_poll_event
11269
11270 Purpose: Poll an event from a remote server. Gets called by
11271 rpc_client_dispatch() and by cm_yield()
11272
11273 Function value:
11274 BM_SUCCESS At least one event was received and dispatched
11275 BM_ASYNC_RETURN No events received
11276 SS_ABORT Network connection broken
11277
11278\********************************************************************/
11279{
11280 BOOL dispatched_something = FALSE;
11281
11282 //printf("bm_poll_event!\n");
11283
11284 DWORD start_time = ss_millitime();
11285
11286 std::vector<char> vec;
11287
11288 /* loop over all requests */
11289 _request_list_mutex.lock();
11290 bool locked = true;
11291 size_t n = _request_list.size();
11292 for (size_t i = 0; i < n; i++) {
11293 if (!locked) {
11294 _request_list_mutex.lock();
11295 locked = true;
11296 }
11297 /* continue if no dispatcher set (manual bm_receive_event) */
11298 if (_request_list[i].dispatcher == NULL)
11299 continue;
11300
11301 int buffer_handle = _request_list[i].buffer_handle;
11302
11303 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11304 _request_list_mutex.unlock();
11305 locked = false;
11306
11307 do {
11308 /* receive event */
11309 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11310
11311 //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());
11312
11313 /* call user function if successful */
11314 if (status == BM_SUCCESS) {
11315 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11316 dispatched_something = TRUE;
11317 }
11318
11319 /* break if no more events */
11320 if (status == BM_ASYNC_RETURN)
11321 break;
11322
11323 /* break if corrupted event buffer */
11324 if (status == BM_TRUNCATED) {
11325 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());
11326 }
11327
11328 /* break if corrupted event buffer */
11329 if (status == BM_CORRUPTED)
11330 return SS_ABORT;
11331
11332 /* break if server died */
11333 if (status == RPC_NET_ERROR) {
11334 return SS_ABORT;
11335 }
11336
11337 /* stop after one second */
11338 if (ss_millitime() - start_time > 1000) {
11339 break;
11340 }
11341
11342 } while (TRUE);
11343 }
11344
11345 if (locked)
11346 _request_list_mutex.unlock();
11347
11348 if (dispatched_something)
11349 return BM_SUCCESS;
11350 else
11351 return BM_ASYNC_RETURN;
11352}
11353
11354/********************************************************************/
11380 if (rpc_is_remote())
11382
11383#ifdef LOCAL_ROUTINES
11384 {
11385 std::vector<BUFFER*> mybuffers;
11386
11387 gBuffersMutex.lock();
11388 mybuffers = gBuffers;
11389 gBuffersMutex.unlock();
11390
11391 /* go through all buffers */
11392 for (BUFFER* pbuf : mybuffers) {
11393 if (!pbuf)
11394 continue;
11395 if (!pbuf->attached)
11396 continue;
11397
11398 int status = bm_skip_event(pbuf);
11399 if (status != BM_SUCCESS)
11400 return status;
11401 }
11402 }
11403#endif /* LOCAL_ROUTINES */
11404
11405 return BM_SUCCESS;
11406}
11407
11409#ifndef DOXYGEN_SHOULD_SKIP_THIS
11410
11411#define MAX_DEFRAG_EVENTS 10
11412
11419
11421
11422/********************************************************************/
11423static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11424 EVENT_HEADER *pevent, void *pdata,
11425 EVENT_HANDLER *dispatcher)
11426/********************************************************************\
11427
11428 Routine: bm_defragment_event
11429
11430 Purpose: Called internally from the event receiving routines
11431 bm_push_event and bm_poll_event to recombine event
11432 fragments and call the user callback routine upon
11433 completion.
11434
11435 Input:
11436 HNDLE buffer_handle Handle for the buffer containing event
11437 HNDLE request_id Handle for event request
11438 EVENT_HEADER *pevent Pointer to event header
11439 void *pata Pointer to event data
11440 dispatcher() User callback routine
11441
11442 Output:
11443 <calls dispatcher() after successfull recombination of event>
11444
11445 Function value:
11446 void
11447
11448\********************************************************************/
11449{
11450 INT i;
11451
11452 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11453 /*---- start new event ----*/
11454
11455 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11456
11457 /* check if fragments already stored */
11458 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11459 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11460 break;
11461
11462 if (i < MAX_DEFRAG_EVENTS) {
11463 free(defrag_buffer[i].pevent);
11464 defrag_buffer[i].pevent = NULL;
11465 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11466 cm_msg(MERROR, "bm_defragement_event",
11467 "Received new event with ID %d while old fragments were not completed",
11468 (pevent->event_id & 0x0FFF));
11469 }
11470
11471 /* search new slot */
11472 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11473 if (defrag_buffer[i].event_id == 0)
11474 break;
11475
11476 if (i == MAX_DEFRAG_EVENTS) {
11477 cm_msg(MERROR, "bm_defragment_event",
11478 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11479 return;
11480 }
11481
11482 /* check event size */
11483 if (pevent->data_size != sizeof(DWORD)) {
11484 cm_msg(MERROR, "bm_defragment_event",
11485 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11486 pevent->data_size, (int) sizeof(DWORD));
11487 return;
11488 }
11489
11490 /* setup defragment buffer */
11491 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11492 defrag_buffer[i].data_size = *(DWORD *) pdata;
11495
11496 if (defrag_buffer[i].pevent == NULL) {
11497 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11498 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11499 return;
11500 }
11501
11502 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11505
11506 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11507 // pevent->serial_number, defrag_buffer[i].data_size);
11508
11509 return;
11510 }
11511
11512 /* search buffer for that event */
11513 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11514 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11515 break;
11516
11517 if (i == MAX_DEFRAG_EVENTS) {
11518 /* no buffer available -> no first fragment received */
11519 cm_msg(MERROR, "bm_defragement_event",
11520 "Received fragment without first fragment (ID %d) Ser#:%d",
11521 pevent->event_id & 0x0FFF, pevent->serial_number);
11522 return;
11523 }
11524
11525 /* add fragment to buffer */
11527 free(defrag_buffer[i].pevent);
11528 defrag_buffer[i].pevent = NULL;
11529 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11530 cm_msg(MERROR, "bm_defragement_event",
11531 "Received fragments with more data (%d) than event size (%d)",
11532 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11533 return;
11534 }
11535
11536 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11537 defrag_buffer[i].received, pdata, pevent->data_size);
11538
11539 defrag_buffer[i].received += pevent->data_size;
11540
11541 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11542 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11543
11544 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11545 /* event complete */
11546 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11547 free(defrag_buffer[i].pevent);
11548 defrag_buffer[i].pevent = NULL;
11549 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11550 }
11551}
11552
11554#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11555
/* end of bmfunctionc */
11558
11564/********************************************************************\
11565* *
11566* RPC functions *
11567* *
11568\********************************************************************/
11569
11571{
11572public:
11573 std::atomic_bool connected{false}; /* socket is connected */
11574 std::string host_name; /* server name */
11575 int port = 0; /* server port */
11576 std::mutex mutex; /* connection lock */
11577 int index = 0; /* index in the connection array */
11578 std::string client_name; /* name of remote client */
11579 int send_sock = 0; /* tcp socket */
11580 int remote_hw_type = 0; /* remote hardware type */
11581 int rpc_timeout = 0; /* timeout in milliseconds */
11582
11583 void print() {
11584 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11585 index,
11586 client_name.c_str(),
11587 host_name.c_str(),
11588 port,
11589 send_sock,
11590 int(connected),
11591 rpc_timeout);
11592 }
11593
11595 if (send_sock > 0) {
11597 }
11598 connected = false;
11599 }
11600};
11601
11602/* globals */
11603
11604//
11605// locking rules for client connections:
11606//
11607// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11608// 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
11609// lock individual connection, check c->connected, work on the connection, unlock connection
11610//
11611// ok to access without locking client connection:
11612//
11613// - c->connected (std::atomic, but must recheck it after taking the lock)
11614// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11615//
11616// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11617// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11618//
11619// lifetime of client connections:
11620//
11621// - client connection slots are allocated by rpc_client_connect()
11622// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11623// - client slots marked NULL are free and will be reused by rpc_client_connect()
11624// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11625// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11626// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11627// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11628// client call will see an empty slot and return an error
11629// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11630// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11631// only when one is sure no other threads are running concurrent rpc client calls.
11632//
11633
11635static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11636
11637static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11638static bool _rpc_is_remote = false;
11639
11640//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11641static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11642static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11643
11648
11650{
11651 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11652 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11653 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11654 return _server_acceptions[idx];
11655 }
11656 }
11657
11659
11660 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11661 if (_server_acceptions[idx] == NULL) {
11662 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11663 _server_acceptions[idx] = sa;
11664 return _server_acceptions[idx];
11665 }
11666 }
11667
11668 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11669 _server_acceptions.push_back(sa);
11670
11671 return sa;
11672}
11673
11675{
11676 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11677
11678 if (is_mserver) {
11679 assert(_mserver_acception == this);
11680 _mserver_acception = NULL;
11681 is_mserver = false;
11682 }
11683
11684 /* close server connection */
11685 if (recv_sock)
11687 if (send_sock)
11689 if (event_sock)
11691
11692 /* free TCP cache */
11693 if (net_buffer) {
11694 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11695 free(net_buffer);
11696 net_buffer = NULL;
11697 net_buffer_size = 0;
11698 }
11699
11700 /* mark this entry as invalid */
11701 clear();
11702}
11703
11704static std::vector<RPC_LIST> rpc_list;
11705static std::mutex rpc_list_mutex;
11706
11708
11709
11710/********************************************************************\
11711* conversion functions *
11712\********************************************************************/
11713
11714void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11715 *convert_flags = 0;
11716
11717 /* big/little endian conversion */
11718 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11719 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11720 && (hw_type & DRI_BIG_ENDIAN)))
11721 *convert_flags |= CF_ENDIAN;
11722
11723 /* float conversion between IEEE and VAX G */
11724 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11725 *convert_flags |= CF_VAX2IEEE;
11726
11727 /* float conversion between VAX G and IEEE */
11728 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11729 *convert_flags |= CF_IEEE2VAX;
11730
11732 //if (remote_hw_type & DR_ASCII)
11733 // *convert_flags |= CF_ASCII;
11734}
11735
11736/********************************************************************/
11740
11741/********************************************************************/
11742void rpc_ieee2vax_float(float *var) {
11743 unsigned short int lo, hi;
11744
11745 /* swap hi and lo word */
11746 lo = *((short int *) (var) + 1);
11747 hi = *((short int *) (var));
11748
11749 /* correct exponent */
11750 if (lo != 0)
11751 lo += 0x100;
11752
11753 *((short int *) (var) + 1) = hi;
11754 *((short int *) (var)) = lo;
11755}
11756
11757void rpc_vax2ieee_float(float *var) {
11758 unsigned short int lo, hi;
11759
11760 /* swap hi and lo word */
11761 lo = *((short int *) (var) + 1);
11762 hi = *((short int *) (var));
11763
11764 /* correct exponent */
11765 if (hi != 0)
11766 hi -= 0x100;
11767
11768 *((short int *) (var) + 1) = hi;
11769 *((short int *) (var)) = lo;
11770
11771}
11772
11773void rpc_vax2ieee_double(double *var) {
11774 unsigned short int i1, i2, i3, i4;
11775
11776 /* swap words */
11777 i1 = *((short int *) (var) + 3);
11778 i2 = *((short int *) (var) + 2);
11779 i3 = *((short int *) (var) + 1);
11780 i4 = *((short int *) (var));
11781
11782 /* correct exponent */
11783 if (i4 != 0)
11784 i4 -= 0x20;
11785
11786 *((short int *) (var) + 3) = i4;
11787 *((short int *) (var) + 2) = i3;
11788 *((short int *) (var) + 1) = i2;
11789 *((short int *) (var)) = i1;
11790}
11791
11792void rpc_ieee2vax_double(double *var) {
11793 unsigned short int i1, i2, i3, i4;
11794
11795 /* swap words */
11796 i1 = *((short int *) (var) + 3);
11797 i2 = *((short int *) (var) + 2);
11798 i3 = *((short int *) (var) + 1);
11799 i4 = *((short int *) (var));
11800
11801 /* correct exponent */
11802 if (i1 != 0)
11803 i1 += 0x20;
11804
11805 *((short int *) (var) + 3) = i4;
11806 *((short int *) (var) + 2) = i3;
11807 *((short int *) (var) + 1) = i2;
11808 *((short int *) (var)) = i1;
11809}
11810
11811/********************************************************************/
11812void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11813
11814 if (convert_flags & CF_ENDIAN) {
11815 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11816 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11817 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11818 }
11819
11820 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11821 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11822 if (tid == TID_FLOAT)
11823 rpc_ieee2vax_float((float *) data);
11824 if (tid == TID_DOUBLE)
11825 rpc_ieee2vax_double((double *) data);
11826 }
11827
11828 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11829 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11830 if (tid == TID_FLOAT)
11831 rpc_vax2ieee_float((float *) data);
11832 if (tid == TID_DOUBLE)
11833 rpc_vax2ieee_double((double *) data);
11834 }
11835}
11836
11837void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11838/********************************************************************\
11839
11840 Routine: rpc_convert_data
11841
11842 Purpose: Convert data format between differenct computers
11843
11844 Input:
11845 void *data Pointer to data
11846 INT tid Type ID of data, one of TID_xxx
11847 INT flags Combination of following flags:
11848 RPC_IN: data is input parameter
11849 RPC_OUT: data is output variable
11850 RPC_FIXARRAY, RPC_VARARRAY: data is array
11851 of "size" bytes (see next param.)
11852 RPC_OUTGOING: data is outgoing
11853 INT total_size Size of bytes of data. Used for variable
11854 length arrays.
11855 INT convert_flags Flags for data conversion
11856
11857 Output:
11858 void *data Is converted according to _convert_flag
11859 value
11860
11861 Function value:
11862 RPC_SUCCESS Successful completion
11863
11864\********************************************************************/
11865{
11866 /* convert array */
11867 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11868 int single_size = rpc_tid_size(tid);
11869 /* don't convert TID_ARRAY & TID_STRUCT */
11870 if (single_size == 0)
11871 return;
11872
11873 int n = total_size / single_size;
11874
11875 for (int i = 0; i < n; i++) {
11876 char* p = (char *) data + (i * single_size);
11877 rpc_convert_single(p, tid, flags, convert_flags);
11878 }
11879 } else {
11880 rpc_convert_single(data, tid, flags, convert_flags);
11881 }
11882}
11883
11884/********************************************************************\
11885* type ID functions *
11886\********************************************************************/
11887
11889 if (id >= 0 && id < TID_LAST)
11890 return tid_size[id];
11891
11892 return 0;
11893}
11894
11895const char *rpc_tid_name(INT id) {
11896 if (id >= 0 && id < TID_LAST)
11897 return tid_name[id];
11898 else
11899 return "<unknown>";
11900}
11901
11902const char *rpc_tid_name_old(INT id) {
11903 if (id >= 0 && id < TID_LAST)
11904 return tid_name_old[id];
11905 else
11906 return "<unknown>";
11907}
11908
11909int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11910{
11911 for (int i=0; i<TID_LAST; i++) {
11912 if (strcmp(name, tid_name[i]) == 0)
11913 return i;
11914 }
11915
11916 for (int i=0; i<TID_LAST; i++) {
11917 if (strcmp(name, tid_name_old[i]) == 0)
11918 return i;
11919 }
11920
11921 return 0;
11922}
11923
11924/********************************************************************\
11925* client functions *
11926\********************************************************************/
11927
11928/********************************************************************/
11943
11944 return RPC_SUCCESS;
11945}
11946
11947/********************************************************************/
11959{
11960 for (int i = 0; new_list[i].id != 0; i++) {
11961 /* check valid ID for user functions */
11962 if (new_list != rpc_get_internal_list(0) &&
11963 new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
11964 || new_list[i].id > RPC_MAX_ID)) {
11965 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11966 }
11967 }
11968
11969 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11970
11971 /* check double defined functions */
11972 for (int i = 0; new_list[i].id != 0; i++) {
11973 for (size_t j = 0; j < rpc_list.size(); j++) {
11974 if (rpc_list[j].id == new_list[i].id) {
11975 return RPC_DOUBLE_DEFINED;
11976 }
11977 }
11978 }
11979
11980 /* append new functions */
11981 for (int i = 0; new_list[i].id != 0; i++) {
11982 RPC_LIST e = new_list[i];
11983
11984 /* set default dispatcher */
11985 if (e.dispatch == NULL) {
11986 e.dispatch = func;
11987 }
11988
11989 rpc_list.push_back(e);
11990 }
11991
11992 return RPC_SUCCESS;
11993}
11994
11995
11996
11998#ifndef DOXYGEN_SHOULD_SKIP_THIS
11999
12000/********************************************************************/
12002/********************************************************************\
12003
12004 Routine: rpc_deregister_functions
12005
12006 Purpose: Free memory of previously registered functions
12007
12008 Input:
12009 none
12010
12011 Output:
12012 none
12013
12014 Function value:
12015 RPC_SUCCESS Successful completion
12016
12017\********************************************************************/
12018{
12019 rpc_list_mutex.lock();
12020 rpc_list.clear();
12021 rpc_list_mutex.unlock();
12022
12023 return RPC_SUCCESS;
12024}
12025
12026
12027/********************************************************************/
12028INT rpc_register_function(INT id, INT(*func)(INT, void **))
12029/********************************************************************\
12030
12031 Routine: rpc_register_function
12032
12033 Purpose: Replace a dispatch function for a specific rpc routine
12034
12035 Input:
12036 INT id RPC ID
12037 INT *func New dispatch function
12038
12039 Output:
12040 <implicit: func gets copied to rpc_list>
12041
12042 Function value:
12043 RPC_SUCCESS Successful completion
12044 RPC_INVALID_ID RPC ID not found
12045
12046\********************************************************************/
12047{
12048 std::lock_guard<std::mutex> guard(rpc_list_mutex);
12049
12050 for (size_t i = 0; i < rpc_list.size(); i++) {
12051 if (rpc_list[i].id == id) {
12052 rpc_list[i].dispatch = func;
12053 return RPC_SUCCESS;
12054 }
12055 }
12056
12057 return RPC_INVALID_ID;
12058}
12059
12060/********************************************************************/
12061
12062static int handle_msg_odb(int n, const NET_COMMAND *nc) {
12063 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
12064 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
12065 /* update a changed record */
12066 HNDLE hDB = *((INT *) nc->param);
12067 HNDLE hKeyRoot = *((INT *) nc->param + 1);
12068 HNDLE hKey = *((INT *) nc->param + 2);
12069 int index = *((INT *) nc->param + 3);
12070 return db_update_record_local(hDB, hKeyRoot, hKey, index);
12071 }
12072 return CM_VERSION_MISMATCH;
12073}
12074
12075/********************************************************************/
12077/********************************************************************\
12078
12079 Routine: rpc_client_dispatch
12080
12081 Purpose: Receive data from the mserver: watchdog and buffer notification messages
12082
12083\********************************************************************/
12084{
12085 INT status = 0;
12086 char net_buffer[256];
12087
12088 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12089 if (n <= 0)
12090 return SS_ABORT;
12091
12092 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
12093
12094 if (nc->header.routine_id == MSG_ODB) {
12095 status = handle_msg_odb(n, nc);
12096 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12097 nc->header.routine_id = 1;
12098 nc->header.param_size = 0;
12099 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12101 } else if (nc->header.routine_id == MSG_BM) {
12102 fd_set readfds;
12103 struct timeval timeout;
12104
12105 //printf("rpc_client_dispatch: received MSG_BM!\n");
12106
12107 /* receive further messages to empty TCP queue */
12108 do {
12109 FD_ZERO(&readfds);
12110 FD_SET(sock, &readfds);
12111
12112 timeout.tv_sec = 0;
12113 timeout.tv_usec = 0;
12114
12115 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12116
12117 if (FD_ISSET(sock, &readfds)) {
12118 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12119 if (n <= 0)
12120 return SS_ABORT;
12121
12122 if (nc->header.routine_id == MSG_ODB) {
12123 status = handle_msg_odb(n, nc);
12124 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12125 nc->header.routine_id = 1;
12126 nc->header.param_size = 0;
12127 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12129 }
12130 }
12131
12132 } while (FD_ISSET(sock, &readfds));
12133
12134 /* poll event from server */
12136 }
12137
12138 return status;
12139}
12140
12141
12142/********************************************************************/
12143INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12144/********************************************************************\
12145
12146 Routine: rpc_client_connect
12147
12148 Purpose: Establish a network connection to a remote client
12149
12150 Input:
12151 char *host_name IP address of host to connect to.
12152 INT port TPC port to connect to.
12153 char *clinet_name Client program name
12154
12155 Output:
12156 HNDLE *hConnection Handle for new connection which can be used
12157 in future rpc_call(hConnection....) calls
12158
12159 Function value:
12160 RPC_SUCCESS Successful completion
12161 RPC_NET_ERROR Error in socket call
12162 RPC_NO_CONNECTION Maximum number of connections reached
12163 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12164
12165\********************************************************************/
12166{
12167 INT i, status;
12168 bool debug = false;
12169
12170 /* check if cm_connect_experiment was called */
12171 if (_client_name.length() == 0) {
12172 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12173 return RPC_NOT_REGISTERED;
12174 }
12175
12176 /* refuse connection to port 0 */
12177 if (port == 0) {
12178 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12179 return RPC_NET_ERROR;
12180 }
12181
12182 RPC_CLIENT_CONNECTION* c = NULL;
12183
12184 static std::mutex gHostnameMutex;
12185
12186 {
12187 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12188
12189 if (debug) {
12190 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12191 for (size_t i = 0; i < _client_connections.size(); i++) {
12192 if (_client_connections[i]) {
12193 printf("client connection %d: ", (int)i);
12194 _client_connections[i]->print();
12195 printf("\n");
12196 }
12197 }
12198 }
12199
12200 // slot with index 0 is not used, fill it with a NULL
12201
12202 if (_client_connections.empty()) {
12203 _client_connections.push_back(NULL);
12204 }
12205
12206 bool hostname_locked = false;
12207
12208 /* check if connection already exists */
12209 for (size_t i = 1; i < _client_connections.size(); i++) {
12211 if (c && c->connected) {
12212
12213 if (!hostname_locked) {
12214 gHostnameMutex.lock();
12215 hostname_locked = true;
12216 }
12217
12218 if ((c->host_name == host_name) && (c->port == port)) {
12219 // NB: we must release the hostname lock before taking
12220 // c->mutex to avoid a locking order inversion deadlock:
12221 // later on we lock the hostname mutex while holding the c->mutex
12222 gHostnameMutex.unlock();
12223 hostname_locked = false;
12224 std::lock_guard<std::mutex> cguard(c->mutex);
12225 // check if socket is still connected
12226 if (c->connected) {
12227 // found connection slot with matching hostname and port number
12228 status = ss_socket_wait(c->send_sock, 0);
12229 if (status == SS_TIMEOUT) { // yes, still connected and empty
12230 // so reuse it connection
12231 *hConnection = c->index;
12232 if (debug) {
12233 printf("already connected: ");
12234 c->print();
12235 printf("\n");
12236 }
12237 // implicit unlock of c->mutex
12238 // gHostnameLock is not locked here
12239 return RPC_SUCCESS;
12240 }
12241 //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);
12242 c->close_locked();
12243 }
12244 // implicit unlock of c->mutex
12245 }
12246 }
12247 }
12248
12249 if (hostname_locked) {
12250 gHostnameMutex.unlock();
12251 hostname_locked = false;
12252 }
12253
12254 // only start reusing connections once we have
12255 // a good number of slots allocated.
12256 if (_client_connections.size() > 10) {
12257 static int last_reused = 0;
12258
12259 int size = _client_connections.size();
12260 for (int j = 1; j < size; j++) {
12261 int i = (last_reused + j) % size;
12262 if (_client_connections[i] && !_client_connections[i]->connected) {
12264 if (debug) {
12265 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12266 c->print();
12267 printf("\n");
12268 }
12269 last_reused = i;
12270 break;
12271 }
12272 }
12273 }
12274
12275 // no slots to reuse, allocate a new slot.
12276 if (!c) {
12278
12279 // if empty slot not found, add to end of array
12280 c->index = _client_connections.size();
12281 _client_connections.push_back(c);
12282
12283 if (debug) {
12284 printf("new connection appended to array: ");
12285 c->print();
12286 printf("\n");
12287 }
12288 }
12289
12290 c->mutex.lock();
12291 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12292
12293 // done with the array of connections
12294 // implicit unlock of _client_connections_mutex
12295 }
12296
12297 // locked connection slot for new connection
12298 assert(c != NULL);
12299
12300 std::string errmsg;
12301
12302 /* create a new socket for connecting to remote server */
12303 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12304 if (status != SS_SUCCESS) {
12305 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12306 c->mutex.unlock();
12307 return RPC_NET_ERROR;
12308 }
12309
12310 gHostnameMutex.lock();
12311
12312 c->host_name = host_name;
12313 c->port = port;
12314
12315 gHostnameMutex.unlock();
12316
12317 c->client_name = client_name;
12318 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12319
12320 /* set TCP_NODELAY option for better performance */
12321 i = 1;
12322 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12323
12324 /* send local computer info */
12325 std::string local_prog_name = rpc_get_name();
12326 std::string local_host_name = ss_gethostname();
12327
12328 int hw_type = rpc_get_hw_type();
12329
12330 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12331
12332 int size = cstr.length() + 1;
12333 i = send(c->send_sock, cstr.c_str(), size, 0);
12334 if (i < 0 || i != size) {
12335 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12336 c->mutex.unlock();
12337 return RPC_NET_ERROR;
12338 }
12339
12340 bool restore_watchdog_timeout = false;
12341 BOOL watchdog_call;
12342 DWORD watchdog_timeout;
12343 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12344
12345 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12346
12347 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12348 restore_watchdog_timeout = true;
12349 cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
12350 }
12351
12352 char str[256];
12353
12354 /* receive remote computer info */
12355 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12356
12357 if (restore_watchdog_timeout) {
12358 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12359 }
12360
12361 if (i <= 0) {
12362 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12363 c->close_locked();
12364 c->mutex.unlock();
12365 return RPC_NET_ERROR;
12366 }
12367
12368 int remote_hw_type = 0;
12369 char remote_version[32];
12370 remote_version[0] = 0;
12371 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12372
12373 c->remote_hw_type = remote_hw_type;
12374
12375 /* print warning if version patch level doesn't agree */
12376 char v1[32];
12377 mstrlcpy(v1, remote_version, sizeof(v1));
12378 if (strchr(v1, '.'))
12379 if (strchr(strchr(v1, '.') + 1, '.'))
12380 *strchr(strchr(v1, '.') + 1, '.') = 0;
12381
12382 mstrlcpy(str, cm_get_version(), sizeof(str));
12383 if (strchr(str, '.'))
12384 if (strchr(strchr(str, '.') + 1, '.'))
12385 *strchr(strchr(str, '.') + 1, '.') = 0;
12386
12387 if (strcmp(v1, str) != 0) {
12388 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12389 }
12390
12391 c->connected = true;
12392
12393 *hConnection = c->index;
12394
12395 c->mutex.unlock();
12396
12397 return RPC_SUCCESS;
12398}
12399
12400/********************************************************************/
12402/********************************************************************\
12403
12404 Routine: rpc_client_check
12405
12406 Purpose: Check all client connections if remote client closed link
12407
12408\********************************************************************/
12409{
12410#if 0
12411 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12412 if (_client_connection[i].send_sock != 0)
12413 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);
12414#endif
12415
12416 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12417
12418 /* check for broken connections */
12419 for (unsigned i = 0; i < _client_connections.size(); i++) {
12421 if (c && c->connected) {
12422 std::lock_guard<std::mutex> cguard(c->mutex);
12423
12424 if (!c->connected) {
12425 // implicit unlock
12426 continue;
12427 }
12428
12429 //printf("rpc_client_check: connection %d: ", i);
12430 //c->print();
12431 //printf("\n");
12432
12433 int ok = 0;
12434
12435 fd_set readfds;
12436 FD_ZERO(&readfds);
12437 FD_SET(c->send_sock, &readfds);
12438
12439 struct timeval timeout;
12440 timeout.tv_sec = 0;
12441 timeout.tv_usec = 0;
12442
12443 int status;
12444
12445#ifdef OS_WINNT
12446 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12447#else
12448 do {
12449 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12450 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12451#endif
12452
12453 if (!FD_ISSET(c->send_sock, &readfds)) {
12454 // implicit unlock
12455 continue;
12456 }
12457
12458 char buffer[64];
12459
12460 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12461 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12462
12463 if (status < 0) {
12464#ifndef OS_WINNT
12465 if (errno == EAGAIN) { // still connected
12466 ok = 1;
12467 } else
12468#endif
12469 {
12470 // connection error
12471 cm_msg(MERROR, "rpc_client_check",
12472 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12473 c->client_name.c_str(),
12474 c->host_name.c_str(),
12475 errno, strerror(errno));
12476 ok = 0;
12477 }
12478 } else if (status == 0) {
12479 // connection closed by remote end without sending an EXIT message
12480 // this can happen if the remote end has crashed, so this message
12481 // is still necessary as a useful diagnostic for unexpected crashes
12482 // of midas programs. K.O.
12483 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());
12484 ok = 0;
12485 } else {
12486 // read some data
12487 ok = 1;
12488 if (equal_ustring(buffer, "EXIT")) {
12489 /* normal exit */
12490 ok = 0;
12491 }
12492 }
12493
12494 if (!ok) {
12495 //printf("rpc_client_check: closing connection %d: ", i);
12496 //c->print();
12497 //printf("\n");
12498
12499 // connection lost, close the socket
12500 c->close_locked();
12501 }
12502
12503 // implicit unlock
12504 }
12505 }
12506
12507 // implicit unlock of _client_connections_mutex
12508}
12509
12510
12511/********************************************************************/
12512INT rpc_server_connect(const char *host_name, const char *exp_name)
12513/********************************************************************\
12514
12515 Routine: rpc_server_connect
12516
12517 Purpose: Extablish a network connection to a remote MIDAS
12518 server using a callback scheme.
12519
12520 Input:
12521 char *host_name IP address of host to connect to.
12522
12523 INT port TPC port to connect to.
12524
12525 char *exp_name Name of experiment to connect to. By using
12526 this name, several experiments (e.g. online
12527 DAQ and offline analysis) can run simultan-
12528 eously on the same host.
12529
12530 Output:
12531 none
12532
12533 Function value:
12534 RPC_SUCCESS Successful completion
12535 RPC_NET_ERROR Error in socket call
12536 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12537 CM_UNDEF_EXP Undefined experiment on server
12538
12539\********************************************************************/
12540{
12541 INT i, status;
12542 INT remote_hw_type, hw_type;
12543 char str[200], version[32], v1[32];
12544 fd_set readfds;
12545 struct timeval timeout;
12546 int port = MIDAS_TCP_PORT;
12547 char *s;
12548
12549#ifdef OS_WINNT
12550 {
12551 WSADATA WSAData;
12552
12553 /* Start windows sockets */
12554 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12555 return RPC_NET_ERROR;
12556 }
12557#endif
12558
12559 /* check if local connection */
12560 if (host_name[0] == 0)
12561 return RPC_SUCCESS;
12562
12563 /* register system functions */
12565
12566 /* check if cm_connect_experiment was called */
12567 if (_client_name.length() == 0) {
12568 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12569 return RPC_NOT_REGISTERED;
12570 }
12571
12572 /* check if connection already exists */
12574 return RPC_SUCCESS;
12575
12579
12580 bool listen_localhost = false;
12581
12582 if (strcmp(host_name, "localhost") == 0)
12583 listen_localhost = true;
12584
12585 int lsock1, lport1;
12586 int lsock2, lport2;
12587 int lsock3, lport3;
12588
12589 std::string errmsg;
12590
12591 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
12592
12593 if (status != SS_SUCCESS) {
12594 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12595 return RPC_NET_ERROR;
12596 }
12597
12598 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
12599
12600 if (status != SS_SUCCESS) {
12601 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12602 return RPC_NET_ERROR;
12603 }
12604
12605 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
12606
12607 if (status != SS_SUCCESS) {
12608 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12609 return RPC_NET_ERROR;
12610 }
12611
12612 /* extract port number from host_name */
12613 mstrlcpy(str, host_name, sizeof(str));
12614 s = strchr(str, ':');
12615 if (s) {
12616 *s = 0;
12617 port = strtoul(s + 1, NULL, 0);
12618 }
12619
12620 int sock;
12621
12622 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12623
12624 if (status != SS_SUCCESS) {
12625 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12626 return RPC_NET_ERROR;
12627 }
12628
12629 /* connect to experiment */
12630 if (exp_name[0] == 0)
12631 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12632 else
12633 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12634
12635 send(sock, str, strlen(str) + 1, 0);
12636 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12637 ss_socket_close(&sock);
12638 if (i <= 0) {
12639 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12640 return RPC_NET_ERROR;
12641 }
12642
12643 status = version[0] = 0;
12644 sscanf(str, "%d %s", &status, version);
12645
12646 if (status == 2) {
12647/* message "undefined experiment" should be displayed by application */
12648 return CM_UNDEF_EXP;
12649 }
12650
12651 /* print warning if version patch level doesn't agree */
12652 strcpy(v1, version);
12653 if (strchr(v1, '.'))
12654 if (strchr(strchr(v1, '.') + 1, '.'))
12655 *strchr(strchr(v1, '.') + 1, '.') = 0;
12656
12657 strcpy(str, cm_get_version());
12658 if (strchr(str, '.'))
12659 if (strchr(strchr(str, '.') + 1, '.'))
12660 *strchr(strchr(str, '.') + 1, '.') = 0;
12661
12662 if (strcmp(v1, str) != 0) {
12663 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12664 cm_get_version());
12665 }
12666
12667 /* wait for callback on send and recv socket with timeout */
12668 FD_ZERO(&readfds);
12669 FD_SET(lsock1, &readfds);
12670 FD_SET(lsock2, &readfds);
12671 FD_SET(lsock3, &readfds);
12672
12673 timeout.tv_sec = _rpc_connect_timeout / 1000;
12674 timeout.tv_usec = 0;
12675
12676 do {
12677 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12678
12679 /* if an alarm signal was cought, restart select with reduced timeout */
12680 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12681 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12682
12683 } while (status == -1); /* dont return if an alarm signal was cought */
12684
12685 if (!FD_ISSET(lsock1, &readfds)) {
12686 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12687 ss_socket_close(&lsock1);
12688 ss_socket_close(&lsock2);
12689 ss_socket_close(&lsock3);
12690 return RPC_NET_ERROR;
12691 }
12692
12693 _server_connection.send_sock = accept(lsock1, NULL, NULL);
12694 _server_connection.recv_sock = accept(lsock2, NULL, NULL);
12695 _server_connection.event_sock = accept(lsock3, NULL, NULL);
12696
12698 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12699 return RPC_NET_ERROR;
12700 }
12701
12702 ss_socket_close(&lsock1);
12703 ss_socket_close(&lsock2);
12704 ss_socket_close(&lsock3);
12705
12706 /* set TCP_NODELAY option for better performance */
12707 int flag = 1;
12708 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12709 setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12710
12711 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12712 flag = 2 * 1024 * 1024;
12713 status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
12714 if (status != 0)
12715 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12716
12717 /* send local computer info */
12718 std::string local_prog_name = rpc_get_name();
12719 hw_type = rpc_get_hw_type();
12720 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12721
12722 send(_server_connection.send_sock, str, strlen(str) + 1, 0);
12723
12724 /* receive remote computer info */
12726 if (i <= 0) {
12727 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12728 return RPC_NET_ERROR;
12729 }
12730
12731 sscanf(str, "%d", &remote_hw_type);
12732 _server_connection.remote_hw_type = remote_hw_type;
12733
12735
12736 _rpc_is_remote = true;
12737
12738 return RPC_SUCCESS;
12739}
12740
12741/********************************************************************/
12742
12744{
12746 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12748 if (c && c->connected) {
12750 c->mutex.lock();
12751 if (!c->connected) {
12752 // disconnected while we were waiting for the lock
12753 c->mutex.unlock();
12754 return NULL;
12755 }
12756 return c;
12757 }
12758 }
12760 return NULL;
12761}
12762
12764{
12765 /* close all open connections */
12766
12768
12769 for (unsigned i = 0; i < _client_connections.size(); i++) {
12771 if (c && c->connected) {
12772 int index = c->index;
12773 // must unlock the array, otherwise we hang -
12774 // rpc_client_disconnect() will do rpc_call_client()
12775 // which needs to lock the array to convert handle
12776 // to connection pointer. Ouch! K.O. Dec 2020.
12780 }
12781 }
12782
12783 for (unsigned i = 0; i < _client_connections.size(); i++) {
12785 //printf("client connection %d %p\n", i, c);
12786 if (c) {
12787 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12788 if (!c->connected) {
12789 delete c;
12790 _client_connections[i] = NULL;
12791 }
12792 }
12793 }
12794
12796
12797 /* close server connection from other clients */
12798 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12799 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12800 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12801 _server_acceptions[i]->close();
12802 }
12803 }
12804}
12805
12806/********************************************************************/
12808/********************************************************************\
12809
12810 Routine: rpc_client_disconnect
12811
12812 Purpose: Close a rpc connection to a MIDAS client
12813
12814 Input:
12815 HNDLE hConn Handle of connection
12816 BOOL bShutdown Shut down remote server if TRUE
12817
12818 Output:
12819 none
12820
12821 Function value:
12822 RPC_SUCCESS Successful completion
12823
12824\********************************************************************/
12825{
12826 /* notify server about exit */
12827
12828 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12829
12831
12832 return RPC_SUCCESS;
12833}
12834
12835/********************************************************************/
12837/********************************************************************\
12838
12839 Routine: rpc_server_disconnect
12840
12841 Purpose: Close a rpc connection to a MIDAS server and close all
12842 server connections from other clients
12843
12844 Input:
12845 none
12846
12847 Output:
12848 none
12849
12850 Function value:
12851 RPC_SUCCESS Successful completion
12852 RPC_NET_ERROR Error in socket call
12853 RPC_NO_CONNECTION Maximum number of connections reached
12854
12855\********************************************************************/
12856{
12857 static int rpc_server_disconnect_recursion_level = 0;
12858
12859 if (rpc_server_disconnect_recursion_level)
12860 return RPC_SUCCESS;
12861
12862 rpc_server_disconnect_recursion_level = 1;
12863
12864 /* flush remaining events */
12866
12867 /* notify server about exit */
12868 if (rpc_is_connected()) {
12870 }
12871
12872 /* close sockets */
12879
12881
12882 /* remove semaphore */
12883 if (_mutex_rpc)
12885 _mutex_rpc = NULL;
12886
12887 rpc_server_disconnect_recursion_level = 0;
12888 return RPC_SUCCESS;
12889}
12890
12891/********************************************************************/
12893/********************************************************************\
12894
12895 Routine: rpc_is_remote
12896
12897 Purpose: Return true if program is connected to a remote server
12898
12899 Input:
12900 none
12901
12902 Output:
12903 none
12904
12905 Function value:
12906 INT true is remote client connected to mserver, false if local connection
12907
12908\********************************************************************/
12909{
12910 return _rpc_is_remote;
12911}
12912
12913/********************************************************************/
12915/********************************************************************\
12916
12917 Routine: rpc_is_connected
12918
12919 Purpose: Return true if connection to mserver is still open
12920
12921 Input:
12922 none
12923
12924 Output:
12925 none
12926
12927 Function value:
12928 INT true if connection to mserver is still open, false if connection to mserver is already closed
12929
12930\********************************************************************/
12931{
12932 return _server_connection.send_sock != 0;
12933}
12934
12935/********************************************************************/
12937/********************************************************************\
12938
12939 Routine: rpc_get_mserver_hostname
12940
12941 Purpose: Return the hostname of the mserver connection (host:port format)
12942
12943\********************************************************************/
12944{
12946}
12947
12948/********************************************************************/
12950/********************************************************************\
12951
12952 Routine: rpc_is_mserver
12953
12954 Purpose: Return true if we are the mserver
12955
12956 Function value:
12957 INT "true" if we are the mserver
12958
12959\********************************************************************/
12960{
12961 return _mserver_acception != NULL;
12962}
12963
12964/********************************************************************/
12966/********************************************************************\
12967
12968 Routine: rpc_get_hw_type
12969
12970 Purpose: get hardware information
12971
12972 Function value:
12973 INT combination of DRI_xxx bits
12974
12975\********************************************************************/
12976{
12977 {
12978 {
12979 INT tmp_type, size;
12980 DWORD dummy;
12981 unsigned char *p;
12982 float f;
12983 double d;
12984
12985 tmp_type = 0;
12986
12987 /* test pointer size */
12988 size = sizeof(p);
12989 if (size == 2)
12990 tmp_type |= DRI_16;
12991 if (size == 4)
12992 tmp_type |= DRI_32;
12993 if (size == 8)
12994 tmp_type |= DRI_64;
12995
12996 /* test if little or big endian machine */
12997 dummy = 0x12345678;
12998 p = (unsigned char *) &dummy;
12999 if (*p == 0x78)
13000 tmp_type |= DRI_LITTLE_ENDIAN;
13001 else if (*p == 0x12)
13002 tmp_type |= DRI_BIG_ENDIAN;
13003 else
13004 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
13005
13006 /* floating point format */
13007 f = (float) 1.2345;
13008 dummy = 0;
13009 memcpy(&dummy, &f, sizeof(f));
13010 if ((dummy & 0xFF) == 0x19 &&
13011 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
13012 && ((dummy >> 24) & 0xFF) == 0x3F)
13013 tmp_type |= DRF_IEEE;
13014 else if ((dummy & 0xFF) == 0x9E &&
13015 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
13016 && ((dummy >> 24) & 0xFF) == 0x04)
13017 tmp_type |= DRF_G_FLOAT;
13018 else
13019 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13020
13021 d = (double) 1.2345;
13022 dummy = 0;
13023 memcpy(&dummy, &d, sizeof(f));
13024 if ((dummy & 0xFF) == 0x8D && /* little endian */
13025 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
13026 && ((dummy >> 24) & 0xFF) == 0x12)
13027 tmp_type |= DRF_IEEE;
13028 else if ((dummy & 0xFF) == 0x83 && /* big endian */
13029 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
13030 && ((dummy >> 24) & 0xFF) == 0x3F)
13031 tmp_type |= DRF_IEEE;
13032 else if ((dummy & 0xFF) == 0x13 &&
13033 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
13034 && ((dummy >> 24) & 0xFF) == 0xC0)
13035 tmp_type |= DRF_G_FLOAT;
13036 else if ((dummy & 0xFF) == 0x9E &&
13037 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
13038 && ((dummy >> 24) & 0xFF) == 0x04)
13039 cm_msg(MERROR, "rpc_get_option",
13040 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
13041 else
13042 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
13043
13044 return tmp_type;
13045 }
13046 }
13047}
13048
13050#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13051
13052/********************************************************************/
13060#if 0
13061INT rpc_set_option(HNDLE hConn, INT item, INT value) {
13062 switch (item) {
13063 case RPC_OTIMEOUT:
13064 if (hConn == -1)
13066 else if (hConn == -2)
13068 else {
13070 if (c) {
13071 c->rpc_timeout = value;
13072 c->mutex.unlock();
13073 }
13074 }
13075 break;
13076
13077 case RPC_NODELAY:
13078 if (hConn == -1) {
13079 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13080 } else {
13082 if (c) {
13083 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
13084 c->mutex.unlock();
13085 }
13086 }
13087 break;
13088
13089 default:
13090 cm_msg(MERROR, "rpc_set_option", "invalid argument");
13091 break;
13092 }
13093
13094 return 0;
13095}
13096#endif
13097
13098/********************************************************************/
13105{
13106 if (hConn == RPC_HNDLE_MSERVER) {
13108 } else if (hConn == RPC_HNDLE_CONNECT) {
13109 return _rpc_connect_timeout;
13110 } else {
13112 if (c) {
13113 int timeout = c->rpc_timeout;
13114 c->mutex.unlock();
13115 return timeout;
13116 }
13117 }
13118 return 0;
13119}
13120
13121/********************************************************************/
13129INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
13130{
13131 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13132
13133 if (hConn == RPC_HNDLE_MSERVER) {
13134 if (old_timeout_msec)
13135 *old_timeout_msec = _server_connection.rpc_timeout;
13136 _server_connection.rpc_timeout = timeout_msec;
13137 } else if (hConn == RPC_HNDLE_CONNECT) {
13138 if (old_timeout_msec)
13139 *old_timeout_msec = _rpc_connect_timeout;
13140 _rpc_connect_timeout = timeout_msec;
13141 } else {
13143 if (c) {
13144 if (old_timeout_msec)
13145 *old_timeout_msec = c->rpc_timeout;
13146 c->rpc_timeout = timeout_msec;
13147 c->mutex.unlock();
13148 } else {
13149 if (old_timeout_msec)
13150 *old_timeout_msec = 0;
13151 }
13152 }
13153 return RPC_SUCCESS;
13154}
13155
13156
13158#ifndef DOXYGEN_SHOULD_SKIP_THIS
13159
13160/********************************************************************/
13162/********************************************************************\
13163
13164 Routine: rpc_get_convert_flags
13165
13166 Purpose: Get RPC convert_flags for the mserver connection
13167
13168 Function value:
13169 INT Actual option
13170
13171\********************************************************************/
13172{
13175 else
13176 return 0;
13177}
13178
13179static std::string _mserver_path;
13180
13181/********************************************************************/
13183/********************************************************************\
13184
13185 Routine: rpc_get_mserver_path()
13186
13187 Purpose: Get path of the mserver executable
13188
13189\********************************************************************/
13190{
13191 return _mserver_path.c_str();
13192}
13193
13194/********************************************************************/
13195INT rpc_set_mserver_path(const char *path)
13196/********************************************************************\
13197
13198 Routine: rpc_set_mserver_path
13199
13200 Purpose: Remember the path of the mserver executable
13201
13202 Input:
13203 char *path Full path of the mserver executable
13204
13205 Function value:
13206 RPC_SUCCESS Successful completion
13207
13208\********************************************************************/
13209{
13210 _mserver_path = path;
13211 return RPC_SUCCESS;
13212}
13213
13214/********************************************************************/
13215std::string rpc_get_name()
13216/********************************************************************\
13217
13218 Routine: rpc_get_name
13219
13220 Purpose: Get name set by rpc_set_name
13221
13222 Input:
13223 none
13224
13225 Output:
13226 char* name The location pointed by *name receives a
13227 copy of the _prog_name
13228
13229 Function value:
13230 RPC_SUCCESS Successful completion
13231
13232\********************************************************************/
13233{
13234 return _client_name;
13235}
13236
13237
13238/********************************************************************/
13240/********************************************************************\
13241
13242 Routine: rpc_set_name
13243
13244 Purpose: Set name of actual program for further rpc connections
13245
13246 Input:
13247 char *name Program name, up to NAME_LENGTH chars,
13248 no blanks
13249
13250 Output:
13251 none
13252
13253 Function value:
13254 RPC_SUCCESS Successful completion
13255
13256\********************************************************************/
13257{
13259
13260 return RPC_SUCCESS;
13261}
13262
13263
13264/********************************************************************/
13265INT rpc_set_debug(void (*func)(const char *), INT mode)
13266/********************************************************************\
13267
13268 Routine: rpc_set_debug
13269
13270 Purpose: Set a function which is called on every RPC call to
13271 display the function name and parameters of the RPC
13272 call.
13273
13274 Input:
13275 void *func(char*) Pointer to function.
13276 INT mode Debug mode
13277
13278 Output:
13279 none
13280
13281 Function value:
13282 RPC_SUCCESS Successful completion
13283
13284\********************************************************************/
13285{
13286 _debug_print = func;
13287 _debug_mode = mode;
13288 return RPC_SUCCESS;
13289}
13290
13291/********************************************************************/
13292void rpc_debug_printf(const char *format, ...)
13293/********************************************************************\
13294
13295 Routine: rpc_debug_print
13296
13297 Purpose: Calls function set via rpc_set_debug to output a string.
13298
13299 Input:
13300 char *str Debug string
13301
13302 Output:
13303 none
13304
13305\********************************************************************/
13306{
13307 va_list argptr;
13308 char str[1000];
13309
13310 if (_debug_mode) {
13311 va_start(argptr, format);
13312 vsprintf(str, (char *) format, argptr);
13313 va_end(argptr);
13314
13315 if (_debug_print) {
13316 strcat(str, "\n");
13318 } else
13319 puts(str);
13320 }
13321}
13322
13323/********************************************************************/
13324void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
13325 switch (arg_type) {
13326 /* On the stack, the minimum parameter size is sizeof(int).
13327 To avoid problems on little endian systems, treat all
13328 smaller parameters as int's */
13329 case TID_UINT8:
13330 case TID_INT8:
13331 case TID_CHAR:
13332 case TID_UINT16:
13333 case TID_INT16:
13334 *((int *) arg) = va_arg(*arg_ptr, int);
13335 break;
13336
13337 case TID_INT32:
13338 case TID_BOOL:
13339 *((INT *) arg) = va_arg(*arg_ptr, INT);
13340 break;
13341
13342 case TID_UINT32:
13343 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13344 break;
13345
13346 /* float variables are passed as double by the compiler */
13347 case TID_FLOAT:
13348 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13349 break;
13350
13351 case TID_DOUBLE:
13352 *((double *) arg) = va_arg(*arg_ptr, double);
13353 break;
13354
13355 case TID_ARRAY:
13356 *((char **) arg) = va_arg(*arg_ptr, char *);
13357 break;
13358 }
13359}
13360
13361/********************************************************************/
13362static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13363{
13364 //printf("rpc_call_encode!\n");
13365
13366 bool debug = false;
13367
13368 if (debug) {
13369 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13370 for (int i=0; rl.param[i].tid != 0; i++) {
13371 int tid = rl.param[i].tid;
13372 int flags = rl.param[i].flags;
13373 int n = rl.param[i].n;
13374 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13375 }
13376 }
13377
13378 char args[MAX_RPC_PARAMS][8];
13379
13380 for (int i=0; rl.param[i].tid != 0; i++) {
13381 int tid = rl.param[i].tid;
13382 int flags = rl.param[i].flags;
13383 int arg_type = 0;
13384
13385 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13386 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13387 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13388
13389 if (bpointer)
13390 arg_type = TID_ARRAY;
13391 else
13392 arg_type = tid;
13393
13394 /* floats are passed as doubles, at least under NT */
13395 if (tid == TID_FLOAT && !bpointer)
13396 arg_type = TID_DOUBLE;
13397
13398 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13399
13400 rpc_va_arg(&ap, arg_type, args[i]);
13401 }
13402
13403 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13404 char* buf = (char *)malloc(buf_size);
13405 assert(buf);
13406
13407 (*nc) = (NET_COMMAND*) buf;
13408
13409 /* find out if we are on a big endian system */
13410 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13411
13412 char* param_ptr = (*nc)->param;
13413
13414 for (int i=0; rl.param[i].tid != 0; i++) {
13415 int tid = rl.param[i].tid;
13416 int flags = rl.param[i].flags;
13417 int arg_type = 0;
13418
13419 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13420 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13421 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13422
13423 if (bpointer)
13424 arg_type = TID_ARRAY;
13425 else
13426 arg_type = tid;
13427
13428 /* floats are passed as doubles, at least under NT */
13429 if (tid == TID_FLOAT && !bpointer)
13430 arg_type = TID_DOUBLE;
13431
13432 /* get pointer to argument */
13433 //char arg[8];
13434 //rpc_va_arg(&ap, arg_type, arg);
13435
13436 char* arg = args[i];
13437
13438 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13439 if (bbig) {
13440 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13441 arg[0] = arg[3];
13442 }
13443 if (tid == TID_UINT16 || tid == TID_INT16) {
13444 arg[0] = arg[2];
13445 arg[1] = arg[3];
13446 }
13447 }
13448
13449 if (flags & RPC_IN) {
13450 int arg_size = 0;
13451
13452 if (bpointer)
13453 arg_size = rpc_tid_size(tid);
13454 else
13455 arg_size = rpc_tid_size(arg_type);
13456
13457 /* for strings, the argument size depends on the string length */
13458 if (tid == TID_STRING || tid == TID_LINK) {
13459 arg_size = 1 + strlen((char *) *((char **) arg));
13460 }
13461
13462 /* for varibale length arrays, the size is given by
13463 the next parameter on the stack */
13464 if (flags & RPC_VARARRAY) {
13465 //va_list aptmp;
13467 //va_copy(aptmp, ap);
13468
13469 //char arg_tmp[8];
13470 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13471
13472 const char* arg_tmp = args[i+1];
13473
13474 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13475 if (flags & RPC_OUT)
13476 arg_size = *((INT *) *((void **) arg_tmp));
13477 else
13478 arg_size = *((INT *) arg_tmp);
13479
13480 *((INT *) param_ptr) = ALIGN8(arg_size);
13481 param_ptr += ALIGN8(sizeof(INT));
13482
13483 //va_end(aptmp);
13484 }
13485
13486 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13487 arg_size = rl.param[i].n;
13488
13489 /* always align parameter size */
13490 int param_size = ALIGN8(arg_size);
13491
13492 {
13493 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13494
13495 if (param_offset + param_size + 16 > buf_size) {
13496 size_t new_size = param_offset + param_size + 1024;
13497 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13498 buf = (char *) realloc(buf, new_size);
13499 assert(buf);
13500 buf_size = new_size;
13501 (*nc) = (NET_COMMAND*) buf;
13502 param_ptr = buf + param_offset;
13503 }
13504 }
13505
13506 if (bpointer) {
13507 if (debug) {
13508 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);
13509 }
13510 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13511 } else if (tid == TID_FLOAT) {
13512 if (debug) {
13513 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);
13514 }
13515 /* floats are passed as doubles on most systems */
13516 *((float *) param_ptr) = (float) *((double *) arg);
13517 } else {
13518 if (debug) {
13519 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);
13520 }
13521 memcpy(param_ptr, arg, arg_size);
13522 }
13523
13524 param_ptr += param_size;
13525 }
13526 }
13527
13528 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13529
13530 if (debug)
13531 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13532}
13533
13534/********************************************************************/
13535static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13536{
13537 //printf("rpc_call_decode!\n");
13538
13539 bool debug = false;
13540
13541 if (debug)
13542 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13543
13544 /* extract result variables and place it to argument list */
13545
13546 const char* param_ptr = buf;
13547
13548 for (int i = 0; rl.param[i].tid != 0; i++) {
13549 int tid = rl.param[i].tid;
13550 int flags = rl.param[i].flags;
13551 int arg_type = 0;
13552
13553 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13554 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13555 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13556
13557 if (bpointer)
13558 arg_type = TID_ARRAY;
13559 else
13560 arg_type = rl.param[i].tid;
13561
13562 if (tid == TID_FLOAT && !bpointer)
13563 arg_type = TID_DOUBLE;
13564
13565 char arg[8];
13566 rpc_va_arg(&ap, arg_type, arg);
13567
13568 if (rl.param[i].flags & RPC_OUT) {
13569
13570 if (param_ptr == NULL) {
13571 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);
13572 return RPC_NET_ERROR;
13573 }
13574
13575 tid = rl.param[i].tid;
13576 int arg_size = rpc_tid_size(tid);
13577
13578 if (tid == TID_STRING || tid == TID_LINK)
13579 arg_size = strlen((char *) (param_ptr)) + 1;
13580
13581 if (flags & RPC_VARARRAY) {
13582 arg_size = *((INT *) param_ptr);
13583 param_ptr += ALIGN8(sizeof(INT));
13584 }
13585
13586 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13587 arg_size = rl.param[i].n;
13588
13589 /* parameter size is always aligned */
13590 int param_size = ALIGN8(arg_size);
13591
13592 /* return parameters are always pointers */
13593 if (*((char **) arg)) {
13594 if (debug)
13595 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);
13596 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13597 }
13598
13599 param_ptr += param_size;
13600 }
13601 }
13602
13603 return RPC_SUCCESS;
13604}
13605
13606/********************************************************************/
13607static void rpc_call_encode_cxx(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13608{
13609 //printf("rpc_call_encode_cxx!\n");
13610
13611 bool debug = false;
13612
13613 //if (rl.id == RPC_TEST2)
13614 // debug = true;
13615
13616 //if (rl.id == RPC_TEST2_CXX)
13617 // debug = true;
13618
13619 if (debug) {
13620 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13621 for (int i=0; rl.param[i].tid != 0; i++) {
13622 int tid = rl.param[i].tid;
13623 int flags = rl.param[i].flags;
13624 int n = rl.param[i].n;
13625 printf("param %2d, tid %2d, flags 0x%02x, n %3d\n", i, tid, flags, n);
13626 }
13627 }
13628
13629 char args[MAX_RPC_PARAMS][sizeof(char*)];
13630
13631 for (int i=0; rl.param[i].tid != 0; i++) {
13632 int tid = rl.param[i].tid;
13633 int flags = rl.param[i].flags;
13634 int arg_type = 0;
13635
13636 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13637 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13638 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13639
13640 if (bpointer)
13641 arg_type = TID_ARRAY;
13642 else
13643 arg_type = tid;
13644
13645 /* floats are passed as doubles, at least under NT */
13646 if (tid == TID_FLOAT && !bpointer)
13647 arg_type = TID_DOUBLE;
13648
13649 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13650
13651 rpc_va_arg(&ap, arg_type, args[i]);
13652 }
13653
13654 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13655 char* buf = (char *)malloc(buf_size);
13656 assert(buf);
13657
13658 (*nc) = (NET_COMMAND*) buf;
13659
13660 /* find out if we are on a big endian system */
13661 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13662
13663 char* param_ptr = (*nc)->param;
13664
13665 for (int i=0; rl.param[i].tid != 0; i++) {
13666 int tid = rl.param[i].tid;
13667 int flags = rl.param[i].flags;
13668 int arg_type = 0;
13669
13670 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13671 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13672 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13673
13674 if (bpointer)
13675 arg_type = TID_ARRAY;
13676 else
13677 arg_type = tid;
13678
13679 /* floats are passed as doubles, at least under NT */
13680 if (tid == TID_FLOAT && !bpointer)
13681 arg_type = TID_DOUBLE;
13682
13683 /* get pointer to argument */
13684 //char arg[8];
13685 //rpc_va_arg(&ap, arg_type, arg);
13686
13687 char* arg = args[i];
13688
13689 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13690 if (bbig) {
13691 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13692 arg[0] = arg[3];
13693 }
13694 if (tid == TID_UINT16 || tid == TID_INT16) {
13695 arg[0] = arg[2];
13696 arg[1] = arg[3];
13697 }
13698 }
13699
13700 if (flags & RPC_IN) {
13701 int arg_size = 0;
13702
13703 if (bpointer)
13704 arg_size = rpc_tid_size(tid);
13705 else
13706 arg_size = rpc_tid_size(arg_type);
13707
13708 void* parg = (void *) *((void **) arg);
13709
13710 /* for strings, the argument size depends on the string length */
13711 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13712 std::string* s = (std::string*)parg;
13713 arg_size = 1 + s->length();
13714 parg = (void*)s->c_str();
13715 bpointer = TRUE;
13716 //printf("STRING %p, len %zu, [%s]\n", s, s->length(), s->c_str());
13717 } else if (tid == TID_STRING || tid == TID_LINK) {
13718 arg_size = 1 + strlen((char *) *((char **) arg));
13719 }
13720
13721 /* for varibale length arrays, the size is given by
13722 the next parameter on the stack */
13723 if (flags & RPC_VARARRAY) {
13724 //va_list aptmp;
13726 //va_copy(aptmp, ap);
13727
13728 //char arg_tmp[8];
13729 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13730
13731 if (flags & RPC_CXX) {
13732 std::vector<char>* pv = (std::vector<char>*)parg;
13733 arg_size = pv->size();
13734 parg = (void*)pv->data();
13735 bpointer = TRUE;
13736 //printf("VECTOR %p, size %zu\n", pv, pv->size());
13737
13738 *((INT *) param_ptr) = arg_size; // NB: for std:vector<char> data, pass true data size. it is safe because decoder always aligns it themselves.
13739
13740 } else {
13741 const char* arg_tmp = args[i+1];
13742
13743 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13744 if (flags & RPC_OUT) {
13745 arg_size = *((INT *) *((void **) arg_tmp));
13746 } else {
13747 arg_size = *((INT *) arg_tmp);
13748 }
13749
13750 *((INT *) param_ptr) = ALIGN8(arg_size);
13751 }
13752
13753 param_ptr += ALIGN8(sizeof(INT));
13754
13755 //va_end(aptmp);
13756 }
13757
13758 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13759 arg_size = rl.param[i].n;
13760
13761 /* always align parameter size */
13762 int param_size = ALIGN8(arg_size);
13763
13764 {
13765 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13766
13767 if (param_offset + param_size + 16 > buf_size) {
13768 size_t new_size = param_offset + param_size + 1024;
13769 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13770 buf = (char *) realloc(buf, new_size);
13771 assert(buf);
13772 buf_size = new_size;
13773 (*nc) = (NET_COMMAND*) buf;
13774 param_ptr = buf + param_offset;
13775 }
13776 }
13777
13778 if (bpointer) {
13779 if (debug) {
13780 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);
13781 }
13782 memcpy(param_ptr, parg, arg_size);
13783 } else if (tid == TID_FLOAT) {
13784 if (debug) {
13785 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);
13786 }
13787 /* floats are passed as doubles on most systems */
13788 *((float *) param_ptr) = (float) *((double *) arg);
13789 } else {
13790 if (debug) {
13791 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);
13792 }
13793 memcpy(param_ptr, arg, arg_size);
13794 }
13795
13796 param_ptr += param_size;
13797 }
13798 }
13799
13800 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13801
13802 if (debug)
13803 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13804}
13805
13806/********************************************************************/
13807static int rpc_call_decode_cxx(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13808{
13809 //printf("rpc_call_decode_cxx!\n");
13810
13811 bool debug = false;
13812
13813 //if (rl.id == RPC_TEST2)
13814 // debug = true;
13815
13816 //if (rl.id == RPC_TEST2_CXX)
13817 // debug = true;
13818
13819 if (debug)
13820 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13821
13822 /* extract result variables and place it to argument list */
13823
13824 const char* param_ptr = buf;
13825
13826 for (int i = 0; rl.param[i].tid != 0; i++) {
13827 const int tid = rl.param[i].tid;
13828 const int flags = rl.param[i].flags;
13829 int arg_type = 0;
13830
13831 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13832 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13833 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13834
13835 if (bpointer)
13836 arg_type = TID_ARRAY;
13837 else
13838 arg_type = tid;
13839
13840 if (tid == TID_FLOAT && !bpointer)
13841 arg_type = TID_DOUBLE;
13842
13843 char arg[sizeof(double)+sizeof(uint64_t)+sizeof(char*)];
13844 rpc_va_arg(&ap, arg_type, arg);
13845
13846 if (flags & RPC_OUT) {
13847
13848 if (param_ptr == NULL) {
13849 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);
13850 return RPC_NET_ERROR;
13851 }
13852
13853 int arg_size = rpc_tid_size(tid);
13854
13855 if (tid == TID_STRING || tid == TID_LINK) {
13856 arg_size = strlen((char *) (param_ptr)) + 1;
13857
13858 if (debug)
13859 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));
13860 }
13861
13862 if (flags & RPC_VARARRAY) {
13863 arg_size = *((INT *) param_ptr);
13864 param_ptr += ALIGN8(sizeof(INT));
13865 }
13866
13867 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13868 arg_size = rl.param[i].n;
13869
13870 /* parameter size is always aligned */
13871 int param_size = ALIGN8(arg_size);
13872
13873 /* return parameters are always pointers */
13874 void* parg = *(char**) arg;
13875 if (parg) {
13876 if ((tid == TID_STRING) && (flags & RPC_CXX)) {
13877 if (debug)
13878 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);
13879 *(std::string*)parg = param_ptr;
13880 } else if ((tid == TID_ARRAY) && (flags & RPC_CXX)) {
13881 if (debug)
13882 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);
13883 std::vector<char>* pvec = (std::vector<char>*)parg;
13884 pvec->clear();
13885 pvec->insert(pvec->end(), param_ptr, param_ptr + arg_size);
13886 } else {
13887 if (debug)
13888 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);
13889 memcpy(parg, param_ptr, arg_size);
13890 }
13891 }
13892
13893 param_ptr += param_size;
13894 }
13895 }
13896
13897 return RPC_SUCCESS;
13898}
13899
13900static int rpc_find_rpc(int routine_id, RPC_LIST* pentry, bool* prpc_cxx)
13901{
13902 rpc_list_mutex.lock();
13903
13904 for (size_t i = 0; i < rpc_list.size(); i++) {
13905 if (rpc_list[i].id == routine_id) {
13906 *pentry = rpc_list[i];
13907
13908 rpc_list_mutex.unlock();
13909
13910 *prpc_cxx = false;
13911
13912 for (int j=0; j<MAX_RPC_PARAMS; j++)
13913 if (pentry->param[j].flags & RPC_CXX)
13914 *prpc_cxx = true;
13915
13916 return RPC_SUCCESS;
13917 }
13918 }
13919
13920 rpc_list_mutex.unlock();
13921
13922 return RPC_INVALID_ID;
13923}
13924
13925/********************************************************************/
13926INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
13927/********************************************************************\
13928
13929 Routine: rpc_client_call
13930
13931 Purpose: Call a function on a MIDAS client
13932
13933 Input:
13934 INT hConn Client connection
13935 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13936
13937 ... variable argument list
13938
13939 Output:
13940 (depends on argument list)
13941
13942 Function value:
13943 RPC_SUCCESS Successful completion
13944 RPC_NET_ERROR Error in socket call
13945 RPC_NO_CONNECTION No active connection
13946 RPC_TIMEOUT Timeout in RPC call
13947 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13948 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13949
13950\********************************************************************/
13951{
13953
13954 if (!c) {
13955 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13956 return RPC_NO_CONNECTION;
13957 }
13958
13959 //printf("rpc_client_call: handle %d, connection: ", hConn);
13960 //c->print();
13961 //printf("\n");
13962
13963 INT i, status;
13964
13965 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13966 routine_id &= ~RPC_NO_REPLY;
13967
13968 //if (rpc_no_reply)
13969 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13970
13971 // make local copy of the client name just in case _client_connection is erased by another thread
13972
13973 /* find rpc_index */
13974
13975 RPC_LIST rpc_entry;
13976 bool rpc_cxx = false;
13977
13978 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
13979
13980 if (status != RPC_SUCCESS) {
13981 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);
13982 c->mutex.unlock();
13983 return RPC_INVALID_ID;
13984 }
13985
13986 const char *rpc_name = rpc_entry.name;
13987
13988 NET_COMMAND *nc = NULL;
13989
13990 /* examine variable argument list and convert it to parameter array */
13991 va_list ap;
13992 va_start(ap, routine_id);
13993
13994 if (rpc_cxx)
13995 rpc_call_encode_cxx(ap, rpc_entry, &nc);
13996 else
13997 rpc_call_encode(ap, rpc_entry, &nc);
13998
13999 va_end(ap);
14000
14001 nc->header.routine_id = routine_id;
14002
14003 if (rpc_no_reply)
14005
14006 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14007
14008 /* in FAST TCP mode, only send call and return immediately */
14009 if (rpc_no_reply) {
14010 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14011
14012 if (i != send_size) {
14013 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);
14014 free(nc);
14015 c->mutex.unlock();
14016 return RPC_NET_ERROR;
14017 }
14018
14019 free(nc);
14020
14021 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
14022 //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);
14023 //c->print();
14024 //printf("\n");
14025 c->close_locked();
14026 }
14027
14028 c->mutex.unlock();
14029 return RPC_SUCCESS;
14030 }
14031
14032 /* in TCP mode, send and wait for reply on send socket */
14033 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
14034 if (i != send_size) {
14035 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);
14036 c->mutex.unlock();
14037 return RPC_NET_ERROR;
14038 }
14039
14040 free(nc);
14041 nc = NULL;
14042
14043 bool restore_watchdog_timeout = false;
14044 BOOL watchdog_call;
14045 DWORD watchdog_timeout;
14046 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14047
14048 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
14049
14050 if (c->rpc_timeout >= (int) watchdog_timeout) {
14051 restore_watchdog_timeout = true;
14052 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
14053 }
14054
14055 DWORD rpc_status = 0;
14056 DWORD buf_size = 0;
14057 char* buf = NULL;
14058
14059 /* receive result on send socket */
14060 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
14061
14062 if (restore_watchdog_timeout) {
14063 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
14064 }
14065
14066 if (status == SS_TIMEOUT) {
14067 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);
14068 if (buf)
14069 free(buf);
14070 c->mutex.unlock();
14071 return RPC_TIMEOUT;
14072 }
14073
14074 if (status != SS_SUCCESS) {
14075 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);
14076 if (buf)
14077 free(buf);
14078 c->mutex.unlock();
14079 return RPC_NET_ERROR;
14080 }
14081
14082 c->mutex.unlock();
14083
14084 if (rpc_status == RPC_INVALID_ID) {
14085 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);
14086 if (buf)
14087 free(buf);
14088 return rpc_status;
14089 }
14090
14091 /* extract result variables and place it to argument list */
14092
14093 va_start(ap, routine_id);
14094
14095 if (rpc_cxx)
14096 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14097 else
14098 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14099
14100 if (status != RPC_SUCCESS) {
14101 rpc_status = status;
14102 }
14103
14104 va_end(ap);
14105
14106 if (buf)
14107 free(buf);
14108 buf = NULL;
14109 buf_size = 0;
14110
14111 return rpc_status;
14112}
14113
14114/********************************************************************/
14115INT rpc_call(DWORD routine_id, ...)
14116/********************************************************************\
14117
14118 Routine: rpc_call
14119
14120 Purpose: Call a function on a MIDAS server
14121
14122 Input:
14123 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
14124
14125 ... variable argument list
14126
14127 Output:
14128 (depends on argument list)
14129
14130 Function value:
14131 RPC_SUCCESS Successful completion
14132 RPC_NET_ERROR Error in socket call
14133 RPC_NO_CONNECTION No active connection
14134 RPC_TIMEOUT Timeout in RPC call
14135 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
14136 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
14137
14138\********************************************************************/
14139{
14140 va_list ap;
14141 INT i, status;
14142
14143 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
14144 routine_id &= ~RPC_NO_REPLY;
14145
14146 //if (rpc_no_reply)
14147 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
14148
14149 int send_sock = _server_connection.send_sock;
14150 int rpc_timeout = _server_connection.rpc_timeout;
14151
14152 if (!send_sock) {
14153 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
14154 return RPC_NET_ERROR;
14155 }
14156
14157 if (!_mutex_rpc) {
14158 /* create a local mutex for multi-threaded applications */
14160 }
14161
14162 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
14163 if (status != SS_SUCCESS) {
14164 cm_msg(MERROR, "rpc_call", "Mutex timeout");
14165 return RPC_MUTEX_TIMEOUT;
14166 }
14167
14168 /* find rpc definition */
14169
14170 RPC_LIST rpc_entry;
14171 bool rpc_cxx = false;
14172
14173 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
14174
14175 if (status != RPC_SUCCESS) {
14177 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
14178 return RPC_INVALID_ID;
14179 }
14180
14181 const char* rpc_name = rpc_entry.name;
14182
14183 /* prepare output buffer */
14184
14185 NET_COMMAND* nc = NULL;
14186
14187 /* examine variable argument list and convert it to parameter array */
14188 va_start(ap, routine_id);
14189
14190 if (rpc_cxx)
14191 rpc_call_encode_cxx(ap, rpc_entry, &nc);
14192 else
14193 rpc_call_encode(ap, rpc_entry, &nc);
14194
14195 va_end(ap);
14196
14197 nc->header.routine_id = routine_id;
14198
14199 if (rpc_no_reply)
14201
14202 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
14203
14204 /* do not wait for reply if requested RPC_NO_REPLY */
14205 if (rpc_no_reply) {
14206 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14207
14208 if (i != send_size) {
14210 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14211 free(nc);
14212 return RPC_NET_ERROR;
14213 }
14214
14216 free(nc);
14217 return RPC_SUCCESS;
14218 }
14219
14220 /* in TCP mode, send and wait for reply on send socket */
14221 i = send_tcp(send_sock, (char *) nc, send_size, 0);
14222 if (i != send_size) {
14224 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
14225 free(nc);
14226 return RPC_NET_ERROR;
14227 }
14228
14229 free(nc);
14230 nc = NULL;
14231
14232 bool restore_watchdog_timeout = false;
14233 BOOL watchdog_call;
14234 DWORD watchdog_timeout;
14235 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
14236
14237 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
14238
14239 if (!rpc_is_remote()) {
14240 // if RPC is remote, we are connected to an mserver,
14241 // the mserver takes care of watchdog timeouts.
14242 // otherwise we should make sure the watchdog timeout
14243 // is longer than the RPC timeout. K.O.
14244 if (rpc_timeout >= (int) watchdog_timeout) {
14245 restore_watchdog_timeout = true;
14246 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
14247 }
14248 }
14249
14250 DWORD rpc_status = 0;
14251 DWORD buf_size = 0;
14252 char* buf = NULL;
14253
14254 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
14255
14256 if (restore_watchdog_timeout) {
14257 cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
14258 }
14259
14260 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
14261
14263
14264 /* check for reply errors */
14265
14266 if (status == SS_TIMEOUT) {
14267 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
14268 if (buf)
14269 free(buf);
14270 abort(); // cannot continue - our mserver is not talking to us!
14271 return RPC_TIMEOUT;
14272 }
14273
14274 if (status != SS_SUCCESS) {
14275 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
14276 if (buf)
14277 free(buf);
14278 abort(); // cannot continue - something is wrong with our mserver connection
14279 return RPC_NET_ERROR;
14280 }
14281
14282 if (rpc_status == RPC_INVALID_ID) {
14283 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
14284 if (buf)
14285 free(buf);
14286 return rpc_status;
14287 }
14288
14289 /* extract result variables and place it to argument list */
14290
14291 va_start(ap, routine_id);
14292
14293 if (rpc_cxx)
14294 status = rpc_call_decode_cxx(ap, rpc_entry, buf, buf_size);
14295 else
14296 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
14297
14298 if (status != RPC_SUCCESS) {
14299 rpc_status = status;
14300 }
14301
14302 va_end(ap);
14303
14304 if (buf)
14305 free(buf);
14306
14307 return rpc_status;
14308}
14309
14310
14311/********************************************************************/
14313 INT old;
14314
14315 old = _opt_tcp_size;
14316 _opt_tcp_size = tcp_size;
14317 return old;
14318}
14319
14321 return _opt_tcp_size;
14322}
14323
14325#endif /* DOXYGEN_SHOULD_SKIP_THIS */
14326
14327/********************************************************************/
14349INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
14350{
14351 if (rpc_is_remote()) {
14352 return rpc_send_event1(buffer_handle, pevent);
14353 } else {
14354 return bm_send_event(buffer_handle, pevent, unused, async_flag);
14355 }
14356}
14357
14358/********************************************************************/
14367INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
14368{
14369 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
14370 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
14371}
14372
14373INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
14374{
14375 if (sg_n < 1) {
14376 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
14377 return BM_INVALID_SIZE;
14378 }
14379
14380 if (sg_ptr[0] == NULL) {
14381 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
14382 return BM_INVALID_SIZE;
14383 }
14384
14385 if (sg_len[0] < sizeof(EVENT_HEADER)) {
14386 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));
14387 return BM_INVALID_SIZE;
14388 }
14389
14390 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
14391
14392 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
14393 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
14394
14395 if (data_size == 0) {
14396 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
14397 return BM_INVALID_SIZE;
14398 }
14399
14400 if (data_size > MAX_DATA_SIZE) {
14401 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);
14402 return BM_INVALID_SIZE;
14403 }
14404
14405 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
14406 const size_t total_size = ALIGN8(event_size);
14407
14408 size_t count = 0;
14409 for (int i=0; i<sg_n; i++) {
14410 count += sg_len[i];
14411 }
14412
14413 if (count != event_size) {
14414 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);
14415 return BM_INVALID_SIZE;
14416 }
14417
14418 // protect non-atomic access to _server_connection.event_sock. K.O.
14419
14420 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
14421
14422 //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);
14423
14424 if (_server_connection.event_sock == 0) {
14425 return RPC_NO_CONNECTION;
14426 }
14427
14428 //
14429 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14430 //
14431 // 4 bytes of buffer handle
14432 // 16 bytes of event header, includes data_size
14433 // ALIGN8(data_size) bytes of event data
14434 //
14435
14436 int status;
14437
14438 /* send buffer handle */
14439
14440 assert(sizeof(DWORD) == 4);
14441 DWORD bh_buf = buffer_handle;
14442
14443 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14444 if (status != SS_SUCCESS) {
14446 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14447 return RPC_NET_ERROR;
14448 }
14449
14450 /* send data */
14451
14452 for (int i=0; i<sg_n; i++) {
14453 status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
14454 if (status != SS_SUCCESS) {
14456 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14457 return RPC_NET_ERROR;
14458 }
14459 }
14460
14461 /* send padding */
14462
14463 if (count < total_size) {
14464 char padding[8] = { 0,0,0,0,0,0,0,0 };
14465 size_t padlen = total_size - count;
14466 assert(padlen < 8);
14468 if (status != SS_SUCCESS) {
14470 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14471 return RPC_NET_ERROR;
14472 }
14473 }
14474
14475 return RPC_SUCCESS;
14476}
14477
14478/********************************************************************/
14487 return RPC_SUCCESS;
14488}
14489
14490/********************************************************************/
14491
14492struct TR_FIFO {
14493 int transition = 0;
14494 int run_number = 0;
14495 time_t trans_time = 0;
14497};
14498
14499static std::mutex _tr_fifo_mutex;
14501static int _tr_fifo_wp = 0;
14502static int _tr_fifo_rp = 0;
14503
14504static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
14505/********************************************************************\
14506
14507 Routine: rpc_transition_dispatch
14508
14509 Purpose: Gets called when a transition function was registered and
14510 a transition occured. Internal use only.
14511
14512 Input:
14513 INT idx RPC function ID
14514 void *prpc_param RPC parameters
14515
14516 Output:
14517 none
14518
14519 Function value:
14520 INT return value from called user routine
14521
14522\********************************************************************/
14523{
14524 /* erase error string */
14525 *(CSTRING(2)) = 0;
14526
14527 if (idx == RPC_RC_TRANSITION) {
14528 // find registered handler
14529 // NB: this code should match same code in cm_transition_call_direct()
14530 // NB: only use the first handler, this is how MIDAS always worked
14531 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14532 _trans_table_mutex.lock();
14533 size_t n = _trans_table.size();
14534 _trans_table_mutex.unlock();
14535
14536 for (size_t i = 0; i < n; i++) {
14537 _trans_table_mutex.lock();
14539 _trans_table_mutex.unlock();
14540
14541 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14542 if (tt.func) {
14543 /* execute callback if defined */
14544 return tt.func(CINT(1), CSTRING(2));
14545 } else {
14546 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14547 /* store transition in FIFO */
14550 _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
14552 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14553 // implicit unlock
14554 return RPC_SUCCESS;
14555 }
14556 }
14557 }
14558 // no handler for this transition
14559 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14560 return CM_SUCCESS;
14561 } else {
14562 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14563 return RPC_INVALID_ID;
14564 }
14565}
14566
14567/********************************************************************/
14568int cm_query_transition(int *transition, int *run_number, int *trans_time)
14569/********************************************************************\
14570
14571 Routine: cm_query_transition
14572
14573 Purpose: Query system if transition has occured. Normally, one
14574 registers callbacks for transitions via
14575 cm_register_transition. In some environments however,
14576 callbacks are not possible. In that case one spciefies
14577 a NULL pointer as the callback routine and can query
14578 transitions "manually" by calling this functions. A small
14579 FIFO takes care that no transition is lost if this functions
14580 did not get called between some transitions.
14581
14582 Output:
14583 INT *transition Type of transition, one of TR_xxx
14584 INT *run_nuber Run number for transition
14585 time_t *trans_time Time (in UNIX time) of transition
14586
14587 Function value:
14588 FALSE No transition occured since last call
14589 TRUE Transition occured
14590
14591\********************************************************************/
14592{
14593 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14594
14595 if (_tr_fifo_wp == _tr_fifo_rp)
14596 return FALSE;
14597
14598 if (transition)
14600
14601 if (run_number)
14603
14604 if (trans_time)
14605 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14606
14607 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14608
14609 // implicit unlock
14610 return TRUE;
14611}
14612
14613/********************************************************************\
14614* server functions *
14615\********************************************************************/
14616
14617#if 0
14618void debug_dump(unsigned char *p, int size)
14619{
14620 int i, j;
14621 unsigned char c;
14622
14623 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14624 printf("%p ", p + i * 16);
14625 for (j = 0; j < 16; j++)
14626 if (i * 16 + j < size)
14627 printf("%02X ", p[i * 16 + j]);
14628 else
14629 printf(" ");
14630 printf(" ");
14631
14632 for (j = 0; j < 16; j++) {
14633 c = p[i * 16 + j];
14634 if (i * 16 + j < size)
14635 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14636 }
14637 printf("\n");
14638 }
14639
14640 printf("\n");
14641}
14642#endif
14643
14644/********************************************************************/
14645static int recv_net_command_realloc(RPC_SERVER_ACCEPTION* sa, char **pbuf, int *pbufsize, INT *remaining)
14646/********************************************************************\
14647
14648 Routine: recv_net_command
14649
14650 Purpose: TCP receive routine with local cache. To speed up network
14651 performance, a 64k buffer is read in at once and split into
14652 several RPC command on successive calls to recv_net_command.
14653 Therefore, the number of recv() calls is minimized.
14654
14655 This routine is ment to be called by the server process.
14656 Clients should call recv_tcp instead.
14657
14658 Input:
14659 INT idx Index of server connection
14660 DWORD buffer_size Size of the buffer in bytes.
14661 INT flags Flags passed to recv()
14662 INT convert_flags Convert flags needed for big/little
14663 endian conversion
14664
14665 Output:
14666 char *buffer Network receive buffer.
14667 INT *remaining Remaining data in cache
14668
14669 Function value:
14670 INT Same as recv()
14671
14672\********************************************************************/
14673{
14674 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14675
14676 int sock = sa->recv_sock;
14677
14678 if (!sa->net_buffer) {
14679 if (sa->is_mserver)
14681 else
14683
14684 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14685 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14686 sa->write_ptr = 0;
14687 sa->read_ptr = 0;
14688 sa->misalign = 0;
14689 }
14690 if (!sa->net_buffer) {
14691 cm_msg(MERROR, "recv_net_command_realloc", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14692 return -1;
14693 }
14694
14695 int copied = 0;
14696 int param_size = -1;
14697
14698 int write_ptr = sa->write_ptr;
14699 int read_ptr = sa->read_ptr;
14700 int misalign = sa->misalign;
14701 char *net_buffer = sa->net_buffer;
14702
14703 do {
14704 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14705 if (param_size == -1) {
14706 if (copied > 0) {
14707 /* assemble split header */
14708 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14709 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14710 param_size = (INT) nc->header.param_size;
14711 } else {
14712 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14713 param_size = (INT) nc->header.param_size;
14714 }
14715
14716 if (sa->convert_flags)
14717 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14718 }
14719
14720 //printf("recv_net_command_realloc: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14721
14722 /* check if parameters fit in buffer */
14723 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14724 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14725 char *p = (char *) realloc(*pbuf, new_size);
14726 //printf("recv_net_command_realloc: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14727 if (p == NULL) {
14728 cm_msg(MERROR, "recv_net_command_realloc", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14729 sa->read_ptr = 0;
14730 sa->write_ptr = 0;
14731 return -1;
14732 }
14733 *pbuf = p;
14734 *pbufsize = new_size;
14735 }
14736
14737 buffer = *pbuf;
14738
14739 /* check if we have all parameters in buffer */
14740 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14741 break;
14742 }
14743
14744 /* not enough data, so copy partially and get new */
14745 int size = write_ptr - read_ptr;
14746
14747 if (size > 0) {
14748 memcpy(buffer + copied, net_buffer + read_ptr, size);
14749 copied += size;
14750 read_ptr = write_ptr;
14751 }
14752#ifdef OS_UNIX
14753 do {
14754 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14755
14756 /* don't return if an alarm signal was cought */
14757 } while (write_ptr == -1 && errno == EINTR);
14758#else
14759 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14760#endif
14761
14762 /* abort if connection broken */
14763 if (write_ptr <= 0) {
14764 if (write_ptr == 0)
14765 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());
14766 else
14767 cm_msg(MERROR, "recv_net_command_realloc", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14768
14769 if (remaining)
14770 *remaining = 0;
14771
14772 return write_ptr;
14773 }
14774
14775 read_ptr = misalign;
14776 write_ptr += misalign;
14777
14778 misalign = write_ptr % 8;
14779 } while (TRUE);
14780
14781 /* copy rest of parameters */
14782 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14783 memcpy(buffer + copied, net_buffer + read_ptr, size);
14784 read_ptr += size;
14785
14786 if (remaining) {
14787 /* don't keep rpc_server_receive in an infinite loop */
14788 if (write_ptr - read_ptr < param_size)
14789 *remaining = 0;
14790 else
14791 *remaining = write_ptr - read_ptr;
14792 }
14793
14794 sa->write_ptr = write_ptr;
14795 sa->read_ptr = read_ptr;
14796 sa->misalign = misalign;
14797
14798 return size + copied;
14799}
14800
14801
14802/********************************************************************/
14804/********************************************************************\
14805
14806 Routine: recv_tcp_check
14807
14808 Purpose: Check if in TCP receive buffer associated with sock is
14809 some data. Called by ss_suspend.
14810
14811 Input:
14812 INT sock TCP receive socket
14813
14814 Output:
14815 none
14816
14817 Function value:
14818 INT count Number of bytes remaining in TCP buffer
14819
14820\********************************************************************/
14821{
14822 /* figure out to which connection socket belongs */
14823 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14824 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14825 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14826 }
14827
14828 return 0;
14829}
14830
14831
14832/********************************************************************/
14833static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
14834/********************************************************************\
14835
14836 Routine: recv_event_server_realloc
14837
14838 Purpose: receive events sent by rpc_send_event()
14839
14840 Input:
14841 INT idx Index of server connection
14842 DWORD buffer_size Size of the buffer in bytes.
14843 INT flags Flags passed to recv()
14844 INT convert_flags Convert flags needed for big/little
14845 endian conversion
14846
14847 Output:
14848 char *buffer Network receive buffer.
14849 INT *remaining Remaining data in cache
14850
14851 Function value:
14852 INT Same as recv()
14853
14854\********************************************************************/
14855{
14856 int sock = psa->event_sock;
14857
14858 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14859
14860 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14861
14862 char header_buf[header_size];
14863
14864 // First read the header.
14865 //
14866 // Data format is:
14867 // INT buffer handle (4 bytes)
14868 // EVENT_HEADER (16 bytes)
14869 // event data
14870 // ALIGN8() padding
14871 // ...next event
14872
14873 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14874
14875 if (hrd == 0) {
14876 // timeout waiting for data
14877 return 0;
14878 }
14879
14880 /* abort if connection broken */
14881 if (hrd < 0) {
14882 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14883 return -1;
14884 }
14885
14886 if (hrd < (int) header_size) {
14887 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14888
14889 /* abort if connection broken */
14890 if (hrd1 <= 0) {
14891 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14892 return -1;
14893 }
14894
14895 hrd += hrd1;
14896 }
14897
14898 /* abort if connection broken */
14899 if (hrd != (int) header_size) {
14900 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14901 return -1;
14902 }
14903
14904 INT *pbh = (INT *) header_buf;
14905 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14906
14907 /* convert header little endian/big endian */
14908 if (psa->convert_flags) {
14915 }
14916
14917 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14918 int total_size = ALIGN8(event_size);
14919
14920 //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);
14921
14922 if (pevent->data_size == 0) {
14923 for (int i=0; i<5; i++) {
14924 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14925 }
14926 abort();
14927 }
14928
14929 /* check for sane event size */
14930 if (event_size <= 0 || total_size <= 0) {
14931 cm_msg(MERROR, "recv_event_server",
14932 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14933 event_size, total_size);
14934 return -1;
14935 }
14936
14937 //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);
14938
14939
14940 int bufsize = sizeof(INT) + total_size;
14941
14942 // Second, check that output buffer is big enough
14943
14944 /* check if data part fits in buffer */
14945 if (*pbuffer_size < bufsize) {
14946 int newsize = 1024 + ALIGN8(bufsize);
14947
14948 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14949
14950 char *newbuf = (char *) realloc(*pbuffer, newsize);
14951 if (newbuf == NULL) {
14952 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14953 newsize);
14954 return -1;
14955 }
14956 *pbuffer = newbuf;
14957 *pbuffer_size = newsize;
14958 }
14959
14960 // Third, copy header into output buffer
14961
14962 memcpy(*pbuffer, header_buf, header_size);
14963
14964 // Forth, read the event data
14965
14966 int to_read = sizeof(INT) + total_size - header_size;
14967 int rptr = header_size;
14968
14969 if (to_read > 0) {
14970 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14971
14972 /* abort if connection broken */
14973 if (drd <= 0) {
14974 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14975 return -1;
14976 }
14977 }
14978
14979 return bufsize;
14980}
14981
14982
14983/********************************************************************/
14984INT rpc_register_server(int port, int *plsock, int *pport)
14985/********************************************************************\
14986
14987 Routine: rpc_register_listener
14988
14989 Purpose: Register the calling process as a MIDAS RPC server. Note
14990 that cm_connnect_experiment must be called prior to any call of
14991 rpc_register_server.
14992
14993 Input:
14994 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14995
14996 Output:
14997 int *plsock Listener socket, can be NULL
14998 int *pport Port under which server is listening, can be NULL
14999
15000 Function value:
15001 RPC_SUCCESS Successful completion
15002 RPC_NET_ERROR Error in socket call
15003 RPC_NOT_REGISTERED cm_connect_experiment was not called
15004
15005\********************************************************************/
15006{
15007 int status;
15008 int lsock;
15009
15010 status = rpc_register_listener(port, NULL, &lsock, pport);
15011 if (status != RPC_SUCCESS)
15012 return status;
15013
15015 if (status != SS_SUCCESS)
15016 return status;
15017
15018 if (plsock)
15019 *plsock = lsock;
15020
15021 return RPC_SUCCESS;
15022}
15023
15024/********************************************************************/
15025INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
15026/********************************************************************\
15027
15028 Routine: rpc_register_listener
15029
15030 Purpose: Register the calling process as a MIDAS RPC server. Note
15031 that cm_connnect_experiment must be called prior to any call of
15032 rpc_register_listener.
15033
15034 Input:
15035 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
15036 INT *func Default dispatch function
15037
15038 Output:
15039 int *plsock Listener socket, should not be NULL
15040 int *pport Port under which server is listening, can be NULL
15041
15042 Function value:
15043 RPC_SUCCESS Successful completion
15044 RPC_NET_ERROR Error in socket call
15045 RPC_NOT_REGISTERED cm_connect_experiment was not called
15046
15047\********************************************************************/
15048{
15049 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
15051
15052 /* create a socket for listening */
15053 int lsock = 0;
15054 int lport = 0;
15055 std::string errmsg;
15056
15057 int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
15058
15059 if (status != SS_SUCCESS) {
15060 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
15061 return RPC_NET_ERROR;
15062 }
15063
15064 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
15065#if defined(F_SETFD) && defined(FD_CLOEXEC)
15066 status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
15067 if (status < 0) {
15068 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
15069 return RPC_NET_ERROR;
15070 }
15071#endif
15072
15073 /* return port wich OS has choosen */
15074 if (pport) {
15075 *pport = lport;
15076 }
15077
15078 if (plsock)
15079 *plsock = lsock;
15080
15081 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
15082
15083 return RPC_SUCCESS;
15084}
15085
15091
15093static int tls_size = 0;
15094
15095/********************************************************************/
15096static INT rpc_execute_old(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15097/********************************************************************\
15098
15099 Routine: rpc_execute
15100
15101 Purpose: Execute a RPC command received over the network
15102
15103 Input:
15104 INT sock TCP socket to which the result should be
15105 send back
15106
15107 char *buffer Command buffer
15108 INT convert_flags Flags for data conversion
15109
15110 Output:
15111 none
15112
15113 Function value:
15114 RPC_SUCCESS Successful completion
15115 RPC_INVALID_ID Invalid routine_id received
15116 RPC_NET_ERROR Error in socket call
15117 RPC_EXCEED_BUFFER Not enough memory for network buffer
15118 RPC_SHUTDOWN Shutdown requested
15119 SS_ABORT TCP connection broken
15120 SS_EXIT TCP connection closed
15121
15122\********************************************************************/
15123{
15124 INT i, routine_id, status;
15125 char *in_param_ptr, *out_param_ptr, *last_param_ptr;
15126 INT tid, flags;
15127 NET_COMMAND *nc_in, *nc_out;
15128 INT param_size, max_size;
15129 void *prpc_param[20];
15130 char debug_line[1024], *return_buffer;
15131 int return_buffer_size;
15132 int return_buffer_tls;
15133#ifdef FIXED_BUFFER
15134 int initial_buffer_size = NET_BUFFER_SIZE;
15135#else
15136 int initial_buffer_size = 1024;
15137#endif
15138
15139 /* return buffer must must use thread local storage multi-thread servers */
15140 if (!tls_size) {
15141 tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
15143 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15144 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15145 tls_size = 1;
15146 }
15147 for (i = 0; i < tls_size; i++)
15148 if (tls_buffer[i].thread_id == ss_gettid())
15149 break;
15150 if (i == tls_size) {
15151 /* new thread -> allocate new buffer */
15152 tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
15154 tls_buffer[tls_size].buffer_size = initial_buffer_size;
15155 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
15156 tls_size++;
15157 }
15158
15159 return_buffer_tls = i;
15160 return_buffer_size = tls_buffer[i].buffer_size;
15161 return_buffer = tls_buffer[i].buffer;
15162 assert(return_buffer);
15163
15164 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
15165 // and valgrind complains that we transmit uninitialized data
15166 //memset(return_buffer, 0, return_buffer_size);
15167
15168 /* extract pointer array to parameters */
15169 nc_in = (NET_COMMAND *) buffer;
15170
15171 /* convert header format (byte swapping) */
15172 if (convert_flags) {
15173 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15174 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15175 }
15176
15177 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15178 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15179 //}
15180
15181 /* no result return as requested */
15182 if (nc_in->header.routine_id & RPC_NO_REPLY)
15183 sock = 0;
15184
15185 /* find entry in rpc_list */
15186 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15187
15188 assert(xroutine_id == routine_id);
15189
15190 again:
15191
15192 in_param_ptr = nc_in->param;
15193
15194 nc_out = (NET_COMMAND *) return_buffer;
15195 out_param_ptr = nc_out->param;
15196
15197 sprintf(debug_line, "%s(", rl.name);
15198
15199 for (i = 0; rl.param[i].tid != 0; i++) {
15200 tid = rl.param[i].tid;
15201 flags = rl.param[i].flags;
15202
15203 if (flags & RPC_IN) {
15204 param_size = ALIGN8(rpc_tid_size(tid));
15205
15206 if (tid == TID_STRING || tid == TID_LINK)
15207 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
15208
15209 if (flags & RPC_VARARRAY) {
15210 /* for arrays, the size is stored as a INT in front of the array */
15211 param_size = *((INT *) in_param_ptr);
15212 if (convert_flags)
15213 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
15214 param_size = ALIGN8(param_size);
15215
15216 in_param_ptr += ALIGN8(sizeof(INT));
15217 }
15218
15219 if (tid == TID_STRUCT)
15220 param_size = ALIGN8(rl.param[i].n);
15221
15222 prpc_param[i] = in_param_ptr;
15223
15224 /* convert data format */
15225 if (convert_flags) {
15226 if (flags & RPC_VARARRAY)
15227 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15228 else
15229 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
15230 convert_flags);
15231 }
15232
15233 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
15234 if (rl.param[i].tid == TID_STRING) {
15235 /* check for long strings (db_create_record...) */
15236 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
15237 strcat(debug_line, "\"");
15238 strcat(debug_line, str.c_str());
15239 strcat(debug_line, "\"");
15240 } else
15241 strcat(debug_line, "...");
15242 } else
15243 strcat(debug_line, str.c_str());
15244
15245 in_param_ptr += param_size;
15246 }
15247
15248 if (flags & RPC_OUT) {
15249 param_size = ALIGN8(rpc_tid_size(tid));
15250
15251 if (flags & RPC_VARARRAY || tid == TID_STRING) {
15252
15253 /* save maximum array length from the value of the next argument.
15254 * this means RPC_OUT arrays and strings should always be passed like this:
15255 * rpc_call(..., array_ptr, array_max_size, ...); */
15256
15257 max_size = *((INT *) in_param_ptr);
15258
15259 if (convert_flags)
15260 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15261 max_size = ALIGN8(max_size);
15262
15263 *((INT *) out_param_ptr) = max_size;
15264
15265 /* save space for return array length */
15266 out_param_ptr += ALIGN8(sizeof(INT));
15267
15268 /* use maximum array length from input */
15269 param_size = max_size;
15270 }
15271
15272 if (rl.param[i].tid == TID_STRUCT)
15273 param_size = ALIGN8(rl.param[i].n);
15274
15275 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
15276#ifdef FIXED_BUFFER
15277 cm_msg(MERROR, "rpc_execute",
15278 "return parameters (%d) too large for network buffer (%d)",
15279 (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
15280
15281 return RPC_EXCEED_BUFFER;
15282#else
15283 int itls;
15284 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
15285
15286#if 0
15287 cm_msg(MINFO, "rpc_execute",
15288 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
15289 (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
15290#endif
15291
15292 itls = return_buffer_tls;
15293
15294 tls_buffer[itls].buffer_size = new_size;
15295 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
15296
15297 if (!tls_buffer[itls].buffer) {
15298 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
15299 return RPC_EXCEED_BUFFER;
15300 }
15301
15302 return_buffer_size = tls_buffer[itls].buffer_size;
15303 return_buffer = tls_buffer[itls].buffer;
15304 assert(return_buffer);
15305
15306 goto again;
15307#endif
15308 }
15309
15310 /* if parameter goes both directions, copy input to output */
15311 if (rl.param[i].flags & RPC_IN)
15312 memcpy(out_param_ptr, prpc_param[i], param_size);
15313
15314 if (_debug_print && !(flags & RPC_IN))
15315 strcat(debug_line, "-");
15316
15317 prpc_param[i] = out_param_ptr;
15318 out_param_ptr += param_size;
15319 }
15320
15321 if (rl.param[i + 1].tid)
15322 strcat(debug_line, ", ");
15323 }
15324
15325 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
15326
15327 strcat(debug_line, ")");
15328 rpc_debug_printf(debug_line);
15329
15330 last_param_ptr = out_param_ptr;
15331
15332 /*********************************\
15333 * call dispatch function *
15334 \*********************************/
15335 if (rl.dispatch)
15336 status = rl.dispatch(routine_id, prpc_param);
15337 else
15339
15340 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15342
15343 /* return immediately for closed down client connections */
15344 if (!sock && routine_id == RPC_ID_EXIT)
15345 return SS_EXIT;
15346
15347 if (!sock && routine_id == RPC_ID_SHUTDOWN)
15348 return RPC_SHUTDOWN;
15349
15350 /* Return if TCP connection broken */
15351 if (status == SS_ABORT)
15352 return SS_ABORT;
15353
15354 /* if sock == 0, we are in FTCP mode and may not sent results */
15355 if (!sock)
15356 return RPC_SUCCESS;
15357
15358 /* compress variable length arrays */
15359 out_param_ptr = nc_out->param;
15360 for (i = 0; rl.param[i].tid != 0; i++)
15361 if (rl.param[i].flags & RPC_OUT) {
15362 tid = rl.param[i].tid;
15363 flags = rl.param[i].flags;
15364 param_size = ALIGN8(rpc_tid_size(tid));
15365
15366 if (tid == TID_STRING) {
15367 max_size = *((INT *) out_param_ptr);
15368 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15369 // and prpc_param() is now pointing to the wrong place. here we know our string data
15370 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
15371 //const char* param_ptr = (char *) prpc_param[i];
15372 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
15373 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
15374 param_size = strlen(param_ptr) + 1;
15375 param_size = ALIGN8(param_size);
15376
15377 /* move string ALIGN8(sizeof(INT)) left */
15378 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
15379
15380 /* move remaining parameters to end of string */
15381 memmove(out_param_ptr + param_size,
15382 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
15383 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
15384 }
15385
15386 if (flags & RPC_VARARRAY) {
15387 /* store array length at current out_param_ptr */
15388 max_size = *((INT *) out_param_ptr);
15389 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
15390 // and prpc_param() is now pointing to the wrong place. instead, compute location
15391 // of next parameter using max_size. K.O.
15392 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
15393 if (rl.param[i+1].flags & RPC_OUT)
15394 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
15395 else
15396 param_size = *((INT *) prpc_param[i + 1]);
15397 *((INT *) out_param_ptr) = param_size; // store new array size
15398 if (convert_flags)
15399 rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
15400
15401 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
15402
15403 param_size = ALIGN8(param_size);
15404
15405 /* move remaining parameters to end of array */
15406 memmove(out_param_ptr + param_size,
15407 out_param_ptr + max_size,
15408 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
15409 }
15410
15411 if (tid == TID_STRUCT)
15412 param_size = ALIGN8(rl.param[i].n);
15413
15414 /* convert data format */
15415 if (convert_flags) {
15416 if (flags & RPC_VARARRAY)
15417 rpc_convert_data(out_param_ptr, tid,
15418 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15419 else
15420 rpc_convert_data(out_param_ptr, tid,
15421 rl.param[i].flags | RPC_OUTGOING,
15422 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15423 }
15424
15425 out_param_ptr += param_size;
15426 }
15427
15428 /* send return parameters */
15429 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15430 nc_out->header.routine_id = status;
15431 nc_out->header.param_size = param_size;
15432
15433 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15434
15435 /* convert header format (byte swapping) if necessary */
15436 if (convert_flags) {
15437 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15438 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15439 }
15440
15441 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15442 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15443
15444 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15445
15446 if (status < 0) {
15447 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15448 return RPC_NET_ERROR;
15449 }
15450
15451 /* print return buffer */
15452/*
15453 printf("Return buffer, ID %d:\n", routine_id);
15454 for (i=0; i<param_size ; i++)
15455 {
15456 status = (char) nc_out->param[i];
15457 printf("%02X ", status);
15458 if (i%8 == 7)
15459 printf("\n");
15460 }
15461*/
15462 /* return SS_EXIT if RPC_EXIT is called */
15463 if (routine_id == RPC_ID_EXIT)
15464 return SS_EXIT;
15465
15466 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15467 if (routine_id == RPC_ID_SHUTDOWN)
15468 return RPC_SHUTDOWN;
15469
15470 return RPC_SUCCESS;
15471}
15472
15473
15474
15475class RPE
15476{
15477public:
15478 size_t offset = 0;
15479 size_t arg_size = 0;
15480 size_t param_size = 0;
15482 size_t out_max_size = 0;
15483 std::string* ps = NULL;
15484 std::vector<char>* pv = NULL;
15485 void* pparam = NULL;
15486
15487 RPE() // ctor
15488 {
15489 }
15490 ~RPE() // dtor
15491 {
15492 if (ps)
15493 delete ps;
15494 if (pv)
15495 delete pv;
15496 ps = NULL; // poison the pointer
15497 pv = NULL; // poison the pointer
15498 pparam = NULL; // poison the pointer
15499 }
15500};
15501
15502/********************************************************************/
15503static INT rpc_execute_cxx(INT sock, int xroutine_id, const RPC_LIST& rl, char *buffer, INT convert_flags)
15504/********************************************************************\
15505
15506 Routine: rpc_execute
15507
15508 Purpose: Execute a RPC command received over the network
15509
15510 Input:
15511 INT sock TCP socket to which the result should be
15512 send back
15513
15514 char *buffer Command buffer
15515 INT convert_flags Flags for data conversion
15516
15517 Output:
15518 none
15519
15520 Function value:
15521 RPC_SUCCESS Successful completion
15522 RPC_INVALID_ID Invalid routine_id received
15523 RPC_NET_ERROR Error in socket call
15524 RPC_EXCEED_BUFFER Not enough memory for network buffer
15525 RPC_SHUTDOWN Shutdown requested
15526 SS_ABORT TCP connection broken
15527 SS_EXIT TCP connection closed
15528
15529\********************************************************************/
15530{
15531 INT status;
15532
15533 bool debug = false;
15534
15535 /* extract pointer array to parameters */
15536 NET_COMMAND* nc_in = (NET_COMMAND *) buffer;
15537
15538 /* convert header format (byte swapping) */
15539 if (convert_flags) {
15540 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
15541 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
15542 }
15543
15544 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
15545 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
15546 //}
15547
15548 /* no result return as requested */
15549 if (nc_in->header.routine_id & RPC_NO_REPLY)
15550 sock = 0;
15551
15552 int routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
15553
15554 assert(xroutine_id == routine_id);
15555
15556#if 0
15557 if (routine_id == RPC_TEST2)
15558 debug = true;
15559
15560 if (routine_id == RPC_TEST2_CXX)
15561 debug = true;
15562
15563 if (routine_id == RPC_TEST3_CXX)
15564 debug = true;
15565
15566 if (routine_id == RPC_TEST4_CXX)
15567 debug = true;
15568#endif
15569
15570 /* find entry in rpc_list */
15571
15572 char* in_param_ptr = (char*)nc_in->param;
15573
15574 if (debug)
15575 printf("rpc_execute_cxx: routine_id %d, name \"%s\"\n", routine_id, rl.name);
15576
15577 void *prpc_param[MAX_RPC_PARAMS];
15578
15579 size_t in_param_size[MAX_RPC_PARAMS];
15580 size_t in_param_offset[MAX_RPC_PARAMS];
15581
15582 std::vector<RPE> params;
15583
15584 int nparams = 0;
15585 for (int i = 0; rl.param[i].tid != 0; i++) {
15586 nparams++;
15587 }
15588
15589 params.resize(nparams);
15590
15591 size_t in_offset = 0;
15592
15593 for (int i = 0; i < nparams; i++) {
15594 in_param_size[i] = 0;
15595 in_param_offset[i] = 0;
15596
15597 int tid = rl.param[i].tid;
15598 int flags = rl.param[i].flags;
15599
15600 if (flags & RPC_IN) {
15601 int arg_size = rpc_tid_size(tid);
15602
15603 if (tid == TID_STRING || tid == TID_LINK) {
15604 arg_size = 1 + strlen((char *) (in_param_ptr));
15605 }
15606
15607 if (flags & RPC_VARARRAY) {
15608 /* for arrays, the size is stored as a INT in front of the array */
15609 int arg_size_align8 = *((INT *) in_param_ptr);
15610 if (convert_flags)
15611 rpc_convert_single(&arg_size_align8, TID_INT32, 0, convert_flags);
15612 in_param_ptr += ALIGN8(sizeof(INT));
15613 in_offset += ALIGN8(sizeof(INT));
15614 if (flags & RPC_CXX) {
15615 /* for std::vector<char> data, it is the true length of the array */
15616 arg_size = arg_size_align8;
15617 } else {
15618 /* true size is stored in the next parameter */
15619 arg_size = *((INT *) (((char*)in_param_ptr) + ALIGN8(arg_size_align8))); // NB: this ALIGN8() is redundant with ALIGN8() in the RPC client *encoder*
15620 if (convert_flags)
15621 rpc_convert_single(&arg_size, TID_INT32, 0, convert_flags);
15622 }
15623 //printf("RPC_VARARRAY: arg_size %d %d\n", arg_size_align8, arg_size);
15624
15625 if (ALIGN8(arg_size_align8) != ALIGN8(arg_size)) {
15626 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);
15627 return RPC_INVALID_ID;
15628 }
15629 }
15630
15631 if (tid == TID_STRUCT) {
15632 arg_size = rl.param[i].n;
15633 }
15634
15635 int param_size = ALIGN8(arg_size);
15636
15637 in_param_size[i] = param_size;
15638 in_param_offset[i] = in_offset;
15639
15640 params[i].offset = in_offset;
15641 params[i].arg_size = arg_size;
15642 params[i].param_size = param_size;
15643
15644 /* convert data format */
15645 if (convert_flags) {
15646 if (flags & RPC_VARARRAY) {
15647 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
15648 } else {
15649 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid), convert_flags);
15650 }
15651 }
15652
15653 in_param_ptr += param_size;
15654 in_offset += param_size;
15655 }
15656
15657 if (flags & RPC_OUT) {
15658 params[i].out_max_size = rpc_tid_size(tid);
15659
15660 if (flags & RPC_CXX) {
15661 params[i].out_max_size = 0; // no max size!
15662 } else if (flags & RPC_VARARRAY || tid == TID_STRING) {
15663
15664 /* save maximum array length from the value of the next argument.
15665 * this means RPC_OUT arrays and strings should always be passed like this:
15666 * rpc_call(..., array_ptr, array_max_size, ...); */
15667
15668 params[i].out_max_size_offset = in_offset;
15669
15670 INT max_size = *((INT *) in_param_ptr);
15671
15672 if (convert_flags)
15673 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
15674
15675 params[i].out_max_size = max_size;
15676 }
15677
15678 if (rl.param[i].tid == TID_STRUCT) {
15679 params[i].out_max_size = rl.param[i].n;
15680 }
15681 }
15682
15683 if (flags & RPC_CXX) {
15684 if (tid == TID_STRING) {
15685 params[i].ps = new std::string;
15686 if (flags & RPC_IN) {
15687 *(params[i].ps) = (char*)nc_in->param + in_param_offset[i];
15688 //printf("STRING %d decode [%s]\n", i, (char*)nc_in->param + in_param_offset[i]);
15689 }
15690 prpc_param[i] = (void*) params[i].ps;
15691 } else if (tid == TID_ARRAY) {
15692 params[i].pv = new std::vector<char>;
15693 if (flags & RPC_IN) {
15694 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);
15695 //printf("VECTOR %d decode %zu bytes\n", i, params[i].pv->size());
15696 }
15697 prpc_param[i] = (void*) params[i].pv;
15698 } else {
15699 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);
15700 return RPC_INVALID_ID;
15701 }
15702 } else {
15703 if ((flags & RPC_IN) && (flags & RPC_OUT)) {
15704 params[i].pv = new std::vector<char>;
15705 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);
15706 size_t want_size = params[i].out_max_size;
15707 //printf("param %d size %zu want %zu\n", i, params[i].pv->size(), want_size);
15708 if (params[i].pv->size() < want_size)
15709 params[i].pv->resize(want_size);
15710 prpc_param[i] = params[i].pv->data();
15711 } else if (flags & RPC_IN) {
15712 prpc_param[i] = (char*)nc_in->param + in_param_offset[i];
15713 //printf("param %d input value %d [%s]\n", i, *(int*)prpc_param[i], (char*)prpc_param[i]);
15714 } else if (flags & RPC_OUT) {
15715 params[i].pv = new std::vector<char>;
15716 params[i].pv->resize(params[i].out_max_size);
15717 prpc_param[i] = params[i].pv->data();
15718 //printf("param %d size %zu\n", i, params[i].pv->size());
15719 }
15720 }
15721
15722 if (debug)
15723 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]);
15724 }
15725
15726 if (debug)
15727 printf("rpc_execute_cxx: nc_in size %d, in_offset %zu\n", nc_in->header.param_size, in_offset);
15728
15729 if (routine_id == RPC_TEST2) {
15730 bool ok = true;
15731
15732 ok &= in_param_offset[ 0] == 0; ok &= in_param_size[ 0] == 8; // int_in
15733 ok &= in_param_offset[ 1] == 0; ok &= in_param_size[ 1] == 0; // int_out
15734 ok &= in_param_offset[ 2] == 8; ok &= in_param_size[ 2] == 8; // &int_inout
15735 ok &= in_param_offset[ 3] == 16; ok &= in_param_size[ 3] == 16; // string_in
15736 ok &= in_param_offset[ 4] == 0; ok &= in_param_size[ 4] == 0; // string_out
15737 ok &= in_param_offset[ 5] == 32; ok &= in_param_size[ 5] == 8; // string_out size
15738 ok &= in_param_offset[ 6] == 0; ok &= in_param_size[ 6] == 0; // string2_out
15739 ok &= in_param_offset[ 7] == 40; ok &= in_param_size[ 7] == 8; // string2_out size
15740 ok &= in_param_offset[ 8] == 48; ok &= in_param_size[ 8] == 16; // string_inout
15741 ok &= in_param_offset[ 9] == 64; ok &= in_param_size[ 9] == 8; // string_inout size
15742 ok &= in_param_offset[10] == 72; ok &= in_param_size[10] == 72; // struct_in
15743 ok &= in_param_offset[11] == 0; ok &= in_param_size[11] == 0; // struct_out
15744 ok &= in_param_offset[12] == 144; ok &= in_param_size[12] == 72; // struct_inout
15745 ok &= in_param_offset[13] == 224; ok &= in_param_size[13] == 40; // uint32_t[10] array inout
15746 ok &= in_param_offset[14] == 264; ok &= in_param_size[14] == 8; // &size
15747 ok &= in_param_offset[15] == 280; ok &= in_param_size[15] == 16; // char[10] array in
15748 ok &= in_param_offset[16] == 296; ok &= in_param_size[16] == 8; // size
15749 ok &= in_param_offset[17] == 0; ok &= in_param_size[17] == 0; // char[16] array out
15750 ok &= in_param_offset[18] == 304; ok &= in_param_size[18] == 8; // &size
15751 ok &= in_offset == 312;
15752
15753 if (!ok) {
15754 cm_msg(MERROR, "rpc_execute_cxx", "RPC_TEST2 parameters encoding error!");
15755 return RPC_INVALID_ID;
15756 }
15757 }
15758
15759 if (debug) {
15760 printf("rpc_execute_cxx: calling dispatch()\n");
15761 }
15762
15763 /*********************************\
15764 * call dispatch function *
15765 \*********************************/
15766 if (rl.dispatch)
15767 status = rl.dispatch(routine_id, prpc_param);
15768 else
15770
15771 if (debug) {
15772 printf("rpc_execute_cxx: dispatch() status %d\n", status);
15773 }
15774
15775 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
15777
15778 /* return immediately for closed down client connections */
15779 if (!sock && routine_id == RPC_ID_EXIT) {
15780 return SS_EXIT;
15781 }
15782
15783 if (!sock && routine_id == RPC_ID_SHUTDOWN) {
15784 return RPC_SHUTDOWN;
15785 }
15786
15787 /* Return if TCP connection broken */
15788 if (status == SS_ABORT) {
15789 return SS_ABORT;
15790 }
15791
15792 /* if sock == 0, we are in FTCP mode and may not sent results */
15793 if (!sock) {
15794 return RPC_SUCCESS;
15795 }
15796
15797 std::vector<char> v_out;
15798
15799 v_out.resize(sizeof(NET_COMMAND_HEADER));
15800
15801 for (int i = 0; i < nparams; i++) {
15802 if (rl.param[i].flags & RPC_OUT) {
15803 int tid = rl.param[i].tid;
15804 int flags = rl.param[i].flags;
15805
15806 if (flags & RPC_CXX) {
15807 if (tid == TID_STRING) {
15808 size_t arg_size = 1 + params[i].ps->length();
15809 size_t param_size = ALIGN8(arg_size);
15810
15811 if (debug)
15812 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());
15813
15814 v_out.insert(v_out.end(), params[i].ps->c_str(), params[i].ps->c_str() + arg_size);
15815 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15816 } else if (tid == TID_ARRAY) {
15817 size_t arg_size = params[i].pv->size();
15818 size_t param_size = ALIGN8(arg_size);
15819
15820 if (debug)
15821 printf("rpc_execute_cxx: param %2d, std::vector arg_size %zu, param_size %zu\n", i, arg_size, param_size);
15822
15823 char buf[ALIGN8(sizeof(INT))];
15824 *((INT *) buf) = arg_size; // store new array size
15825 if (convert_flags)
15826 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15827 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15828 v_out.insert(v_out.end(), params[i].pv->data(), params[i].pv->data() + arg_size); // data
15829 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15830 } else {
15831 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);
15832 return RPC_INVALID_ID;
15833 }
15834 } else {
15835 size_t convert_offset = 0;
15836 size_t convert_size = 0;
15837
15838 if (tid == TID_STRING) {
15839 size_t max_size = params[i].out_max_size;
15840 char* param_ptr = (char *) prpc_param[i];
15841 //printf("param %d string param [%s] max_size %zu\n", i, param_ptr, max_size);
15842 size_t arg_size = 1 + strlen(param_ptr);
15843 if (arg_size > max_size) {
15844 param_ptr[max_size] = 0; // truncate!
15845 size_t arg_size = 1 + strlen(param_ptr);
15846 assert(arg_size == max_size);
15847 }
15848 size_t param_size = ALIGN8(arg_size);
15849
15850 if (debug)
15851 printf("rpc_execute_cxx: param %2d, string max_size %zu, string_size %zu, param_size %zu\n", i, max_size, arg_size, param_size);
15852
15853 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size);
15854 v_out.resize(v_out.size() + param_size - arg_size); // pad to 8 bytes
15855 } else if (flags & RPC_VARARRAY) {
15856 size_t max_size = params[i].out_max_size;
15857 size_t arg_size = *((INT *) prpc_param[i + 1]);
15858 char* param_ptr = (char*)prpc_param[i];
15859 size_t param_size = ALIGN8(arg_size);
15860
15861 if (debug)
15862 printf("rpc_execute_cxx: param %2d, array max_size %zu, param_size %zu\n", i, max_size, param_size);
15863
15864 char buf[ALIGN8(sizeof(INT))];
15865 *((INT *) buf) = arg_size; // store new array size
15866 if (convert_flags)
15867 rpc_convert_single(buf, TID_INT32, RPC_OUTGOING, convert_flags);
15868 v_out.insert(v_out.end(), buf, buf + ALIGN8(sizeof(INT))); // 8 bytes of param_size
15869 convert_offset = v_out.size();
15870 convert_size = arg_size;
15871 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
15872 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15873 } else {
15874 char* param_ptr = (char*)prpc_param[i];
15875 size_t arg_size = rpc_tid_size(tid);
15876 if (tid == TID_STRUCT)
15877 arg_size = rl.param[i].n;
15878 size_t param_size = ALIGN8(arg_size);
15879
15880 if (debug) {
15881 if (tid == TID_INT) {
15882 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);
15883 } else {
15884 printf("rpc_execute_cxx: param %2d, tid %2d, arg_size %zu, param_size %zu\n", i, tid, arg_size, param_size);
15885 }
15886 }
15887
15888 convert_offset = v_out.size();
15889 convert_size = arg_size;
15890 v_out.insert(v_out.end(), param_ptr, param_ptr + arg_size); // data
15891 v_out.resize(v_out.size() + param_size - arg_size); // pad data to 8 bytes
15892 }
15893
15894 /* convert data format */
15895 if (convert_flags) {
15896 if (flags & RPC_VARARRAY)
15897 rpc_convert_data(v_out.data()+convert_offset, tid, rl.param[i].flags | RPC_OUTGOING, convert_size, convert_flags);
15898 else
15899 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);
15900 }
15901 }
15902 }
15903 }
15904
15905 NET_COMMAND* nc_out = (NET_COMMAND*)v_out.data();
15906
15907 /* send return parameters */
15908 nc_out->header.routine_id = status;
15909 nc_out->header.param_size = v_out.size() - sizeof(NET_COMMAND_HEADER);
15910
15911
15912 /* convert header format (byte swapping) if necessary */
15913 if (convert_flags) {
15914 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15915 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15916 }
15917
15918 status = send_tcp(sock, v_out.data(), v_out.size(), 0);
15919
15920 if (status < 0) {
15921 cm_msg(MERROR, "rpc_execute_cxx", "send_tcp() failed, status %d", status);
15922 return RPC_NET_ERROR;
15923 }
15924
15925 if (debug)
15926 printf("rpc_execute_cxx: send_tcp() sent %d bytes\n", status);
15927
15928 /* return SS_EXIT if RPC_EXIT is called */
15929 if (routine_id == RPC_ID_EXIT)
15930 return SS_EXIT;
15931
15932 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15933 if (routine_id == RPC_ID_SHUTDOWN)
15934 return RPC_SHUTDOWN;
15935
15936 return RPC_SUCCESS;
15937}
15938
15939/********************************************************************/
15941/********************************************************************\
15942 Routine: rpc_test_rpc
15943
15944 Purpose: Test RPC parameters encoding and decoding
15945
15946 Input:
15947 none
15948
15949 Output:
15950 none
15951
15952 Function value:
15953 RPC_SUCCESS Successful completion
15954
15955\********************************************************************/
15956{
15957 int status = RPC_SUCCESS;
15958
15959 printf("rpc_test_rpc_test2!\n");
15960
15961 int int_out = 0;
15962 int int_inout = 456;
15963
15964 char string_out[33];
15965 char string2_out[49];
15966
15967 char string_inout[25];
15968 strcpy(string_inout, "string_inout");
15969
15970 KEY struct_in;
15971
15972 struct_in.type = 111;
15973 struct_in.num_values = 222;
15974 strcpy(struct_in.name, "name");
15975 struct_in.last_written = 333;
15976
15977 KEY struct_out;
15978 KEY struct_inout;
15979
15980 struct_inout.type = 111111;
15981 struct_inout.num_values = 222222;
15982 strcpy(struct_inout.name, "name_name");
15983 struct_inout.last_written = 333333;
15984
15985 uint32_t dwordarray_inout[9];
15986 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15987
15988 for (int i=0; i<9; i++) {
15989 dwordarray_inout[i] = i*10;
15990 }
15991
15992 char array_in[10];
15993
15994 for (size_t i=0; i<sizeof(array_in); i++) {
15995 array_in[i] = 'a' + i;
15996 }
15997
15998 char array_out[16];
15999 size_t array_out_size = sizeof(array_out);
16000
16001 for (size_t i=0; i<sizeof(array_out); i++) {
16002 array_out[i] = 'Z';
16003 }
16004
16006 123,
16007 &int_out,
16008 &int_inout,
16009 "test string",
16010 string_out, sizeof(string_out),
16011 string2_out, sizeof(string2_out),
16012 string_inout, sizeof(string_inout),
16013 &struct_in,
16014 &struct_out,
16015 &struct_inout,
16016 dwordarray_inout, &dwordarray_inout_size,
16017 array_in, sizeof(array_in),
16018 array_out, &array_out_size
16019 );
16020
16021 if (status != RPC_SUCCESS) {
16022 printf("rpc_call(RPC_TEST2) status %d\n", status);
16023 return status;
16024 }
16025
16026 if (int_out != 789) {
16027 printf("int_out mismatch!\n");
16028 status = 0;
16029 }
16030
16031 if (int_inout != 456*2) {
16032 printf("int_inout mismatch!\n");
16033 status = 0;
16034 }
16035
16036 if (strcmp(string_out, "string_out") != 0) {
16037 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16038 status = 0;
16039 }
16040
16041 if (strcmp(string2_out, "second string_out") != 0) {
16042 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
16043 status = 0;
16044 }
16045
16046 if (strcmp(string_inout, "return string_inout") != 0) {
16047 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
16048 status = 0;
16049 }
16050
16051 KEY* pkey;
16052
16053 pkey = &struct_in;
16054
16055 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16056
16057 pkey = &struct_out;
16058
16059 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16060 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);
16061 status = 0;
16062 }
16063
16064 pkey = &struct_inout;
16065
16066 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16067 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);
16068 status = 0;
16069 }
16070
16071 if (dwordarray_inout_size != 4*5) {
16072 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16073 status = 0;
16074 } else {
16075 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16076 if (dwordarray_inout[i] != i*10+i) {
16077 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16078 status = 0;
16079 }
16080 }
16081 }
16082
16083 //printf("array_out_size %d\n", array_out_size);
16084 //for (int i=0; i<array_out_size; i++) {
16085 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16086 //}
16087
16088 if (array_out_size != 15) {
16089 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16090 status = 0;
16091 } else {
16092 if (strcmp(array_out, "test test test") != 0) {
16093 printf("array_out data mismatch\n");
16094 status = 0;
16095 }
16096 }
16097
16098 return status;
16099}
16100
16101/********************************************************************/
16103/********************************************************************\
16104 Routine: rpc_test_rpc
16105
16106 Purpose: Test RPC parameters encoding and decoding
16107
16108 Input:
16109 none
16110
16111 Output:
16112 none
16113
16114 Function value:
16115 RPC_SUCCESS Successful completion
16116
16117\********************************************************************/
16118{
16119 int status = RPC_SUCCESS;
16120
16121 printf("rpc_test_rpc_test2_cxx!\n");
16122
16123 int int_out = 0;
16124 int int_inout = 456;
16125
16126 char string_out[33];
16127 std::string string2_out;
16128 std::string string_inout = "string_inout";
16129
16130 KEY struct_in;
16131
16132 struct_in.type = 111;
16133 struct_in.num_values = 222;
16134 strcpy(struct_in.name, "name");
16135 struct_in.last_written = 333;
16136
16137 KEY struct_out;
16138 KEY struct_inout;
16139
16140 struct_inout.type = 111111;
16141 struct_inout.num_values = 222222;
16142 strcpy(struct_inout.name, "name_name");
16143 struct_inout.last_written = 333333;
16144
16145 uint32_t dwordarray_inout[9];
16146 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16147
16148 for (int i=0; i<9; i++) {
16149 dwordarray_inout[i] = i*10;
16150 }
16151
16152 std::vector<char> array_in;
16153 int array_in_size = 10;
16154
16155 for (int i=0; i<array_in_size; i++) {
16156 array_in.push_back('a' + i);
16157 }
16158
16159 std::vector<char> array_out;
16160 size_t array_out_size = 16;
16161
16163 123,
16164 &int_out,
16165 &int_inout,
16166 "test string",
16167 string_out, sizeof(string_out),
16168 &string2_out, 48,
16169 &string_inout, 25,
16170 &struct_in,
16171 &struct_out,
16172 &struct_inout,
16173 dwordarray_inout, &dwordarray_inout_size,
16174 &array_in, array_in_size,
16175 &array_out, &array_out_size
16176 );
16177
16178 if (status != RPC_SUCCESS) {
16179 printf("rpc_call(RPC_TEST2_CXX) status %d\n", status);
16180 return status;
16181 }
16182
16183 if (int_out != 789) {
16184 printf("int_out mismatch!\n");
16185 status = 0;
16186 }
16187
16188 if (int_inout != 456*2) {
16189 printf("int_inout mismatch!\n");
16190 status = 0;
16191 }
16192
16193 if (strcmp(string_out, "string_out") != 0) {
16194 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16195 status = 0;
16196 }
16197
16198 if (string2_out != "second string_out") {
16199 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16200 status = 0;
16201 }
16202
16203 if (string_inout != "return string_inout") {
16204 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16205 status = 0;
16206 }
16207
16208 KEY* pkey;
16209
16210 pkey = &struct_in;
16211
16212 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16213
16214 pkey = &struct_out;
16215
16216 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16217 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);
16218 status = 0;
16219 }
16220
16221 pkey = &struct_inout;
16222
16223 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16224 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);
16225 status = 0;
16226 }
16227
16228 if (dwordarray_inout_size != 4*5) {
16229 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16230 status = 0;
16231 } else {
16232 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16233 if (dwordarray_inout[i] != i*10+i) {
16234 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16235 status = 0;
16236 }
16237 }
16238 }
16239
16240 //printf("array_out_size %d\n", array_out_size);
16241 //for (int i=0; i<array_out_size; i++) {
16242 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16243 //}
16244
16245 if (array_out_size != 15) {
16246 printf("array_out_size mismatch %d vs %d\n", (int)array_out_size, 15);
16247 status = 0;
16248 } else if (array_out.size() != 15) {
16249 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16250 status = 0;
16251 } else {
16252 if (strcmp(array_out.data(), "test test test") != 0) {
16253 printf("array_out data mismatch\n");
16254 status = 0;
16255 }
16256 }
16257
16258 return status;
16259}
16260
16261/********************************************************************/
16263/********************************************************************\
16264 Routine: rpc_test_rpc
16265
16266 Purpose: Test RPC parameters encoding and decoding
16267
16268 Input:
16269 none
16270
16271 Output:
16272 none
16273
16274 Function value:
16275 RPC_SUCCESS Successful completion
16276
16277\********************************************************************/
16278{
16279 int status = RPC_SUCCESS;
16280
16281 printf("rpc_test_rpc_test3_cxx!\n");
16282
16283 int int_out = 0;
16284 int int_inout = 456;
16285
16286 char string_out[33];
16287 std::string string2_out;
16288 std::string string_inout = "string_inout";
16289
16290 KEY struct_in;
16291
16292 struct_in.type = 111;
16293 struct_in.num_values = 222;
16294 strcpy(struct_in.name, "name");
16295 struct_in.last_written = 333;
16296
16297 KEY struct_out;
16298 KEY struct_inout;
16299
16300 struct_inout.type = 111111;
16301 struct_inout.num_values = 222222;
16302 strcpy(struct_inout.name, "name_name");
16303 struct_inout.last_written = 333333;
16304
16305 uint32_t dwordarray_inout[9];
16306 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
16307
16308 for (int i=0; i<9; i++) {
16309 dwordarray_inout[i] = i*10;
16310 }
16311
16312 std::vector<char> array_in;
16313 int array_in_size = 10;
16314
16315 for (int i=0; i<array_in_size; i++) {
16316 array_in.push_back('a' + i);
16317 }
16318
16319 std::vector<char> array_out;
16320
16322 123,
16323 &int_out,
16324 &int_inout,
16325 "test string",
16326 string_out, sizeof(string_out),
16327 &string2_out,
16328 &string_inout,
16329 &struct_in,
16330 &struct_out,
16331 &struct_inout,
16332 dwordarray_inout, &dwordarray_inout_size,
16333 &array_in,
16334 &array_out
16335 );
16336
16337 if (status != RPC_SUCCESS) {
16338 printf("rpc_call(RPC_TEST3_CXX) status %d\n", status);
16339 return status;
16340 }
16341
16342 if (int_out != 789) {
16343 printf("int_out mismatch!\n");
16344 status = 0;
16345 }
16346
16347 if (int_inout != 456*2) {
16348 printf("int_inout mismatch!\n");
16349 status = 0;
16350 }
16351
16352 if (strcmp(string_out, "string_out") != 0) {
16353 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
16354 status = 0;
16355 }
16356
16357 if (string2_out != "second string_out") {
16358 printf("string2_out mismatch [%s] vs [%s]\n", string2_out.c_str(), "second string_out");
16359 status = 0;
16360 }
16361
16362 if (string_inout != "return string_inout") {
16363 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16364 status = 0;
16365 }
16366
16367 KEY* pkey;
16368
16369 pkey = &struct_in;
16370
16371 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
16372
16373 pkey = &struct_out;
16374
16375 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
16376 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);
16377 status = 0;
16378 }
16379
16380 pkey = &struct_inout;
16381
16382 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
16383 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);
16384 status = 0;
16385 }
16386
16387 if (dwordarray_inout_size != 4*5) {
16388 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
16389 status = 0;
16390 } else {
16391 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
16392 if (dwordarray_inout[i] != i*10+i) {
16393 printf("dwordarray_inout[%d] data mismatch %d vs %zu\n", (int)i, dwordarray_inout[i], i*10+i);
16394 status = 0;
16395 }
16396 }
16397 }
16398
16399 //printf("array_out_size %d\n", array_out_size);
16400 //for (int i=0; i<array_out_size; i++) {
16401 // printf("array_out[%d] is %3d (%c)\n", i, array_out[i], array_out[i]);
16402 //}
16403
16404 if (array_out.size() != 15) {
16405 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16406 status = 0;
16407 } else {
16408 if (strcmp(array_out.data(), "test test test") != 0) {
16409 printf("array_out data mismatch\n");
16410 status = 0;
16411 }
16412 }
16413
16414 return status;
16415}
16416
16417/********************************************************************/
16419/********************************************************************\
16420 Routine: rpc_test_rpc
16421
16422 Purpose: Test RPC parameters encoding and decoding
16423
16424 Input:
16425 none
16426
16427 Output:
16428 none
16429
16430 Function value:
16431 RPC_SUCCESS Successful completion
16432
16433\********************************************************************/
16434{
16435 int status = RPC_SUCCESS;
16436
16437 printf("rpc_test_rpc_test4_cxx!\n");
16438
16439 int int_out = 0;
16440 int int_inout = 456;
16441
16442 std::string string_in = "test string";
16443 std::string string_out;
16444 std::string string_inout = "string_inout";
16445
16446 std::vector<char> array_in;
16447 int array_in_size = 10;
16448
16449 for (int i=0; i<array_in_size; i++) {
16450 array_in.push_back('a' + i);
16451 }
16452
16453 std::vector<char> array_out;
16454
16455 std::vector<char> array_inout;
16456 int array_inout_size = 6;
16457
16458 for (int i=0; i<array_inout_size; i++) {
16459 array_inout.push_back('0' + i);
16460 }
16461
16463 123,
16464 &int_out,
16465 &int_inout,
16466 &string_in,
16467 &string_out,
16468 &string_inout,
16469 &array_in,
16470 &array_out,
16471 &array_inout
16472 );
16473
16474 if (status != RPC_SUCCESS) {
16475 printf("rpc_call(RPC_TEST4_CXX) status %d\n", status);
16476 return status;
16477 }
16478
16479 if (int_out != 789) {
16480 printf("int_out mismatch!\n");
16481 status = 0;
16482 }
16483
16484 if (int_inout != 456*2) {
16485 printf("int_inout mismatch!\n");
16486 status = 0;
16487 }
16488
16489 if (string_out != "return string_out") {
16490 printf("string_out mismatch [%s] vs [%s]\n", string_out.c_str(), "return string_out");
16491 status = 0;
16492 }
16493
16494 if (string_inout != "return string_inout") {
16495 printf("string_inout mismatch [%s] vs [%s]\n", string_inout.c_str(), "return string_inout");
16496 status = 0;
16497 }
16498
16499 if (array_out.size() != 15) {
16500 printf("array_out.size() mismatch %d vs %d\n", (int)array_out.size(), 15);
16501 status = 0;
16502 } else {
16503 if (strcmp(array_out.data(), "test test test") != 0) {
16504 printf("array_out data mismatch\n");
16505 status = 0;
16506 }
16507 }
16508
16509 if (array_inout.size() != 12) {
16510 printf("array_inout.size() mismatch %d vs %d\n", (int)array_inout.size(), 12);
16511 status = 0;
16512 } else {
16513 for (int i=0; i<6; i++) {
16514 if (array_inout[i] != '0' + i) {
16515 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], ('0'+i));
16516 status = 0;
16517 }
16518 }
16519 for (int i=6; i<12; i++) {
16520 if (array_inout[i] != 2*('0' + (i-6))) {
16521 printf("array_inout data mismatch, index %d, value %d should be %d\n", i, array_inout[i], 2*('0'+i));
16522 status = 0;
16523 }
16524 }
16525 }
16526
16527 return status;
16528}
16529
16530/********************************************************************/
16532/********************************************************************\
16533 Routine: rpc_test_rpc
16534
16535 Purpose: Test RPC parameters encoding and decoding
16536
16537 Input:
16538 none
16539
16540 Output:
16541 none
16542
16543 Function value:
16544 RPC_SUCCESS Successful completion
16545
16546\********************************************************************/
16547{
16548 int status;
16550 if (status != RPC_SUCCESS)
16551 return status;
16553 if (status != RPC_SUCCESS)
16554 return status;
16556 if (status != RPC_SUCCESS)
16557 return status;
16559 if (status != RPC_SUCCESS)
16560 return status;
16561 return RPC_SUCCESS;
16562}
16563
16564static std::atomic_bool gAllowedHostsEnabled(false);
16565static std::vector<std::string> gAllowedHosts;
16566static std::mutex gAllowedHostsMutex;
16567
16568/********************************************************************/
16570/********************************************************************\
16571 Routine: rpc_clear_allowed_hosts
16572
16573 Purpose: Clear list of allowed hosts and permit connections from anybody
16574
16575 Input:
16576 none
16577
16578 Output:
16579 none
16580
16581 Function value:
16582 RPC_SUCCESS Successful completion
16583
16584\********************************************************************/
16585{
16586 gAllowedHostsMutex.lock();
16587 gAllowedHosts.clear();
16588 gAllowedHostsEnabled = false;
16589 gAllowedHostsMutex.unlock();
16590 return RPC_SUCCESS;
16591}
16592
16593/********************************************************************/
16594INT rpc_add_allowed_host(const char *hostname)
16595/********************************************************************\
16596 Routine: rpc_add_allowed_host
16597
16598 Purpose: Permit connections from listed hosts only
16599
16600 Input:
16601 none
16602
16603 Output:
16604 none
16605
16606 Function value:
16607 RPC_SUCCESS Successful completion
16608 RPC_NO_MEMORY Too many allowed hosts
16609
16610\********************************************************************/
16611{
16612 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
16613
16614 gAllowedHostsMutex.lock();
16615 gAllowedHosts.push_back(hostname);
16616 gAllowedHostsEnabled = true;
16617 gAllowedHostsMutex.unlock();
16618
16619 return RPC_SUCCESS;
16620}
16621
16622/********************************************************************/
16623INT rpc_check_allowed_host(const char *hostname)
16624/********************************************************************\
16625 Routine: rpc_check_allowed_host
16626
16627 Purpose: Check if hostname is permitted to connect
16628
16629 Function value:
16630 RPC_SUCCESS hostname is permitted to connect
16631 RPC_NOT_REGISTERED hostname not permitted to connect
16632
16633\********************************************************************/
16634{
16635 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
16636
16638 return RPC_SUCCESS;
16639
16640 if (strcmp(hostname, "localhost") == 0)
16641 return RPC_SUCCESS;
16642
16643 if (strcmp(hostname, "localhost.localdomain") == 0)
16644 return RPC_SUCCESS;
16645
16646 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
16647 return RPC_SUCCESS;
16648
16649 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
16650 return RPC_SUCCESS;
16651
16653
16654 gAllowedHostsMutex.lock();
16655
16656 for (const auto& h: gAllowedHosts) {
16657 if (h == hostname) {
16659 break;
16660 }
16661 }
16662
16663 gAllowedHostsMutex.unlock();
16664
16665 //if (status != RPC_SUCCESS)
16666 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
16667
16668 return status;
16669}
16670
16671/*------------------------------------------------------------------*/
16673{
16674 std::string hostname;
16675
16676 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
16677
16678 if (status != SS_SUCCESS)
16679 return status;
16680
16681 status = rpc_check_allowed_host(hostname.c_str());
16682
16683 if (status == RPC_SUCCESS)
16684 return RPC_SUCCESS;
16685
16686 static std::atomic_int max_report(10);
16687 if (max_report > 0) {
16688 max_report--;
16689 if (max_report == 0) {
16690 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
16691 } else {
16692 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());
16693 }
16694 }
16695
16696 return RPC_NET_ERROR;
16697}
16698
16699/********************************************************************/
16701/********************************************************************\
16702
16703 Routine: rpc_server_accept
16704
16705 Purpose: Accept new incoming connections
16706
16707 Input:
16708 INT lscok Listen socket
16709
16710 Output:
16711 none
16712
16713 Function value:
16714 RPC_SUCCESS Successful completion
16715 RPC_NET_ERROR Error in socket call
16716 RPC_CONNCLOSED Connection was closed
16717 RPC_SHUTDOWN Listener shutdown
16718 RPC_EXCEED_BUFFER Not enough memory for network buffer
16719
16720\********************************************************************/
16721{
16722 INT i;
16723 INT sock;
16724 char version[NAME_LENGTH], v1[32];
16725 char experiment[NAME_LENGTH];
16726 INT port1, port2, port3;
16727 char *ptr;
16728 char net_buffer[256];
16729 struct linger ling;
16730
16731 static struct callback_addr callback;
16732
16733 if (lsock > 0) {
16734 sock = accept(lsock, NULL, NULL);
16735
16736 if (sock == -1)
16737 return RPC_NET_ERROR;
16738 } else {
16739 /* lsock is stdin -> already connected from inetd */
16740
16741 sock = lsock;
16742 }
16743
16744 /* check access control list */
16747
16748 if (status != RPC_SUCCESS) {
16749 ss_socket_close(&sock);
16750 return RPC_NET_ERROR;
16751 }
16752 }
16753
16754 /* receive string with timeout */
16755 i = recv_string(sock, net_buffer, 256, 10000);
16756 rpc_debug_printf("Received command: %s", net_buffer);
16757
16758 if (i > 0) {
16759 char command = (char) toupper(net_buffer[0]);
16760
16761 //printf("rpc_server_accept: command [%c]\n", command);
16762
16763 switch (command) {
16764 case 'S': {
16765
16766 /*----------- shutdown listener ----------------------*/
16767 ss_socket_close(&sock);
16768 return RPC_SHUTDOWN;
16769 }
16770 case 'I': {
16771
16772 /*----------- return available experiments -----------*/
16773#ifdef LOCAL_ROUTINES
16774 exptab_struct exptab;
16775 cm_read_exptab(&exptab); // thread safe!
16776 for (unsigned i=0; i<exptab.exptab.size(); i++) {
16777 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
16778 const char* str = exptab.exptab[i].name.c_str();
16779 send(sock, str, strlen(str) + 1, 0);
16780 }
16781 send(sock, "", 1, 0);
16782#endif
16783 ss_socket_close(&sock);
16784 break;
16785 }
16786 case 'C': {
16787
16788 /*----------- connect to experiment -----------*/
16789
16790 /* get callback information */
16791 callback.experiment[0] = 0;
16792 port1 = port2 = version[0] = 0;
16793
16794 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
16795
16796 /* parse string in format "C port1 port2 port3 version expt" */
16797 /* example: C 51046 45838 56832 2.0.0 alpha */
16798
16799 port1 = strtoul(net_buffer + 2, &ptr, 0);
16800 port2 = strtoul(ptr, &ptr, 0);
16801 port3 = strtoul(ptr, &ptr, 0);
16802
16803 while (*ptr == ' ')
16804 ptr++;
16805
16806 i = 0;
16807 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
16808 version[i++] = *ptr++;
16809
16810 // ensure that we do not overwrite buffer "version"
16811 assert(i < (int) sizeof(version));
16812 version[i] = 0;
16813
16814 // skip wjatever is left from the "version" string
16815 for (; *ptr != 0 && *ptr != ' ';)
16816 ptr++;
16817
16818 while (*ptr == ' ')
16819 ptr++;
16820
16821 i = 0;
16822 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
16823 experiment[i++] = *ptr++;
16824
16825 // ensure that we do not overwrite buffer "experiment"
16826 assert(i < (int) sizeof(experiment));
16827 experiment[i] = 0;
16828
16830
16831 /* print warning if version patch level doesn't agree */
16832 mstrlcpy(v1, version, sizeof(v1));
16833 if (strchr(v1, '.'))
16834 if (strchr(strchr(v1, '.') + 1, '.'))
16835 *strchr(strchr(v1, '.') + 1, '.') = 0;
16836
16837 char str[100];
16838 mstrlcpy(str, cm_get_version(), sizeof(str));
16839 if (strchr(str, '.'))
16840 if (strchr(strchr(str, '.') + 1, '.'))
16841 *strchr(strchr(str, '.') + 1, '.') = 0;
16842
16843 if (strcmp(v1, str) != 0) {
16844 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
16845 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
16846 }
16847
16848 callback.host_port1 = (short) port1;
16849 callback.host_port2 = (short) port2;
16850 callback.host_port3 = (short) port3;
16852
16854
16855 if (status != SS_SUCCESS) {
16856 ss_socket_close(&sock);
16857 break;
16858 }
16859
16860#ifdef LOCAL_ROUTINES
16861 /* update experiment definition */
16862 exptab_struct exptab;
16863 cm_read_exptab(&exptab); // thread safe!
16864
16865 unsigned idx = 0;
16866 bool found = false;
16867 /* lookup experiment */
16868 if (equal_ustring(callback.experiment.c_str(), "Default")) {
16869 found = true;
16870 idx = 0;
16871 } else {
16872 for (idx = 0; idx < exptab.exptab.size(); idx++) {
16873 if (exptab.exptab[idx].name == callback.experiment) {
16874 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
16875 found = true;
16876 break;
16877 }
16878 }
16879 }
16880 }
16881
16882 if (!found) {
16883 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
16884
16885 send(sock, "2", 2, 0); /* 2 means exp. not found */
16886 ss_socket_close(&sock);
16887 break;
16888 }
16889
16890 callback.directory = exptab.exptab[idx].directory;
16891 callback.user = exptab.exptab[idx].user;
16892
16893 /* create a new process */
16894 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
16895 char debug_str[30];
16896
16897 sprintf(host_port1_str, "%d", callback.host_port1);
16898 sprintf(host_port2_str, "%d", callback.host_port2);
16899 sprintf(host_port3_str, "%d", callback.host_port3);
16900 sprintf(debug_str, "%d", callback.debug);
16901
16902 const char *mserver_path = rpc_get_mserver_path();
16903
16904 const char *argv[10];
16905 argv[0] = mserver_path;
16906 argv[1] = callback.host_name.c_str();
16907 argv[2] = host_port1_str;
16908 argv[3] = host_port2_str;
16909 argv[4] = host_port3_str;
16910 argv[5] = debug_str;
16911 argv[6] = callback.experiment.c_str();
16912 argv[7] = callback.directory.c_str();
16913 argv[8] = callback.user.c_str();
16914 argv[9] = NULL;
16915
16916 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
16917 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
16918 argv[9]);
16919
16920 status = ss_spawnv(P_NOWAIT, mserver_path, argv);
16921
16922 if (status != SS_SUCCESS) {
16923 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
16924
16925 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
16926 send(sock, str, strlen(str) + 1, 0);
16927 ss_socket_close(&sock);
16928 break;
16929 }
16930
16931 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
16932 send(sock, str, strlen(str) + 1, 0);
16933#endif // LOCAL_ROUTINES
16934 ss_socket_close(&sock);
16935
16936 break;
16937 }
16938 default: {
16939 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
16940 ss_socket_close(&sock);
16941 break;
16942 }
16943 }
16944 } else { /* if i>0 */
16945
16946 /* lingering needed for PCTCP */
16947 ling.l_onoff = 1;
16948 ling.l_linger = 0;
16949 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16950 ss_socket_close(&sock);
16951 }
16952
16953 return RPC_SUCCESS;
16954}
16955
16956/********************************************************************/
16958/********************************************************************\
16959
16960 Routine: rpc_client_accept
16961
16962 Purpose: midas program accept new RPC connection (run transitions, etc)
16963
16964 Input:
16965 INT lsock Listen socket
16966
16967 Output:
16968 none
16969
16970 Function value:
16971 RPC_SUCCESS Successful completion
16972 RPC_NET_ERROR Error in socket call
16973 RPC_CONNCLOSED Connection was closed
16974 RPC_SHUTDOWN Listener shutdown
16975 RPC_EXCEED_BUFFER Not enough memory for network buffer
16976
16977\********************************************************************/
16978{
16979 INT i, status;
16980 INT client_hw_type = 0, hw_type;
16981 std::string client_program;
16982 std::string host_name;
16983 INT convert_flags;
16984 char net_buffer[256], *p;
16985
16986 int sock = accept(lsock, NULL, NULL);
16987
16988 if (sock == -1)
16989 return RPC_NET_ERROR;
16990
16991 /* check access control list */
16994
16995 if (status != RPC_SUCCESS) {
16996 ss_socket_close(&sock);
16997 return RPC_NET_ERROR;
16998 }
16999 }
17000
17001 host_name = "(unknown)";
17002 client_program = "(unknown)";
17003
17004 /* receive string with timeout */
17005 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
17006 if (i <= 0) {
17007 ss_socket_close(&sock);
17008 return RPC_NET_ERROR;
17009 }
17010
17011 /* get remote computer info */
17012 p = strtok(net_buffer, " ");
17013 if (p != NULL) {
17014 client_hw_type = atoi(p);
17015 p = strtok(NULL, " ");
17016 }
17017 if (p != NULL) {
17018 //version = atoi(p);
17019 p = strtok(NULL, " ");
17020 }
17021 if (p != NULL) {
17022 client_program = p;
17023 p = strtok(NULL, " ");
17024 }
17025 if (p != NULL) {
17026 host_name = p;
17027 p = strtok(NULL, " ");
17028 }
17029
17030 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
17031
17033
17034 /* save information in _server_acception structure */
17035 sa->recv_sock = sock;
17036 sa->send_sock = 0;
17037 sa->event_sock = 0;
17038 sa->remote_hw_type = client_hw_type;
17039 sa->host_name = host_name;
17040 sa->prog_name = client_program;
17042 sa->watchdog_timeout = 0;
17043 sa->is_mserver = FALSE;
17044
17045 /* send my own computer id */
17046 hw_type = rpc_get_hw_type();
17047 std::string str = msprintf("%d %s", hw_type, cm_get_version());
17048 status = send(sock, str.c_str(), str.length() + 1, 0);
17049 if (status != (INT) str.length() + 1)
17050 return RPC_NET_ERROR;
17051
17052 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17053 sa->convert_flags = convert_flags;
17054
17056
17057 return RPC_SUCCESS;
17058}
17059
17060/********************************************************************/
17062/********************************************************************\
17063
17064 Routine: rpc_server_callback
17065
17066 Purpose: Callback a remote client. Setup _server_acception entry
17067 with optional conversion flags and establish two-way
17068 TCP connection.
17069
17070 Input:
17071 callback_addr pcallback Pointer to a callback structure
17072
17073 Output:
17074 none
17075
17076 Function value:
17077 RPC_SUCCESS Successful completion
17078
17079\********************************************************************/
17080{
17081 INT status;
17082 int recv_sock, send_sock, event_sock;
17083 char str[100];
17084 std::string client_program;
17085 INT client_hw_type, hw_type;
17086 INT convert_flags;
17087 char net_buffer[256];
17088 char *p;
17089 int flag;
17090
17091 /* copy callback information */
17092 struct callback_addr callback = *pcallback;
17093 //idx = callback.index;
17094
17095 std::string errmsg;
17096
17097 /* create new sockets for TCP */
17098 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
17099
17100 if (status != SS_SUCCESS) {
17101 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());
17102 ss_socket_close(&recv_sock);
17103 //ss_socket_close(&send_sock);
17104 //ss_socket_close(&event_sock);
17105 return RPC_NET_ERROR;
17106 }
17107
17108 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
17109
17110 if (status != SS_SUCCESS) {
17111 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());
17112 ss_socket_close(&recv_sock);
17113 ss_socket_close(&send_sock);
17114 //ss_socket_close(&event_sock);
17115 return RPC_NET_ERROR;
17116 }
17117
17118 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
17119
17120 if (status != SS_SUCCESS) {
17121 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());
17122 ss_socket_close(&recv_sock);
17123 ss_socket_close(&send_sock);
17124 ss_socket_close(&event_sock);
17125 return RPC_NET_ERROR;
17126 }
17127#ifndef OS_ULTRIX /* crashes ULTRIX... */
17128 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
17129 flag = 2 * 1024 * 1024;
17130 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
17131 if (status != 0)
17132 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
17133 strerror(errno));
17134#endif
17135
17136 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
17137 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
17138 ss_socket_close(&recv_sock);
17139 ss_socket_close(&send_sock);
17140 ss_socket_close(&event_sock);
17141 return RPC_NET_ERROR;
17142 }
17143 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
17144
17145 /* get remote computer info */
17146 client_hw_type = strtoul(net_buffer, &p, 0);
17147
17148 while (*p == ' ')
17149 p++;
17150
17151 client_program = p;
17152
17153 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
17154
17155 std::string host_name;
17156
17157 status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
17158
17159 if (status != SS_SUCCESS)
17160 host_name = "unknown";
17161
17162 //printf("rpc_server_callback: mserver acception\n");
17163
17165
17166 /* save information in _server_acception structure */
17167 sa->recv_sock = recv_sock;
17168 sa->send_sock = send_sock;
17169 sa->event_sock = event_sock;
17170 sa->remote_hw_type = client_hw_type;
17171 sa->host_name = host_name;
17172 sa->prog_name = client_program;
17174 sa->watchdog_timeout = 0;
17175 sa->is_mserver = TRUE;
17176
17177 assert(_mserver_acception == NULL);
17178
17179 _mserver_acception = sa;
17180
17181 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
17182
17183 /* send my own computer id */
17184 hw_type = rpc_get_hw_type();
17185 sprintf(str, "%d", hw_type);
17186 send(recv_sock, str, strlen(str) + 1, 0);
17187
17188 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
17189 sa->convert_flags = convert_flags;
17190
17192
17193 if (rpc_is_mserver()) {
17194 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
17195 }
17196
17197 return RPC_SUCCESS;
17198}
17199
17200
17201/********************************************************************/
17203/********************************************************************\
17204
17205 Routine: rpc_server_loop
17206
17207 Purpose: mserver main event loop
17208
17209\********************************************************************/
17210{
17211 while (1) {
17212 int status = ss_suspend(1000, 0);
17213
17214 if (status == SS_ABORT || status == SS_EXIT)
17215 break;
17216
17218 break;
17219
17220 /* check alarms, etc */
17222
17224 }
17225
17226 return RPC_SUCCESS;
17227}
17228
17229/********************************************************************/
17231/********************************************************************\
17232
17233 Routine: rpc_server_receive_rpc
17234
17235 Purpose: Receive rpc commands and execute them. Close the connection
17236 if client has broken TCP pipe.
17237
17238 Function value:
17239 RPC_SUCCESS Successful completion
17240 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17241 SS_EXIT Server connection was closed
17242 SS_ABORT Server connection was broken
17243
17244\********************************************************************/
17245{
17246 int status = 0;
17247 int remaining = 0;
17248
17249 char *buf = NULL;
17250 int bufsize = 0;
17251
17252 do {
17253 int n_received = recv_net_command_realloc(sa, &buf, &bufsize, &remaining);
17254
17255 if (n_received <= 0) {
17256 status = SS_ABORT;
17257 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d", n_received);
17258 goto error;
17259 }
17260
17261 /* extract pointer array to parameters */
17262 NET_COMMAND nc_in;
17263
17264 memcpy(&nc_in, buf, sizeof(nc_in));
17265
17266 /* convert header format (byte swapping) */
17267 if (sa->convert_flags) {
17270 }
17271
17272 int routine_id = nc_in.header.routine_id & ~RPC_NO_REPLY;
17273
17274 RPC_LIST rpc_entry;
17275 bool rpc_cxx = false;
17276
17277 status = rpc_find_rpc(routine_id, &rpc_entry, &rpc_cxx);
17278
17279 if (status != RPC_SUCCESS) {
17280 cm_msg(MERROR, "rpc_server_receive_rpc", "Unknown RPC routine_id %d", routine_id);
17281 goto error;
17282 }
17283
17284 if (rpc_cxx)
17285 status = rpc_execute_cxx(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17286 else
17287 status = rpc_execute_old(sa->recv_sock, routine_id, rpc_entry, buf, sa->convert_flags);
17288
17289 if (status == SS_ABORT) {
17290 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
17291 goto error;
17292 }
17293
17294 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
17295 if (rpc_is_mserver())
17296 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
17297 goto exit;
17298 }
17299
17300 } while (remaining);
17301
17302 if (buf) {
17303 free(buf);
17304 buf = NULL;
17305 bufsize = 0;
17306 }
17307
17308 return RPC_SUCCESS;
17309
17310 error:
17311
17312 {
17313 char str[80];
17314 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17315 if (strchr(str, '.'))
17316 *strchr(str, '.') = 0;
17317 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17318 }
17319
17320 exit:
17321
17323
17324 if (buf) {
17325 free(buf);
17326 buf = NULL;
17327 bufsize = 0;
17328 }
17329
17330 /* disconnect from experiment as MIDAS server */
17331 if (rpc_is_mserver()) {
17332
17333 if (status != SS_EXIT)
17334 cm_msg(MERROR, "rpc_server_receive_rpc", "mserver unexpected shutdown, status %d", status);
17335
17336 HNDLE hDB, hKey;
17337
17339
17340 /* only disconnect from experiment if previously connected.
17341 Necessary for pure RPC servers (RPC_SRVR) */
17342 if (hDB) {
17346
17348
17350 }
17351 }
17352
17353 bool is_mserver = sa->is_mserver;
17354
17355 sa->close();
17356
17357 /* signal caller a shutdonw */
17358 if (status == RPC_SHUTDOWN)
17359 return status;
17360
17361 /* only the mserver should stop on server connection closure */
17362 if (!is_mserver) {
17363 return SS_SUCCESS;
17364 }
17365
17366 return status;
17367}
17368
17369/********************************************************************/
17371/********************************************************************\
17372
17373 Routine: rpc_server_receive_event
17374
17375 Purpose: Receive event and dispatch it
17376
17377 Function value:
17378 RPC_SUCCESS Successful completion
17379 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
17380 SS_EXIT Server connection was closed
17381 SS_ABORT Server connection was broken
17382
17383\********************************************************************/
17384{
17385 int status = 0;
17386
17387 DWORD start_time = ss_millitime();
17388
17389 //
17390 // THIS IS NOT THREAD SAFE!!!
17391 //
17392 // IT IS ONLY USED BY THE MSERVER
17393 // MSERVER IS SINGLE-THREADED!!!
17394 //
17395
17396 static char *xbuf = NULL;
17397 static int xbufsize = 0;
17398 static bool xbufempty = true;
17399
17400 // short cut
17401 if (sa == NULL && xbufempty)
17402 return RPC_SUCCESS;
17403
17404 static bool recurse = false;
17405
17406 if (recurse) {
17407 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
17408 // do not do anything if we are called recursively
17409 // via recursive ss_suspend() or otherwise. K.O.
17410 if (xbufempty)
17411 return RPC_SUCCESS;
17412 else
17413 return BM_ASYNC_RETURN;
17414 }
17415
17416 recurse = true;
17417
17418 do {
17419 if (xbufempty && sa) {
17420 int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
17421
17422 if (n_received < 0) {
17423 status = SS_ABORT;
17424 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
17425 goto error;
17426 }
17427
17428 if (n_received == 0) {
17429 // no more data in the tcp socket
17430 recurse = false;
17431 return RPC_SUCCESS;
17432 }
17433
17434 xbufempty = false;
17435 }
17436
17437 if (xbufempty) {
17438 // no event in xbuf buffer
17439 recurse = false;
17440 return RPC_SUCCESS;
17441 }
17442
17443 /* send event to buffer */
17444 INT *pbh = (INT *) xbuf;
17445 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
17446
17447 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
17448
17449 //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);
17450
17451 if (status == SS_ABORT) {
17452 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
17453 goto error;
17454 }
17455
17456 if (status == BM_ASYNC_RETURN) {
17457 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
17458 recurse = false;
17459 return status;
17460 }
17461
17462 if (status != BM_SUCCESS) {
17463 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
17464 }
17465
17466 xbufempty = true;
17467
17468 /* repeat for maximum 0.5 sec */
17469 } while (ss_millitime() - start_time < 500);
17470
17471 recurse = false;
17472 return RPC_SUCCESS;
17473
17474 error:
17475
17476 {
17477 char str[80];
17478 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
17479 if (strchr(str, '.'))
17480 *strchr(str, '.') = 0;
17481 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
17482 }
17483
17484 //exit:
17485
17487
17488 /* disconnect from experiment as MIDAS server */
17489 if (rpc_is_mserver()) {
17490 HNDLE hDB, hKey;
17491
17493
17494 /* only disconnect from experiment if previously connected.
17495 Necessary for pure RPC servers (RPC_SRVR) */
17496 if (hDB) {
17500
17502
17504 }
17505 }
17506
17507 bool is_mserver = sa->is_mserver;
17508
17509 sa->close();
17510
17511 /* signal caller a shutdonw */
17512 if (status == RPC_SHUTDOWN)
17513 return status;
17514
17515 /* only the mserver should stop on server connection closure */
17516 if (!is_mserver) {
17517 return SS_SUCCESS;
17518 }
17519
17520 return status;
17521}
17522
17523
17524/********************************************************************/
17525int rpc_flush_event_socket(int timeout_msec)
17526/********************************************************************\
17527
17528 Routine: rpc_flush_event_socket
17529
17530 Purpose: Receive and en-buffer events from the mserver event socket
17531
17532 Function value:
17533 BM_SUCCESS Event socket is empty, all data was read an en-buffered
17534 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
17535 SS_EXIT Server connection was closed
17536 SS_ABORT Server connection was broken
17537
17538\********************************************************************/
17539{
17540 bool has_data = ss_event_socket_has_data();
17541
17542 //printf("ss_event_socket_has_data() returned %d\n", has_data);
17543
17544 if (has_data) {
17545 if (timeout_msec == BM_NO_WAIT) {
17546 return BM_ASYNC_RETURN;
17547 } else if (timeout_msec == BM_WAIT) {
17548 return BM_ASYNC_RETURN;
17549 } else {
17550 int status = ss_suspend(timeout_msec, MSG_BM);
17551 if (status == SS_ABORT || status == SS_EXIT)
17552 return status;
17553 return BM_ASYNC_RETURN;
17554 }
17555 }
17556
17557 int status = rpc_server_receive_event(0, NULL, timeout_msec);
17558
17559 //printf("rpc_server_receive_event() status %d\n", status);
17560
17561 if (status == BM_ASYNC_RETURN) {
17562 return BM_ASYNC_RETURN;
17563 }
17564
17565 if (status == SS_ABORT || status == SS_EXIT)
17566 return status;
17567
17568 return BM_SUCCESS;
17569}
17570
17571/********************************************************************/
17573/********************************************************************\
17574
17575 Routine: rpc_server_shutdown
17576
17577 Purpose: Shutdown RPC server, abort all connections
17578
17579 Input:
17580 none
17581
17582 Output:
17583 none
17584
17585 Function value:
17586 RPC_SUCCESS Successful completion
17587
17588\********************************************************************/
17589{
17590 //printf("rpc_server_shutdown!\n");
17591
17592 struct linger ling;
17593
17594 /* close all open connections */
17595 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17596 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
17598 /* lingering needed for PCTCP */
17599 ling.l_onoff = 1;
17600 ling.l_linger = 0;
17601 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17603
17604 if (sa->send_sock) {
17605 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17607 }
17608
17609 if (sa->event_sock) {
17610 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
17612 }
17613 }
17614 }
17615
17616 /* avoid memory leak */
17617 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17619 if (sa) {
17620 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
17621 if (sa == _mserver_acception) {
17622 // do not leave behind a stale pointer!
17623 _mserver_acception = NULL;
17624 }
17625 delete sa;
17626 _server_acceptions[idx] = NULL;
17627 }
17628 }
17629
17630 if (_rpc_registered) {
17633 }
17634
17635 /* free suspend structures */
17637
17638 return RPC_SUCCESS;
17639}
17640
17641
17642/********************************************************************/
17644/********************************************************************\
17645
17646 Routine: rpc_check_channels
17647
17648 Purpose: Check open rpc channels by sending watchdog messages
17649
17650 Input:
17651 none
17652
17653 Output:
17654 none
17655
17656 Function value:
17657 RPC_SUCCESS Channel is still alive
17658 RPC_NET_ERROR Connection is broken
17659
17660\********************************************************************/
17661{
17662 INT status;
17663 NET_COMMAND nc;
17664 fd_set readfds;
17665 struct timeval timeout;
17666
17667 //printf("rpc_check_channels!\n");
17668
17669 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
17670 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
17672 if (sa == NULL)
17673 continue;
17674
17675 if (sa->watchdog_timeout == 0) {
17676 continue;
17677 }
17678
17679 DWORD elapsed = ss_millitime() - sa->last_activity;
17680
17681 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
17682
17683 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
17684
17685 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
17686
17687 /* send a watchdog message */
17689 nc.header.param_size = 0;
17690
17691 int convert_flags = sa->convert_flags;
17692 if (convert_flags) {
17695 }
17696
17697 /* send the header to the client */
17698 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
17699
17700 if (i < 0) {
17701 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
17702 sa->prog_name.c_str(),
17703 sa->host_name.c_str(),
17704 sa->watchdog_timeout / 1000,
17705 i);
17706
17707 /* disconnect from experiment */
17708 if (rpc_is_mserver()) {
17710 return RPC_NET_ERROR;
17711 }
17712
17713 sa->close();
17714 return RPC_NET_ERROR;
17715 }
17716
17717 DWORD timeout_end_ms = ss_millitime() + sa->watchdog_timeout;
17718
17719 while (1) {
17720 FD_ZERO(&readfds);
17721 FD_SET(sa->send_sock, &readfds);
17722 FD_SET(sa->recv_sock, &readfds);
17723
17724 timeout.tv_sec = 1;
17725 timeout.tv_usec = 0;
17726
17727 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
17728
17729 DWORD now = ss_millitime();
17730
17731 //printf("waiting for reply: %d %d, diff %d, select() status %d\n", now, timeout_end_ms, timeout_end_ms - now, status);
17732
17733 if (now > timeout_end_ms) // timeout
17734 break;
17735
17736 if (status > 0) // select has something to read
17737 break;
17738
17739 // select() returned 0, timeout
17740 // select() returned -1, error, likely EAGAIN or EINTR
17741
17744 }
17745
17746 if (!FD_ISSET(sa->send_sock, &readfds) &&
17747 !FD_ISSET(sa->recv_sock, &readfds)) {
17748
17749 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
17750 sa->prog_name.c_str(),
17751 sa->host_name.c_str(),
17752 sa->watchdog_timeout / 1000);
17753
17754 /* disconnect from experiment */
17755 if (rpc_is_mserver()) {
17757 return RPC_NET_ERROR;
17758 }
17759
17760 sa->close();
17761 return RPC_NET_ERROR;
17762 }
17763
17764 /* receive result on send socket */
17765 if (FD_ISSET(sa->send_sock, &readfds)) {
17766 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
17767 if (i <= 0) {
17768 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
17769 sa->prog_name.c_str(),
17770 sa->host_name.c_str(),
17771 sa->watchdog_timeout / 1000,
17772 i);
17773
17774 /* disconnect from experiment */
17775 if (rpc_is_mserver()) {
17777 return RPC_NET_ERROR;
17778 }
17779
17780 sa->close();
17781 return RPC_NET_ERROR;
17782 }
17783 }
17784 }
17785 }
17786 }
17787
17788 return RPC_SUCCESS;
17789}
17790
17798/********************************************************************\
17799* *
17800* Bank functions *
17801* *
17802\********************************************************************/
17803
17804/********************************************************************/
17810void bk_init(void *event) {
17811 ((BANK_HEADER *) event)->data_size = 0;
17812 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
17813}
17814
17816#ifndef DOXYGEN_SHOULD_SKIP_THIS
17817
17818/********************************************************************/
17819BOOL bk_is32(const void *event)
17820/********************************************************************\
17821
17822 Routine: bk_is32
17823
17824 Purpose: Return true if banks inside event are 32-bit banks
17825
17826 Input:
17827 void *event pointer to the event
17828
17829 Output:
17830 none
17831
17832 Function value:
17833 none
17834
17835\********************************************************************/
17836{
17837 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
17838}
17839
17840/********************************************************************/
17841BOOL bk_is32a(const void *event)
17842/********************************************************************\
17843
17844 Routine: bk_is32a
17845
17846 Purpose: Return true if banks inside event are 32-bit banks
17847 and banks are 64-bit aligned
17848
17849 Input:
17850 void *event pointer to the event
17851
17852 Output:
17853 none
17854
17855 Function value:
17856 none
17857
17858\********************************************************************/
17859{
17860 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
17861}
17862
17864#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17865
17866/********************************************************************/
17873void bk_init32(void *event) {
17874 ((BANK_HEADER *) event)->data_size = 0;
17875 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
17876}
17877
17878/********************************************************************/
17886void bk_init32a(void *event) {
17887 ((BANK_HEADER *) event)->data_size = 0;
17889}
17890
17891/********************************************************************/
17899INT bk_size(const void *event) {
17900 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
17901}
17902
17903static void copy_bk_name(char* dst, const char* src)
17904{
17905 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
17906
17907 if (src[0] == 0) {
17908 // invalid empty name
17909 dst[0] = 0;
17910 dst[1] = 0;
17911 dst[2] = 0;
17912 dst[3] = 0;
17913 return;
17914 }
17915
17916 dst[0] = src[0];
17917
17918 if (src[1] == 0) {
17919 dst[1] = 0;
17920 dst[2] = 0;
17921 dst[3] = 0;
17922 return;
17923 }
17924
17925 dst[1] = src[1];
17926
17927 if (src[2] == 0) {
17928 dst[2] = 0;
17929 dst[3] = 0;
17930 return;
17931 }
17932
17933 dst[2] = src[2];
17934
17935 if (src[3] == 0) {
17936 dst[3] = 0;
17937 return;
17938 }
17939
17940 dst[3] = src[3];
17941}
17942
17943/********************************************************************/
17965void bk_create(void *event, const char *name, WORD type, void **pdata) {
17966 if (bk_is32a((BANK_HEADER *) event)) {
17967 if (((PTYPE) event & 0x07) != 0) {
17968 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
17969 return;
17970 }
17971 BANK32A *pbk32a;
17972
17973 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17974 copy_bk_name(pbk32a->name, name);
17975 pbk32a->type = type;
17976 pbk32a->data_size = 0;
17977 *pdata = pbk32a + 1;
17978 } else if (bk_is32((BANK_HEADER *) event)) {
17979 BANK32 *pbk32;
17980
17981 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17982 copy_bk_name(pbk32->name, name);
17983 pbk32->type = type;
17984 pbk32->data_size = 0;
17985 *pdata = pbk32 + 1;
17986 } else {
17987 BANK *pbk;
17988
17989 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
17990 copy_bk_name(pbk->name, name);
17991 pbk->type = type;
17992 pbk->data_size = 0;
17993 *pdata = pbk + 1;
17994 }
17995}
17996
17998#ifndef DOXYGEN_SHOULD_SKIP_THIS
17999
18000/********************************************************************/
18008INT bk_copy(char *pevent, char *psrce, const char *bkname) {
18009
18010 INT status;
18011 DWORD bklen, bktype, bksze;
18012 BANK_HEADER *psBkh;
18013 BANK *psbkh;
18014 char *pdest;
18015 void *psdata;
18016
18017 // source pointing on the BANKxx
18018 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
18019 // Find requested bank
18020 status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
18021 // Return 0 if not found
18022 if (status != SUCCESS) return 0;
18023
18024 // Check bank type...
18025 // You cannot mix BANK and BANK32 so make sure all the FE use either
18026 // bk_init(pevent) or bk_init32(pevent).
18027 if (bk_is32a(psBkh)) {
18028
18029 // pointer to the source bank header
18030 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
18031 // Data size in the bank
18032 bksze = psbkh32a->data_size;
18033
18034 // Get to the end of the event
18035 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18036 // Copy from BANK32 to end of Data
18037 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
18038 // Bring pointer to the next free location
18039 pdest += ALIGN8(bksze) + sizeof(BANK32A);
18040
18041 } else if (bk_is32(psBkh)) {
18042
18043 // pointer to the source bank header
18044 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
18045 // Data size in the bank
18046 bksze = psbkh32->data_size;
18047
18048 // Get to the end of the event
18049 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18050 // Copy from BANK32 to end of Data
18051 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
18052 // Bring pointer to the next free location
18053 pdest += ALIGN8(bksze) + sizeof(BANK32);
18054
18055 } else {
18056
18057 // pointer to the source bank header
18058 psbkh = ((BANK *) psdata - 1);
18059 // Data size in the bank
18060 bksze = psbkh->data_size;
18061
18062 // Get to the end of the event
18063 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
18064 // Copy from BANK to end of Data
18065 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
18066 // Bring pointer to the next free location
18067 pdest += ALIGN8(bksze) + sizeof(BANK);
18068 }
18069
18070 // Close bank (adjust BANK_HEADER size)
18071 bk_close(pevent, pdest);
18072 // Adjust EVENT_HEADER size
18073 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
18074 return SUCCESS;
18075}
18076
18077/********************************************************************/
18078int bk_delete(void *event, const char *name)
18079/********************************************************************\
18080
18081 Routine: bk_delete
18082
18083 Purpose: Delete a MIDAS bank inside an event
18084
18085 Input:
18086 void *event pointer to the event
18087 char *name Name of bank (exactly four letters)
18088
18089 Function value:
18090 CM_SUCCESS Bank has been deleted
18091 0 Bank has not been found
18092
18093\********************************************************************/
18094{
18095 BANK *pbk;
18096 DWORD dname;
18097 int remaining;
18098
18099 if (bk_is32a((BANK_HEADER *) event)) {
18100 /* locate bank */
18101 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18102 copy_bk_name((char *) &dname, name);
18103 do {
18104 if (*((DWORD *) pbk32a->name) == dname) {
18105 /* bank found, delete it */
18106 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18107 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18108
18109 /* reduce total event size */
18110 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
18111
18112 /* copy remaining bytes */
18113 if (remaining > 0)
18114 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
18115 return CM_SUCCESS;
18116 }
18117
18118 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18119 } while ((DWORD) ((char *) pbk32a - (char *) event) <
18120 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18121 } else if (bk_is32((BANK_HEADER *) event)) {
18122 /* locate bank */
18123 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18124 copy_bk_name((char *) &dname, name);
18125 do {
18126 if (*((DWORD *) pbk32->name) == dname) {
18127 /* bank found, delete it */
18128 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18129 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18130
18131 /* reduce total event size */
18132 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
18133
18134 /* copy remaining bytes */
18135 if (remaining > 0)
18136 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
18137 return CM_SUCCESS;
18138 }
18139
18140 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18141 } while ((DWORD) ((char *) pbk32 - (char *) event) <
18142 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18143 } else {
18144 /* locate bank */
18145 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18146 copy_bk_name((char *) &dname, name);
18147 do {
18148 if (*((DWORD *) pbk->name) == dname) {
18149 /* bank found, delete it */
18150 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
18151 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18152
18153 /* reduce total event size */
18154 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
18155
18156 /* copy remaining bytes */
18157 if (remaining > 0)
18158 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
18159 return CM_SUCCESS;
18160 }
18161
18162 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18163 } while ((DWORD) ((char *) pbk - (char *) event) <
18164 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
18165 }
18166
18167 return 0;
18168}
18169
18171#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18172
18173/********************************************************************/
18184INT bk_close(void *event, void *pdata) {
18185 if (bk_is32a((BANK_HEADER *) event)) {
18186 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18187 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
18188 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
18189 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]);
18190 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
18191 return pbk32a->data_size;
18192 } else if (bk_is32((BANK_HEADER *) event)) {
18193 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18194 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
18195 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
18196 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]);
18197 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
18198 return pbk32->data_size;
18199 } else {
18200 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
18201 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
18202 if (size > 0xFFFF) {
18203 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18204 size = 0;
18205 }
18206 pbk->data_size = (WORD) (size);
18207 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
18208 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]);
18209 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
18210 if (size > 0xFFFF) {
18211 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
18212 size = 0;
18213 }
18214 ((BANK_HEADER *) event)->data_size = size;
18215 return pbk->data_size;
18216 }
18217}
18218
18219/********************************************************************/
18244INT bk_list(const void *event, char *bklist) { /* Full event */
18245 INT nbk;
18246 BANK *pmbk = NULL;
18247 BANK32 *pmbk32 = NULL;
18248 BANK32A *pmbk32a = NULL;
18249 char *pdata;
18250
18251 /* compose bank list */
18252 bklist[0] = 0;
18253 nbk = 0;
18254 do {
18255 /* scan all banks for bank name only */
18256 if (bk_is32a(event)) {
18257 bk_iterate32a(event, &pmbk32a, &pdata);
18258 if (pmbk32a == NULL)
18259 break;
18260 } else if (bk_is32(event)) {
18261 bk_iterate32(event, &pmbk32, &pdata);
18262 if (pmbk32 == NULL)
18263 break;
18264 } else {
18265 bk_iterate(event, &pmbk, &pdata);
18266 if (pmbk == NULL)
18267 break;
18268 }
18269 nbk++;
18270
18271 if (nbk > BANKLIST_MAX) {
18272 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
18273 return (nbk - 1);
18274 }
18275 if (bk_is32a(event))
18276 strncat(bklist, (char *) pmbk32a->name, 4);
18277 else if (bk_is32(event))
18278 strncat(bklist, (char *) pmbk32->name, 4);
18279 else
18280 strncat(bklist, (char *) pmbk->name, 4);
18281 } while (1);
18282 return (nbk);
18283}
18284
18285/********************************************************************/
18293INT bk_locate(const void *event, const char *name, void *pdata) {
18294 BANK *pbk;
18295 BANK32 *pbk32;
18296 BANK32A *pbk32a;
18297 DWORD dname;
18298
18299 if (bk_is32a(event)) {
18300 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18301 copy_bk_name((char *) &dname, name);
18302 while ((DWORD) ((char *) pbk32a - (char *) event) <
18303 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18304 if (*((DWORD *) pbk32a->name) == dname) {
18305 *((void **) pdata) = pbk32a + 1;
18306 if (tid_size[pbk32a->type & 0xFF] == 0)
18307 return pbk32a->data_size;
18308 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
18309 }
18310 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18311 }
18312 } else if (bk_is32(event)) {
18313 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
18314 copy_bk_name((char *) &dname, name);
18315 while ((DWORD) ((char *) pbk32 - (char *) event) <
18316 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18317 if (*((DWORD *) pbk32->name) == dname) {
18318 *((void **) pdata) = pbk32 + 1;
18319 if (tid_size[pbk32->type & 0xFF] == 0)
18320 return pbk32->data_size;
18321 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
18322 }
18323 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18324 }
18325 } else {
18326 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18327 copy_bk_name((char *) &dname, name);
18328 while ((DWORD) ((char *) pbk - (char *) event) <
18329 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18330 if (*((DWORD *) pbk->name) == dname) {
18331 *((void **) pdata) = pbk + 1;
18332 if (tid_size[pbk->type & 0xFF] == 0)
18333 return pbk->data_size;
18334 return pbk->data_size / tid_size[pbk->type & 0xFF];
18335 }
18336 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18337 }
18338
18339 }
18340
18341 /* bank not found */
18342 *((void **) pdata) = NULL;
18343 return 0;
18344}
18345
18346/********************************************************************/
18356INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
18357 DWORD dname;
18358
18359 if (bk_is32a(pbkh)) {
18360 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
18361 copy_bk_name((char *) &dname, name);
18362 do {
18363 if (*((DWORD *) pbk32a->name) == dname) {
18364 *((void **) pdata) = pbk32a + 1;
18365 if (tid_size[pbk32a->type & 0xFF] == 0)
18366 *bklen = pbk32a->data_size;
18367 else
18368 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
18369
18370 *bktype = pbk32a->type;
18371 return 1;
18372 }
18373 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18374 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18375 } else if (bk_is32(pbkh)) {
18376 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
18377 copy_bk_name((char *) &dname, name);
18378 do {
18379 if (*((DWORD *) pbk32->name) == dname) {
18380 *((void **) pdata) = pbk32 + 1;
18381 if (tid_size[pbk32->type & 0xFF] == 0)
18382 *bklen = pbk32->data_size;
18383 else
18384 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
18385
18386 *bktype = pbk32->type;
18387 return 1;
18388 }
18389 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18390 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18391 } else {
18392 BANK *pbk = (BANK *) (pbkh + 1);
18393 copy_bk_name((char *) &dname, name);
18394 do {
18395 if (*((DWORD *) pbk->name) == dname) {
18396 *((void **) pdata) = pbk + 1;
18397 if (tid_size[pbk->type & 0xFF] == 0)
18398 *bklen = pbk->data_size;
18399 else
18400 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
18401
18402 *bktype = pbk->type;
18403 return 1;
18404 }
18405 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18406 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
18407 }
18408
18409 /* bank not found */
18410 *((void **) pdata) = NULL;
18411 return 0;
18412}
18413
18414/********************************************************************/
18450INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
18451 if (*pbk == NULL)
18452 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
18453 else
18454 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18455
18456 *((void **) pdata) = (*pbk) + 1;
18457
18458 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18459 *pbk = *((BANK **) pdata) = NULL;
18460 return 0;
18461 }
18462
18463 return (*pbk)->data_size;
18464}
18465
18466
18468#ifndef DOXYGEN_SHOULD_SKIP_THIS
18469
18470/********************************************************************/
18471INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
18472/********************************************************************\
18473
18474 Routine: bk_iterate32
18475
18476 Purpose: Iterate through 32 bit MIDAS banks inside an event
18477
18478 Input:
18479 void *event pointer to the event
18480 BANK32 **pbk32 must be NULL for the first call to bk_iterate
18481
18482 Output:
18483 BANK32 **pbk32 pointer to the bank header
18484 void *pdata pointer to data area of the bank
18485
18486 Function value:
18487 INT size of the bank in bytes
18488
18489\********************************************************************/
18490{
18491 if (*pbk == NULL)
18492 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
18493 else
18494 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
18495
18496 *((void **) pdata) = (*pbk) + 1;
18497
18498 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18499 *pbk = NULL;
18500 pdata = NULL;
18501 return 0;
18502 }
18503
18504 return (*pbk)->data_size;
18505}
18506
18507INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
18508/********************************************************************\
18509
18510 Routine: bk_iterate32a
18511
18512 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
18513
18514 Input:
18515 void *event pointer to the event
18516 BANK32A **pbk32a must be NULL for the first call to bk_iterate
18517
18518 Output:
18519 BANK32A **pbk32 pointer to the bank header
18520 void *pdata pointer to data area of the bank
18521
18522 Function value:
18523 INT size of the bank in bytes
18524
18525\********************************************************************/
18526{
18527 if (*pbk32a == NULL)
18528 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
18529 else
18530 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
18531
18532 *((void **) pdata) = (*pbk32a) + 1;
18533
18534 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
18535 *pbk32a = NULL;
18536 pdata = NULL;
18537 return 0;
18538 }
18539
18540 return (*pbk32a)->data_size;
18541}
18542
18544#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18545
18546/********************************************************************/
18561INT bk_swap(void *event, BOOL force) {
18562 BANK_HEADER *pbh;
18563 BANK *pbk;
18564 BANK32 *pbk32;
18565 BANK32A *pbk32a;
18566 void *pdata;
18567 WORD type;
18568
18569 pbh = (BANK_HEADER *) event;
18570
18571 /* only swap if flags in high 16-bit */
18572 if (pbh->flags < 0x10000 && !force)
18573 return 0;
18574
18575 /* swap bank header */
18576 DWORD_SWAP(&pbh->data_size);
18577 DWORD_SWAP(&pbh->flags);
18578
18579 pbk = (BANK *) (pbh + 1);
18580 pbk32 = (BANK32 *) pbk;
18581 pbk32a = (BANK32A *) pbk;
18582
18583 /* scan event */
18584 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
18585 /* swap bank header */
18586 if (bk_is32a(event)) {
18587 DWORD_SWAP(&pbk32a->type);
18588 DWORD_SWAP(&pbk32a->data_size);
18589 pdata = pbk32a + 1;
18590 type = (WORD) pbk32a->type;
18591 } else if (bk_is32(event)) {
18592 DWORD_SWAP(&pbk32->type);
18593 DWORD_SWAP(&pbk32->data_size);
18594 pdata = pbk32 + 1;
18595 type = (WORD) pbk32->type;
18596 } else {
18597 WORD_SWAP(&pbk->type);
18598 WORD_SWAP(&pbk->data_size);
18599 pdata = pbk + 1;
18600 type = pbk->type;
18601 }
18602
18603 /* pbk points to next bank */
18604 if (bk_is32a(event)) {
18605 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
18606 pbk = (BANK *) pbk32a;
18607 } else if (bk_is32(event)) {
18608 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
18609 pbk = (BANK *) pbk32;
18610 } else {
18611 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
18612 pbk32 = (BANK32 *) pbk;
18613 }
18614
18615 switch (type) {
18616 case TID_UINT16:
18617 case TID_INT16:
18618 while ((char *) pdata < (char *) pbk) {
18619 WORD_SWAP(pdata);
18620 pdata = (void *) (((WORD *) pdata) + 1);
18621 }
18622 break;
18623
18624 case TID_UINT32:
18625 case TID_INT32:
18626 case TID_BOOL:
18627 case TID_FLOAT:
18628 while ((char *) pdata < (char *) pbk) {
18629 DWORD_SWAP(pdata);
18630 pdata = (void *) (((DWORD *) pdata) + 1);
18631 }
18632 break;
18633
18634 case TID_DOUBLE:
18635 case TID_INT64:
18636 case TID_UINT64:
18637 while ((char *) pdata < (char *) pbk) {
18638 QWORD_SWAP(pdata);
18639 pdata = (void *) (((double *) pdata) + 1);
18640 }
18641 break;
18642 }
18643 }
18644
18645 return CM_SUCCESS;
18646}
18647
/* end of bkfunctionc */
18651
18652
18659#ifndef DOXYGEN_SHOULD_SKIP_THIS
18660/********************************************************************/
18661
18662/********************************************************************\
18663* *
18664* Ring buffer functions *
18665* *
18666* Provide an inter-thread buffer scheme for handling front-end *
18667* events. This code allows concurrent data acquisition, calibration *
18668* and network transfer on a multi-CPU machine. One thread reads *
18669* out the data, passes it vis the ring buffer functions *
18670* to another thread running on the other CPU, which can then *
18671* calibrate and/or send the data over the network. *
18672* *
18673\********************************************************************/
18674
18675typedef struct {
18676 unsigned char *buffer;
18677 unsigned int size;
18678 unsigned int max_event_size;
18679 unsigned char *rp;
18680 unsigned char *wp;
18681 unsigned char *ep;
18682} RING_BUFFER;
18683
18684#define MAX_RING_BUFFER 100
18685
18687
18688static volatile int _rb_nonblocking = 0;
18689
18691#endif /* DOXYGEN_SHOULD_SKIP_THIS */
18692
18693/********************************************************************/
18700/********************************************************************\
18701
18702 Routine: rb_set_nonblocking
18703
18704 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
18705 environments for stopping all theads without deadlock
18706
18707 Input:
18708 NONE
18709
18710 Output:
18711 NONE
18712
18713 Function value:
18714 DB_SUCCESS Successful completion
18715
18716\********************************************************************/
18717{
18718 _rb_nonblocking = 1;
18719
18720 return DB_SUCCESS;
18721}
18722
18723/********************************************************************/
18740int rb_create(int size, int max_event_size, int *handle)
18741/********************************************************************\
18742
18743 Routine: rb_create
18744
18745 Purpose: Create a ring buffer with a given size
18746
18747 Input:
18748 int size Size of ring buffer, must be larger than
18749 2*max_event_size
18750 int max_event_size Maximum event size to be placed into
18751 ring buffer
18752 Output:
18753 int *handle Handle to ring buffer
18754
18755 Function value:
18756 DB_SUCCESS Successful completion
18757 DB_NO_MEMORY Maximum number of ring buffers exceeded
18758 DB_INVALID_PARAM Invalid event size specified
18759
18760\********************************************************************/
18761{
18762 int i;
18763
18764 for (i = 0; i < MAX_RING_BUFFER; i++)
18765 if (rb[i].buffer == NULL)
18766 break;
18767
18768 if (i == MAX_RING_BUFFER)
18769 return DB_NO_MEMORY;
18770
18771 if (size < max_event_size * 2)
18772 return DB_INVALID_PARAM;
18773
18774 memset(&rb[i], 0, sizeof(RING_BUFFER));
18775 rb[i].buffer = (unsigned char *) M_MALLOC(size);
18776 assert(rb[i].buffer);
18777 rb[i].size = size;
18779 rb[i].rp = rb[i].buffer;
18780 rb[i].wp = rb[i].buffer;
18781 rb[i].ep = rb[i].buffer;
18782
18783 *handle = i + 1;
18784
18785 return DB_SUCCESS;
18786}
18787
18788/********************************************************************/
18794int rb_delete(int handle)
18795/********************************************************************\
18796
18797 Routine: rb_delete
18798
18799 Purpose: Delete a ring buffer
18800
18801 Input:
18802 none
18803 Output:
18804 int handle Handle to ring buffer
18805
18806 Function value:
18807 DB_SUCCESS Successful completion
18808
18809\********************************************************************/
18810{
18811 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18812 return DB_INVALID_HANDLE;
18813
18814 M_FREE(rb[handle - 1].buffer);
18815 rb[handle - 1].buffer = NULL;
18816 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
18817
18818 return DB_SUCCESS;
18819}
18820
18821/********************************************************************/
18831int rb_get_wp(int handle, void **p, int millisec)
18832/********************************************************************\
18833
18834Routine: rb_get_wp
18835
18836 Purpose: Retrieve write pointer where new data can be written
18837
18838 Input:
18839 int handle Ring buffer handle
18840 int millisec Optional timeout in milliseconds if
18841 buffer is full. Zero to not wait at
18842 all (non-blocking)
18843
18844 Output:
18845 char **p Write pointer
18846
18847 Function value:
18848 DB_SUCCESS Successful completion
18849
18850\********************************************************************/
18851{
18852 int h, i;
18853 unsigned char *rp;
18854
18855 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18856 return DB_INVALID_HANDLE;
18857
18858 h = handle - 1;
18859
18860 for (i = 0; i <= millisec / 10; i++) {
18861
18862 rp = rb[h].rp; // keep local copy for convenience
18863
18864 /* check if enough size for wp >= rp without wrap-around */
18865 if (rb[h].wp >= rp
18866 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
18867 *p = rb[h].wp;
18868 return DB_SUCCESS;
18869 }
18870
18871 /* check if enough size for wp >= rp with wrap-around */
18872 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
18873 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
18874 *p = rb[h].wp;
18875 return DB_SUCCESS;
18876 }
18877
18878 /* check if enough size for wp < rp */
18879 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
18880 *p = rb[h].wp;
18881 return DB_SUCCESS;
18882 }
18883
18884 if (millisec == 0)
18885 return DB_TIMEOUT;
18886
18887 if (_rb_nonblocking)
18888 return DB_TIMEOUT;
18889
18890 /* wait one time slice */
18891 ss_sleep(10);
18892 }
18893
18894 return DB_TIMEOUT;
18895}
18896
18897/********************************************************************/
18906int rb_increment_wp(int handle, int size)
18907/********************************************************************\
18908
18909 Routine: rb_increment_wp
18910
18911 Purpose: Increment current write pointer, making the data at
18912 the write pointer available to the receiving thread
18913
18914 Input:
18915 int handle Ring buffer handle
18916 int size Number of bytes placed at the WP
18917
18918 Output:
18919 NONE
18920
18921 Function value:
18922 DB_SUCCESS Successful completion
18923 DB_INVALID_PARAM Event size too large or invalid handle
18924\********************************************************************/
18925{
18926 int h;
18927 unsigned char *new_wp;
18928
18929 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18930 return DB_INVALID_HANDLE;
18931
18932 h = handle - 1;
18933
18934 if ((DWORD) size > rb[h].max_event_size) {
18935 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
18936 size/1024/1024, rb[h].max_event_size/1024/1024);
18937 abort();
18938 }
18939
18940 new_wp = rb[h].wp + size;
18941
18942 /* wrap around wp if not enough space */
18943 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
18944 rb[h].ep = new_wp;
18945 new_wp = rb[h].buffer;
18946 assert(rb[h].rp != rb[h].buffer);
18947 } else
18948 if (new_wp > rb[h].ep)
18949 rb[h].ep = new_wp;
18950
18951 rb[h].wp = new_wp;
18952
18953 return DB_SUCCESS;
18954}
18955
18956/********************************************************************/
18972int rb_get_rp(int handle, void **p, int millisec)
18973/********************************************************************\
18974
18975 Routine: rb_get_rp
18976
18977 Purpose: Obtain the current read pointer at which new data is
18978 available with optional timeout
18979
18980 Input:
18981 int handle Ring buffer handle
18982 int millisec Optional timeout in milliseconds if
18983 buffer is full. Zero to not wait at
18984 all (non-blocking)
18985
18986 Output:
18987 char **p Address of pointer pointing to newly
18988 available data. If p == NULL, only
18989 return status.
18990
18991 Function value:
18992 DB_SUCCESS Successful completion
18993
18994\********************************************************************/
18995{
18996 int i, h;
18997
18998 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
18999 return DB_INVALID_HANDLE;
19000
19001 h = handle - 1;
19002
19003 for (i = 0; i <= millisec / 10; i++) {
19004
19005 if (rb[h].wp != rb[h].rp) {
19006 if (p != NULL)
19007 *p = rb[handle - 1].rp;
19008 return DB_SUCCESS;
19009 }
19010
19011 if (millisec == 0)
19012 return DB_TIMEOUT;
19013
19014 if (_rb_nonblocking)
19015 return DB_TIMEOUT;
19016
19017 /* wait one time slice */
19018 ss_sleep(10);
19019 }
19020
19021 return DB_TIMEOUT;
19022}
19023
19024/********************************************************************/
19034int rb_increment_rp(int handle, int size)
19035/********************************************************************\
19036
19037 Routine: rb_increment_rp
19038
19039 Purpose: Increment current read pointer, freeing up space for
19040 the writing thread.
19041
19042 Input:
19043 int handle Ring buffer handle
19044 int size Number of bytes to free up at current
19045 read pointer
19046
19047 Output:
19048 NONE
19049
19050 Function value:
19051 DB_SUCCESS Successful completion
19052 DB_INVALID_PARAM Event size too large or invalid handle
19053
19054\********************************************************************/
19055{
19056 int h;
19057
19058 unsigned char *new_rp;
19059 unsigned char *ep;
19060
19061 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19062 return DB_INVALID_HANDLE;
19063
19064 h = handle - 1;
19065
19066 if ((DWORD) size > rb[h].max_event_size)
19067 return DB_INVALID_PARAM;
19068
19069 new_rp = rb[h].rp + size;
19070 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
19071
19072 /* wrap around if end pointer reached */
19073 if (new_rp >= ep && rb[h].wp < ep)
19074 new_rp = rb[h].buffer;
19075
19076 rb[handle - 1].rp = new_rp;
19077
19078 return DB_SUCCESS;
19079}
19080
19081/********************************************************************/
19089int rb_get_buffer_level(int handle, int *n_bytes)
19090/********************************************************************\
19091
19092 Routine: rb_get_buffer_level
19093
19094 Purpose: Return number of bytes in a ring buffer
19095
19096 Input:
19097 int handle Handle of the buffer to get the info
19098
19099 Output:
19100 int *n_bytes Number of bytes in buffer
19101
19102 Function value:
19103 DB_SUCCESS Successful completion
19104 DB_INVALID_HANDLE Buffer handle is invalid
19105
19106\********************************************************************/
19107{
19108 int h;
19109
19110 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
19111 return DB_INVALID_HANDLE;
19112
19113 h = handle - 1;
19114
19115 if (rb[h].wp >= rb[h].rp)
19116 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
19117 else
19118 *n_bytes =
19119 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
19120
19121 return DB_SUCCESS;
19122}
19123
/* end of rbfunctionc */
19125
19126
19128{
19129 if (format == FORMAT_FIXED) {
19130 int status;
19131 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
19132 if (status != DB_SUCCESS) {
19133 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
19134 return status;
19135 }
19136 return SUCCESS;
19137 } else if (format == FORMAT_MIDAS) {
19138 INT size, i, status, n_data;
19139 int n;
19140 char *pdata, *pdata0;
19141
19142 char name[5];
19143 BANK_HEADER *pbh;
19144 BANK *pbk;
19145 BANK32 *pbk32;
19146 BANK32A *pbk32a;
19147 DWORD bkname;
19148 WORD bktype;
19149 HNDLE hKeyRoot, hKeyl, *hKeys;
19150 KEY key;
19151
19152 pbh = (BANK_HEADER *) (pevent + 1);
19153 pbk = NULL;
19154 pbk32 = NULL;
19155 pbk32a = NULL;
19156
19157 /* count number of banks */
19158 for (n=0 ; ; n++) {
19159 if (bk_is32a(pbh)) {
19160 bk_iterate32a(pbh, &pbk32a, &pdata);
19161 if (pbk32a == NULL)
19162 break;
19163 } else if (bk_is32(pbh)) {
19164 bk_iterate32(pbh, &pbk32, &pdata);
19165 if (pbk32 == NULL)
19166 break;
19167 } else {
19168 bk_iterate(pbh, &pbk, &pdata);
19169 if (pbk == NULL)
19170 break;
19171 }
19172 }
19173
19174 /* build array of keys */
19175 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
19176
19177 pbk = NULL;
19178 pbk32 = NULL;
19179 n = 0;
19180 do {
19181 /* scan all banks */
19182 if (bk_is32a(pbh)) {
19183 size = bk_iterate32a(pbh, &pbk32a, &pdata);
19184 if (pbk32a == NULL)
19185 break;
19186 bkname = *((DWORD *) pbk32a->name);
19187 bktype = (WORD) pbk32a->type;
19188 } else if (bk_is32(pbh)) {
19189 size = bk_iterate32(pbh, &pbk32, &pdata);
19190 if (pbk32 == NULL)
19191 break;
19192 bkname = *((DWORD *) pbk32->name);
19193 bktype = (WORD) pbk32->type;
19194 } else {
19195 size = bk_iterate(pbh, &pbk, &pdata);
19196 if (pbk == NULL)
19197 break;
19198 bkname = *((DWORD *) pbk->name);
19199 bktype = (WORD) pbk->type;
19200 }
19201
19202 n_data = size;
19203 if (rpc_tid_size(bktype & 0xFF))
19204 n_data /= rpc_tid_size(bktype & 0xFF);
19205
19206 /* get bank key */
19207 *((DWORD *) name) = bkname; // NB: "name" is only byte-aligned, and may have wrong alignement for DWORD, flagged by UBSAN. K.O.
19208
19209 //name[0] = bkname>>0 & 0xFF;
19210 //name[1] = bkname>>8 & 0xFF;
19211 //name[2] = bkname>>16 & 0xFF;
19212 //name[3] = bkname>>24 & 0xFF;
19213 name[4] = 0;
19214
19215 //printf("bkname 0x%08x, name [%s]\n", bkname, name);
19216
19217 /* record the start of the data in case it is struct */
19218 pdata0 = pdata;
19219 if (bktype == TID_STRUCT) {
19220 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19221 if (status != DB_SUCCESS) {
19222 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
19223 continue;
19224 }
19225
19226 /* write structured bank */
19227 for (i = 0;; i++) {
19228 status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
19230 break;
19231
19232 db_get_key(hDB, hKeyl, &key);
19233
19234 /* adjust for alignment */
19235 if (key.type != TID_STRING && key.type != TID_LINK)
19236 pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
19237
19239 if (status != DB_SUCCESS) {
19240 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19241 continue;
19242 }
19243 hKeys[n++] = hKeyl;
19244
19245 /* shift data pointer to next item */
19246 pdata += key.item_size * key.num_values;
19247 }
19248 } else {
19249 /* write variable length bank */
19250 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19251 if (status != DB_SUCCESS) {
19252 status = db_create_key(hDB, hKey, name, bktype);
19253 if (status != DB_SUCCESS) {
19254 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);
19255 continue;
19256 }
19257 status = db_find_key(hDB, hKey, name, &hKeyRoot);
19258 if (status != DB_SUCCESS) {
19259 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);
19260 continue;
19261 }
19262 }
19263 if (n_data > 0) {
19264 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
19265 if (status != DB_SUCCESS) {
19266 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
19267 }
19268 hKeys[n++] = hKeyRoot;
19269 }
19270 }
19271 } while (1);
19272
19273 /* notify all hot-lined clients in one go */
19274 db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
19275
19276 free(hKeys);
19277
19278 return SUCCESS;
19279 } else {
19280 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
19281 return CM_DB_ERROR;
19282 }
19283}
19284
19285/* emacs
19286 * Local Variables:
19287 * tab-width: 8
19288 * c-basic-offset: 3
19289 * indent-tabs-mode: nil
19290 * End:
19291 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11574
std::atomic_bool connected
Definition midas.cxx:11573
std::string client_name
Definition midas.cxx:11578
void * pparam
Definition midas.cxx:15485
size_t param_size
Definition midas.cxx:15480
size_t offset
Definition midas.cxx:15478
size_t out_max_size
Definition midas.cxx:15482
size_t out_max_size_offset
Definition midas.cxx:15481
std::string * ps
Definition midas.cxx:15483
std::vector< char > * pv
Definition midas.cxx:15484
size_t arg_size
Definition midas.cxx:15479
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:1614
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:877
INT al_check()
Definition alarm.cxx:631
void bk_init32a(void *event)
Definition midas.cxx:17886
INT bk_close(void *event, void *pdata)
Definition midas.cxx:18184
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:18507
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:17903
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:18561
BOOL bk_is32a(const void *event)
Definition midas.cxx:17841
int bk_delete(void *event, const char *name)
Definition midas.cxx:18078
BOOL bk_is32(const void *event)
Definition midas.cxx:17819
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:18471
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:18293
void bk_init(void *event)
Definition midas.cxx:17810
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:18244
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:18008
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:18450
void bk_init32(void *event)
Definition midas.cxx:17873
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:17965
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:18356
INT bk_size(const void *event)
Definition midas.cxx:17899
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8798
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:10509
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:9689
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:9998
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:10972
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10089
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:10617
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7291
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11411
INT bm_delete_request(INT request_id)
Definition midas.cxx:8595
INT bm_close_all_buffers(void)
Definition midas.cxx:7254
INT bm_poll_event()
Definition midas.cxx:11265
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:8325
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:10948
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9614
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9600
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8977
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:11379
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8731
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9080
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:8476
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:8151
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:10789
INT bm_check_buffers()
Definition midas.cxx:11093
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9003
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:8303
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8529
static double _bm_mutex_timeout_sec
Definition midas.cxx:5938
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7107
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:9789
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8292
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:9515
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11420
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:8947
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8876
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:8866
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:10233
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8834
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8633
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:10290
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:9091
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7086
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9716
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:8902
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10870
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:11041
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:9403
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:7411
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:7334
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:11423
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:7366
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:11057
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:7915
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:7381
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:7986
INT cm_periodic_tasks()
Definition midas.cxx:5597
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8048
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:7621
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:7721
std::string cm_get_client_name()
Definition midas.cxx:2075
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7957
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:12763
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7333
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:11187
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:7360
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7753
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:7768
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:7936
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:7332
const char * cm_get_revision()
Definition midas.cxx:1500
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7340
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:7531
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:5784
INT ss_suspend(INT millisec, INT msg)
Definition system.cxx:4615
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3229
INT ss_suspend_init_odb_port()
Definition system.cxx:4377
bool ss_event_socket_has_data()
Definition system.cxx:4592
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3437
DWORD ss_millitime()
Definition system.cxx:3465
int ss_file_exist(const char *path)
Definition system.cxx:7196
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:5634
INT ss_suspend_set_client_listener(int listen_socket)
Definition system.cxx:4356
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5318
int ss_file_link_exist(const char *path)
Definition system.cxx:7232
std::string ss_getcwd()
Definition system.cxx:5848
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3283
int ss_socket_wait(int sock, INT millisec)
Definition system.cxx:4970
INT ss_suspend_set_server_acceptions(RPC_SERVER_ACCEPTION_LIST *acceptions)
Definition system.cxx:4370
INT ss_getpid(void)
Definition system.cxx:1379
DWORD ss_settime(DWORD seconds)
Definition system.cxx:3547
char * ss_getpass(const char *prompt)
Definition system.cxx:7518
INT ss_suspend_set_client_connection(RPC_SERVER_CONNECTION *connection)
Definition system.cxx:4363
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3013
void ss_tzset()
Definition system.cxx:3427
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:5471
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5424
int ss_dir_exist(const char *path)
Definition system.cxx:7264
bool ss_timed_mutex_wait_for_sec(std::timed_mutex &mutex, const char *mutex_name, double timeout_sec)
Definition system.cxx:3337
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2853
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:7297
DWORD ss_time()
Definition system.cxx:3534
INT ss_suspend_exit()
Definition system.cxx:4298
INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5526
INT ss_resume(INT port, const char *message)
Definition system.cxx:4916
midas_thread_t ss_gettid(void)
Definition system.cxx:1591
std::string ss_asctime()
Definition system.cxx:3621
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2941
INT ss_sleep(INT millisec)
Definition system.cxx:3700
INT ss_socket_connect_tcp(const char *hostname, int tcp_port, int *sockp, std::string *error_msg_p)
Definition system.cxx:5039
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2711
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:5134
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7969
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:4425
INT ss_socket_close(int *sockp)
Definition system.cxx:5303
char * ss_gets(char *string, int size)
Definition system.cxx:7848
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5707
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:5357
void * ss_ctrlc_handler(void(*func)(int))
Definition system.cxx:3971
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:3109
INT ss_file_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6791
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:3285
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2306
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6917
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3933
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3143
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5185
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:13322
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1820
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2911
std::string strcomb1(const char **list)
Definition odb.cxx:668
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6563
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3392
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8040
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6043
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:13967
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3098
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7668
INT db_close_all_records()
Definition odb.cxx:13546
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13845
INT db_close_all_databases(void)
Definition odb.cxx:2398
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7239
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:3999
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10865
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2776
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7337
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:5028
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4256
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13584
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:3060
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13631
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2981
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2876
static DATABASE * db_lock_database(HNDLE hDB, int *pstatus, const char *caller, bool check_attached=true)
Definition odb.cxx:2508
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2440
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12654
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12320
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5357
static void db_unlock_database(DATABASE *pdb, const char *caller)
Definition odb.cxx:2670
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12831
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14038
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2748
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7523
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3251
int rb_get_rp(int handle, void **p, int millisec)
Definition midas.cxx:18972
#define MAX_RING_BUFFER
Definition midas.cxx:18684
int rb_delete(int handle)
Definition midas.cxx:18794
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:18831
int rb_increment_rp(int handle, int size)
Definition midas.cxx:19034
int rb_set_nonblocking()
Definition midas.cxx:18699
static volatile int _rb_nonblocking
Definition midas.cxx:18688
int rb_increment_wp(int handle, int size)
Definition midas.cxx:18906
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:18740
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:19089
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:18686
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:16594
#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:11837
#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:12143
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14984
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:15093
#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:14833
INT rpc_get_opt_tcp_size()
Definition midas.cxx:14320
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12807
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11707
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13926
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11958
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:17061
static std::mutex _client_connections_mutex
Definition midas.cxx:11634
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13129
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
#define RPC_TEST4_CXX
Definition mrpc.h:127
static std::mutex gAllowedHostsMutex
Definition midas.cxx:16566
INT recv_tcp_check(int sock)
Definition midas.cxx:14803
#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:13182
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11644
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11714
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12512
std::string rpc_get_name()
Definition midas.cxx:13215
int rpc_test_rpc_test2_cxx()
Definition midas.cxx:16102
#define RPC_ID_WATCHDOG
Definition mrpc.h:139
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11649
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
INT rpc_server_receive_rpc(RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:17230
bool rpc_is_remote(void)
Definition midas.cxx:12892
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13292
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11902
#define RPC_TEST2_CXX
Definition mrpc.h:125
static int _tr_fifo_rp
Definition midas.cxx:14502
int rpc_test_rpc_test2()
Definition midas.cxx:15940
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14568
#define RPC_RC_TRANSITION
Definition mrpc.h:117
int rpc_test_rpc_test3_cxx()
Definition midas.cxx:16262
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13324
#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:15096
INT rpc_server_loop(void)
Definition midas.cxx:17202
INT rpc_clear_allowed_hosts()
Definition midas.cxx:16569
int rpc_test_rpc()
Definition midas.cxx:16531
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12936
INT rpc_get_hw_type()
Definition midas.cxx:12965
INT rpc_deregister_functions()
Definition midas.cxx:12001
bool rpc_is_connected(void)
Definition midas.cxx:12914
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13195
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11704
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:15092
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11635
#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:13362
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12743
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:16565
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:14115
static std::mutex rpc_list_mutex
Definition midas.cxx:11705
void rpc_client_check()
Definition midas.cxx:12401
const char * rpc_tid_name(INT id)
Definition midas.cxx:11895
static void rpc_call_encode_cxx(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13607
INT rpc_flush_event()
Definition midas.cxx:14486
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11939
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11641
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:16672
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:17572
static int _tr_fifo_wp
Definition midas.cxx:14501
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:17525
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:14349
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14500
static std::string _mserver_path
Definition midas.cxx:13179
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:13104
INT rpc_server_disconnect()
Definition midas.cxx:12836
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13265
INT rpc_client_accept(int lsock)
Definition midas.cxx:16957
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11757
static int rpc_find_rpc(int routine_id, RPC_LIST *pentry, bool *prpc_cxx)
Definition midas.cxx:13900
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:14312
#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:11909
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:15025
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:17370
#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:16700
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14499
bool rpc_is_mserver(void)
Definition midas.cxx:12949
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11642
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11742
#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:14367
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11638
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13535
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:14373
INT rpc_set_name(const char *name)
Definition midas.cxx:13239
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:12028
#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:15503
INT rpc_client_dispatch(int sock)
Definition midas.cxx:12076
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11637
INT rpc_check_channels(void)
Definition midas.cxx:17643
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14504
int rpc_test_rpc_test4_cxx()
Definition midas.cxx:16418
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:12062
#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:11792
static int rpc_call_decode_cxx(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13807
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11773
#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:14645
INT rpc_get_convert_flags(void)
Definition midas.cxx:13161
INT rpc_tid_size(INT id)
Definition midas.cxx:11888
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:16623
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11812
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:7802
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:7849
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:19127
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8074
#define DIR_SEPARATOR
Definition midas.h:193
#define MAX_CLIENTS
Definition midas.h:274
INT HNDLE
Definition midas.h:132
#define O_LARGEFILE
Definition midas.h:210
#define CF_ENDIAN
Definition midas.h:1611
#define M_MALLOC(x)
Definition midas.h:1551
#define CINT(_i)
Definition midas.h:1622
#define RPC_OUT
Definition midas.h:1580
#define RPC_CXX
Definition midas.h:1585
#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:1579
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1606
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1607
#define NET_TCP_SIZE
Definition midas.h:266
int INT
Definition midas.h:129
#define DEFAULT_RPC_TIMEOUT
Definition midas.h:287
#define MAX_RPC_PARAMS
Definition midas.h:1595
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1447
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:908
#define RPC_OUTGOING
Definition midas.h:1584
#define MAX_WRITE_CACHE_EVENT_SIZE_DIV
Definition midas.h:259
#define MIDAS_TCP_PORT
Definition midas.h:283
#define CSTRING(_i)
Definition midas.h:1646
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1581
#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:1553
#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:1612
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1583
#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:1613
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define MAX_WRITE_CACHE_SIZE_DIV
Definition midas.h:258
#define TRANSITION_ERROR_STRING_LENGTH
Definition midas.h:280
#define RPC_FIXARRAY
Definition midas.h:1582
#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:11417
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:18680
unsigned char * buffer
Definition midas.cxx:18676
unsigned int max_event_size
Definition midas.cxx:18678
unsigned int size
Definition midas.cxx:18677
unsigned char * rp
Definition midas.cxx:18679
unsigned char * ep
Definition midas.cxx:18681
RPC_PARAM param[MAX_RPC_PARAMS]
Definition midas.h:1600
INT id
Definition midas.h:1598
RPC_HANDLER * dispatch
Definition midas.h:1601
const char * name
Definition midas.h:1599
WORD flags
Definition midas.h:1591
WORD tid
Definition midas.h:1590
INT n
Definition midas.h:1592
midas_thread_t thread_id
Definition midas.cxx:15087
int buffer_size
Definition midas.cxx:15088
char * buffer
Definition midas.cxx:15089
int transition
Definition midas.cxx:14493
time_t trans_time
Definition midas.cxx:14495
int run_number
Definition midas.cxx:14494
int sequence_number
Definition midas.cxx:14496
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