MIDAS
Loading...
Searching...
No Matches
midas.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: MIDAS.C
4 Created by: Stefan Ritt
5
6 Contents: MIDAS main library funcitons
7
8 $Id$
9
10\********************************************************************/
11
12#undef NDEBUG // midas required assert() to be always enabled
13
14#include "midas.h"
15#include "msystem.h"
16#include "git-revision.h"
17#include "mstrlcpy.h"
18#include "odbxx.h"
19
20#include <assert.h>
21#include <signal.h>
22#include <sys/resource.h>
23
24#include <mutex>
25#include <deque>
26#include <thread>
27#include <atomic>
28#include <algorithm>
29
62#ifndef DOXYGEN_SHOULD_SKIP_THIS
63
64/********************************************************************/
65/* data type sizes */
66static const int tid_size[] = {
67 0, /* tid == 0 not defined */
68 1, /* TID_UINT8 unsigned byte 0 255 */
69 1, /* TID_INT8 signed byte -128 127 */
70 1, /* TID_CHAR single character 0 255 */
71 2, /* TID_UINT16 two bytes 0 65535 */
72 2, /* TID_INT16 signed word -32768 32767 */
73 4, /* TID_UINT32 four bytes 0 2^32-1 */
74 4, /* TID_INT32 signed dword -2^31 2^31-1 */
75 4, /* TID_BOOL four bytes bool 0 1 */
76 4, /* TID_FLOAT 4 Byte float format */
77 8, /* TID_DOUBLE 8 Byte float format */
78 4, /* TID_BITFIELD 32 Bits Bitfield 0000... 11111... */
79 0, /* TID_STRING zero terminated string */
80 0, /* TID_ARRAY variable length array of unkown type */
81 0, /* TID_STRUCT C structure */
82 0, /* TID_KEY key in online database */
83 0, /* TID_LINK link in online database */
84 8, /* TID_INT64 8 bytes int -2^63 2^63-1 */
85 8 /* TID_UINT64 8 bytes unsigned int 0 2^64-1 */
86};
87
88/* data type names */
89static const char *tid_name_old[] = {
90 "NULL",
91 "BYTE",
92 "SBYTE",
93 "CHAR",
94 "WORD",
95 "SHORT",
96 "DWORD",
97 "INT",
98 "BOOL",
99 "FLOAT",
100 "DOUBLE",
101 "BITFIELD",
102 "STRING",
103 "ARRAY",
104 "STRUCT",
105 "KEY",
106 "LINK",
107 "INT64",
108 "UINT64"
109};
110
111static const char *tid_name[] = {
112 "NULL",
113 "UINT8",
114 "INT8",
115 "CHAR",
116 "UINT16",
117 "INT16",
118 "UINT32",
119 "INT32",
120 "BOOL",
121 "FLOAT",
122 "DOUBLE",
123 "BITFIELD",
124 "STRING",
125 "ARRAY",
126 "STRUCT",
127 "KEY",
128 "LINK",
129 "INT64",
130 "UINT64"
131};
132
134{
135 if (transition == TR_START) return "START";
136 if (transition == TR_STOP) return "STOP";
137 if (transition == TR_PAUSE) return "PAUSE";
138 if (transition == TR_RESUME) return "RESUME";
139 if (transition == TR_STARTABORT) return "STARTABORT";
140 if (transition == TR_DEFERRED) return "DEFERRED";
141 return msprintf("UNKNOWN TRANSITION %d", transition);
142}
143
144const char *mname[] = {
145 "January",
146 "February",
147 "March",
148 "April",
149 "May",
150 "June",
151 "July",
152 "August",
153 "September",
154 "October",
155 "November",
156 "December"
157};
158
159/* Globals */
160#ifdef OS_MSDOS
161extern unsigned _stklen = 60000U;
162#endif
163
164extern DATABASE *_database;
166
167//
168// locking rules for gBuffers and gBuffersMutex:
169//
170// - all access to gBuffers must be done while holding gBufferMutex
171// - while holding gBufferMutex:
172// - taking additional locks not permitted (no calling odb, no locking event buffers, etc)
173// - calling functions that can take additional locks not permitted (no calling db_xxx(), bm_xxx(), etc)
174// - calling functions that can come back recursively not permitted
175//
176// after obtaining a BUFFER*pbuf pointer from gBuffers:
177//
178// - holding gBuffersMutex is not required
179// - to access pbuf data, must hold buffer_mutex or call bm_lock_buffer()
180// - except for:
181// pbuf->attached - no need to hold a lock (std::atomic)
182// pbuf->buffer_name - no need to hold a lock (constant data, only changed by bm_open_buffer())
183//
184// object life time:
185//
186// - gBuffers never shrinks
187// - new BUFFER objects are created by bm_open_buffer(), added to gBuffers when ready for use, pbuf->attached set to true
188// - bm_close_buffer() sets pbuf->attached to false
189// - BUFFER objects are never deleted to avoid race between delete and bm_send_event() & co
190// - BUFFER objects are never reused, bm_open_buffer() always creates a new object
191// - gBuffers[i] set to NULL are empty slots available for reuse
192// - closed buffers have corresponding gBuffers[i]->attached set to false
193//
194
195static std::mutex gBuffersMutex; // protects gBuffers vector itself, but not it's contents!
196static std::vector<BUFFER*> gBuffers;
197
198static INT _msg_buffer = 0;
200
201/* Event request descriptor */
202
204{
205 INT buffer_handle = 0; /* Buffer handle */
206 short int event_id = 0; /* same as in EVENT_HEADER */
207 short int trigger_mask = 0; /* same as in EVENT_HEADER */
208 EVENT_HANDLER* dispatcher = NULL; /* Dispatcher func. */
209
210 void clear()
211 {
212 buffer_handle = 0;
213 event_id = 0;
214 trigger_mask = 0;
216 }
217};
218
219static std::mutex _request_list_mutex;
220static std::vector<EventRequest> _request_list;
221
222//static char *_tcp_buffer = NULL;
223//static INT _tcp_wp = 0;
224//static INT _tcp_rp = 0;
225//static INT _tcp_sock = 0;
226
227static MUTEX_T *_mutex_rpc = NULL; // mutex to protect RPC calls
228
229static void (*_debug_print)(const char *) = NULL;
230
231static INT _debug_mode = 0;
232
233static int _rpc_connect_timeout = 10000;
234
235// for use on a single machine it is best to restrict RPC access to localhost
236// by binding the RPC listener socket to the localhost IP address.
238
239/* table for transition functions */
240
246
247static std::mutex _trans_table_mutex;
248static std::vector<TRANS_TABLE> _trans_table;
249
251 {TR_START, 0, NULL},
252 {TR_STOP, 0, NULL},
253 {TR_PAUSE, 0, NULL},
254 {TR_RESUME, 0, NULL},
255 {0, 0, NULL}
256};
257
259static int _rpc_listen_socket = 0;
260
262
263void cm_ctrlc_handler(int sig);
264
265typedef struct {
267 const char *string;
269
270static const ERROR_TABLE _error_table[] = {
271 {CM_WRONG_PASSWORD, "Wrong password"},
272 {CM_UNDEF_EXP, "Experiment not defined"},
274 "\"exptab\" file not found and MIDAS_DIR or MIDAS_EXPTAB environment variable is not defined"},
275 {RPC_NET_ERROR, "Cannot connect to remote host"},
276 {0, NULL}
277};
278
279typedef struct {
280 void *adr;
281 int size;
282 char file[80];
283 int line;
285
287static INT _n_mem = 0;
288
290{
293 char *errstr;
297 std::atomic_int status{0};
298 std::atomic_bool finished{false};
299 std::atomic<std::thread*> thread{NULL};
300};
301
303
304/*------------------------------------------------------------------*/
305
306void *dbg_malloc(unsigned int size, char *file, int line) {
307 FILE *f;
308 void *adr;
309 int i;
310
311 adr = malloc(size);
312
313 /* search for deleted entry */
314 for (i = 0; i < _n_mem; i++)
315 if (_mem_loc[i].adr == NULL)
316 break;
317
318 if (i == _n_mem) {
319 _n_mem++;
320 if (!_mem_loc)
321 _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
322 else
324 }
325
326 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 va_list ap, ap1;
421 va_start(ap, format);
422 va_copy(ap1, ap);
423 size_t size = vsnprintf(nullptr, 0, format, ap1) + 1;
424 char *buffer = (char *)malloc(size);
425 if (!buffer) {
426 va_end(ap);
427 va_end(ap1);
428 return "";
429 }
430 vsnprintf(buffer, size, format, ap);
431 va_end(ap);
432 std::string s(buffer);
433 free(buffer);
434 return s;
435}
436
437/********************************************************************\
438* *
439* Common message functions *
440* *
441\********************************************************************/
442
443typedef int (*MessagePrintCallback)(const char *);
444
445static std::atomic<MessagePrintCallback> _message_print{puts};
446
447static std::atomic_int _message_mask_system{MT_ALL};
448static std::atomic_int _message_mask_user{MT_ALL};
449
450
452#endif /* DOXYGEN_SHOULD_SKIP_THIS */
453
459/********************************************************************/
467std::string cm_get_error(INT code)
468{
469 for (int i = 0; _error_table[i].code; i++) {
470 if (_error_table[i].code == code) {
471 return _error_table[i].string;
472 }
473 }
474
475 return msprintf("unlisted status code %d", code);
476}
477
478/********************************************************************/
480
481 return CM_SUCCESS;
482}
483
484/********************************************************************/
485
487 //printf("cm_msg_open_buffer!\n");
488 if (_msg_buffer == 0) {
490 if (status != BM_SUCCESS && status != BM_CREATED) {
491 return status;
492 }
493 }
494 return CM_SUCCESS;
495}
496
497/********************************************************************/
498
500 //printf("cm_msg_close_buffer!\n");
501 if (_msg_buffer) {
503 _msg_buffer = 0;
504 }
505 return CM_SUCCESS;
506}
507
508/********************************************************************/
509
517 std::string path;
518
519 cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
520
521 /* extract directory name from full path name of midas.log */
522 size_t pos = path.rfind(DIR_SEPARATOR);
523 if (pos != std::string::npos) {
524 path.resize(pos);
525 } else {
526 path = "";
527 }
528
529 //printf("cm_msg_facilities: path [%s]\n", path.c_str());
530
532
533 ss_file_find(path.c_str(), "*.log", &flist);
534
535 for (size_t i = 0; i < flist.size(); i++) {
536 const char *p = flist[i].c_str();
537 if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
538 size_t pos = flist[i].rfind('.');
539 if (pos != std::string::npos) {
540 flist[i].resize(pos);
541 }
542 list->push_back(flist[i]);
543 }
544 }
545
546 return SUCCESS;
547}
548
549/********************************************************************/
550
551void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
552 HNDLE hDB;
553 int status;
554
556
557 // check for call to cm_msg() before MIDAS is fully initialized
558 // or after MIDAS is partially shutdown.
559 if (status != CM_SUCCESS) {
560 if (filename)
561 *filename = std::string(fac) + ".log";
562 if (linkname)
563 *linkname = "";
564 if (linktarget)
565 *linktarget = "";
566 return;
567 }
568
569 if (filename)
570 *filename = "";
571 if (linkname)
572 *linkname = "";
573 if (linktarget)
574 *linktarget = "";
575
576 std::string facility;
577 if (fac && fac[0])
578 facility = fac;
579 else
580 facility = "midas";
581
582 std::string message_format;
583 db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
584 if (message_format.find('%') != std::string::npos) {
585 /* replace stings such as %y%m%d with current date */
586 struct tm tms;
587
588 ss_tzset();
589 if (t == 0)
590 time(&t);
591 localtime_r(&t, &tms);
592
593 char de[256];
594 de[0] = '_';
595 strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
597 }
598
599 std::string message_dir;
600 db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
601 if (message_dir.empty()) {
602 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
603 if (message_dir.empty()) {
605 if (message_dir.empty()) {
607 }
608 }
609 }
610
611 // prepend experiment directory
612 if (message_dir[0] != DIR_SEPARATOR)
614
615 if (message_dir.back() != DIR_SEPARATOR)
616 message_dir.push_back(DIR_SEPARATOR);
617
618 if (filename)
619 *filename = message_dir + facility + message_format + ".log";
620 if (!message_format.empty()) {
621 if (linkname)
622 *linkname = message_dir + facility + ".log";
623 if (linktarget)
624 *linktarget = facility + message_format + ".log";
625 }
626}
627
628/********************************************************************/
659INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
662 _message_print = func;
663
664 return BM_SUCCESS;
665}
666
667/********************************************************************/
676INT cm_msg_log(INT message_type, const char *facility, const char *message) {
677 INT status;
678
679 if (rpc_is_remote()) {
680 if (rpc_is_connected()) {
681 status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
682 if (status != RPC_SUCCESS) {
683 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);
684 }
685 return status;
686 } else {
687 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
688 return RPC_NET_ERROR;
689 }
690 }
691
692 if (message_type != MT_DEBUG) {
693 std::string filename, linkname, linktarget;
694
696
697#ifdef OS_LINUX
698 if (!linkname.empty()) {
699 //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
700 // If filename does not exist, user just switched from non-date format to date format.
701 // In that case we must copy linkname to filename, otherwise messages might get lost.
702 if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
703 ss_file_copy(linkname.c_str(), filename.c_str(), true);
704 }
705
706 unlink(linkname.c_str());
707 status = symlink(linktarget.c_str(), linkname.c_str());
708 if (status != 0) {
710 "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
711 linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
712 }
713 }
714#endif
715
716 int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
717 if (fh < 0) {
719 "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
720 message, filename.c_str(), errno, strerror(errno));
721 } else {
722
723 struct timeval tv;
724 struct tm tms;
725
726 ss_tzset();
728 localtime_r(&tv.tv_sec, &tms);
729
730 char str[256];
731 strftime(str, sizeof(str), "%H:%M:%S", &tms);
732 sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
733 strftime(str + strlen(str), sizeof(str), "%G/%m/%d", &tms);
734
735 std::string msg;
736 msg += str;
737 msg += " ";
738 msg += message;
739 msg += "\n";
740
741 /* avoid c++ complaint about comparison between
742 unsigned size_t returned by msg.length() and
743 signed ssize_t returned by write() */
744 ssize_t len = msg.length();
745
746 /* atomic write, no need to take a semaphore */
747 ssize_t wr = write(fh, msg.c_str(), len);
748
749 if (wr < 0) {
750 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
751 } else if (wr != len) {
752 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);
753 }
754
755 close(fh);
756 }
757 }
758
759 return CM_SUCCESS;
760}
761
762
763static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
764{
765 /* strip path */
766 const char* pc = filename + strlen(filename);
767 while (*pc != '\\' && *pc != '/' && pc != filename)
768 pc--;
769 if (pc != filename)
770 pc++;
771
772 /* convert type to string */
773 std::string type_str;
774 if (message_type & MT_ERROR)
776 if (message_type & MT_INFO)
778 if (message_type & MT_DEBUG)
780 if (message_type & MT_USER)
782 if (message_type & MT_LOG)
784 if (message_type & MT_TALK)
786
787 std::string message;
788
789 /* print client name into string */
790 if (message_type == MT_USER)
791 message = msprintf("[%s] ", routine);
792 else {
793 std::string name = rpc_get_name();
794 if (name.length() > 0)
795 message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
796 else
797 message = "";
798 }
799
800 /* preceed error messages with file and line info */
801 if (message_type == MT_ERROR) {
802 message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
803 } else if (message_type == MT_USER) {
804 message = msprintf("[%s,%s] ", routine, type_str.c_str());
805 }
806
807 int bufsize = 1024;
808 char* buf = (char*)malloc(bufsize);
809 assert(buf);
810
811 for (int i=0; i<10; i++) {
812 va_list ap;
813 va_copy(ap, *argptr);
814
815 /* print argument list into message */
816 int n = vsnprintf(buf, bufsize-1, format, ap);
817
818 //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
819
820 va_end(ap);
821
822 if (n < bufsize) {
823 break;
824 }
825
826 bufsize += 100;
827 bufsize *= 2;
828 buf = (char*)realloc(buf, bufsize);
829 assert(buf);
830 }
831
832 message += buf;
833 free(buf);
834
835 return message;
836}
837
838static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
839 //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
840
841 /* send event if not of type MLOG */
842 if (message_type != MT_LOG) {
843 if (_msg_buffer) {
844 /* copy message to event */
845 size_t len = strlen(send_message);
846 int event_length = sizeof(EVENT_HEADER) + len + 1;
847 char event[event_length];
848 EVENT_HEADER *pevent = (EVENT_HEADER *) event;
849
850 memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
851
852 /* setup the event header and send the message */
853 bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
854 if (ts)
855 pevent->time_stamp = ts;
856 //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)));
857 bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
858 }
859 }
860
861 return CM_SUCCESS;
862}
863
867 std::string message;
868};
869
870static std::deque<msg_buffer_entry> gMsgBuf;
871static std::mutex gMsgBufMutex;
872
873/********************************************************************/
880 int i;
881
882 //printf("cm_msg_flush_buffer!\n");
883
884 for (i = 0; i < 100; i++) {
886 {
887 std::lock_guard<std::mutex> lock(gMsgBufMutex);
888 if (gMsgBuf.empty())
889 break;
890 e = gMsgBuf.front();
891 gMsgBuf.pop_front();
892 // implicit unlock
893 }
894
895 /* log message */
896 cm_msg_log(e.message_type, "midas", e.message.c_str());
897
898 /* send message to SYSMSG */
899 int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
900 if (status != CM_SUCCESS)
901 return status;
902 }
903
904 return CM_SUCCESS;
905}
906
907/********************************************************************/
929INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
930{
931 DWORD ts = ss_time();
932
933 /* print argument list into message */
934 std::string message;
936 va_start(argptr, format);
937 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
938 va_end(argptr);
939
940 //printf("message [%s]\n", message.c_str());
941
942 /* call user function if set via cm_set_msg_print */
944 if (f != NULL && (message_type & _message_mask_user) != 0) {
945 if (message_type != MT_LOG) { // do not print MLOG messages
946 (*f)(message.c_str());
947 }
948 }
949
950 /* return if system mask is not set */
951 if ((message_type & _message_mask_system) == 0) {
952 return CM_SUCCESS;
953 }
954
955 gMsgBufMutex.lock();
956 gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
957 gMsgBufMutex.unlock();
958
959 return CM_SUCCESS;
960}
961
962/********************************************************************/
987INT cm_msg1(INT message_type, const char *filename, INT line,
988 const char *facility, const char *routine, const char *format, ...) {
990 std::string message;
991 static BOOL in_routine = FALSE;
992
993 /* avoid recursive calles */
994 if (in_routine)
995 return 0;
996
998
999 /* print argument list into message */
1000 va_start(argptr, format);
1001 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
1002 va_end(argptr);
1003
1004 /* call user function if set via cm_set_msg_print */
1006 if (f != NULL && (message_type & _message_mask_user) != 0)
1007 (*f)(message.c_str());
1008
1009 /* return if system mask is not set */
1010 if ((message_type & _message_mask_system) == 0) {
1011 in_routine = FALSE;
1012 return CM_SUCCESS;
1013 }
1014
1015 /* send message to SYSMSG */
1016 cm_msg_send_event(0, message_type, message.c_str());
1017
1018 /* log message */
1019 cm_msg_log(message_type, facility, message.c_str());
1020
1021 in_routine = FALSE;
1022
1023 return CM_SUCCESS;
1024}
1025
1026/********************************************************************/
1066 INT status, id;
1067
1068 // we should only come here after the message buffer
1069 // was opened by cm_connect_experiment()
1070 assert(_msg_buffer);
1071
1072 _msg_dispatch = func;
1073
1075
1076 return status;
1077}
1078
1079static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1081 int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1082 char buf[100];
1083 int buf_length;
1084
1085 //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);
1086
1087 if (*length + new_message_length + 100 > *allocated) {
1088 *messages = (char *) realloc(*messages, new_allocated);
1089 assert(*messages != NULL);
1091 }
1092
1093 if (*length > 0)
1094 if ((*messages)[(*length) - 1] != '\n') {
1095 (*messages)[*length] = '\n'; // separator between messages
1096 (*length) += 1;
1097 }
1098
1099 sprintf(buf, "%ld ", tstamp);
1100 buf_length = strlen(buf);
1101 memcpy(&((*messages)[*length]), buf, buf_length);
1102 (*length) += buf_length;
1103
1105 (*length) += new_message_length;
1106 (*messages)[*length] = 0; // make sure string is NUL terminated
1107}
1108
1109/* Retrieve message from an individual file. Internal use only */
1110static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1111 int *num_messages) {
1112 BOOL stop;
1113 int fh;
1114 char *p, str[1000];
1115 struct stat stat_buf;
1117
1118 ss_tzset(); // required by localtime_r()
1119
1120 *num_messages = 0;
1121
1122 fh = open(filename, O_RDONLY | O_TEXT, 0644);
1123 if (fh < 0) {
1124 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1125 strerror(errno));
1126 return SS_FILE_ERROR;
1127 }
1128
1129 /* read whole file into memory */
1130 fstat(fh, &stat_buf);
1131 ssize_t size = stat_buf.st_size;
1132
1133 /* if file is too big, only read tail of file */
1134 ssize_t maxsize = 10 * 1024 * 1024;
1135 if (size > maxsize) {
1137 //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1138 size = maxsize;
1139 }
1140
1141 char *buffer = (char *) malloc(size + 1);
1142
1143 if (buffer == NULL) {
1144 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1145 filename, errno, strerror(errno));
1146 close(fh);
1147 return SS_FILE_ERROR;
1148 }
1149
1150 ssize_t rd = read(fh, buffer, size);
1151
1152 if (rd != size) {
1153 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1154 (int) size, filename, (int) rd, errno, strerror(errno));
1155 close(fh);
1156 return SS_FILE_ERROR;
1157 }
1158
1159 buffer[size] = 0;
1160 close(fh);
1161
1162 p = buffer + size - 1;
1164 stop = FALSE;
1165
1166 while (*p == '\n' || *p == '\r')
1167 p--;
1168
1169 int n;
1170 for (n = 0; !stop && p > buffer;) {
1171
1172 /* go to beginning of line */
1173 int i;
1174 for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1175 p--;
1176
1177 /* limit line length to sizeof(str) */
1178 if (i >= (int) sizeof(str))
1179 i = sizeof(str) - 1;
1180
1181 if (p == buffer) {
1182 i++;
1183 memcpy(str, p, i);
1184 } else
1185 memcpy(str, p + 1, i);
1186 str[i] = 0;
1187 if (strchr(str, '\n'))
1188 *strchr(str, '\n') = 0;
1189 if (strchr(str, '\r'))
1190 *strchr(str, '\r') = 0;
1191 mstrlcat(str, "\n", sizeof(str));
1192
1193 // extract time tag
1194 time_t now;
1195 time(&now);
1196
1197 struct tm tms;
1198 localtime_r(&now, &tms); // must call tzset() beforehand!
1199
1200 if (str[0] >= '0' && str[0] <= '9') {
1201 // new format
1202 tms.tm_hour = atoi(str);
1203 tms.tm_min = atoi(str + 3);
1204 tms.tm_sec = atoi(str + 6);
1205 tms.tm_year = atoi(str + 13) - 1900;
1206 tms.tm_mon = atoi(str + 18) - 1;
1207 tms.tm_mday = atoi(str + 21);
1208 } else {
1209 // old format
1210 tms.tm_hour = atoi(str + 11);
1211 tms.tm_min = atoi(str + 14);
1212 tms.tm_sec = atoi(str + 17);
1213 tms.tm_year = atoi(str + 20) - 1900;
1214 for (i = 0; i < 12; i++)
1215 if (strncmp(str + 4, mname[i], 3) == 0)
1216 break;
1217 tms.tm_mon = i;
1218 tms.tm_mday = atoi(str + 8);
1219 }
1220 tstamp = ss_mktime(&tms);
1221 if (tstamp != -1)
1223
1224 // for new messages (n=0!), stop when t reached
1225 if (n_messages == 0) {
1226 if (tstamp_valid < t)
1227 break;
1228 }
1229
1230 // for old messages, stop when all messages belonging to tstamp_last are sent
1231 if (n_messages != 0) {
1233 break;
1234 }
1235
1236 if (t == 0 || tstamp == -1 ||
1237 (n_messages > 0 && tstamp <= t) ||
1238 (n_messages == 0 && tstamp >= t)) {
1239
1240 n++;
1241
1243 }
1244
1245 while (*p == '\n' || *p == '\r')
1246 p--;
1247
1248 if (n_messages == 1)
1249 stop = TRUE;
1250 else if (n_messages > 1) {
1251 // continue collecting messages until time stamp differs from current one
1252 if (n == n_messages)
1254
1255 // if all messages without time tags, just return after n
1256 if (n == n_messages && tstamp_valid == 0)
1257 break;
1258 }
1259 }
1260
1261 free(buffer);
1262
1263 *num_messages = n;
1264
1265 return CM_SUCCESS;
1266}
1267
1268/********************************************************************/
1279 std::string filename, linkname;
1280 INT n, i;
1282 int length = 0;
1283 int allocated = 0;
1284
1285 time(&filedate);
1287
1288 //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1289
1290 // see if file exists, use linkname if not
1291 if (!linkname.empty()) {
1292 if (!ss_file_exist(filename.c_str()))
1293 filename = linkname;
1294 }
1295
1296 if (ss_file_exist(filename.c_str())) {
1297 cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1298 } else {
1299 n = 0;
1300 }
1301
1302 /* if there is no symlink, then there is no additional log files to read */
1303 if (linkname.empty()) {
1304 *num_messages = n;
1305 return CM_SUCCESS;
1306 }
1307
1308 //printf("read more messages %d %d!\n", n, n_message);
1309
1310 int missing = 0;
1311 while (n < n_message) {
1312 filedate -= 3600 * 24; // go one day back
1313
1315
1316 //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1317
1318 if (ss_file_exist(filename.c_str())) {
1319 cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1320 n += i;
1321 missing = 0;
1322 } else {
1323 missing++;
1324 }
1325
1326 // stop if ten consecutive files are not found
1327 if (missing > 10)
1328 break;
1329 }
1330
1331 *num_messages = n;
1332
1333 return CM_SUCCESS;
1334}
1335
1336/********************************************************************/
1349 int status;
1350 char *messages = NULL;
1351 int num_messages = 0;
1352
1353 if (rpc_is_remote())
1354 return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1355
1357
1358 if (messages) {
1359 mstrlcpy(message, messages, buf_size);
1360 int len = strlen(messages);
1361 if (len > buf_size)
1363 free(messages);
1364 }
1365
1366 return status;
1367}
1368
/* end of msgfunctionc */
1371
1377/********************************************************************/
1384 INT sec, status;
1385
1386 /* if connected to server, get time from there */
1387 if (rpc_is_remote()) {
1389
1390 /* set local time */
1391 if (status == CM_SUCCESS)
1392 ss_settime(sec);
1393 }
1394
1395 /* return time to caller */
1396 if (seconds != NULL) {
1397 *seconds = ss_time();
1398 }
1399
1400 return CM_SUCCESS;
1401}
1402
1403/********************************************************************/
1410INT cm_asctime(char *str, INT buf_size) {
1411 /* if connected to server, get time from there */
1412 if (rpc_is_remote())
1413 return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1414
1415 /* return local time */
1416 mstrlcpy(str, ss_asctime().c_str(), buf_size);
1417
1418 return CM_SUCCESS;
1419}
1420
1421/********************************************************************/
1426std::string cm_asctime() {
1427 /* if connected to server, get time from there */
1428 if (rpc_is_remote()) {
1429 char buf[256];
1430 int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1431 if (status == CM_SUCCESS) {
1432 return buf;
1433 } else {
1434 return "";
1435 }
1436 }
1437
1438 /* return local time */
1439 return ss_asctime();
1440}
1441
1442/********************************************************************/
1449 /* if connected to server, get time from there */
1450 if (rpc_is_remote())
1451 return rpc_call(RPC_CM_TIME, t);
1452
1453 /* return local time */
1454 *t = ss_time();
1455
1456 return CM_SUCCESS;
1457}
1458
/* end of cmfunctionc */
1461
1462/********************************************************************\
1463* *
1464* cm_xxx - Common Functions to buffer & database *
1465* *
1466\********************************************************************/
1467
1468/* Globals */
1469
1470static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1471static HNDLE _hDB = 0; /* Database handle */
1472static std::string _experiment_name;
1473static std::string _client_name;
1474static std::string _path_name;
1479//INT _semaphore_msg = -1;
1480
1490const char *cm_get_version() {
1491 return MIDAS_VERSION;
1492}
1493
1498const char *cm_get_revision() {
1499 return GIT_REVISION;
1500}
1501
1502/********************************************************************/
1511INT cm_set_path(const char *path) {
1512 assert(path);
1513 assert(path[0] != 0);
1514
1515 _path_name = path;
1516
1517 if (_path_name.back() != DIR_SEPARATOR) {
1519 }
1520
1521 //printf("cm_set_path [%s]\n", _path_name.c_str());
1522
1523 return CM_SUCCESS;
1524}
1525
1526/********************************************************************/
1532INT cm_get_path(char *path, int path_size) {
1533 // check that we were not accidentally called
1534 // with the size of the pointer to a string
1535 // instead of the size of the string buffer
1536 assert(path_size != sizeof(char *));
1537 assert(path);
1538 assert(_path_name.length() > 0);
1539
1540 mstrlcpy(path, _path_name.c_str(), path_size);
1541
1542 return CM_SUCCESS;
1543}
1544
1545/********************************************************************/
1551std::string cm_get_path() {
1552 assert(_path_name.length() > 0);
1553 return _path_name;
1554}
1555
1556/********************************************************************/
1557/* C++ wrapper for cm_get_path */
1558
1559INT EXPRT cm_get_path_string(std::string *path) {
1560 assert(path != NULL);
1561 assert(_path_name.length() > 0);
1562 *path = _path_name;
1563 return CM_SUCCESS;
1564}
1565
1566/********************************************************************/
1574 return CM_SUCCESS;
1575}
1576
1577/********************************************************************/
1588
1589/********************************************************************/
1595 return _experiment_name;
1596}
1597
/* end of cmfunctionc */
1600
1606#ifdef LOCAL_ROUTINES
1607
1609 std::string name;
1610 std::string directory;
1611 std::string user;
1612};
1613
1615 std::string filename;
1616 std::vector<exptab_entry> exptab;
1617};
1618
1619static exptab_struct _exptab; // contents of exptab file
1620
1629{
1630 exptab->exptab.clear();
1631
1632 /* MIDAS_DIR overrides exptab */
1633 if (getenv("MIDAS_DIR")) {
1634 exptab->filename = "MIDAS_DIR";
1635
1637
1638 if (getenv("MIDAS_EXPT_NAME")) {
1639 e.name = getenv("MIDAS_EXPT_NAME");
1640 } else {
1641 e.name = "Default";
1642 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());
1643 }
1644
1645 e.directory = getenv("MIDAS_DIR");
1646 e.user = "";
1647
1648 exptab->exptab.push_back(e);
1649
1650 return CM_SUCCESS;
1651 }
1652
1653 /* default directory for different OSes */
1654#if defined (OS_WINNT)
1655 std::string str;
1656 if (getenv("SystemRoot"))
1657 str = getenv("SystemRoot");
1658 else if (getenv("windir"))
1659 str = getenv("windir");
1660 else
1661 str = "";
1662
1663 std::string alt_str = str;
1664 str += "\\system32\\exptab";
1665 alt_str += "\\system\\exptab";
1666#elif defined (OS_UNIX)
1667 std::string str = "/etc/exptab";
1668 std::string alt_str = "/exptab";
1669#else
1670 std::strint str = "exptab";
1671 std::string alt_str = "exptab";
1672#endif
1673
1674 /* MIDAS_EXPTAB overrides default directory */
1675 if (getenv("MIDAS_EXPTAB")) {
1676 str = getenv("MIDAS_EXPTAB");
1677 alt_str = getenv("MIDAS_EXPTAB");
1678 }
1679
1680 exptab->filename = str;
1681
1682 /* read list of available experiments */
1683 FILE* f = fopen(str.c_str(), "r");
1684 if (f == NULL) {
1685 f = fopen(alt_str.c_str(), "r");
1686 if (f == NULL)
1687 return CM_UNDEF_ENVIRON;
1688 exptab->filename = alt_str;
1689 }
1690
1691 if (f != NULL) {
1692 do {
1693 char buf[256];
1694 memset(buf, 0, sizeof(buf));
1695 char* str = fgets(buf, sizeof(buf)-1, f);
1696 if (str == NULL)
1697 break;
1698 if (str[0] == 0) continue; // empty line
1699 if (str[0] == '#') continue; // comment line
1700
1702
1703 // following code emulates the function of this sprintf():
1704 //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1705
1706 // skip leading spaces
1707 while (*str && isspace(*str))
1708 str++;
1709
1710 char* p1 = str;
1711 char* p2 = str;
1712
1713 while (*p2 && !isspace(*p2))
1714 p2++;
1715
1716 ssize_t len = p2-p1;
1717
1718 if (len<1)
1719 continue;
1720
1721 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1722
1723 e.name = std::string(p1, len);
1724
1725 if (*p2 == 0)
1726 continue;
1727
1728 str = p2;
1729
1730 // skip leading spaces
1731 while (*str && isspace(*str))
1732 str++;
1733
1734 p1 = str;
1735 p2 = str;
1736
1737 while (*p2 && !isspace(*p2))
1738 p2++;
1739
1740 len = p2-p1;
1741
1742 if (len<1)
1743 continue;
1744
1745 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1746
1747 e.directory = std::string(p1, len);
1748
1749 if (*p2 == 0)
1750 continue;
1751
1752 str = p2;
1753
1754 // skip leading spaces
1755 while (*str && isspace(*str))
1756 str++;
1757
1758 p1 = str;
1759 p2 = str;
1760
1761 while (*p2 && !isspace(*p2))
1762 p2++;
1763
1764 len = p2-p1;
1765
1766 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1767
1768 e.user = std::string(p1, len);
1769
1770 /* check for trailing directory separator */
1771 if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1772 e.directory += DIR_SEPARATOR_STR;
1773 }
1774
1775 exptab->exptab.push_back(e);
1776 } while (!feof(f));
1777 fclose(f);
1778 }
1779
1780#if 0
1781 cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1782 for (unsigned j=0; j<exptab->exptab.size(); j++) {
1783 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());
1784 }
1785#endif
1786
1787 return CM_SUCCESS;
1788}
1789
1790/********************************************************************/
1797int cm_get_exptab_filename(char *s, int size) {
1798 mstrlcpy(s, _exptab.filename.c_str(), size);
1799 return CM_SUCCESS;
1800}
1801
1803 return _exptab.filename;
1804}
1805
1806/********************************************************************/
1813int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1814
1815 if (_exptab.exptab.size() == 0) {
1817 if (status != CM_SUCCESS)
1818 return status;
1819 }
1820
1821 for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1822 if (_exptab.exptab[i].name == expname) {
1823 if (dir)
1824 *dir = _exptab.exptab[i].directory;
1825 if (user)
1826 *user = _exptab.exptab[i].user;
1827 return CM_SUCCESS;
1828 }
1829 }
1830 if (dir)
1831 *dir = "";
1832 if (user)
1833 *user = "";
1834 return CM_UNDEF_EXP;
1835}
1836
1837/********************************************************************/
1844int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1845 std::string sdir, suser;
1847 if (status == CM_SUCCESS) {
1848 if (dir)
1849 mstrlcpy(dir, sdir.c_str(), dir_size);
1850 if (user)
1851 mstrlcpy(user, suser.c_str(), user_size);
1852 return CM_SUCCESS;
1853 }
1854 return CM_UNDEF_EXP;
1855}
1856
1857#endif // LOCAL_ROUTINES
1858
1859/********************************************************************/
1867 /* only do it if local */
1868 if (!rpc_is_remote()) {
1870 }
1871 return CM_SUCCESS;
1872}
1873
1874/********************************************************************/
1884 if (rpc_is_remote())
1886
1887#ifdef LOCAL_ROUTINES
1889#endif /*LOCAL_ROUTINES */
1890 return CM_SUCCESS;
1891}
1892
1893/********************************************************************/
1908 char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1909 if (rpc_is_remote())
1911 host_name, client_name, hw_type, password, watchdog_timeout);
1912
1913#ifdef LOCAL_ROUTINES
1914 {
1915 INT status, pid, data, i, idx, size;
1920
1921 /* check security if password is present */
1922 status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1923 if (hKey) {
1924 /* get password */
1925 size = sizeof(pwd);
1926 db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1927
1928 /* first check allowed hosts list */
1929 allow = FALSE;
1930 db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1932 allow = TRUE;
1933
1934 /* check allowed programs list */
1935 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1936 if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
1937 allow = TRUE;
1938
1939 /* now check password */
1940 if (!allow && strcmp(password, pwd) != 0) {
1941 if (password[0])
1942 cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
1943 return CM_WRONG_PASSWORD;
1944 }
1945 }
1946
1947 /* make following operation atomic by locking database */
1949
1950 /* check if entry with this pid exists already */
1951 pid = ss_getpid();
1952
1953 sprintf(str, "System/Clients/%0d", pid);
1954 status = db_find_key(hDB, 0, str, &hKey);
1955 if (status == DB_SUCCESS) {
1958 }
1959
1960 if (strlen(client_name) >= NAME_LENGTH)
1961 client_name[NAME_LENGTH] = 0;
1962
1963 strcpy(name, client_name);
1964 strcpy(orig_name, client_name);
1965
1966 /* check if client name already exists */
1967 status = db_find_key(hDB, 0, "System/Clients", &hKey);
1968
1969 for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
1970 for (i = 0;; i++) {
1973 break;
1974
1975 if (status == DB_SUCCESS) {
1976 size = sizeof(str);
1977 status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
1978 if (status != DB_SUCCESS)
1979 continue;
1980 }
1981
1982 /* check if client is living */
1984 continue;
1985
1986 if (equal_ustring(str, name)) {
1987 sprintf(name, "%s%d", client_name, idx);
1988 break;
1989 }
1990 }
1991 }
1992
1993 /* set name */
1994 sprintf(str, "System/Clients/%0d/Name", pid);
1996 if (status != DB_SUCCESS) {
1998 cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
1999 return status;
2000 }
2001
2002 /* copy new client name */
2003 strcpy(client_name, name);
2004 db_set_client_name(hDB, client_name);
2005
2006 /* set also as rpc name */
2007 rpc_set_name(client_name);
2008
2009 /* use /system/clients/PID as root */
2010 sprintf(str, "System/Clients/%0d", pid);
2011 db_find_key(hDB, 0, str, &hKey);
2012
2013 /* set host name */
2015 if (status != DB_SUCCESS) {
2017 return status;
2018 }
2019
2020 /* set computer id */
2021 status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2022 if (status != DB_SUCCESS) {
2024 return status;
2025 }
2026
2027 /* set server port */
2028 data = 0;
2029 status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2030 if (status != DB_SUCCESS) {
2032 return status;
2033 }
2034
2035 /* lock client entry */
2037
2038 /* get (set) default watchdog timeout */
2039 size = sizeof(watchdog_timeout);
2040 sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
2041 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2042
2043 /* define /programs entry */
2044 sprintf(str, "/Programs/%s", orig_name);
2046
2047 /* save handle for ODB and client */
2049
2050 /* save watchdog timeout */
2052 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2053
2054 /* end of atomic operations */
2056
2057 /* touch notify key to inform others */
2058 data = 0;
2059 db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2060
2061 *hKeyClient = hKey;
2062 }
2063#endif /* LOCAL_ROUTINES */
2064
2065 return CM_SUCCESS;
2066}
2067
2068/********************************************************************/
2074{
2075 INT status;
2076 HNDLE hDB, hKey;
2077
2078 /* get root key of client */
2080 if (!hDB) {
2081 return "unknown";
2082 }
2083
2084 std::string name;
2085
2086 status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2087 if (status != DB_SUCCESS) {
2088 return "unknown";
2089 }
2090
2091 //printf("get client name: [%s]\n", name.c_str());
2092
2093 return name;
2094}
2095
2096/********************************************************************/
2149 if (host_name)
2150 host_name[0] = 0;
2151 if (exp_name)
2152 exp_name[0] = 0;
2153
2154 if (host_name && getenv("MIDAS_SERVER_HOST"))
2155 mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2156
2157 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2158 mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2159
2160 return CM_SUCCESS;
2161}
2162
2163INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2164 if (host_name)
2165 *host_name = "";
2166 if (exp_name)
2167 *exp_name = "";
2168
2169 if (host_name && getenv("MIDAS_SERVER_HOST"))
2170 *host_name = getenv("MIDAS_SERVER_HOST");
2171
2172 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2173 *exp_name = getenv("MIDAS_EXPT_NAME");
2174
2175 return CM_SUCCESS;
2176}
2177
2178#ifdef LOCAL_ROUTINES
2179
2181{
2182 std::string exp_name1;
2183
2184 if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2186 } else {
2188 if (status != CM_SUCCESS)
2189 return status;
2190 }
2191
2192 std::string expdir, expuser;
2193
2194 int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2195
2196 if (status != CM_SUCCESS) {
2197 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());
2198 return CM_UNDEF_EXP;
2199 }
2200
2201 if (!ss_dir_exist(expdir.c_str())) {
2202 cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2203 return CM_UNDEF_EXP;
2204 }
2205
2207 cm_set_path(expdir.c_str());
2208
2209 return CM_SUCCESS;
2210}
2211
2212#endif // LOCAL_ROUTINES
2213
2214/********************************************************************/
2216 if (_hKeyClient) {
2217 cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2219 }
2220}
2221
2222/********************************************************************/
2292INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2293 INT status;
2294
2297 if (status != CM_SUCCESS) {
2298 std::string s = cm_get_error(status);
2299 puts(s.c_str());
2300 }
2301
2302 return status;
2303}
2304
2305/********************************************************************/
2312 const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2313 INT status, size;
2315 char password[NAME_LENGTH], str[256];
2316 HNDLE hDB = 0, hKeyClient = 0;
2318
2319 ss_tzset(); // required for localtime_r()
2320
2321 if (_hKeyClient)
2323
2325
2326 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2327 //cm_msg_flush_buffer();
2328
2329 rpc_set_name(client_name);
2330
2331 /* check for local host */
2332 if (equal_ustring(host_name, "local"))
2333 host_name = NULL;
2334
2335#ifdef OS_WINNT
2336 {
2338
2339 /* Start windows sockets */
2340 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2341 return RPC_NET_ERROR;
2342 }
2343#endif
2344
2345 std::string default_exp_name1;
2346 if (default_exp_name)
2348
2349 /* connect to MIDAS server */
2350 if (host_name && host_name[0]) {
2351 if (default_exp_name1.length() == 0) {
2353 if (status != CM_SUCCESS)
2354 return status;
2355 }
2356
2358
2360 if (status != RPC_SUCCESS)
2361 return status;
2362
2363 /* register MIDAS library functions */
2365 if (status != RPC_SUCCESS)
2366 return status;
2367 } else {
2368 /* lookup path for *SHM files and save it */
2369
2370#ifdef LOCAL_ROUTINES
2372 if (status != CM_SUCCESS)
2373 return status;
2374
2376
2378
2380
2381 /* create alarm and elog semaphores */
2383 if (status != SS_CREATED && status != SS_SUCCESS) {
2384 cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2385 return status;
2386 }
2388 if (status != SS_CREATED && status != SS_SUCCESS) {
2389 cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2390 return status;
2391 }
2393 if (status != SS_CREATED && status != SS_SUCCESS) {
2394 cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2395 return status;
2396 }
2398 if (status != SS_CREATED && status != SS_SUCCESS) {
2399 cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2400 return status;
2401 }
2402
2404#else
2405 return CM_UNDEF_EXP;
2406#endif
2407 }
2408
2409 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2410 //cm_msg_flush_buffer();
2411
2412 /* open ODB */
2413 if (odb_size == 0)
2415
2416 status = db_open_database("ODB", odb_size, &hDB, client_name);
2417 if (status != DB_SUCCESS && status != DB_CREATED) {
2418 cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2419 return status;
2420 }
2421
2422 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2423 //cm_msg_flush_buffer();
2424
2426 size = sizeof(odb_timeout);
2427 status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2428 if (status != DB_SUCCESS) {
2429 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2430 }
2431
2432 if (odb_timeout > 0) {
2434 }
2435
2437 size = sizeof(protect_odb);
2438 status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2439 if (status != DB_SUCCESS) {
2440 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2441 }
2442
2443 if (protect_odb) {
2445 }
2446
2448 size = sizeof(enable_core_dumps);
2449 status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2450 if (status != DB_SUCCESS) {
2451 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2452 }
2453
2454 if (enable_core_dumps) {
2455#ifdef RLIMIT_CORE
2456 struct rlimit limit;
2457 limit.rlim_cur = RLIM_INFINITY;
2458 limit.rlim_max = RLIM_INFINITY;
2460 if (status != 0) {
2461 cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2462 strerror(errno));
2463 }
2464#else
2465#warning setrlimit(RLIMIT_CORE) is not available
2466#endif
2467 }
2468
2469 size = sizeof(disable_bind_rpc_to_localhost);
2470 status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2471 TID_BOOL, TRUE);
2472 if (status != DB_SUCCESS) {
2473 cm_msg(MERROR, "cm_connect_experiment1",
2474 "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2475 }
2476
2477 std::string local_host_name;
2478
2479 /* now setup client info */
2481 local_host_name = "localhost";
2482 else
2484
2485 /* check watchdog timeout */
2486 if (watchdog_timeout == 0)
2487 watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2488
2489 strcpy(client_name1, client_name);
2490 password[0] = 0;
2491 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2492
2493 if (status == CM_WRONG_PASSWORD) {
2494 if (func == NULL)
2495 strcpy(str, ss_getpass("Password: "));
2496 else
2497 func(str);
2498
2499 strcpy(password, ss_crypt(str, "mi"));
2500 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2501 if (status != CM_SUCCESS) {
2502 /* disconnect */
2503 if (rpc_is_remote())
2506
2507 return status;
2508 }
2509 }
2510
2511 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2512 //cm_msg_flush_buffer();
2513
2514 /* tell the rest of MIDAS that ODB is open for business */
2515
2517
2518 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2519 //cm_msg_flush_buffer();
2520
2521 /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2522 * to get event buffer size, etc */
2523
2525 if (status != CM_SUCCESS) {
2526 cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2527 return status;
2528 }
2529
2530 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2531 //cm_msg_flush_buffer();
2532
2533 /* set experiment name in ODB if not present */
2534 std::string current_name;
2535 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
2536 if (current_name.length() == 0 || current_name == "Default") {
2537 db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2538 }
2539
2540 if (!rpc_is_remote()) {
2541 /* experiment path is only set for local connections */
2542 /* set data dir in ODB */
2543 std::string path = cm_get_path();
2544 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2545 }
2546
2547 /* register server to be able to be called by other clients */
2549 if (status != CM_SUCCESS) {
2550 cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2551 if (!equal_ustring(client_name, "odbedit")) {
2552 return status;
2553 }
2554 }
2555
2556 /* set watchdog timeout */
2557 cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2558 size = sizeof(watchdog_timeout);
2559 sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2560 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2561 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2562
2563 /* set command line */
2564 std::string cmdline = ss_get_cmdline();
2565 std::string path = "/Programs/" + std::string(client_name);
2566 midas::odb prog(path);
2567 if (!midas::odb::exists(path + "/Start command") ||
2568 prog["Start command"] == std::string(""))
2569 prog["Start command"].set_string_size(cmdline, 256);
2570
2571 /* get final client name */
2572 std::string xclient_name = rpc_get_name();
2573
2574 /* startup message is not displayed */
2575 cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2576
2577 /* enable system and user messages to stdout as default */
2579
2580 /* call cm_check_connect when exiting */
2581 atexit((void (*)(void)) cm_check_connect);
2582
2583 /* register ctrl-c handler */
2585
2586 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2587 //cm_msg_flush_buffer();
2588
2589 return CM_SUCCESS;
2590}
2591
2592#ifdef LOCAL_ROUTINES
2593/********************************************************************/
2601 assert(exp_names != NULL);
2602 exp_names->clear();
2603
2604 if (_exptab.exptab.size() == 0) {
2606 if (status != CM_SUCCESS)
2607 return status;
2608 }
2609
2610 for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2611 exp_names->push_back(_exptab.exptab[i].name);
2612 }
2613
2614 return CM_SUCCESS;
2615}
2616#endif // LOCAL_ROUTINES
2617
2618/********************************************************************/
2627 INT status;
2628 INT sock;
2629 int port = MIDAS_TCP_PORT;
2630 char hname[256];
2631 char *s;
2632
2633 assert(exp_names != NULL);
2634 exp_names->clear();
2635
2636 /* extract port number from host_name */
2637 mstrlcpy(hname, host_name, sizeof(hname));
2638 s = strchr(hname, ':');
2639 if (s) {
2640 *s = 0;
2641 port = strtoul(s + 1, NULL, 0);
2642 }
2643
2644 std::string errmsg;
2645
2646 status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2647
2648 if (status != SS_SUCCESS) {
2649 cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2650 return RPC_NET_ERROR;
2651 }
2652
2653 /* request experiment list */
2654 send(sock, "I", 2, 0);
2655
2656 while (1) {
2657 char str[256];
2658
2659 status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2660
2661 if (status < 0)
2662 return RPC_NET_ERROR;
2663
2664 if (status == 0)
2665 break;
2666
2667 exp_names->push_back(str);
2668 }
2669
2670 ss_socket_close(&sock);
2671
2672 return CM_SUCCESS;
2673}
2674
2675#ifdef LOCAL_ROUTINES
2676/********************************************************************/
2685 INT status;
2687
2688 assert(exp_name != NULL);
2689
2690 /* retrieve list of experiments and make selection */
2692 if (status != CM_SUCCESS)
2693 return status;
2694
2695 if (expts.size() == 1) {
2696 *exp_name = expts[0];
2697 } else if (expts.size() > 1) {
2698 printf("Available experiments on local computer:\n");
2699
2700 for (unsigned i = 0; i < expts.size(); i++) {
2701 printf("%d : %s\n", i, expts[i].c_str());
2702 }
2703
2704 while (1) {
2705 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2706 char str[32];
2707 ss_gets(str, 32);
2708 int isel = atoi(str);
2709 if (isel < 0)
2710 continue;
2711 if (isel >= (int)expts.size())
2712 continue;
2713 *exp_name = expts[isel];
2714 break;
2715 }
2716 } else {
2717 return CM_UNDEF_EXP;
2718 }
2719
2720 return CM_SUCCESS;
2721}
2722#endif // LOCAL_ROUTINES
2723
2724/********************************************************************/
2734 INT status;
2736
2737 assert(exp_name != NULL);
2738
2739 /* retrieve list of experiments and make selection */
2741 if (status != CM_SUCCESS)
2742 return status;
2743
2744 if (expts.size() > 1) {
2745 printf("Available experiments on server %s:\n", host_name);
2746
2747 for (unsigned i = 0; i < expts.size(); i++) {
2748 printf("%d : %s\n", i, expts[i].c_str());
2749 }
2750
2751 while (1) {
2752 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2753 char str[32];
2754 ss_gets(str, 32);
2755 int isel = atoi(str);
2756 if (isel < 0)
2757 continue;
2758 if (isel >= (int)expts.size())
2759 continue;
2760 *exp_name = expts[isel];
2761 break;
2762 }
2763 } else {
2764 *exp_name = expts[0];
2765 }
2766
2767 return CM_SUCCESS;
2768}
2769
2770/********************************************************************/
2780INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2782 INT status, i, length, port;
2784
2785 /* find client entry in ODB */
2787
2788 status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2789 if (status != DB_SUCCESS)
2790 return status;
2791
2792 i = 0;
2793 do {
2794 /* search for client with specific name */
2797 return CM_NO_CLIENT;
2798
2799 status = db_find_key(hDB, hSubkey, "Name", &hKey);
2800 if (status != DB_SUCCESS)
2801 return status;
2802
2805 if (status != DB_SUCCESS)
2806 return status;
2807
2808 if (equal_ustring(name, client_name)) {
2809 status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2810 if (status != DB_SUCCESS)
2811 return status;
2812
2813 length = sizeof(INT);
2814 status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2815 if (status != DB_SUCCESS)
2816 return status;
2817
2818 status = db_find_key(hDB, hSubkey, "Host", &hKey);
2819 if (status != DB_SUCCESS)
2820 return status;
2821
2822 length = sizeof(host_name);
2824 if (status != DB_SUCCESS)
2825 return status;
2826
2827 /* client found -> connect to its server port */
2828 return rpc_client_connect(host_name, port, client_name, hConn);
2829 }
2830
2831
2832 } while (TRUE);
2833}
2834
2835static void rpc_client_shutdown();
2836
2837/********************************************************************/
2850
2851/********************************************************************/
2861 HNDLE hDB, hKey;
2862
2863 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2864 //cm_msg_flush_buffer();
2865
2866 /* wait on any transition thread */
2867 if (_trp.transition && !_trp.finished) {
2868 printf("Waiting for transition to finish...\n");
2869 do {
2870 ss_sleep(10);
2871 } while (!_trp.finished);
2872 }
2873
2874 /* stop the watchdog thread */
2876
2877 /* send shutdown notification */
2878 std::string client_name = rpc_get_name();
2879
2880 std::string local_host_name;
2881
2883 local_host_name = "localhost";
2884 else {
2886 //if (strchr(local_host_name, '.'))
2887 // *strchr(local_host_name, '.') = 0;
2888 }
2889
2890 /* disconnect message not displayed */
2891 cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2893
2894 if (rpc_is_remote()) {
2895 if (rpc_is_connected()) {
2896 /* close open records */
2898
2900 }
2901
2904
2906 } else {
2908
2909 /* delete client info */
2911
2912 if (hDB)
2914
2915 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2916 //cm_msg_flush_buffer();
2917
2921
2923
2924 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2925 //cm_msg_flush_buffer();
2926 }
2927
2928 if (!rpc_is_mserver())
2930
2931 /* free RPC list */
2933
2934 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2935 //cm_msg_flush_buffer();
2936
2937 /* last flush before we delete the message ring buffer */
2939
2940 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2941 //cm_msg_flush_buffer();
2942
2943 return CM_SUCCESS;
2944}
2945
2946/********************************************************************/
2954 //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
2955
2956 _hDB = hDB;
2958
2959 //if (hDB == 0) {
2960 // rpc_set_server_option(RPC_ODB_HANDLE, 0);
2961 //}
2962
2963 return CM_SUCCESS;
2964}
2965
2966
2967
2969#ifndef DOXYGEN_SHOULD_SKIP_THIS
2970
2971/********************************************************************/
2973/********************************************************************\
2974
2975 Routine: cm_set_experiment_semaphore
2976
2977 Purpose: Set the handle to the experiment wide semaphorees
2978
2979 Input:
2980 INT semaphore_alarm Alarm semaphore
2981 INT semaphore_elog Elog semaphore
2982 INT semaphore_history History semaphore
2983 INT semaphore_msg Message semaphore
2984
2985 Output:
2986 none
2987
2988 Function value:
2989 CM_SUCCESS Successful completion
2990
2991\********************************************************************/
2992{
2996 //_semaphore_msg = semaphore_msg;
2997
2998 return CM_SUCCESS;
2999}
3000
3002#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3003
3004/********************************************************************/
3026 if (_hDB) {
3027 //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3028 if (hDB != NULL)
3029 *hDB = _hDB;
3030 if (hKeyClient != NULL)
3032 return CM_SUCCESS;
3033 } else {
3034 //printf("cm_get_experiment_database no init\n");
3035 if (hDB != NULL)
3036 *hDB = 0;
3037 if (hKeyClient != NULL)
3038 *hKeyClient = 0;
3039 return CM_DB_ERROR;
3040 }
3041}
3042
3044#ifndef DOXYGEN_SHOULD_SKIP_THIS
3045
3046/********************************************************************/
3048/********************************************************************\
3049
3050 Routine: cm_get_experiment_semaphore
3051
3052 Purpose: Get the handle to the experiment wide semaphores
3053
3054 Input:
3055 none
3056
3057 Output:
3058 INT semaphore_alarm Alarm semaphore
3059 INT semaphore_elog Elog semaphore
3060 INT semaphore_history History semaphore
3061 INT semaphore_msg Message semaphore
3062
3063 Function value:
3064 CM_SUCCESS Successful completion
3065
3066\********************************************************************/
3067{
3068 if (semaphore_alarm)
3070 if (semaphore_elog)
3074 //if (semaphore_msg)
3075 // *semaphore_msg = _semaphore_msg;
3076 if (semaphore_msg)
3077 *semaphore_msg = -1;
3078
3079 return CM_SUCCESS;
3080}
3081
3083#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3084
3085#ifdef LOCAL_ROUTINES
3086static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3089static int bm_lock_buffer_mutex(BUFFER *pbuf);
3090static int xbm_lock_buffer(BUFFER *pbuf);
3091static void xbm_unlock_buffer(BUFFER *pbuf);
3092
3094{
3095public:
3096 bool fDebug = false;
3097
3098public:
3100 {
3101 assert(pbuf != NULL);
3102 fBuf = pbuf;
3103 if (do_not_lock) {
3104 if (fDebug)
3105 printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3106 return;
3107 }
3108 if (fDebug)
3109 printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3111 if (status != BM_SUCCESS) {
3112 fLocked = false;
3113 fError = true;
3114 fStatus = status;
3115 } else {
3116 fLocked = true;
3117 }
3118 }
3119
3121 {
3122 if (fInvalid) {
3123 if (fDebug)
3124 printf("lock_buffer_guard(invalid) dtor\n");
3125 } else {
3126 assert(fBuf != NULL);
3127 if (fDebug)
3128 printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3129 if (fLocked) {
3131 fLocked = false;
3132 fError = false;
3133 }
3134 fBuf = NULL;
3135 }
3136 }
3137
3138 // make object uncopyable
3141
3142 void unlock()
3143 {
3144 assert(fBuf != NULL);
3145 if (fDebug)
3146 printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3147 assert(fLocked);
3149 fLocked = false;
3150 fError = false;
3151 }
3152
3153 bool relock()
3154 {
3155 assert(fBuf != NULL);
3156 if (fDebug)
3157 printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3158 assert(!fLocked);
3160 if (status != BM_SUCCESS) {
3161 fLocked = false;
3162 fError = true;
3163 fStatus = status;
3164 } else {
3165 fLocked = true;
3166 }
3167 return fLocked;
3168 }
3169
3171 {
3172 assert(fBuf != NULL);
3173 if (fDebug)
3174 printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3175 assert(!fLocked);
3176 fInvalid = true;
3177 fBuf = NULL;
3178 }
3179
3180 bool is_locked() const
3181 {
3182 return fLocked;
3183 }
3184
3185 bool is_error() const
3186 {
3187 return fError;
3188 }
3189
3190 int get_status() const
3191 {
3192 return fStatus;
3193 }
3194
3196 {
3197 assert(!fInvalid); // pbuf was deleted
3198 assert(fBuf); // we do not return NULL
3199 return fBuf;
3200 }
3201
3202private:
3204 bool fLocked = false;
3205 bool fError = false;
3206 bool fInvalid = false;
3207 int fStatus = 0;
3208};
3209
3211
3212#endif
3213
3214static INT bm_notify_client(const char *buffer_name, int s);
3215
3216static INT bm_push_event(const char *buffer_name);
3217
3218static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3219 EVENT_HEADER *pevent, void *pdata,
3220 EVENT_HANDLER *dispatcher);
3221
3222/********************************************************************/
3259{
3260#ifdef LOCAL_ROUTINES
3261 _watchdog_timeout = timeout;
3262
3263 std::vector<BUFFER*> mybuffers;
3264
3265 gBuffersMutex.lock();
3267 gBuffersMutex.unlock();
3268
3269 /* set watchdog timeout of all open buffers */
3270 for (BUFFER* pbuf : mybuffers) {
3271
3272 if (!pbuf || !pbuf->attached)
3273 continue;
3274
3276
3277 if (!pbuf_guard.is_locked())
3278 continue;
3279
3281
3282 /* clear entry from client structure in buffer header */
3283 pclient->watchdog_timeout = timeout;
3284
3285 /* show activity */
3286 pclient->last_activity = ss_millitime();
3287 }
3288
3289 /* set watchdog timeout for ODB */
3290 db_set_watchdog_params(timeout);
3291
3292#endif /* LOCAL_ROUTINES */
3293
3294 return CM_SUCCESS;
3295}
3296
3298{
3299 /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3300 _watchdog_timeout = timeout;
3301
3302 if (rpc_is_remote()) { // we are connected remotely
3303
3305
3306 } else if (rpc_is_mserver()) { // we are the mserver
3307
3309 if (sa)
3310 sa->watchdog_timeout = timeout;
3311
3312 /* write timeout value to client entry in ODB */
3313 HNDLE hDB, hKey;
3315
3316 if (hDB) {
3318 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3320 }
3321
3322 /* set the watchdog for the local mserver program */
3324
3325 } else { // only running locally
3326
3328
3329 }
3330}
3331
3332/********************************************************************/
3340 if (call_watchdog)
3342 if (timeout)
3343 *timeout = _watchdog_timeout;
3344
3345 return CM_SUCCESS;
3346}
3347
3348/********************************************************************/
3358INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3359 if (rpc_is_remote())
3360 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3361
3362#ifdef LOCAL_ROUTINES
3363 return db_get_watchdog_info(hDB, client_name, timeout, last);
3364#else /* LOCAL_ROUTINES */
3365 return CM_SUCCESS;
3366#endif /* LOCAL_ROUTINES */
3367}
3368
3369
3371#ifndef DOXYGEN_SHOULD_SKIP_THIS
3372
3373/********************************************************************/
3374
3375static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3376 int status;
3377 int i, last;
3378 KEY key;
3379 int max_size;
3380 char *str;
3381
3382// if (index != -99)
3383// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3384
3386
3387 if (status != DB_SUCCESS)
3388 return;
3389
3390 //printf("clear rpc hosts!\n");
3392
3394 str = (char *) malloc(max_size);
3395
3396 last = 0;
3397 for (i = 0; i < key.num_values; i++) {
3398 int size = max_size;
3400 if (status != DB_SUCCESS)
3401 break;
3402
3403 if (strlen(str) < 1) // skip emties
3404 continue;
3405
3406 if (str[0] == '#') // skip commented-out entries
3407 continue;
3408
3409 //printf("add rpc hosts %d [%s]\n", i, str);
3411 last = i;
3412 }
3413
3414 if (key.num_values - last < 10) {
3415 int new_size = last + 10;
3417 if (status != DB_SUCCESS) {
3418 cm_msg(MERROR, "load_rpc_hosts",
3419 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3420 }
3421 }
3422
3423 free(str);
3424}
3425
3427 int status;
3428 char buf[256];
3429 int size, i;
3430 HNDLE hKey;
3431
3432 strcpy(buf, "localhost");
3433 size = sizeof(buf);
3434
3435 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3436
3437 if (status != DB_SUCCESS) {
3438 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3439 status);
3440 return;
3441 }
3442
3443 size = sizeof(i);
3444 i = 0;
3445 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3446
3447 if (status != DB_SUCCESS) {
3448 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3449 return;
3450 }
3451
3452 if (i != 0) // RPC hosts check is disabled
3453 return;
3454
3455 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3456
3457 if (status != DB_SUCCESS || hKey == 0) {
3458 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3459 status);
3460 return;
3461 }
3462
3463 load_rpc_hosts(hDB, hKey, -99, NULL);
3464
3466
3467 if (status != DB_SUCCESS) {
3468 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3469 return;
3470 }
3471}
3472
3473/********************************************************************/
3475/********************************************************************\
3476
3477 Routine: cm_register_server
3478
3479 Purpose: Register a server which can be called from other clients
3480 of a specific experiment.
3481
3482 Input:
3483 none
3484
3485 Output:
3486 none
3487
3488 Function value:
3489 CM_SUCCESS Successful completion
3490
3491\********************************************************************/
3492{
3493 if (!_rpc_registered) {
3494 INT status;
3495 int size;
3496 HNDLE hDB, hKey;
3497 char name[NAME_LENGTH];
3498 char str[256];
3499 int port = 0;
3500
3502
3503 size = sizeof(name);
3504 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3505
3506 if (status != DB_SUCCESS) {
3507 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3508 return status;
3509 }
3510
3511 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3512 mstrlcat(str, name, sizeof(str));
3513
3514 size = sizeof(port);
3515 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3516
3517 if (status != DB_SUCCESS) {
3518 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3519 return status;
3520 }
3521
3522 int lport = 0; // actual port number assigned to us by the OS
3523
3525 if (status != RPC_SUCCESS) {
3526 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3527 return status;
3528 }
3529
3531
3532 /* register MIDAS library functions */
3534
3535 /* store port number in ODB */
3536
3537 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3538 if (status != DB_SUCCESS) {
3539 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3540 return status;
3541 }
3542
3543 /* unlock database */
3545
3546 /* set value */
3547 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3548 if (status != DB_SUCCESS) {
3549 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3550 return status;
3551 }
3552
3553 /* lock database */
3555
3557 }
3558
3559 return CM_SUCCESS;
3560}
3561
3563#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3564
3565/********************************************************************/
3615INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3616 INT status;
3618 KEY key;
3619 char str[256];
3620
3621 /* check for valid transition */
3623 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3624 return CM_INVALID_TRANSITION;
3625 }
3626
3628
3630
3631 /* register new transition request */
3632
3633 {
3634 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3635
3636 for (size_t i = 0; i < _trans_table.size(); i++) {
3637 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3638 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3639 return CM_INVALID_TRANSITION;
3640 }
3641 }
3642
3643 bool found = false;
3644 for (size_t i = 0; i < _trans_table.size(); i++) {
3645 if (!_trans_table[i].transition) {
3646 _trans_table[i].transition = transition;
3647 _trans_table[i].sequence_number = sequence_number;
3648 _trans_table[i].func = func;
3649 found = true;
3650 break;
3651 }
3652 }
3653
3654 if (!found) {
3657 tt.sequence_number = sequence_number;
3658 tt.func = func;
3659 _trans_table.push_back(tt);
3660 }
3661
3662 // implicit unlock
3663 }
3664
3665 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3666
3667 /* unlock database */
3669
3670 /* set value */
3672 if (!hKeyTrans) {
3673 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3674 if (status != DB_SUCCESS)
3675 return status;
3676 } else {
3678 if (status != DB_SUCCESS)
3679 return status;
3680 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3681 if (status != DB_SUCCESS)
3682 return status;
3683 }
3684
3685 /* re-lock database */
3687
3688 return CM_SUCCESS;
3689}
3690
3692 INT status;
3694 char str[256];
3695
3696 /* check for valid transition */
3698 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3699 return CM_INVALID_TRANSITION;
3700 }
3701
3703
3704 {
3705 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3706
3707 /* remove existing transition request */
3708 for (size_t i = 0; i < _trans_table.size(); i++) {
3710 _trans_table[i].transition = 0;
3711 _trans_table[i].sequence_number = 0;
3712 _trans_table[i].func = NULL;
3713 }
3714 }
3715
3716 // implicit unlock
3717 }
3718
3719 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3720
3721 /* unlock database */
3723
3724 /* set value */
3726 if (hKeyTrans) {
3728 if (status != DB_SUCCESS)
3729 return status;
3730 }
3731
3732 /* re-lock database */
3734
3735 return CM_SUCCESS;
3736}
3737
3738/********************************************************************/
3746 INT status;
3747 HNDLE hDB, hKey;
3748 char str[256];
3749
3750 /* check for valid transition */
3752 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3753 return CM_INVALID_TRANSITION;
3754 }
3755
3756 {
3757 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3758
3759 int count = 0;
3760 for (size_t i = 0; i < _trans_table.size(); i++) {
3762 _trans_table[i].sequence_number = sequence_number;
3763 count++;
3764 }
3765 }
3766
3767 if (count == 0) {
3768 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3769 return CM_INVALID_TRANSITION;
3770 } else if (count > 1) {
3771 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3772 return CM_INVALID_TRANSITION;
3773 }
3774
3775 /* Change local sequence number for this transition type */
3776
3777 for (size_t i = 0; i < _trans_table.size(); i++) {
3779 _trans_table[i].sequence_number = sequence_number;
3780 }
3781 }
3782
3783 // implicit unlock
3784 }
3785
3787
3788 /* unlock database */
3790
3791 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3792
3793 /* set value */
3794 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3795 if (status != DB_SUCCESS)
3796 return status;
3797
3798 /* re-lock database */
3800
3801 return CM_SUCCESS;
3802
3803}
3804
3806 INT status;
3807 HNDLE hDB, hKey;
3808 KEY key;
3809
3811
3812 /* check that hKey is still valid */
3814
3815 if (status != DB_SUCCESS) {
3816 cm_msg(MERROR, "cm_set_client_run_state",
3817 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3818 hKey);
3819 return status;
3820 }
3821
3822 /* unlock database */
3824
3825 /* set value */
3826 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3827 if (status != DB_SUCCESS)
3828 return status;
3829
3830 /* re-lock database */
3832
3833 return CM_SUCCESS;
3834
3835}
3836
3838#ifndef DOXYGEN_SHOULD_SKIP_THIS
3839
3842
3844#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3845
3846/********************************************************************/
3860 INT status, size;
3861 char tr_key_name[256];
3862 HNDLE hDB, hKey;
3863
3865
3866 for (int i = 0; _deferred_trans_table[i].transition; i++)
3868 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3869
3870 /* set new transition mask */
3872
3873 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3874
3875 /* unlock database */
3877
3878 /* set value */
3879 int i = 0;
3880 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3881 if (status != DB_SUCCESS)
3882 return status;
3883
3884 /* re-lock database */
3886
3887 /* hot link requested transition */
3888 size = sizeof(_requested_transition);
3889 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3890 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3892 if (status != DB_SUCCESS) {
3893 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3894 return status;
3895 }
3896
3897 return CM_SUCCESS;
3898}
3899
3900/********************************************************************/
3912 INT i, status;
3913 char str[256];
3914 static BOOL first;
3915
3916 if (_requested_transition == 0)
3917 first = TRUE;
3918
3920 for (i = 0; _deferred_trans_table[i].transition; i++)
3922 break;
3923
3927 if (status != CM_SUCCESS)
3928 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3929
3930 /* bypass hotlink and set _requested_transition directly to zero */
3932
3933 return status;
3934 }
3935 first = FALSE;
3936 }
3937 }
3938
3939 return SUCCESS;
3940}
3941
3942
3944#ifndef DOXYGEN_SHOULD_SKIP_THIS
3945
3946/********************************************************************/
3947
3949#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3950
3951struct TrClient {
3952 int transition = 0;
3953 int run_number = 0;
3954 int async_flag = 0;
3955 int debug_flag = 0;
3957 std::vector<int> wait_for_index;
3958 std::string host_name;
3959 std::string client_name;
3960 int port = 0;
3961 std::string key_name; /* this client key name in /System/Clients */
3962 std::atomic_int status{0};
3963 std::thread* thread = NULL;
3964 std::string errorstr;
3965 DWORD init_time = 0; // time when tr_client created
3966 std::string waiting_for_client; // name of client we are waiting for
3968 DWORD connect_start_time = 0; // time when client rpc connection is started
3969 DWORD connect_end_time = 0; // time when client rpc connection is finished
3971 DWORD rpc_start_time = 0; // time client rpc call is started
3972 DWORD rpc_end_time = 0; // time client rpc call is finished
3973 DWORD end_time = 0; // time client thread is finished
3974
3975 TrClient() // ctor
3976 {
3977 // empty
3978 }
3979
3980 ~TrClient() // dtor
3981 {
3982 //printf("TrClient::dtor: client \"%s\"\n", client_name);
3983 assert(thread == NULL);
3984 }
3985
3986 void Print() const
3987 {
3988 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3989 if (wait_for_index.size() > 0) {
3990 printf(", wait for:");
3991 for (size_t i=0; i<wait_for_index.size(); i++) {
3992 printf(" %d", wait_for_index[i]);
3993 }
3994 }
3995 }
3996};
3997
3998static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
3999 return arg1->sequence_number < arg2->sequence_number;
4000}
4001
4002/*------------------------------------------------------------------*/
4003
4004struct TrState {
4005 int transition = 0;
4006 int run_number = 0;
4007 int async_flag = 0;
4008 int debug_flag = 0;
4009 int status = 0;
4010 std::string errorstr;
4013 std::vector<std::unique_ptr<TrClient>> clients;
4014};
4015
4016/*------------------------------------------------------------------*/
4017
4018static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4019{
4020 DWORD end_time = ss_millitime();
4021
4022 if (transition != TR_STARTABORT) {
4023 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4024 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4025
4026 if (errorstr) {
4027 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4028 } else if (status == CM_SUCCESS) {
4029 const char *buf = "Success";
4030 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4031 } else {
4032 char buf[256];
4033 sprintf(buf, "status %d", status);
4034 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4035 }
4036 }
4037
4038 tr->status = status;
4039 tr->end_time = end_time;
4040 if (errorstr) {
4041 tr->errorstr = errorstr;
4042 } else {
4043 tr->errorstr = "(null)";
4044 }
4045
4046 return status;
4047}
4048
4049/*------------------------------------------------------------------*/
4050
4052 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4053
4054 int status;
4055 HNDLE hKey;
4056
4057 if (tr_client->transition == TR_STARTABORT) {
4058 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4059 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4060 if (status != DB_SUCCESS)
4061 return;
4062 } else {
4063 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4064 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4065 if (status != DB_SUCCESS)
4066 return;
4067 }
4068
4069 // same client_name can exist with different sequence numbers!
4070 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4071
4073 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4074 if (status != DB_SUCCESS)
4075 return;
4076
4078
4079 //int transition;
4080 //int run_number;
4081 //int async_flag;
4082 //int debug_flag;
4083 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4084 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4085 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4086 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4087 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4088 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);
4089 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4090 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4091 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4092 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4093 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4094 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4095 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4096 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4097 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4098 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4099}
4100
4101/*------------------------------------------------------------------*/
4102
4103/* Perform a detached transition through the external "mtransition" program */
4104static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4105 HNDLE hDB;
4106 int status;
4107 const char *args[100];
4108 std::string path;
4109 char debug_arg[256];
4110 char start_arg[256];
4111 std::string expt_name;
4112 std::string mserver_hostname;
4113
4114 int iarg = 0;
4115
4117
4118 const char *midassys = getenv("MIDASSYS");
4119 if (midassys) {
4120 path += midassys;
4121 path += DIR_SEPARATOR_STR;
4122 path += "bin";
4123 path += DIR_SEPARATOR_STR;
4124 }
4125 path += "mtransition";
4126
4127 args[iarg++] = path.c_str();
4128
4129 if (rpc_is_remote()) {
4130 /* if connected to mserver, pass connection info to mtransition */
4132 args[iarg++] = "-h";
4133 args[iarg++] = mserver_hostname.c_str();
4134 }
4135
4136 /* get experiment name from ODB */
4137 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4138
4139 if (expt_name.length() > 0) {
4140 args[iarg++] = "-e";
4141 args[iarg++] = expt_name.c_str();
4142 }
4143
4144 if (debug_flag) {
4145 args[iarg++] = "-d";
4146
4147 sprintf(debug_arg, "%d", debug_flag);
4148 args[iarg++] = debug_arg;
4149 }
4150
4151 if (transition == TR_STOP)
4152 args[iarg++] = "STOP";
4153 else if (transition == TR_PAUSE)
4154 args[iarg++] = "PAUSE";
4155 else if (transition == TR_RESUME)
4156 args[iarg++] = "RESUME";
4157 else if (transition == TR_START) {
4158 args[iarg++] = "START";
4159
4161 args[iarg++] = start_arg;
4162 }
4163
4164 args[iarg++] = NULL;
4165
4166#if 0
4167 for (iarg = 0; args[iarg] != NULL; iarg++) {
4168 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4169 }
4170#endif
4171
4173
4174 if (status != SS_SUCCESS) {
4175 if (errstr != NULL) {
4176 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4177 }
4178 return CM_SET_ERROR;
4179 }
4180
4181 return CM_SUCCESS;
4182}
4183
4184/*------------------------------------------------------------------*/
4185
4186/* contact a client via RPC and execute the remote transition */
4187static int cm_transition_call(TrState* s, int idx) {
4188 INT old_timeout, status, i, t1, t0, size;
4189 HNDLE hDB;
4190 HNDLE hConn = -1;
4191 int connect_timeout = 10000;
4192 int timeout = 120000;
4193
4195 assert(hDB);
4196
4197 TrClient *tr_client = s->clients[idx].get();
4198
4199 tr_client->errorstr = "";
4200 //tr_client->init_time = ss_millitime();
4201 tr_client->waiting_for_client = "";
4202 tr_client->connect_timeout = 0;
4203 tr_client->connect_start_time = 0;
4204 tr_client->connect_end_time = 0;
4205 tr_client->rpc_timeout = 0;
4206 tr_client->rpc_start_time = 0;
4207 tr_client->rpc_end_time = 0;
4208 tr_client->end_time = 0;
4209
4211
4212 /* wait for predecessor if set */
4213 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4214 while (1) {
4216
4217 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4218 int wait_for_index = tr_client->wait_for_index[i];
4219
4220 assert(wait_for_index >= 0);
4221 assert(wait_for_index < (int)s->clients.size());
4222
4223 TrClient *t = s->clients[wait_for_index].get();
4224
4225 if (!t)
4226 continue;
4227
4228 if (t->status == 0) {
4229 wait_for = t;
4230 break;
4231 }
4232
4233 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4234 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));
4235 tr_client->status = -1;
4236 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4237 tr_client->end_time = ss_millitime();
4239 return CM_SUCCESS;
4240 }
4241 }
4242
4243 if (wait_for == NULL)
4244 break;
4245
4246 tr_client->waiting_for_client = wait_for->client_name;
4248
4249 if (tr_client->debug_flag == 1)
4250 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4251
4252 i = 0;
4253 size = sizeof(i);
4254 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4255
4256 if (status == DB_SUCCESS && i == 0) {
4257 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());
4258 tr_client->status = -1;
4259 tr_client->errorstr = "Canceled";
4260 tr_client->end_time = ss_millitime();
4262 return CM_SUCCESS;
4263 }
4264
4265 ss_sleep(100);
4266 };
4267 }
4268
4269 tr_client->waiting_for_client[0] = 0;
4270
4271 /* contact client if transition mask set */
4272 if (tr_client->debug_flag == 1)
4273 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4274 if (tr_client->debug_flag == 2)
4275 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());
4276
4277 /* get transition timeout for rpc connect */
4278 size = sizeof(timeout);
4279 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4280
4281 if (connect_timeout < 1000)
4282 connect_timeout = 1000;
4283
4284 /* get transition timeout */
4285 size = sizeof(timeout);
4286 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4287
4288 if (timeout < 1000)
4289 timeout = 1000;
4290
4291 /* set our timeout for rpc_client_connect() */
4292 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4293 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4294
4295 tr_client->connect_timeout = connect_timeout;
4296 tr_client->connect_start_time = ss_millitime();
4297
4299
4300 /* client found -> connect to its server port */
4301 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4302
4304
4305 tr_client->connect_end_time = ss_millitime();
4307
4308 if (status != RPC_SUCCESS) {
4309 cm_msg(MERROR, "cm_transition_call",
4310 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4311 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4312 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4313
4314 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4315 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4316 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4317
4318 if (tr_client->transition != TR_STOP) {
4319 /* indicate abort */
4320 i = 1;
4321 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4322 i = 0;
4323 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4324 }
4325
4326 tr_client->status = status;
4327 tr_client->end_time = ss_millitime();
4328
4330 return status;
4331 }
4332
4333 if (tr_client->debug_flag == 1)
4334 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4335 if (tr_client->debug_flag == 2)
4336 cm_msg(MINFO, "cm_transition_call",
4337 "cm_transition: Connection established to client \"%s\" on host %s",
4338 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4339
4340 /* call RC_TRANSITION on remote client with increased timeout */
4341 //old_timeout = rpc_get_timeout(hConn);
4342 rpc_set_timeout(hConn, timeout, &old_timeout);
4343
4344 tr_client->rpc_timeout = timeout;
4345 tr_client->rpc_start_time = ss_millitime();
4347
4348 if (tr_client->debug_flag == 1)
4349 printf("Executing RPC transition client \"%s\" on host %s...\n",
4350 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4351 if (tr_client->debug_flag == 2)
4352 cm_msg(MINFO, "cm_transition_call",
4353 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4354 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4355
4356 t0 = ss_millitime();
4357
4358 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4359 errorstr[0] = 0;
4360
4361 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4362
4363 tr_client->errorstr = errorstr;
4364
4365 t1 = ss_millitime();
4366
4367 tr_client->rpc_end_time = ss_millitime();
4368
4370
4371 /* fix for clients returning 0 as error code */
4372 if (status == 0)
4373 status = FE_ERR_HW;
4374
4375 /* reset timeout */
4377
4378 //DWORD t2 = ss_millitime();
4379
4380 if (tr_client->debug_flag == 1)
4381 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4382 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4383 if (tr_client->debug_flag == 2)
4384 cm_msg(MINFO, "cm_transition_call",
4385 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4386 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4387
4388 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4389 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());
4390 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4391 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4392 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4393 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4394 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());
4395 }
4396
4397 tr_client->status = status;
4398 tr_client->end_time = ss_millitime();
4399
4400 // write updated status and end_time to ODB
4401
4403
4404#if 0
4405 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4406 hConn,
4407 tr_client->client_name.c_str(),
4408 tr_client->init_time - tr_client->init_time,
4409 tr_client->connect_start_time - tr_client->init_time,
4410 tr_client->connect_end_time - tr_client->init_time,
4411 tr_client->rpc_start_time - tr_client->init_time,
4412 tr_client->rpc_end_time - tr_client->init_time,
4413 t2 - tr_client->init_time,
4414 tr_client->end_time - tr_client->init_time);
4415#endif
4416
4417 return CM_SUCCESS;
4418}
4419
4420/*------------------------------------------------------------------*/
4421
4423{
4424 HNDLE hDB;
4425
4427
4429
4430 tr_client->errorstr = "";
4431 //tr_client->init_time = now;
4432 tr_client->waiting_for_client = "";
4433 tr_client->connect_timeout = 0;
4434 tr_client->connect_start_time = now;
4435 tr_client->connect_end_time = now;
4436 tr_client->rpc_timeout = 0;
4437 tr_client->rpc_start_time = 0;
4438 tr_client->rpc_end_time = 0;
4439 tr_client->end_time = 0;
4440
4442
4443 // find registered handler
4444 // NB: this code should match same code in rpc_transition_dispatch()
4445 // NB: only use the first handler, this is how MIDAS always worked
4446 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4447
4448 _trans_table_mutex.lock();
4449 size_t n = _trans_table.size();
4450 _trans_table_mutex.unlock();
4451
4452 for (size_t i = 0; i < n; i++) {
4453 _trans_table_mutex.lock();
4455 _trans_table_mutex.unlock();
4456 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4457 /* call registered function */
4458 if (tt.func) {
4459 if (tr_client->debug_flag == 1)
4460 printf("Calling local transition callback\n");
4461 if (tr_client->debug_flag == 2)
4462 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4463
4464 tr_client->rpc_start_time = ss_millitime();
4465
4467
4468 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4469 errorstr[0] = 0;
4470
4471 tr_client->status = tt.func(tr_client->run_number, errorstr);
4472
4473 tr_client->errorstr = errorstr;
4474
4475 tr_client->rpc_end_time = ss_millitime();
4476
4477 if (tr_client->debug_flag == 1)
4478 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4479 if (tr_client->debug_flag == 2)
4480 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4481
4482 tr_client->end_time = ss_millitime();
4483
4484 // write status and end_time to ODB
4485
4487
4488 return tr_client->status;
4489 }
4490 }
4491 }
4492
4493 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4494
4495 tr_client->status = CM_SUCCESS;
4496 tr_client->end_time = ss_millitime();
4497
4498 // write status and end_time to ODB
4499
4501
4502 return CM_SUCCESS;
4503}
4504
4505/********************************************************************/
4545static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4546{
4547 INT i, status, size, sequence_number, port, state;
4549 DWORD seconds;
4550 char tr_key_name[256];
4551 KEY key;
4552 BOOL deferred;
4554
4555 //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);
4556
4557 /* if needed, use internal error string */
4558 if (!errstr) {
4559 errstr = xerrstr;
4560 errstr_size = sizeof(xerrstr);
4561 }
4562
4563 /* erase error string */
4564 errstr[0] = 0;
4565
4566 /* get key of local client */
4568
4571
4572 /* check for valid transition */
4574 && transition != TR_STARTABORT) {
4575 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4576 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4577 return CM_INVALID_TRANSITION;
4578 }
4579
4580 /* check if transition in progress */
4581 if (!deferred) {
4582 i = 0;
4583 size = sizeof(i);
4584 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4585 if (i == 1) {
4586 if (errstr) {
4587 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4588 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4589 }
4590 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4592 }
4593 }
4594
4595 /* indicate transition in progress */
4596 i = transition;
4597 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4598
4599 /* clear run abort flag */
4600 i = 0;
4601 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4602
4603 /* construct new transition state */
4604
4605 TrState s;
4606
4609 s.async_flag = async_flag;
4610 s.debug_flag = debug_flag;
4611 s.status = 0;
4612 s.errorstr[0] = 0;
4614 s.end_time = 0;
4615
4616 /* construct the ODB tree /System/Transition */
4617
4618 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4619 if (status == DB_SUCCESS) {
4621 }
4622
4623 if (transition != TR_STARTABORT) {
4624 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4625 if (status == DB_SUCCESS) {
4627 }
4628 }
4629
4630 if (transition != TR_STARTABORT) {
4631 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4632 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4633 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4634 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4635 status = 0;
4636 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4637 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4638 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4639 }
4640
4641 /* check for alarms */
4642 i = 0;
4643 size = sizeof(i);
4644 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4645 if (i == TRUE && transition == TR_START) {
4646 al_check();
4647 std::string alarms;
4648 if (al_get_alarms(&alarms) > 0) {
4649 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4650 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4651 mstrlcat(errstr, alarms.c_str(), errstr_size);
4652 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4653 }
4654 }
4655
4656 /* check for required programs */
4657 i = 0;
4658 size = sizeof(i);
4659 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4660 if (i == TRUE && transition == TR_START) {
4661
4663
4664 /* check /programs alarms */
4665 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4666 if (hkeyroot) {
4667 for (i = 0;; i++) {
4671 break;
4672
4673 db_get_key(hDB, hkey, &key);
4674
4675 /* don't check "execute on xxx" */
4676 if (key.type != TID_KEY)
4677 continue;
4678
4679 size = sizeof(program_info_required);
4680 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4681 if (status != DB_SUCCESS) {
4682 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4683 continue;
4684 }
4685
4687 std::string name = rpc_get_name();
4688 std::string str = name;
4689 str.resize(strlen(key.name));
4690 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4691 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4692 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4693 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4694 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4695 }
4696 }
4697 }
4698 }
4699 }
4700
4701 /* do detached transition via mtransition tool */
4702 if (async_flag & TR_DETACH) {
4703 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4704 return tr_finish(hDB, &s, transition, status, errstr);
4705 }
4706
4707 mstrlcpy(errstr, "Unknown error", errstr_size);
4708
4709 if (debug_flag == 0) {
4710 size = sizeof(i);
4711 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4712 }
4713
4714 /* if no run number is given, get it from ODB and increment it */
4715 if (run_number == 0) {
4716 size = sizeof(run_number);
4717 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4718 assert(status == SUCCESS);
4719 if (transition == TR_START) {
4720 run_number++;
4721 }
4723
4724 if (transition != TR_STARTABORT) {
4725 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4726 }
4727 }
4728
4729 if (run_number <= 0) {
4730 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4731 abort();
4732 }
4733
4734 /* Set new run number in ODB */
4735 if (transition == TR_START) {
4736 if (debug_flag == 1)
4737 printf("Setting run number %d in ODB\n", run_number);
4738 if (debug_flag == 2)
4739 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4740
4741 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4742 if (status != DB_SUCCESS) {
4743 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4744 abort();
4745 }
4746 }
4747
4748 if (deferred) {
4749 if (debug_flag == 1)
4750 printf("Clearing /Runinfo/Requested transition\n");
4751 if (debug_flag == 2)
4752 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4753
4754 /* remove transition request */
4755 i = 0;
4756 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4757 } else {
4758 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4759 if (status != DB_SUCCESS) {
4760 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4761 if (errstr)
4762 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4763 return tr_finish(hDB, &s, transition, status, errstr);
4764 }
4765
4766 /* check if deferred transition already in progress */
4767 size = sizeof(i);
4768 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4769 if (i) {
4770 if (errstr) {
4771 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4772 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4773 }
4774 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4775 }
4776
4777 std::string trname = cm_transition_name(transition);
4778
4779 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4780
4781 /* search database for clients with deferred transition request */
4782 for (i = 0, status = 0;; i++) {
4785 break;
4786
4787 if (status == DB_SUCCESS) {
4788 size = sizeof(sequence_number);
4789 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4790
4791 /* if registered for deferred transition, set flag in ODB and return */
4792 if (status == DB_SUCCESS) {
4793 char str[256];
4794 size = NAME_LENGTH;
4795 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4796
4797 if (debug_flag == 1)
4798 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4799 if (debug_flag == 2)
4800 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4801
4802 if (debug_flag == 1)
4803 printf("Setting /Runinfo/Requested transition\n");
4804 if (debug_flag == 2)
4805 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4806
4807 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4808 * will activate the deferred transition code in the frontend.
4809 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4810
4811 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4812
4813 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4814
4815 if (errstr)
4816 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4817
4818 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4819 }
4820 }
4821 }
4822 }
4823
4824 /* execute programs on start */
4825 if (transition == TR_START) {
4826 char str[256];
4827 str[0] = 0;
4828 size = sizeof(str);
4829 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4830 if (str[0])
4831 ss_system(str);
4832
4833 db_find_key(hDB, 0, "/Programs", &hRootKey);
4834 if (hRootKey) {
4835 for (i = 0;; i++) {
4839 break;
4840
4841 db_get_key(hDB, hKey, &key);
4842
4843 /* don't check "execute on xxx" */
4844 if (key.type != TID_KEY)
4845 continue;
4846
4847 size = sizeof(program_info_auto_start);
4848 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4849 if (status != DB_SUCCESS) {
4850 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4851 continue;
4852 }
4853
4855 char start_command[MAX_STRING_LENGTH];
4856 start_command[0] = 0;
4857
4858 size = sizeof(start_command);
4859 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4860 if (status != DB_SUCCESS) {
4861 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4862 continue;
4863 }
4864
4865 if (start_command[0]) {
4866 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4867 start_command);
4868 ss_system(start_command);
4869 }
4870 }
4871 }
4872 }
4873 }
4874
4875 /* execute programs on startabort */
4876 if (transition == TR_STARTABORT) {
4877 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4878 std::string cmd;
4879 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4880
4881 if (!cmd.empty())
4882 ss_system(cmd.c_str());
4883 }
4884
4885 /* set new start time in database */
4886 if (transition == TR_START) {
4887 /* ASCII format */
4888 std::string now = cm_asctime();
4889 now.reserve(32);
4890 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4891
4892 /* reset stop time */
4893 seconds = 0;
4894 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4895
4896 /* Seconds since 1.1.1970 */
4897 cm_time(&seconds);
4898 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4899 }
4900
4901 size = sizeof(state);
4902 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4903
4904 /* set stop time in database */
4905 if (transition == TR_STOP) {
4906 if (status != DB_SUCCESS)
4907 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4908
4909 if (state != STATE_STOPPED) {
4910 /* stop time binary */
4911 cm_time(&seconds);
4912 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4913 if (status != DB_SUCCESS)
4914 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4915
4916 /* stop time ascii */
4917 std::string now = cm_asctime();
4918 now.reserve(32);
4919 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4920 if (status != DB_SUCCESS)
4921 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4922 }
4923 }
4924
4925 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4926 if (status != DB_SUCCESS) {
4927 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4928 if (errstr)
4929 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4930 return tr_finish(hDB, &s, transition, status, errstr);
4931 }
4932
4933 std::string trname = cm_transition_name(transition);
4934
4935 /* check that all transition clients are alive */
4936 for (int i = 0;;) {
4938 if (status != DB_SUCCESS)
4939 break;
4940
4942
4943 if (status == DB_SUCCESS) {
4944 /* this client is alive. Check next one! */
4945 i++;
4946 continue;
4947 }
4948
4949 assert(status == CM_NO_CLIENT);
4950
4951 /* start from scratch: removing odb entries as we iterate over them
4952 * does strange things to db_enum_key() */
4953 i = 0;
4954 }
4955
4956 /* check for broken RPC connections */
4958
4959 if (debug_flag == 1)
4960 printf("---- Transition %s started ----\n", trname.c_str());
4961 if (debug_flag == 2)
4962 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4963
4964 sprintf(tr_key_name, "Transition %s", trname.c_str());
4965
4966 /* search database for clients which registered for transition */
4967
4968 for (int i = 0, status = 0;; i++) {
4969 KEY subkey;
4972 break;
4973
4975 assert(status == DB_SUCCESS);
4976
4977 if (status == DB_SUCCESS) {
4979
4980 if (status == DB_SUCCESS) {
4981
4983
4984 for (int j = 0; j < key.num_values; j++) {
4985 size = sizeof(sequence_number);
4986 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4987 assert(status == DB_SUCCESS);
4988
4989 TrClient *c = new TrClient;
4990
4992 c->transition = transition;
4993 c->run_number = run_number;
4994 c->async_flag = async_flag;
4995 c->debug_flag = debug_flag;
4996 c->sequence_number = sequence_number;
4997 c->status = 0;
4998 c->key_name = subkey.name;
4999
5000 /* get client info */
5001 char client_name[NAME_LENGTH];
5002 size = sizeof(client_name);
5003 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
5004 c->client_name = client_name;
5005
5007 size = sizeof(host_name);
5008 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
5009 c->host_name = host_name;
5010
5011 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
5012
5013 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5014 /* remember own client */
5015 c->port = 0;
5016 } else {
5017 size = sizeof(port);
5018 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5019 c->port = port;
5020 }
5021
5022 /* check for duplicates */
5023
5024 bool found = false;
5025 for (size_t k=0; k<s.clients.size(); k++) {
5026 TrClient* cc = s.clients[k].get();
5027 if (cc->client_name == c->client_name)
5028 if (cc->host_name == c->host_name)
5029 if (cc->port == c->port)
5030 if (cc->sequence_number == c->sequence_number)
5031 found = true;
5032 }
5033
5034 if (!found) {
5035 s.clients.push_back(std::unique_ptr<TrClient>(c));
5036 c = NULL;
5037 } else {
5038 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);
5039 delete c;
5040 c = NULL;
5041 }
5042 }
5043 }
5044 }
5045 }
5046
5047 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5048
5049 /* set predecessor for multi-threaded transitions */
5050 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5051 if (s.clients[idx]->sequence_number == 0) {
5052 // sequence number 0 means "don't care"
5053 } else {
5054 /* find clients with smaller sequence number */
5055 if (idx > 0) {
5056 for (size_t i = idx - 1; ; i--) {
5057 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5058 if (s.clients[i]->sequence_number > 0) {
5059 s.clients[idx]->wait_for_index.push_back(i);
5060 }
5061 }
5062 if (i==0)
5063 break;
5064 }
5065 }
5066 }
5067 }
5068
5069 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5071 }
5072
5073#if 0
5074 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5075 printf("TrClient[%d]: ", int(idx));
5076 s.clients[idx]->Print();
5077 printf("\n");
5078 }
5079#endif
5080
5081 /* contact ordered clients for transition -----------------------*/
5083 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5084 if (debug_flag == 1)
5085 printf("\n==== Found client \"%s\" with sequence number %d\n",
5086 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5087 if (debug_flag == 2)
5088 cm_msg(MINFO, "cm_transition",
5089 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5090 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5091
5092 if (async_flag & TR_MTHREAD) {
5094 assert(s.clients[idx]->thread == NULL);
5095 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5096 } else {
5097 if (s.clients[idx]->port == 0) {
5098 /* if own client call transition callback directly */
5100 } else {
5101 /* if other client call transition via RPC layer */
5103 }
5104
5105 if (status == CM_SUCCESS && transition != TR_STOP)
5106 if (s.clients[idx]->status != SUCCESS) {
5107 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5108 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5109 break;
5110 }
5111 }
5112
5113 if (status != CM_SUCCESS)
5114 break;
5115 }
5116
5117 /* wait until all threads have finished */
5118 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5119 if (s.clients[idx]->thread) {
5120 // join() will wait forever until thread finishes
5121 s.clients[idx]->thread->join();
5122 delete s.clients[idx]->thread;
5123 s.clients[idx]->thread = NULL;
5124 }
5125 }
5126
5127 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5128
5129 i = 0;
5130 size = sizeof(i);
5131 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5132
5133 if (status == DB_SUCCESS && i == 0) {
5134 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5135
5136 if (errstr != NULL)
5137 mstrlcpy(errstr, "Canceled", errstr_size);
5138
5139 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5140 }
5141
5142 /* search for any error */
5143 for (size_t idx = 0; idx < s.clients.size(); idx++)
5144 if (s.clients[idx]->status != CM_SUCCESS) {
5145 status = s.clients[idx]->status;
5146 if (errstr)
5147 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5148 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5149 break;
5150 }
5151
5152 if (transition != TR_STOP && status != CM_SUCCESS) {
5153 /* indicate abort */
5154 i = 1;
5155 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5156 i = 0;
5157 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5158
5159 return tr_finish(hDB, &s, transition, status, errstr);
5160 }
5161
5162 if (debug_flag == 1)
5163 printf("\n---- Transition %s finished ----\n", trname.c_str());
5164 if (debug_flag == 2)
5165 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5166
5167 /* set new run state in database */
5170
5171 if (transition == TR_PAUSE)
5173
5174 if (transition == TR_STOP)
5176
5179
5180 size = sizeof(state);
5181 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5182 if (status != DB_SUCCESS)
5183 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5184
5185 /* send notification message */
5186 if (transition == TR_START)
5187 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5188 if (transition == TR_STOP)
5189 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5190 if (transition == TR_PAUSE)
5191 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5192 if (transition == TR_RESUME)
5193 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5195 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5196
5197 /* lock/unlock ODB values if present */
5198 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5199 if (hKey) {
5200 if (state == STATE_STOPPED)
5202 else
5204 }
5205
5206 /* flush online database */
5207 if (transition == TR_STOP)
5209
5210 /* execute/stop programs on stop */
5211 if (transition == TR_STOP) {
5212 std::string cmd;
5213 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5214 if (!cmd.empty())
5215 ss_system(cmd.c_str());
5216
5217 db_find_key(hDB, 0, "/Programs", &hRootKey);
5218 if (hRootKey) {
5219 for (i = 0;; i++) {
5223 break;
5224
5225 db_get_key(hDB, hKey, &key);
5226
5227 /* don't check "execute on xxx" */
5228 if (key.type != TID_KEY)
5229 continue;
5230
5231 size = sizeof(program_info_auto_stop);
5232 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5233 if (status != DB_SUCCESS) {
5234 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5235 continue;
5236 }
5237
5239 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5241 }
5242 }
5243 }
5244 }
5245
5246
5247 /* indicate success */
5248 i = 0;
5249 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5250
5251 if (errstr != NULL)
5252 mstrlcpy(errstr, "Success", errstr_size);
5253
5254 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5255}
5256
5257/*------------------------------------------------------------------*/
5258
5259/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5260static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5261 int status;
5262
5263 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5264
5265 if (transition == TR_START && status != CM_SUCCESS) {
5266 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5267 errstr);
5268 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5269 }
5270
5271 return status;
5272}
5273
5274/*------------------------------------------------------------------*/
5275
5276static INT tr_main_thread(void *param) {
5277 INT status;
5278 TR_PARAM *trp;
5279
5280 trp = (TR_PARAM *) param;
5281 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5282
5283 trp->status = status;
5284 trp->finished = TRUE;
5285
5286 return 0;
5287}
5288
5290{
5291 if (_trp.thread && !_trp.finished) {
5292 //printf("main transition thread did not finish yet!\n");
5294 }
5295
5296 std::thread* t = _trp.thread.exchange(NULL);
5297
5298 if (t) {
5299 t->join();
5300 delete t;
5301 t = NULL;
5302 }
5303
5304 return CM_SUCCESS;
5305}
5306
5307/* wrapper around cm_transition1() for detached multi-threaded transitions */
5308INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5309 int mflag = async_flag & TR_MTHREAD;
5310 int sflag = async_flag & TR_SYNC;
5311
5313
5314 if (status != CM_SUCCESS) {
5315 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5317 }
5318
5319 /* get key of local client */
5320 HNDLE hDB;
5322
5323 bool deferred = (transition & TR_DEFERRED) > 0;
5325
5326 /* check for valid transition */
5328 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5329 if (errstr) {
5330 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5331 }
5332 return CM_INVALID_TRANSITION;
5333 }
5334
5335 /* check if transition in progress */
5336 if (!deferred) {
5337 int i = 0;
5338 int size = sizeof(i);
5339 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5340 if (i == 1) {
5341 if (errstr) {
5342 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5343 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5344 }
5345 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5347 }
5348 }
5349
5350 if (mflag) {
5353 if (sflag) {
5354 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5355 _trp.errstr = errstr;
5356 _trp.errstr_size = errstr_size;
5357 } else {
5358 /* in normal MTHREAD mode, we return right away and
5359 * if errstr is a local variable in the caller and they return too,
5360 * errstr becomes a stale reference and writing into it will corrupt the stack
5361 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5362 * and we definitely corrupt mlogger memory with out this: */
5363 _trp.errstr = NULL;
5364 _trp.errstr_size = 0;
5365 }
5366 _trp.async_flag = async_flag;
5367 _trp.debug_flag = debug_flag;
5368 _trp.status = 0;
5370
5371 if (errstr)
5372 *errstr = 0; // null error string
5373
5374 //ss_thread_create(tr_main_thread, &_trp);
5375
5376 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5377
5378 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5379
5380 if (sflag) {
5381
5382 /* wait until main thread has finished */
5383 do {
5384 ss_sleep(10);
5385 } while (!_trp.finished);
5386
5387 std::thread* t = _trp.thread.exchange(NULL);
5388
5389 if (t) {
5390 t->join();
5391 delete t;
5392 t = NULL;
5393 }
5394
5395 return _trp.status;
5396 }
5397 } else
5398 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5399
5400 return CM_SUCCESS;
5401}
5402
5404#ifndef DOXYGEN_SHOULD_SKIP_THIS
5405
5406/********************************************************************/
5408/********************************************************************\
5409
5410 Routine: cm_dispatch_ipc
5411
5412 Purpose: Called from ss_suspend if an IPC message arrives
5413
5414 Input:
5415 INT msg IPC message we got, MSG_ODB/MSG_BM
5416 INT p1, p2 Optional parameters
5417 int s Optional server socket
5418
5419 Output:
5420 none
5421
5422 Function value:
5423 CM_SUCCESS Successful completion
5424
5425\********************************************************************/
5426{
5427 if (message[0] == 'O') {
5429 INT index;
5430 index = 0;
5431 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5432 if (client_socket) {
5434 } else {
5436 }
5437 }
5438
5439 /* message == "B" means "resume event sender" */
5440 if (message[0] == 'B' && message[2] != ' ') {
5441 char str[NAME_LENGTH];
5442
5443 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5444
5445 mstrlcpy(str, message + 2, sizeof(str));
5446 if (strchr(str, ' '))
5447 *strchr(str, ' ') = 0;
5448
5449 if (client_socket)
5451 else
5452 return bm_push_event(str);
5453 }
5454
5455 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5456
5457 return CM_SUCCESS;
5458}
5459
5460/********************************************************************/
5462
5463void cm_ctrlc_handler(int sig) {
5464 if (_ctrlc_pressed) {
5465 printf("Received 2nd Ctrl-C, hard abort\n");
5466 exit(0);
5467 }
5468 printf("Received Ctrl-C, aborting...\n");
5470
5472}
5473
5477
5481
5482/********************************************************************/
5484/********************************************************************\
5485
5486 Routine: cm_exec_script
5487
5488 Purpose: Execute script from /Script tree
5489
5490 exec_script is enabled by the tree /Script
5491 The /Script struct is composed of list of keys
5492 from which the name of the key is the button name
5493 and the sub-structure is a record as follow:
5494
5495 /Script/<button_name> = <script command> (TID_STRING)
5496
5497 The "Script command", containing possible arguements,
5498 is directly executed.
5499
5500 /Script/<button_name>/<script command>
5501 <soft link1>|<arg1>
5502 <soft link2>|<arg2>
5503 ...
5504
5505 The arguments for the script are derived from the
5506 subtree below <button_name>, where <button_name> must be
5507 TID_KEY. The subtree may then contain arguments or links
5508 to other values in the ODB, like run number etc.
5509
5510\********************************************************************/
5511{
5512 HNDLE hDB, hkey;
5513 KEY key;
5514 int status;
5515
5517 if (status != DB_SUCCESS)
5518 return status;
5519
5521 if (status != DB_SUCCESS)
5522 return status;
5523
5525 if (status != DB_SUCCESS)
5526 return status;
5527
5528 std::string command;
5529
5530 if (key.type == TID_STRING) {
5531 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5532 if (status != DB_SUCCESS) {
5533 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5535 return status;
5536 }
5537 } else if (key.type == TID_KEY) {
5538 for (int i = 0;; i++) {
5539 HNDLE hsubkey;
5540 KEY subkey;
5542 if (!hsubkey)
5543 break;
5545
5546 if (i > 0)
5547 command += " ";
5548
5549 if (subkey.type == TID_KEY) {
5550 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5551 subkey.name);
5552 return DB_TYPE_MISMATCH;
5553 } else {
5554 int size = subkey.item_size;
5555 char *buf = (char *) malloc(size);
5556 assert(buf != NULL);
5557 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5558 if (status != DB_SUCCESS) {
5559 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5560 odb_path_to_script, subkey.name, subkey.type, status);
5561 free(buf);
5562 return status;
5563 }
5564 if (subkey.type == TID_STRING) {
5565 command += buf;
5566 } else {
5567 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5568 }
5569 free(buf);
5570 }
5571 }
5572 } else {
5573 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5575 return DB_TYPE_MISMATCH;
5576 }
5577
5578 // printf("exec_script: %s\n", command.c_str());
5579
5580 if (command.length() > 0) {
5581 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5582 ss_system(command.c_str());
5583 }
5584
5585 return SUCCESS;
5586}
5587
5589#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5590
5591static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5592
5593/********************************************************************/
5602 static DWORD alarm_last_checked_sec = 0;
5603 DWORD now_sec = ss_time();
5604
5606 static DWORD last_millitime = 0;
5608 const DWORD kPeriod = 1000;
5609 if (last_millitime == 0) {
5611 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5612 }
5613
5614 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5615
5616 //if (now_millitime < last_millitime) {
5617 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5618 //}
5619
5620 /* check alarms once every 10 seconds */
5621 if (now_sec - alarm_last_checked_sec > 10) {
5622 al_check();
5624 }
5625
5626 /* run periodic checks previously done by cm_watchdog */
5627
5628 if (tdiff_millitime >= kPeriod) {
5630 if (tdiff_millitime > 60000)
5632
5633 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5634
5635 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5636 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5637
5639
5641 }
5642
5643 /* reap transition thread */
5644
5646
5647 return CM_SUCCESS;
5648}
5649
5650/********************************************************************/
5665 INT status;
5666 INT bMore;
5667 //static DWORD last_yield = 0;
5668 //static DWORD last_yield_time = 0;
5669 //DWORD start_yield = ss_millitime();
5670
5671 /* check for ctrl-c */
5672 if (_ctrlc_pressed)
5673 return RPC_SHUTDOWN;
5674
5675 /* flush the cm_msg buffer */
5677
5678 if (!rpc_is_remote()) {
5679 /* flush the ODB to its binary file */
5680 /* for remote clients, ODB is flushed by the mserver */
5681 HNDLE hDB;
5684 }
5685
5686 /* check for available events */
5687 if (rpc_is_remote()) {
5688 //printf("cm_yield() calling bm_poll_event()\n");
5690
5691 if (status == SS_ABORT) {
5692 return status;
5693 }
5694
5695 if (status == BM_SUCCESS) {
5696 /* one or more events received by bm_poll_event() */
5697 status = ss_suspend(0, 0);
5698 } else {
5700 }
5701
5702 return status;
5703 }
5704
5706
5707 if (status != CM_SUCCESS)
5708 return status;
5709
5710 //DWORD start_check = ss_millitime();
5711
5713
5714 //DWORD end_check = ss_millitime();
5715 //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);
5716 //fflush(stdout);
5717
5718 if (bMore == BM_CORRUPTED) {
5719 status = SS_ABORT;
5720 } else if (bMore) {
5721 /* if events available, quickly check other IPC channels */
5722 status = ss_suspend(0, 0);
5723 } else {
5725 }
5726
5727 /* flush the cm_msg buffer */
5729
5730 //DWORD end_yield = ss_millitime();
5731 //last_yield_time = end_yield - start_yield;
5732 //last_yield = start_yield;
5733
5734 return status;
5735}
5736
5737/********************************************************************/
5745INT cm_execute(const char *command, char *result, INT bufsize) {
5746 char str[256];
5747 INT n;
5748 int fh;
5749 int status = 0;
5750 static int check_cm_execute = 1;
5751 static int enable_cm_execute = 0;
5752
5753 if (rpc_is_remote())
5754 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5755
5756 if (check_cm_execute) {
5757 int status;
5758 int size;
5759 HNDLE hDB;
5760 check_cm_execute = 0;
5761
5763 assert(status == DB_SUCCESS);
5764
5765 size = sizeof(enable_cm_execute);
5766 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5767 assert(status == DB_SUCCESS);
5768
5769 //printf("enable_cm_execute %d\n", enable_cm_execute);
5770 }
5771
5772 if (!enable_cm_execute) {
5773 char buf[32];
5774 mstrlcpy(buf, command, sizeof(buf));
5775 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5776 return CM_WRONG_PASSWORD;
5777 }
5778
5779 if (bufsize > 0) {
5780 strcpy(str, command);
5781 sprintf(str, "%s > %d.tmp", command, ss_getpid());
5782
5783 status = system(str);
5784
5785 sprintf(str, "%d.tmp", ss_getpid());
5786 fh = open(str, O_RDONLY, 0644);
5787 result[0] = 0;
5788 if (fh) {
5789 n = read(fh, result, bufsize - 1);
5790 result[MAX(0, n)] = 0;
5791 close(fh);
5792 }
5793 remove(str);
5794 } else {
5795 status = system(command);
5796 }
5797
5798 if (status < 0) {
5799 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5800 return CM_SET_ERROR;
5801 }
5802
5803 return CM_SUCCESS;
5804}
5805
5806
5807
5809#ifndef DOXYGEN_SHOULD_SKIP_THIS
5810
5811/********************************************************************/
5812INT cm_register_function(INT id, INT(*func)(INT, void **))
5813/********************************************************************\
5814
5815 Routine: cm_register_function
5816
5817 Purpose: Call rpc_register_function and publish the registered
5818 function under system/clients/<pid>/RPC
5819
5820 Input:
5821 INT id RPC ID
5822 INT *func New dispatch function
5823
5824 Output:
5825 <implicit: func gets copied to rpc_list>
5826
5827 Function value:
5828 CM_SUCCESS Successful completion
5829 RPC_INVALID_ID RPC ID not found
5830
5831\********************************************************************/
5832{
5833 HNDLE hDB, hKey;
5834 INT status;
5835 char str[80];
5836
5837 status = rpc_register_function(id, func);
5838 if (status != RPC_SUCCESS)
5839 return status;
5840
5842
5843 /* create new key for this id */
5844 status = 1;
5845 sprintf(str, "RPC/%d", id);
5846
5848 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5850
5851 if (status != DB_SUCCESS)
5852 return status;
5853
5854 return CM_SUCCESS;
5855}
5856
5857
5859#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5860
5861//
5862// Return "/"-terminated file path for given history channel
5863//
5864
5865std::string cm_get_history_path(const char* history_channel)
5866{
5867 int status;
5868 HNDLE hDB;
5869 std::string path;
5870
5872
5873 if (history_channel && (strlen(history_channel) > 0)) {
5874 std::string p;
5875 p += "/Logger/History/";
5876 p += history_channel;
5877 p += "/History dir";
5878
5879 // NB: be careful to avoid creating odb entries under /logger
5880 // for whatever values of "history_channel" we get called with!
5881 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5882 if (status == DB_SUCCESS && path.length() > 0) {
5883 // if not absolute path, prepend with experiment directory
5884 if (path[0] != DIR_SEPARATOR)
5885 path = cm_get_path() + path;
5886 // append directory separator
5887 if (path.back() != DIR_SEPARATOR)
5888 path += DIR_SEPARATOR_STR;
5889 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5890 return path;
5891 }
5892 }
5893
5894 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5895 if (status == DB_SUCCESS && path.length() > 0) {
5896 // if not absolute path, prepend with experiment directory
5897 if (path[0] != DIR_SEPARATOR)
5898 path = cm_get_path() + path;
5899 // append directory separator
5900 if (path.back() != DIR_SEPARATOR)
5901 path += DIR_SEPARATOR_STR;
5902 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5903 return path;
5904 }
5905
5906 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5907 if (status == DB_SUCCESS && path.length() > 0) {
5908 // if not absolute path, prepend with experiment directory
5909 if (path[0] != DIR_SEPARATOR)
5910 path = cm_get_path() + path;
5911 // append directory separator
5912 if (path.back() != DIR_SEPARATOR)
5913 path += DIR_SEPARATOR_STR;
5914 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5915 return path;
5916 }
5917
5918 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5919 return cm_get_path();
5920}
5921
/* end of cmfunctionc */
5924
5930/********************************************************************\
5931* *
5932* bm_xxx - Buffer Manager Functions *
5933* *
5934\********************************************************************/
5935
5937
5938#ifdef LOCAL_ROUTINES
5939
5940// see locking code in xbm_lock_buffer()
5941static int _bm_lock_timeout = 5 * 60 * 1000;
5942static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5943
5945{
5946 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5947
5948 bool badindex = false;
5949 bool badclient = false;
5950
5951 int idx = pbuf->client_index;
5952
5953 if (idx < 0) {
5954 badindex = true;
5955 } else if (idx > pbuf->buffer_header->max_client_index) {
5956 badindex = true;
5957 } else {
5958 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5959 if (pclient->name[0] == 0)
5960 badclient = true;
5961 else if (pclient->pid != ss_getpid())
5962 badclient = true;
5963
5964 //if (strcmp(pclient->name,"mdump")==0) {
5965 // for (int i=0; i<15; i++) {
5966 // printf("sleep %d\n", i);
5967 // ::sleep(1);
5968 // }
5969 //}
5970 }
5971
5972#if 0
5973 if (badindex) {
5974 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5975 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5976 badindex, ss_getpid());
5977 } else if (badclient) {
5978 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",
5979 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5980 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5981 ss_getpid(), badclient);
5982 } else {
5983 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",
5984 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5985 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5986 ss_getpid());
5987 }
5988#endif
5989
5990 if (badindex || badclient) {
5991 static int prevent_recursion = 1;
5992
5993 if (prevent_recursion) {
5995
5996 if (badindex) {
5997 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());
5998 } else {
5999 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());
6000 }
6001
6002 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
6003 }
6004
6005 if (badindex) {
6006 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());
6007 } else {
6008 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());
6009 }
6010
6011 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
6012
6013 pbuf_guard.unlock();
6014
6015 abort();
6016 }
6017
6018 return idx;
6019}
6020
6025
6026#endif // LOCAL_ROUTINES
6027
6028/********************************************************************/
6037INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6038 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6039 // because of mismatch in sign-extension between signed 16-bit event_id and
6040 // unsigned 16-bit constants. K.O.
6041
6042 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)))
6043 /* fragmented event */
6044 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6046
6047 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6049}
6050
6051#ifdef LOCAL_ROUTINES
6052
6053/********************************************************************/
6058 int k, nc;
6060
6061 /* clear entry from client structure in buffer header */
6062 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6063
6064 /* calculate new max_client_index entry */
6065 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6066 if (pheader->client[k].pid != 0)
6067 break;
6068 pheader->max_client_index = k + 1;
6069
6070 /* count new number of clients */
6071 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6072 if (pheader->client[k].pid != 0)
6073 nc++;
6074 pheader->num_clients = nc;
6075
6076 /* check if anyone is waiting and wake him up */
6077 pbctmp = pheader->client;
6078
6079 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6080 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6081 ss_resume(pbctmp->port, "B ");
6082}
6083
6084/********************************************************************/
6089 BUFFER_HEADER *pheader;
6091 int j;
6092
6093 pheader = pbuf->buffer_header;
6094 pbclient = pheader->client;
6095
6096 /* now check other clients */
6097 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6098 if (pbclient->pid) {
6099 if (!ss_pid_exists(pbclient->pid)) {
6100 cm_msg(MINFO, "bm_cleanup",
6101 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6102 pheader->name, who, pbclient->pid);
6103
6104 bm_remove_client_locked(pheader, j);
6105 continue;
6106 }
6107 }
6108
6109 /* If client process has no activity, clear its buffer entry. */
6110 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6111 DWORD tdiff = actual_time - pbclient->last_activity;
6112#if 0
6113 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6114 pheader->name,
6115 pbclient->name,
6116 pbclient->last_activity,
6118 tdiff,
6119 tdiff,
6120 pbclient->watchdog_timeout);
6121#endif
6122 if (actual_time > pbclient->last_activity &&
6123 tdiff > pbclient->watchdog_timeout) {
6124
6125 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6126 pbclient->name, pheader->name, who,
6127 tdiff / 1000.0,
6128 pbclient->watchdog_timeout / 1000.0);
6129
6130 bm_remove_client_locked(pheader, j);
6131 }
6132 }
6133 }
6134}
6135
6140 int pid = ss_getpid();
6141
6142 std::vector<BUFFER*> mybuffers;
6143
6144 gBuffersMutex.lock();
6146 gBuffersMutex.unlock();
6147
6148 for (BUFFER* pbuf : mybuffers) {
6149 if (!pbuf)
6150 continue;
6151 if (pbuf->attached) {
6152
6154
6155 if (!pbuf_guard.is_locked())
6156 continue;
6157
6158 BUFFER_HEADER *pheader = pbuf->buffer_header;
6159 for (int j = 0; j < pheader->max_client_index; j++) {
6160 BUFFER_CLIENT *pclient = pheader->client + j;
6161 if (pclient->pid == pid) {
6163 }
6164 }
6165 }
6166 }
6167}
6168
6169#endif // LOCAL_ROUTINES
6170
6175{
6176#ifdef LOCAL_ROUTINES
6177
6178 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6179
6180 std::vector<BUFFER*> mybuffers;
6181
6182 gBuffersMutex.lock();
6184 gBuffersMutex.unlock();
6185
6186 /* check buffers */
6187 for (BUFFER* pbuf : mybuffers) {
6188 if (!pbuf)
6189 continue;
6190 if (pbuf->attached) {
6191 /* update the last_activity entry to show that we are alive */
6192
6194
6195 if (!pbuf_guard.is_locked())
6196 continue;
6197
6199 pclient->last_activity = actual_time;
6200
6201 /* don't check other clients if interval is strange */
6202 if (!wrong_interval)
6204 }
6205 }
6206#endif // LOCAL_ROUTINES
6207}
6208
6209#ifdef LOCAL_ROUTINES
6210
6211static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6212 if (rp < 0 || rp > pheader->size) {
6213 cm_msg(MERROR, "bm_validate_rp",
6214 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6215 pheader->name,
6216 rp,
6217 pheader->read_pointer,
6218 pheader->write_pointer,
6219 pheader->size,
6220 who);
6221 return FALSE;
6222 }
6223
6224 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6225 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6226 cm_msg(MERROR, "bm_validate_rp",
6227 "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",
6228 pheader->name,
6229 rp,
6230 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6231 pheader->read_pointer,
6232 pheader->write_pointer,
6233 pheader->size,
6234 who);
6235 return FALSE;
6236 }
6237
6238 return TRUE;
6239}
6240
6241#if 0
6242static FILE* gRpLog = NULL;
6243#endif
6244
6245static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6246{
6247#if 0
6248 if (gRpLog == NULL) {
6249 gRpLog = fopen("rp.log", "a");
6250 }
6251 if (gRpLog && (total_size < 16)) {
6252 const char *pdata = (const char *) (pheader + 1);
6253 const DWORD *pevent = (const DWORD*) (pdata + rp);
6254 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,
6255 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6256 }
6257#endif
6258
6259 // these checks are already done before we come here.
6260 // but we check again as last-ressort protection. K.O.
6261 assert(total_size > 0);
6262 assert(total_size >= (int)sizeof(EVENT_HEADER));
6263
6264 rp += total_size;
6265 if (rp >= pheader->size) {
6266 rp -= pheader->size;
6267 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6268 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6269 // if at the end of the buffer, the remaining free space is exactly
6270 // equal to the size of an event header, the event header
6271 // is written there, the pointer is wrapped and the event data
6272 // is written to the beginning of the buffer.
6273 rp = 0;
6274 }
6275 return rp;
6276}
6277
6278static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6279 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6280 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6281 int total_size = ALIGN8(event_size);
6282
6283 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6284 cm_msg(MERROR, "bm_next_rp",
6285 "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",
6286 pheader->name,
6287 rp,
6288 pevent->data_size,
6289 event_size,
6290 total_size,
6291 pheader->read_pointer,
6292 pheader->write_pointer,
6293 pheader->size,
6294 who);
6295 return -1;
6296 }
6297
6298 int remaining = 0;
6299 if (rp < pheader->write_pointer) {
6300 remaining = pheader->write_pointer - rp;
6301 } else {
6302 remaining = pheader->size - rp;
6303 remaining += pheader->write_pointer;
6304 }
6305
6306 //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);
6307
6308 if (total_size > remaining) {
6309 cm_msg(MERROR, "bm_next_rp",
6310 "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",
6311 pheader->name,
6312 rp,
6313 pevent->data_size,
6314 event_size,
6315 total_size,
6316 pheader->read_pointer,
6317 pheader->write_pointer,
6318 pheader->size,
6319 remaining,
6320 who);
6321 return -1;
6322 }
6323
6324 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6325
6326 return rp;
6327}
6328
6330 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6331 const char *pdata = (const char *) (pheader + 1);
6332
6333 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6334
6335 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6336
6337 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6338
6339 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6340 cm_msg(MERROR, "bm_validate_buffer",
6341 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6342 pheader->read_pointer, pheader->size, pheader->write_pointer);
6343 return BM_CORRUPTED;
6344 }
6345
6346 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6347 cm_msg(MERROR, "bm_validate_buffer",
6348 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6349 pheader->write_pointer, pheader->size, pheader->read_pointer);
6350 return BM_CORRUPTED;
6351 }
6352
6353 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6354 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6355 pheader->read_pointer);
6356 return BM_CORRUPTED;
6357 }
6358
6359 int rp = pheader->read_pointer;
6360 int rp0 = -1;
6361 while (rp != pheader->write_pointer) {
6362 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6363 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6364 pheader->name, rp, rp0);
6365 return BM_CORRUPTED;
6366 }
6367 //bm_print_event(pdata, rp);
6368 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6369 if (rp1 < 0) {
6370 cm_msg(MERROR, "bm_validate_buffer",
6371 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6372 return BM_CORRUPTED;
6373 }
6374 rp0 = rp;
6375 rp = rp1;
6376 }
6377
6378 int i;
6379 for (i = 0; i < MAX_CLIENTS; i++) {
6380 const BUFFER_CLIENT *c = &pheader->client[i];
6381 if (c->pid == 0)
6382 continue;
6383 BOOL get_all = FALSE;
6384 int j;
6385 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6386 const EVENT_REQUEST *r = &c->event_request[j];
6387 if (!r->valid)
6388 continue;
6390 get_all = (get_all || xget_all);
6391 //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);
6392 }
6393
6394 int rp = c->read_pointer;
6395 int rp0 = -1;
6396 while (rp != pheader->write_pointer) {
6397 //bm_print_event(pdata, rp);
6398 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6399 if (rp1 < 0) {
6400 cm_msg(MERROR, "bm_validate_buffer",
6401 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6402 pheader->name, c->name, c->read_pointer, rp, rp0);
6403 return BM_CORRUPTED;
6404 }
6405 rp0 = rp;
6406 rp = rp1;
6407 }
6408 }
6409
6410 return BM_SUCCESS;
6411}
6412
6414 BUFFER_HEADER *pheader = pbuf->buffer_header;
6415
6416 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6417
6418 pheader->read_pointer = 0;
6419 pheader->write_pointer = 0;
6420
6421 int i;
6422 for (i = 0; i < pheader->max_client_index; i++) {
6423 BUFFER_CLIENT *pc = pheader->client + i;
6424 if (pc->pid) {
6425 pc->read_pointer = 0;
6426 }
6427 }
6428}
6429
6431 HNDLE hKey;
6432 int status;
6433
6434 char str[256 + 2 * NAME_LENGTH];
6435 sprintf(str, "/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6436 //printf("delete [%s]\n", str);
6437 status = db_find_key(hDB, 0, str, &hKey);
6438 if (status == DB_SUCCESS) {
6440 }
6441}
6442
6444{
6447 /* buffer statistics */
6448 int count_lock = 0;
6449 int count_sent = 0;
6450 double bytes_sent = 0;
6457 int count_read = 0;
6458 double bytes_read = 0;
6463 {
6464 get_all_flag = pbuf->get_all_flag;
6465
6466 /* buffer statistics */
6467 count_lock = pbuf->count_lock;
6468 count_sent = pbuf->count_sent;
6469 bytes_sent = pbuf->bytes_sent;
6470 count_write_wait = pbuf->count_write_wait;
6471 time_write_wait = pbuf->time_write_wait;
6472 last_count_lock = pbuf->last_count_lock;
6473 wait_start_time = pbuf->wait_start_time;
6474 wait_client_index = pbuf->wait_client_index;
6475 max_requested_space = pbuf->max_requested_space;
6476 count_read = pbuf->count_read;
6477 bytes_read = pbuf->bytes_read;
6478
6479 for (int i=0; i<MAX_CLIENTS; i++) {
6480 client_count_write_wait[i] = pbuf->client_count_write_wait[i];
6481 client_time_write_wait[i] = pbuf->client_time_write_wait[i];
6482 }
6483 };
6484};
6485
6486static 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)
6487{
6488 int status;
6489
6491
6492 HNDLE hKey;
6493 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6494 if (status != DB_SUCCESS) {
6495 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6496 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6497 if (status != DB_SUCCESS)
6498 return;
6499 }
6500
6503 if (status != DB_SUCCESS) {
6506 if (status != DB_SUCCESS)
6507 return;
6508 }
6509
6510 double buf_size = pheader->size;
6511 double buf_rptr = pheader->read_pointer;
6512 double buf_wptr = pheader->write_pointer;
6513
6514 double buf_fill = 0;
6515 double buf_cptr = 0;
6516 double buf_cused = 0;
6517 double buf_cused_pct = 0;
6518
6519 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6520 buf_cptr = pheader->client[client_index].read_pointer;
6521
6522 if (buf_wptr == buf_cptr) {
6523 buf_cused = 0;
6524 } else if (buf_wptr > buf_cptr) {
6526 } else {
6527 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6528 }
6529
6530 buf_cused_pct = buf_cused / buf_size * 100.0;
6531
6532 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6533 // because some other GET_ALL client may have different buf_cused & etc,
6534 // so they must be written into the per-client statistics
6535 // and the web page should look at all the GET_ALL clients and used
6536 // the biggest buf_cused as the whole-buffer "bytes used" value.
6537 }
6538
6539 if (buf_wptr == buf_rptr) {
6540 buf_fill = 0;
6541 } else if (buf_wptr > buf_rptr) {
6543 } else {
6544 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6545 }
6546
6547 double buf_fill_pct = buf_fill / buf_size * 100.0;
6548
6549 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6550 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6551 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6552 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6553 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6554
6555 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6556 if (status != DB_SUCCESS) {
6557 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6558 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6559 if (status != DB_SUCCESS)
6560 return;
6561 }
6562
6564 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6565 if (status != DB_SUCCESS) {
6566 db_create_key(hDB, hKey, client_name, TID_KEY);
6567 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6568 if (status != DB_SUCCESS)
6569 return;
6570 }
6571
6572 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6573 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6574 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6575 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6576 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6577 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6578 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6579 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6580 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6581 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6582 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6583 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6584
6585 for (int i = 0; i < MAX_CLIENTS; i++) {
6586 if (!pbuf->client_count_write_wait[i])
6587 continue;
6588
6589 if (pheader->client[i].pid == 0)
6590 continue;
6591
6592 if (pheader->client[i].name[0] == 0)
6593 continue;
6594
6595 char str[100 + NAME_LENGTH];
6596
6597 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6598 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6599
6600 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6601 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6602 }
6603
6604 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6605 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6606}
6607
6609{
6610 //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);
6611
6613
6614 if (!pbuf_guard.is_locked())
6615 return;
6616
6617 if (!force) {
6618 if (pbuf->count_lock == pbuf->last_count_lock) {
6619 return;
6620 }
6621 }
6622
6623 std::string buffer_name = pbuf->buffer_name;
6624 std::string client_name = pbuf->client_name;
6625
6626 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6627 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6628 pbuf_guard.unlock(); // unlock before cm_msg()
6629 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());
6630 return;
6631 }
6632
6633 pbuf->last_count_lock = pbuf->count_lock;
6634
6636 BUFFER_HEADER xheader = *pbuf->buffer_header;
6637 int client_index = pbuf->client_index;
6638
6639 pbuf_guard.unlock();
6640
6641 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6642}
6643
6644static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6645{
6646 size_t sbuffer_handle = buffer_handle;
6647
6648 size_t nbuf = 0;
6649 BUFFER* pbuf = NULL;
6650
6651 gBuffersMutex.lock();
6652
6653 nbuf = gBuffers.size();
6654 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6655 pbuf = gBuffers[buffer_handle-1];
6656 }
6657
6658 gBuffersMutex.unlock();
6659
6660 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6661 if (who)
6662 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6663 if (pstatus)
6665 return NULL;
6666 }
6667
6668 if (!pbuf) {
6669 if (who)
6670 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6671 if (pstatus)
6673 return NULL;
6674 }
6675
6676 if (!pbuf->attached) {
6677 if (who)
6678 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6679 if (pstatus)
6681 return NULL;
6682 }
6683
6684 if (pstatus)
6686
6687 return pbuf;
6688}
6689
6690#endif // LOCAL_ROUTINES
6691
6692/********************************************************************/
6739INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6740 INT status;
6741
6742 if (rpc_is_remote()) {
6743 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6744
6745 HNDLE hDB;
6747 if (status != SUCCESS || hDB == 0) {
6748 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6749 return BM_NO_SHM;
6750 }
6751
6753
6754 int size = sizeof(INT);
6755 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6756
6757 if (status != DB_SUCCESS) {
6758 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6759 status);
6760 return status;
6761 }
6762
6763 return status;
6764 }
6765#ifdef LOCAL_ROUTINES
6766 {
6767 HNDLE shm_handle;
6768 size_t shm_size;
6769 HNDLE hDB;
6770 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6771
6772 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6773
6774 if (!buffer_name || !buffer_name[0]) {
6775 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6776 return BM_INVALID_PARAM;
6777 }
6778
6779 if (strlen(buffer_name) >= NAME_LENGTH) {
6780 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6781 return BM_INVALID_PARAM;
6782 }
6783
6785
6786 if (status != SUCCESS || hDB == 0) {
6787 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6788 return BM_NO_SHM;
6789 }
6790
6791 /* get buffer size from ODB, user parameter as default if not present in ODB */
6792 std::string odb_path;
6793 odb_path += "/Experiment/Buffer sizes/";
6794 odb_path += buffer_name;
6795
6796 int size = sizeof(INT);
6797 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6798
6800 cm_msg(MERROR, "bm_open_buffer",
6801 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6802 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6803 return BM_INVALID_PARAM;
6804 }
6805
6807
6808 size = sizeof(INT);
6809 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6810
6811 if (status != DB_SUCCESS) {
6812 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6813 status);
6814 return status;
6815 }
6816
6817 /* check if buffer already is open */
6818 gBuffersMutex.lock();
6819 for (size_t i = 0; i < gBuffers.size(); i++) {
6820 BUFFER* pbuf = gBuffers[i];
6821 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6822 *buffer_handle = i + 1;
6823 gBuffersMutex.unlock();
6824 return BM_SUCCESS;
6825 }
6826 }
6827 gBuffersMutex.unlock();
6828
6829 // only one thread at a time should create new buffers
6830
6831 static std::mutex gNewBufferMutex;
6832 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6833
6834 // if we had a race against another thread
6835 // and while we were waiting for gNewBufferMutex
6836 // the other thread created this buffer, we return it.
6837
6838 gBuffersMutex.lock();
6839 for (size_t i = 0; i < gBuffers.size(); i++) {
6840 BUFFER* pbuf = gBuffers[i];
6841 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6842 *buffer_handle = i + 1;
6843 gBuffersMutex.unlock();
6844 return BM_SUCCESS;
6845 }
6846 }
6847 gBuffersMutex.unlock();
6848
6849 /* allocate new BUFFER object */
6850
6851 BUFFER* pbuf = new BUFFER;
6852
6853 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6854
6855 for (int i=0; i<MAX_CLIENTS; i++) {
6857 pbuf->client_time_write_wait[i] = 0;
6858 }
6859
6860 /* create buffer semaphore */
6861
6862 status = ss_semaphore_create(buffer_name, &(pbuf->semaphore));
6863
6864 if (status != SS_CREATED && status != SS_SUCCESS) {
6865 *buffer_handle = 0;
6866 delete pbuf;
6867 return BM_NO_SEMAPHORE;
6868 }
6869
6870 std::string client_name = cm_get_client_name();
6871
6872 /* store client name */
6873 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6874
6875 /* store buffer name */
6876 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6877
6878 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6879
6880 pbuf->attached = true; // required by bm_lock_buffer()
6881
6883
6884 if (!pbuf_guard.is_locked()) {
6885 // cannot happen, no other thread can see this pbuf
6886 abort();
6887 return BM_NO_SEMAPHORE;
6888 }
6889
6890 /* open shared memory */
6891
6892 void *p = NULL;
6893 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6894
6895 if (status != SS_SUCCESS && status != SS_CREATED) {
6896 *buffer_handle = 0;
6897 pbuf_guard.unlock();
6898 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6899 delete pbuf;
6900 return BM_NO_SHM;
6901 }
6902
6903 pbuf->buffer_header = (BUFFER_HEADER *) p;
6904
6905 BUFFER_HEADER *pheader = pbuf->buffer_header;
6906
6907 bool shm_created = (status == SS_CREATED);
6908
6909 if (shm_created) {
6910 /* initialize newly created shared memory */
6911
6912 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6913
6914 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6915 pheader->size = buffer_size;
6916
6917 } else {
6918 /* validate existing shared memory */
6919
6920 if (!equal_ustring(pheader->name, buffer_name)) {
6921 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6922 pbuf_guard.unlock();
6923 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6924 cm_msg(MERROR, "bm_open_buffer",
6925 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6926 pheader->name);
6927 *buffer_handle = 0;
6928 delete pbuf;
6929 return BM_CORRUPTED;
6930 }
6931
6932 if ((pheader->num_clients < 0) || (pheader->num_clients > 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, num_clients %d exceeds MAX_CLIENTS %d",
6938 *buffer_handle = 0;
6939 delete pbuf;
6940 return BM_CORRUPTED;
6941 }
6942
6943 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6944 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6945 pbuf_guard.unlock();
6946 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6947 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6949 *buffer_handle = 0;
6950 delete pbuf;
6951 return BM_CORRUPTED;
6952 }
6953
6954 /* check if buffer size is identical */
6955 if (pheader->size != buffer_size) {
6956 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6957 buffer_name, buffer_size, pheader->size);
6958
6959 buffer_size = pheader->size;
6960
6961 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6962
6963 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6964
6965 if (status != SS_SUCCESS) {
6966 *buffer_handle = 0;
6967 pbuf_guard.unlock();
6968 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6969 delete pbuf;
6970 return BM_NO_SHM;
6971 }
6972
6973 pbuf->buffer_header = (BUFFER_HEADER *) p;
6974 pheader = pbuf->buffer_header;
6975 }
6976 }
6977
6978 /* shared memory is good from here down */
6979
6980 pbuf->attached = true;
6981
6982 pbuf->shm_handle = shm_handle;
6983 pbuf->shm_size = shm_size;
6984 pbuf->callback = FALSE;
6985
6986 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6987
6989 if (status != BM_SUCCESS) {
6990 cm_msg(MERROR, "bm_open_buffer",
6991 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6992 status);
6994 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6995 }
6996
6997 /* add our client BUFFER_HEADER */
6998
6999 int iclient = 0;
7000 for (; iclient < MAX_CLIENTS; iclient++)
7001 if (pheader->client[iclient].pid == 0)
7002 break;
7003
7004 if (iclient == MAX_CLIENTS) {
7005 *buffer_handle = 0;
7006 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
7007 pbuf_guard.unlock();
7008 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
7009 delete pbuf;
7010 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
7011 return BM_NO_SLOT;
7012 }
7013
7014 /* store slot index in _buffer structure */
7015 pbuf->client_index = iclient;
7016
7017 /*
7018 Save the index of the last client of that buffer so that later only
7019 the clients 0..max_client_index-1 have to be searched through.
7020 */
7021 pheader->num_clients++;
7022 if (iclient + 1 > pheader->max_client_index)
7023 pheader->max_client_index = iclient + 1;
7024
7025 /* setup buffer header and client structure */
7026 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7027
7028 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7029
7030 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7031
7032 pclient->pid = ss_getpid();
7033
7035
7036 pclient->read_pointer = pheader->write_pointer;
7037 pclient->last_activity = ss_millitime();
7038
7039 cm_get_watchdog_params(NULL, &pclient->watchdog_timeout);
7040
7041 pbuf_guard.unlock();
7042
7043 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7044
7045 pheader = NULL;
7046
7047 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7048
7051
7052 /* add pbuf to buffer list */
7053
7054 gBuffersMutex.lock();
7055
7056 bool added = false;
7057 for (size_t i=0; i<gBuffers.size(); i++) {
7058 if (gBuffers[i] == NULL) {
7059 gBuffers[i] = pbuf;
7060 added = true;
7061 *buffer_handle = i+1;
7062 break;
7063 }
7064 }
7065 if (!added) {
7066 *buffer_handle = gBuffers.size() + 1;
7067 gBuffers.push_back(pbuf);
7068 }
7069
7070 /* from here down we should not touch pbuf without locking it */
7071
7072 pbuf = NULL;
7073
7074 gBuffersMutex.unlock();
7075
7076 /* new buffer is now ready for use */
7077
7078 /* initialize buffer counters */
7079 bm_init_buffer_counters(*buffer_handle);
7080
7081 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7082
7083 if (shm_created)
7084 return BM_CREATED;
7085 }
7086#endif /* LOCAL_ROUTINES */
7087
7088 return BM_SUCCESS;
7089}
7090
7091/********************************************************************/
7097INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7098{
7099 gBuffersMutex.lock();
7100 for (size_t i = 0; i < gBuffers.size(); i++) {
7101 BUFFER* pbuf = gBuffers[i];
7102 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7103 *buffer_handle = i + 1;
7104 gBuffersMutex.unlock();
7105 return BM_SUCCESS;
7106 }
7107 }
7108 gBuffersMutex.unlock();
7109 return BM_NOT_FOUND;
7110}
7111
7112/********************************************************************/
7118INT bm_close_buffer(INT buffer_handle) {
7119 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7120
7121 if (rpc_is_remote())
7122 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7123
7124#ifdef LOCAL_ROUTINES
7125 {
7126 int status = 0;
7127
7128 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7129
7130 if (!pbuf)
7131 return status;
7132
7133 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7134
7135 int i;
7136
7137 { /* delete all requests for this buffer */
7138 _request_list_mutex.lock();
7139 std::vector<EventRequest> request_list_copy = _request_list;
7140 _request_list_mutex.unlock();
7141 for (size_t i = 0; i < request_list_copy.size(); i++) {
7142 if (request_list_copy[i].buffer_handle == buffer_handle) {
7144 }
7145 }
7146 }
7147
7148 HNDLE hDB;
7150
7151 if (hDB) {
7152 /* write statistics to odb */
7154 }
7155
7156 /* lock buffer in correct order */
7157
7159
7160 if (status != BM_SUCCESS) {
7161 return status;
7162 }
7163
7165
7166 if (status != BM_SUCCESS) {
7167 pbuf->read_cache_mutex.unlock();
7168 return status;
7169 }
7170
7172
7173 if (!pbuf_guard.is_locked()) {
7174 pbuf->write_cache_mutex.unlock();
7175 pbuf->read_cache_mutex.unlock();
7176 return pbuf_guard.get_status();
7177 }
7178
7179 BUFFER_HEADER *pheader = pbuf->buffer_header;
7180
7181 /* mark entry in _buffer as empty */
7182 pbuf->attached = false;
7183
7185
7186 if (pclient) {
7187 /* clear entry from client structure in buffer header */
7188 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7189 }
7190
7191 /* calculate new max_client_index entry */
7192 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7193 if (pheader->client[i].pid != 0)
7194 break;
7195 pheader->max_client_index = i + 1;
7196
7197 /* count new number of clients */
7198 int j = 0;
7199 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7200 if (pheader->client[i].pid != 0)
7201 j++;
7202 pheader->num_clients = j;
7203
7204 int destroy_flag = (pheader->num_clients == 0);
7205
7206 // we hold the locks on the read cache and the write cache.
7207
7208 /* free cache */
7209 if (pbuf->read_cache_size > 0) {
7210 free(pbuf->read_cache);
7211 pbuf->read_cache = NULL;
7212 pbuf->read_cache_size = 0;
7213 pbuf->read_cache_rp = 0;
7214 pbuf->read_cache_wp = 0;
7215 }
7216
7217 if (pbuf->write_cache_size > 0) {
7218 free(pbuf->write_cache);
7219 pbuf->write_cache = NULL;
7220 pbuf->write_cache_size = 0;
7221 pbuf->write_cache_rp = 0;
7222 pbuf->write_cache_wp = 0;
7223 }
7224
7225 /* check if anyone is waiting and wake him up */
7226
7227 for (int i = 0; i < pheader->max_client_index; i++) {
7228 BUFFER_CLIENT *pclient = pheader->client + i;
7229 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7230 ss_resume(pclient->port, "B ");
7231 }
7232
7233 /* unmap shared memory, delete it if we are the last */
7234
7235 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7236
7237 /* after ss_shm_close() these are invalid: */
7238
7239 pheader = NULL;
7240 pbuf->buffer_header = NULL;
7241 pbuf->shm_size = 0;
7242 pbuf->shm_handle = 0;
7243
7244 /* unlock buffer in correct order */
7245
7246 pbuf_guard.unlock();
7247
7248 pbuf->write_cache_mutex.unlock();
7249 pbuf->read_cache_mutex.unlock();
7250
7251 /* delete semaphore */
7252
7254 }
7255#endif /* LOCAL_ROUTINES */
7256
7257 return BM_SUCCESS;
7258}
7259
7260/********************************************************************/
7266 if (rpc_is_remote())
7268
7269#ifdef LOCAL_ROUTINES
7270 {
7272
7273 gBuffersMutex.lock();
7274 size_t nbuf = gBuffers.size();
7275 gBuffersMutex.unlock();
7276
7277 for (size_t i = nbuf; i > 0; i--) {
7279 }
7280
7281 gBuffersMutex.lock();
7282 for (size_t i=0; i< gBuffers.size(); i++) {
7283 BUFFER* pbuf = gBuffers[i];
7284 if (!pbuf)
7285 continue;
7286 delete pbuf;
7287 pbuf = NULL;
7288 gBuffers[i] = NULL;
7289 }
7290 gBuffersMutex.unlock();
7291 }
7292#endif /* LOCAL_ROUTINES */
7293
7294 return BM_SUCCESS;
7295}
7296
7297/********************************************************************/
7303#ifdef LOCAL_ROUTINES
7304 {
7305 int status;
7306 HNDLE hDB;
7307
7309
7310 if (status != CM_SUCCESS) {
7311 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7312 return BM_SUCCESS;
7313 }
7314
7315 std::vector<BUFFER*> mybuffers;
7316
7317 gBuffersMutex.lock();
7319 gBuffersMutex.unlock();
7320
7321 for (BUFFER* pbuf : mybuffers) {
7322 if (!pbuf || !pbuf->attached)
7323 continue;
7325 }
7326 }
7327#endif /* LOCAL_ROUTINES */
7328
7329 return BM_SUCCESS;
7330}
7331
/* end of bmfunctionc */
7334
7340/*-- Watchdog routines ---------------------------------------------*/
7341#ifdef LOCAL_ROUTINES
7342
7343static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7344static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7345static std::atomic<std::thread*> _watchdog_thread{NULL};
7346
7347/********************************************************************/
7353 //printf("cm_watchdog_thread started!\n");
7354 while (_watchdog_thread_run) {
7355 //printf("cm_watchdog_thread runs!\n");
7359 int i;
7360 for (i = 0; i < 20; i++) {
7361 ss_sleep(100);
7363 break;
7364 }
7365 }
7366 //printf("cm_watchdog_thread stopped!\n");
7368 return 0;
7369}
7370
7371static void xcm_watchdog_thread() {
7373}
7374
7375#endif
7376
7378 /* watchdog does not run inside remote clients.
7379 * watchdog timeout timers are maintained by the mserver */
7380 if (rpc_is_remote())
7381 return CM_SUCCESS;
7382#ifdef LOCAL_ROUTINES
7383 /* only start once */
7384 if (_watchdog_thread)
7385 return CM_SUCCESS;
7386 _watchdog_thread_run = true;
7387 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7388#endif
7389 return CM_SUCCESS;
7390}
7391
7393 /* watchdog does not run inside remote clients.
7394 * watchdog timeout timers are maintained by the mserver */
7395 if (rpc_is_remote())
7396 return CM_SUCCESS;
7397#ifdef LOCAL_ROUTINES
7398 _watchdog_thread_run = false;
7400 //printf("waiting for watchdog thread to shut down\n");
7401 ss_sleep(10);
7402 }
7403 if (_watchdog_thread != NULL) {
7404 _watchdog_thread.load()->join();
7405 delete static_cast<std::thread *>(_watchdog_thread);
7407 }
7408#endif
7409 return CM_SUCCESS;
7410}
7411
7412/********************************************************************/
7423 INT status, return_status, i, size;
7425 KEY key;
7426 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7427 INT port;
7428 DWORD start_time;
7429 DWORD timeout;
7430 DWORD last;
7431
7433
7434 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7435 if (status != DB_SUCCESS)
7436 return DB_NO_KEY;
7437
7439
7440 /* loop over all clients */
7441 for (i = 0;; i++) {
7444 break;
7445
7446 /* don't shutdown ourselves */
7447 if (hSubkey == hKeyClient)
7448 continue;
7449
7450 if (status == DB_SUCCESS) {
7452
7453 /* contact client */
7454 size = sizeof(client_name);
7455 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7456 if (status != DB_SUCCESS)
7457 continue;
7458
7459 if (!bUnique)
7460 client_name[strlen(name)] = 0; /* strip number */
7461
7462 /* check if individual client */
7463 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7464 continue;
7465
7466 size = sizeof(port);
7467 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7468
7469 size = sizeof(remote_host);
7470 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7471
7472 cm_get_watchdog_info(hDB, name, &timeout, &last);
7473 if (timeout == 0)
7474 timeout = 5000;
7475
7476 /* client found -> connect to its server port */
7477 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7478 if (status != RPC_SUCCESS) {
7479 int client_pid = atoi(key.name);
7481 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7482 client_name, remote_host, port);
7483#ifdef SIGKILL
7484 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7485 client_pid);
7489 if (status != CM_SUCCESS)
7490 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7492#endif
7493 } else {
7494 /* call disconnect with shutdown=TRUE */
7496
7497 /* wait until client has shut down */
7498 start_time = ss_millitime();
7499 do {
7500 ss_sleep(100);
7502 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7503
7504 if (status == DB_SUCCESS) {
7505 int client_pid = atoi(key.name);
7507 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7508#ifdef SIGKILL
7509 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7510 client_pid);
7513 if (status != CM_SUCCESS)
7514 cm_msg(MERROR, "cm_shutdown",
7515 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7516 status);
7517#endif
7519 } else {
7521 i--;
7522 }
7523 }
7524 }
7525
7526 /* display any message created during each shutdown */
7528 }
7529
7530 return return_status;
7531}
7532
7533/********************************************************************/
7543 INT status, i, size;
7545 char client_name[NAME_LENGTH];
7546
7547 if (rpc_is_remote())
7549
7551
7552 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7553 if (status != DB_SUCCESS)
7554 return DB_NO_KEY;
7555
7557
7558 /* loop over all clients */
7559 for (i = 0;; i++) {
7562 break;
7563
7564 if (hSubkey == hKeyClient)
7565 continue;
7566
7567 if (status == DB_SUCCESS) {
7568 /* get client name */
7569 size = sizeof(client_name);
7570 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7571
7572 if (status != DB_SUCCESS) {
7573 //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);
7574 continue;
7575 }
7576
7577 if (equal_ustring(client_name, name)) {
7579 return CM_SUCCESS;
7580 }
7581
7582 if (!bUnique) {
7583 client_name[strlen(name)] = 0; /* strip number */
7584 if (equal_ustring(client_name, name)) {
7586 return CM_SUCCESS;
7587 }
7588 }
7589 }
7590 }
7591
7593
7594 return CM_NO_CLIENT;
7595}
7596
7597/********************************************************************/
7632INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7633 if (rpc_is_remote())
7634 return rpc_call(RPC_CM_CLEANUP, client_name);
7635
7636#ifdef LOCAL_ROUTINES
7637 {
7640
7641 std::vector<BUFFER*> mybuffers;
7642
7643 gBuffersMutex.lock();
7645 gBuffersMutex.unlock();
7646
7647 /* check buffers */
7648 for (BUFFER* pbuf : mybuffers) {
7649 if (!pbuf)
7650 continue;
7651 if (pbuf->attached) {
7652 std::string msg;
7653
7655
7656 if (!pbuf_guard.is_locked())
7657 continue;
7658
7659 /* update the last_activity entry to show that we are alive */
7660 BUFFER_HEADER *pheader = pbuf->buffer_header;
7662 pclient->last_activity = ss_millitime();
7663
7664 /* now check other clients */
7665 for (int j = 0; j < pheader->max_client_index; j++) {
7666 BUFFER_CLIENT *pbclient = &pheader->client[j];
7667 if (j != pbuf->client_index && pbclient->pid &&
7668 (client_name == NULL || client_name[0] == 0
7669 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7670 if (ignore_timeout)
7672 else
7674
7675 /* If client process has no activity, clear its buffer entry. */
7676 if (interval > 0
7677 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7678
7679 /* now make again the check with the buffer locked */
7680 if (interval > 0
7681 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7682 msg = msprintf(
7683 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7684 pbclient->name, pheader->name,
7685 (ss_millitime() - pbclient->last_activity) / 1000.0,
7686 interval / 1000.0);
7687
7688 bm_remove_client_locked(pheader, j);
7689 }
7690
7691 /* go again through whole list */
7692 j = 0;
7693 }
7694 }
7695 }
7696
7697 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7698 pbuf_guard.unlock();
7699
7700 /* display info message after unlocking buffer */
7701 if (!msg.empty())
7702 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7703 }
7704 }
7705
7706 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7707 }
7708#endif /* LOCAL_ROUTINES */
7709
7710 return CM_SUCCESS;
7711}
7712
7713/********************************************************************/
7732std::string cm_expand_env(const char *str) {
7733 const char *s = str;
7734 std::string r;
7735 for (; *s;) {
7736 if (*s == '$') {
7737 s++;
7738 std::string envname;
7739 for (; *s;) {
7740 if (*s == DIR_SEPARATOR)
7741 break;
7742 envname += *s;
7743 s++;
7744 }
7745 const char *e = getenv(envname.c_str());
7746 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7747 if (!e) {
7748 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7749 r += '$';
7750 r += envname;
7751 } else {
7752 r += e;
7753 //if (r[r.length()-1] != DIR_SEPARATOR)
7754 //r += DIR_SEPARATOR_STR;
7755 }
7756 } else {
7757 r += *s;
7758 s++;
7759 }
7760 }
7761 return r;
7762}
7763
7764static bool test_cm_expand_env1(const char *str, const char *expected) {
7765 std::string s = cm_expand_env(str);
7766 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7767 str,
7768 s.c_str(),
7769 expected);
7770 if (s != expected) {
7771 printf(", MISMATCH!\n");
7772 return false;
7773 }
7774
7775 printf("\n");
7776 return true;
7777}
7778
7780 printf("Test expand_end()\n");
7781 setenv("FOO", "foo", 1);
7782 setenv("BAR", "bar", 1);
7783 setenv("EMPTY", "", 1);
7784 unsetenv("UNDEF");
7785
7786 bool ok = true;
7787
7788 ok &= test_cm_expand_env1("aaa", "aaa");
7789 ok &= test_cm_expand_env1("$FOO", "foo");
7790 ok &= test_cm_expand_env1("/$FOO", "/foo");
7791 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7792 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7793 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7794 ok &= test_cm_expand_env1("1$FOO", "1foo");
7795 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7796 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7797
7798 if (ok) {
7799 printf("test_expand_env: all tests passed!\n");
7800 } else {
7801 printf("test_expand_env: test FAILED!\n");
7802 }
7803}
7804
/* end of cmfunctionc */
7808
7810#ifndef DOXYGEN_SHOULD_SKIP_THIS
7811
7812/********************************************************************/
7813INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7814/********************************************************************\
7815
7816 Routine: bm_buffer_info
7817
7818 Purpose: Copies the current buffer header referenced by buffer_handle
7819 into the *buffer_header structure which must be supplied
7820 by the calling routine.
7821
7822 Input:
7823 INT buffer_handle Handle of the buffer to get the header from
7824
7825 Output:
7826 BUFFER_HEADER *buffer_header Destination address which gets a copy
7827 of the buffer header structure.
7828
7829 Function value:
7830 BM_SUCCESS Successful completion
7831 BM_INVALID_HANDLE Buffer handle is invalid
7832 RPC_NET_ERROR Network error
7833
7834\********************************************************************/
7835{
7836 if (rpc_is_remote())
7837 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7838
7839#ifdef LOCAL_ROUTINES
7840
7841 int status = 0;
7842 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7843
7844 if (!pbuf)
7845 return status;
7846
7848
7849 if (!pbuf_guard.is_locked())
7850 return pbuf_guard.get_status();
7851
7852 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7853
7854#endif /* LOCAL_ROUTINES */
7855
7856 return BM_SUCCESS;
7857}
7858
7859/********************************************************************/
7860INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7861/********************************************************************\
7862
7863 Routine: bm_get_buffer_level
7864
7865 Purpose: Return number of bytes in buffer or in cache
7866
7867 Input:
7868 INT buffer_handle Handle of the buffer to get the info
7869
7870 Output:
7871 INT *n_bytes Number of bytes in buffer
7872
7873 Function value:
7874 BM_SUCCESS Successful completion
7875 BM_INVALID_HANDLE Buffer handle is invalid
7876 RPC_NET_ERROR Network error
7877
7878\********************************************************************/
7879{
7880 if (rpc_is_remote())
7881 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7882
7883#ifdef LOCAL_ROUTINES
7884 {
7885 int status = 0;
7886
7887 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7888
7889 if (!pbuf)
7890 return status;
7891
7893
7894 if (!pbuf_guard.is_locked())
7895 return pbuf_guard.get_status();
7896
7897 BUFFER_HEADER *pheader = pbuf->buffer_header;
7898
7900
7901 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7902 if (*n_bytes < 0)
7903 *n_bytes += pheader->size;
7904
7905 pbuf_guard.unlock();
7906
7907 if (pbuf->read_cache_size) {
7909 if (status == BM_SUCCESS) {
7910 /* add bytes in cache */
7911 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7912 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7913 pbuf->read_cache_mutex.unlock();
7914 }
7915 }
7916 }
7917#endif /* LOCAL_ROUTINES */
7918
7919 return BM_SUCCESS;
7920}
7921
7922
7923#ifdef LOCAL_ROUTINES
7924
7925/********************************************************************/
7927{
7928 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7929
7930 if (!locked) {
7931 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);
7932 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);
7933 abort();
7934 /* DOES NOT RETURN */
7935 }
7936
7937 if (!pbuf->attached) {
7938 pbuf->read_cache_mutex.unlock();
7939 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);
7940 return BM_INVALID_HANDLE;
7941 }
7942
7943 return BM_SUCCESS;
7944}
7945
7946/********************************************************************/
7948{
7949 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7950
7951 if (!locked) {
7952 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);
7953 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);
7954 abort();
7955 /* DOES NOT RETURN */
7956 }
7957
7958 if (!pbuf->attached) {
7959 pbuf->write_cache_mutex.unlock();
7960 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);
7961 return BM_INVALID_HANDLE;
7962 }
7963
7964 return BM_SUCCESS;
7965}
7966
7967/********************************************************************/
7969{
7970 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7971
7972 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7973
7974 if (!locked) {
7975 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7976 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7977 abort();
7978 /* DOES NOT RETURN */
7979 }
7980
7981 if (!pbuf->attached) {
7982 pbuf->buffer_mutex.unlock();
7983 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);
7984 return BM_INVALID_HANDLE;
7985 }
7986
7987 //static int counter = 0;
7988 //counter++;
7989 //printf("locked %d!\n", counter);
7990 //if (counter > 50)
7991 // ::sleep(3);
7992
7993 return BM_SUCCESS;
7994}
7995
7996/********************************************************************/
7998{
7999 int status;
8000
8001 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8002
8003 //if (pbuf->locked) {
8004 // fprintf(stderr, "double lock, abort!\n");
8005 // abort();
8006 //}
8007
8009
8010 if (status != BM_SUCCESS)
8011 return status;
8012
8013 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8014
8015 if (status != SS_SUCCESS) {
8016 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8017
8018 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8019
8020 if (status != SS_SUCCESS) {
8021 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);
8022
8023 if (pbuf->buffer_header) {
8024 for (int i=0; i<MAX_CLIENTS; i++) {
8025 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);
8026 }
8027 }
8028
8030
8031 if (status != SS_SUCCESS) {
8032 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8033 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8034 abort();
8035 /* DOES NOT RETURN */
8036 }
8037 }
8038 }
8039
8040 // protect against double lock
8041 assert(!pbuf->locked);
8042 pbuf->locked = TRUE;
8043
8044#if 0
8045 int x = MAX_CLIENTS - 1;
8046 if (pbuf->buffer_header->client[x].unused1 != 0) {
8047 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8048 }
8049 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8050 pbuf->buffer_header->client[x].unused1 = getpid();
8051#endif
8052
8053 pbuf->count_lock++;
8054
8055 return BM_SUCCESS;
8056}
8057
8058/********************************************************************/
8060 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8061
8062#if 0
8063 int x = MAX_CLIENTS-1;
8064 if (pbuf->attached) {
8065 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8066 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8067 }
8068 pbuf->buffer_header->client[x].unused1 = 0;
8069 } else {
8070 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8071 }
8072#endif
8073
8074 // protect against double unlock
8075 assert(pbuf->locked);
8076 pbuf->locked = FALSE;
8077
8078 ss_semaphore_release(pbuf->semaphore);
8079 pbuf->buffer_mutex.unlock();
8080}
8081
8082#endif /* LOCAL_ROUTINES */
8083
8084/********************************************************************/
8086/********************************************************************\
8087
8088 Routine: bm_init_event_counters
8089
8090 Purpose: Initialize counters for a specific buffer. This routine
8091 should be called at the beginning of a run.
8092
8093 Input:
8094 INT buffer_handle Handle to the buffer to be
8095 initialized.
8096 Output:
8097 none
8098
8099 Function value:
8100 BM_SUCCESS Successful completion
8101 BM_INVALID_HANDLE Buffer handle is invalid
8102
8103\********************************************************************/
8104{
8105 if (rpc_is_remote())
8106 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8107
8108#ifdef LOCAL_ROUTINES
8109
8110 int status = 0;
8111
8112 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8113
8114 if (!pbuf)
8115 return status;
8116
8118
8119 if (!pbuf_guard.is_locked())
8120 return pbuf_guard.get_status();
8121
8122 pbuf->buffer_header->num_in_events = 0;
8123 pbuf->buffer_header->num_out_events = 0;
8124
8125#endif /* LOCAL_ROUTINES */
8126
8127 return BM_SUCCESS;
8128}
8129
8131#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8132
8138/********************************************************************/
8162INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8163/*------------------------------------------------------------------*/
8164{
8165 if (rpc_is_remote())
8166 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8167
8168#ifdef LOCAL_ROUTINES
8169 {
8170 int status = 0;
8171
8172 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8173
8174 if (!pbuf)
8175 return status;
8176
8177 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8178
8180
8181 if (status != BM_SUCCESS)
8182 return status;
8183
8184 if (write_size < 0)
8185 write_size = 0;
8186
8187 if (write_size > 0) {
8189 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);
8191 }
8192 }
8193
8194 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8195
8196 if (write_size > max_write_size) {
8198 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);
8200 }
8201
8202 pbuf->buffer_mutex.unlock();
8203
8204 /* resize read cache */
8205
8207
8208 if (status != BM_SUCCESS) {
8209 return status;
8210 }
8211
8212 if (pbuf->read_cache_size > 0) {
8213 free(pbuf->read_cache);
8214 pbuf->read_cache = NULL;
8215 }
8216
8217 if (read_size > 0) {
8218 pbuf->read_cache = (char *) malloc(read_size);
8219 if (pbuf->read_cache == NULL) {
8220 pbuf->read_cache_size = 0;
8221 pbuf->read_cache_rp = 0;
8222 pbuf->read_cache_wp = 0;
8223 pbuf->read_cache_mutex.unlock();
8224 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);
8225 return BM_NO_MEMORY;
8226 }
8227 }
8228
8229 pbuf->read_cache_size = read_size;
8230 pbuf->read_cache_rp = 0;
8231 pbuf->read_cache_wp = 0;
8232
8233 pbuf->read_cache_mutex.unlock();
8234
8235 /* resize the write cache */
8236
8238
8239 if (status != BM_SUCCESS)
8240 return status;
8241
8242 // FIXME: should flush the write cache!
8243 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8244 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8245 }
8246
8247 /* manage write cache */
8248 if (pbuf->write_cache_size > 0) {
8249 free(pbuf->write_cache);
8250 pbuf->write_cache = NULL;
8251 }
8252
8253 if (write_size > 0) {
8254 pbuf->write_cache = (char *) M_MALLOC(write_size);
8255 if (pbuf->write_cache == NULL) {
8256 pbuf->write_cache_size = 0;
8257 pbuf->write_cache_rp = 0;
8258 pbuf->write_cache_wp = 0;
8259 pbuf->write_cache_mutex.unlock();
8260 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);
8261 return BM_NO_MEMORY;
8262 }
8263 }
8264
8265 pbuf->write_cache_size = write_size;
8266 pbuf->write_cache_rp = 0;
8267 pbuf->write_cache_wp = 0;
8268
8269 pbuf->write_cache_mutex.unlock();
8270 }
8271#endif /* LOCAL_ROUTINES */
8272
8273 return BM_SUCCESS;
8274}
8275
8276/********************************************************************/
8304{
8305 event_header->event_id = event_id;
8306 event_header->trigger_mask = trigger_mask;
8307 event_header->data_size = data_size;
8308 event_header->time_stamp = ss_time();
8309 event_header->serial_number = serial;
8310
8311 return BM_SUCCESS;
8312}
8313
8315{
8316 static std::mutex mutex;
8317
8318 event_header->event_id = event_id;
8319 event_header->trigger_mask = trigger_mask;
8320 event_header->data_size = data_size;
8321 event_header->time_stamp = ss_time();
8322 {
8323 std::lock_guard<std::mutex> lock(mutex);
8324 event_header->serial_number = *serial;
8325 *serial = *serial + 1;
8326 // implicit unlock
8327 }
8328
8329 return BM_SUCCESS;
8330}
8331
8333#ifndef DOXYGEN_SHOULD_SKIP_THIS
8334
8335/********************************************************************/
8336INT bm_add_event_request(INT buffer_handle, short int event_id,
8337 short int trigger_mask,
8338 INT sampling_type,
8339 EVENT_HANDLER *func,
8340 INT request_id)
8341/********************************************************************\
8342
8343 Routine: bm_add_event_request
8344
8345 Purpose: Place a request for a specific event type in the client
8346 structure of the buffer refereced by buffer_handle.
8347
8348 Input:
8349 INT buffer_handle Handle to the buffer where the re-
8350 quest should be placed in
8351
8352 short int event_id Event ID \
8353 short int trigger_mask Trigger mask / Event specification
8354
8355 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8356
8357
8358 Note: to request all types of events, use
8359 event_id = 0 (all others should be !=0 !)
8360 trigger_mask = TRIGGER_ALL
8361 sampling_typ = GET_ALL
8362
8363
8364 void *func Callback function
8365 INT request_id Request id (unique number assigned
8366 by bm_request_event)
8367
8368 Output:
8369 none
8370
8371 Function value:
8372 BM_SUCCESS Successful completion
8373 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8374 MIDAS.H should be increased.
8375 BM_INVALID_HANDLE Buffer handle is invalid
8376 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8377 RPC_NET_ERROR Network error
8378
8379\********************************************************************/
8380{
8381 if (rpc_is_remote())
8382 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8383 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8384
8385#ifdef LOCAL_ROUTINES
8386 {
8387 int status = 0;
8388
8389 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8390
8391 if (!pbuf)
8392 return status;
8393
8394 /* lock buffer */
8396
8397 if (!pbuf_guard.is_locked())
8398 return pbuf_guard.get_status();
8399
8400 /* avoid callback/non callback requests */
8401 if (func == NULL && pbuf->callback) {
8402 pbuf_guard.unlock(); // unlock before cm_msg()
8403 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8404 return BM_INVALID_MIXING;
8405 }
8406
8407 /* do not allow GET_RECENT with nonzero cache size */
8408 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8409 pbuf_guard.unlock(); // unlock before cm_msg()
8410 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8411 return BM_INVALID_PARAM;
8412 }
8413
8414 /* get a pointer to the proper client structure */
8416
8417 /* look for a empty request entry */
8418 int i;
8419 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8420 if (!pclient->event_request[i].valid)
8421 break;
8422
8423 if (i == MAX_EVENT_REQUESTS) {
8424 // implicit unlock
8425 return BM_NO_MEMORY;
8426 }
8427
8428 /* setup event_request structure */
8429 pclient->event_request[i].id = request_id;
8430 pclient->event_request[i].valid = TRUE;
8431 pclient->event_request[i].event_id = event_id;
8432 pclient->event_request[i].trigger_mask = trigger_mask;
8433 pclient->event_request[i].sampling_type = sampling_type;
8434
8435 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8436
8437 pbuf->get_all_flag = pclient->all_flag;
8438
8439 /* set callback flag in buffer structure */
8440 if (func != NULL)
8441 pbuf->callback = TRUE;
8442
8443 /*
8444 Save the index of the last request in the list so that later only the
8445 requests 0..max_request_index-1 have to be searched through.
8446 */
8447
8448 if (i + 1 > pclient->max_request_index)
8449 pclient->max_request_index = i + 1;
8450 }
8451#endif /* LOCAL_ROUTINES */
8452
8453 return BM_SUCCESS;
8454}
8455
8457#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8458
8459/********************************************************************/
8487INT bm_request_event(HNDLE buffer_handle, short int event_id,
8488 short int trigger_mask,
8489 INT sampling_type, HNDLE *request_id,
8490 EVENT_HANDLER *func)
8491{
8492 assert(request_id != NULL);
8493
8494 EventRequest r;
8495 r.buffer_handle = buffer_handle;
8496 r.event_id = event_id;
8498 r.dispatcher = func;
8499
8500 {
8501 std::lock_guard<std::mutex> guard(_request_list_mutex);
8502
8503 bool found = false;
8504
8505 // find deleted entry
8506 for (size_t i = 0; i < _request_list.size(); i++) {
8507 if (_request_list[i].buffer_handle == 0) {
8508 _request_list[i] = r;
8509 *request_id = i;
8510 found = true;
8511 break;
8512 }
8513 }
8514
8515 if (!found) { // not found
8516 *request_id = _request_list.size();
8517 _request_list.push_back(r);
8518 }
8519
8520 // implicit unlock()
8521 }
8522
8523 /* add request in buffer structure */
8524 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8525 if (status != BM_SUCCESS)
8526 return status;
8527
8528 return BM_SUCCESS;
8529}
8530
8531/********************************************************************/
8540INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8541 if (rpc_is_remote())
8542 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8543
8544#ifdef LOCAL_ROUTINES
8545 {
8546 int status = 0;
8547
8548 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8549
8550 if (!pbuf)
8551 return status;
8552
8553 /* lock buffer */
8555
8556 if (!pbuf_guard.is_locked())
8557 return pbuf_guard.get_status();
8558
8559 INT i, deleted;
8560
8561 /* get a pointer to the proper client structure */
8563
8564 /* check all requests and set to zero if matching */
8565 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8566 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8567 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8568 deleted++;
8569 }
8570
8571 /* calculate new max_request_index entry */
8572 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8573 if (pclient->event_request[i].valid)
8574 break;
8575
8576 pclient->max_request_index = i + 1;
8577
8578 /* calculate new all_flag */
8579 pclient->all_flag = FALSE;
8580
8581 for (i = 0; i < pclient->max_request_index; i++)
8582 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8583 pclient->all_flag = TRUE;
8584 break;
8585 }
8586
8587 pbuf->get_all_flag = pclient->all_flag;
8588
8589 if (!deleted)
8590 return BM_NOT_FOUND;
8591 }
8592#endif /* LOCAL_ROUTINES */
8593
8594 return BM_SUCCESS;
8595}
8596
8597/********************************************************************/
8607{
8608 _request_list_mutex.lock();
8609
8610 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8611 _request_list_mutex.unlock();
8612 return BM_INVALID_HANDLE;
8613 }
8614
8615 int buffer_handle = _request_list[request_id].buffer_handle;
8616
8617 _request_list[request_id].clear();
8618
8619 _request_list_mutex.unlock();
8620
8621 /* remove request entry from buffer */
8622 return bm_remove_event_request(buffer_handle, request_id);
8623}
8624
8625#if 0 // currently not used
8626static void bm_show_pointers(const BUFFER_HEADER * pheader)
8627{
8628 int i;
8629 const BUFFER_CLIENT *pclient;
8630
8631 pclient = pheader->client;
8632
8633 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8634 pheader->write_pointer, pheader->size);
8635 for (i = 0; i < pheader->max_client_index; i++)
8636 if (pclient[i].pid) {
8637 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8638 }
8639
8640 printf("done\n");
8641}
8642#endif
8643
8645 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8646 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8647
8648 if (pheader->read_pointer <= pheader->write_pointer) {
8649
8650 if (pclient->read_pointer < pheader->read_pointer) {
8651 cm_msg(MINFO, "bm_validate_client_pointers",
8652 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8653 pclient->name,
8654 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8655
8656 pclient->read_pointer = pheader->read_pointer;
8657 }
8658
8659 if (pclient->read_pointer > pheader->write_pointer) {
8660 cm_msg(MINFO, "bm_validate_client_pointers",
8661 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8662 pclient->name,
8663 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8664
8665 pclient->read_pointer = pheader->write_pointer;
8666 }
8667
8668 } else {
8669
8670 if (pclient->read_pointer < 0) {
8671 cm_msg(MINFO, "bm_validate_client_pointers",
8672 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8673 pclient->name,
8674 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8675
8676 pclient->read_pointer = pheader->read_pointer;
8677 }
8678
8679 if (pclient->read_pointer >= pheader->size) {
8680 cm_msg(MINFO, "bm_validate_client_pointers",
8681 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8682 pclient->name,
8683 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8684
8685 pclient->read_pointer = pheader->read_pointer;
8686 }
8687
8688 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8689 cm_msg(MINFO, "bm_validate_client_pointers",
8690 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8691 pclient->name,
8692 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8693
8694 pclient->read_pointer = pheader->read_pointer;
8695 }
8696 }
8697}
8698
8699#if 0 // currently not used
8700static void bm_validate_pointers(BUFFER_HEADER * pheader)
8701{
8702 BUFFER_CLIENT *pclient = pheader->client;
8703 int i;
8704
8705 for (i = 0; i < pheader->max_client_index; i++)
8706 if (pclient[i].pid) {
8708 }
8709}
8710#endif
8711
8712//
8713// Buffer pointers
8714//
8715// normal:
8716//
8717// zero -->
8718// ... free space
8719// read_pointer -->
8720// client1 rp -->
8721// client2 rp -->
8722// ... buffered data
8723// write_pointer -->
8724// ... free space
8725// pheader->size -->
8726//
8727// inverted:
8728//
8729// zero -->
8730// client3 rp -->
8731// ... buffered data
8732// client4 rp -->
8733// write_pointer -->
8734// ... free space
8735// read_pointer -->
8736// client1 rp -->
8737// client2 rp -->
8738// ... buffered data
8739// pheader->size -->
8740//
8741
8743 assert(caller_name);
8744
8745 /* calculate global read pointer as "minimum" of client read pointers */
8746 int min_rp = pheader->write_pointer;
8747
8748 int i;
8749 for (i = 0; i < pheader->max_client_index; i++) {
8750 BUFFER_CLIENT *pc = pheader->client + i;
8751 if (pc->pid) {
8753
8754#if 0
8755 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8756 pheader->name,
8757 pheader->read_pointer,
8758 pheader->write_pointer,
8759 pheader->size,
8760 min_rp,
8761 pc->name,
8762 pc->read_pointer);
8763#endif
8764
8765 if (pheader->read_pointer <= pheader->write_pointer) {
8766 // normal pointers
8767 if (pc->read_pointer < min_rp)
8768 min_rp = pc->read_pointer;
8769 } else {
8770 // inverted pointers
8771 if (pc->read_pointer <= pheader->write_pointer) {
8772 // clients 3 and 4
8773 if (pc->read_pointer < min_rp)
8774 min_rp = pc->read_pointer;
8775 } else {
8776 // clients 1 and 2
8777 int xptr = pc->read_pointer - pheader->size;
8778 if (xptr < min_rp)
8779 min_rp = xptr;
8780 }
8781 }
8782 }
8783 }
8784
8785 if (min_rp < 0)
8786 min_rp += pheader->size;
8787
8788 assert(min_rp >= 0);
8789 assert(min_rp < pheader->size);
8790
8791 if (min_rp == pheader->read_pointer) {
8792 return FALSE;
8793 }
8794
8795#if 0
8796 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8797 pheader->name,
8798 pheader->read_pointer,
8799 pheader->write_pointer,
8800 pheader->size,
8801 min_rp);
8802#endif
8803
8804 pheader->read_pointer = min_rp;
8805
8806 return TRUE;
8807}
8808
8809static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8810 int i;
8811 int have_get_all_requests = 0;
8812
8813 for (i = 0; i < pc->max_request_index; i++)
8814 if (pc->event_request[i].valid)
8815 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8816
8817 /* only GET_ALL requests actually free space in the event buffer */
8819 return;
8820
8821 /*
8822 If read pointer has been changed, it may have freed up some space
8823 for waiting producers. So check if free space is now more than 50%
8824 of the buffer size and wake waiting producers.
8825 */
8826
8827 int free_space = pc->read_pointer - pheader->write_pointer;
8828 if (free_space <= 0)
8829 free_space += pheader->size;
8830
8831 if (free_space >= pheader->size * 0.5) {
8832 for (i = 0; i < pheader->max_client_index; i++) {
8833 const BUFFER_CLIENT *pc = pheader->client + i;
8834 if (pc->pid && pc->write_wait) {
8835 BOOL send_wakeup = (pc->write_wait < free_space);
8836 //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);
8837 if (send_wakeup) {
8838 ss_resume(pc->port, "B ");
8839 }
8840 }
8841 }
8842 }
8843}
8844
8845static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8846{
8847 _request_list_mutex.lock();
8848 bool locked = true;
8849 size_t n = _request_list.size();
8850 /* call dispatcher */
8851 for (size_t i = 0; i < n; i++) {
8852 if (!locked) {
8853 _request_list_mutex.lock();
8854 locked = true;
8855 }
8857 if (r.buffer_handle != buffer_handle)
8858 continue;
8859 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8860 continue;
8861 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8862 _request_list_mutex.unlock();
8863 locked = false;
8864 /* if event is fragmented, call defragmenter */
8865 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))) {
8866 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8867 } else {
8868 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8869 }
8870 }
8871 if (locked)
8872 _request_list_mutex.unlock();
8873}
8874
8875#ifdef LOCAL_ROUTINES
8876
8877static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8878 /* increment read cache read pointer */
8879 pbuf->read_cache_rp += total_size;
8880
8881 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8882 pbuf->read_cache_rp = 0;
8883 pbuf->read_cache_wp = 0;
8884 }
8885}
8886
8888{
8889 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8890 return FALSE;
8891
8892 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8893 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8894 int total_size = ALIGN8(event_size);
8895
8896 if (ppevent)
8897 *ppevent = pevent;
8898 if (pevent_size)
8900 if (ptotal_size)
8901 *ptotal_size = total_size;
8902
8903 return TRUE;
8904}
8905
8906//
8907// return values:
8908// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8909// BM_ASYNC_RETURN - buffer is empty
8910// BM_CORRUPTED - buffer is corrupted
8911//
8912
8914{
8915 if (pc->read_pointer == pheader->write_pointer) {
8916 /* no more events buffered for this client */
8917 if (!pc->read_wait) {
8918 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8919 pc->read_wait = TRUE;
8920 }
8921 return BM_ASYNC_RETURN;
8922 }
8923
8924 if (pc->read_wait) {
8925 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8926 pc->read_wait = FALSE;
8927 }
8928
8929 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8930 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);
8931 return BM_CORRUPTED;
8932 }
8933
8934 char *pdata = (char *) (pheader + 1);
8935
8936 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8937 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8938 int total_size = ALIGN8(event_size);
8939
8940 if ((total_size <= 0) || (total_size > pheader->size)) {
8941 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);
8942 return BM_CORRUPTED;
8943 }
8944
8945 assert(total_size > 0);
8946 assert(total_size <= pheader->size);
8947
8948 if (ppevent)
8949 *ppevent = pevent;
8950 if (pevent_size)
8952 if (ptotal_size)
8953 *ptotal_size = total_size;
8954
8955 return BM_SUCCESS;
8956}
8957
8958static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8959{
8960 const char *pdata = (const char *) (pheader + 1);
8961
8962 if (rp + event_size <= pheader->size) {
8963 /* copy event to cache */
8964 memcpy(buf, pdata + rp, event_size);
8965 } else {
8966 /* event is splitted */
8967 int size = pheader->size - rp;
8968 memcpy(buf, pdata + rp, size);
8969 memcpy(buf + size, pdata, event_size - size);
8970 }
8971}
8972
8973static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8974{
8975 const char *pdata = (const char *) (pheader + 1);
8976
8977 if (rp + event_size <= pheader->size) {
8978 /* copy event to cache */
8979 vecptr->assign(pdata + rp, pdata + rp + event_size);
8980 } else {
8981 /* event is splitted */
8982 int size = pheader->size - rp;
8983 vecptr->assign(pdata + rp, pdata + rp + size);
8984 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8985 }
8986}
8987
8988static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8989
8991 int i;
8992 for (i = 0; i < pc->max_request_index; i++) {
8993 const EVENT_REQUEST *prequest = pc->event_request + i;
8994 if (prequest->valid) {
8995 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8996 /* check if this is a recent event */
8997 if (prequest->sampling_type == GET_RECENT) {
8998 if (ss_time() - pevent->time_stamp > 1) {
8999 /* skip that event */
9000 continue;
9001 }
9002 }
9003
9005 break;
9006 }
9007 }
9008 }
9009 return is_requested;
9010}
9011
9013
9015{
9016 BUFFER* pbuf = pbuf_guard.get_pbuf();
9017 BUFFER_HEADER* pheader = pbuf->buffer_header;
9020
9021 //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);
9022
9023 /* loop over all events in the buffer */
9024
9025 while (1) {
9026 EVENT_HEADER *pevent = NULL;
9027 int event_size = 3; // poison value
9028 int total_size = 3; // poison value
9029
9030 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9031 if (status == BM_CORRUPTED) {
9032 return status;
9033 } else if (status != BM_SUCCESS) {
9034 /* event buffer is empty */
9035 if (timeout_msec == BM_NO_WAIT) {
9036 if (need_wakeup)
9038 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9039 // read cache is empty
9040 return BM_ASYNC_RETURN;
9041 }
9042 return BM_SUCCESS;
9043 }
9044
9046
9047 if (status != BM_SUCCESS) {
9048 // we only come here with SS_ABORT & co
9049 return status;
9050 }
9051
9052 // make sure we wait for new event only once
9054 // go back to bm_peek_buffer_locked
9055 continue;
9056 }
9057
9058 /* loop over all requests: if this event matches a request,
9059 * copy it to the read cache */
9060
9062
9063 if (is_requested) {
9064 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9065 /* read cache is full */
9066 if (need_wakeup)
9068 return BM_SUCCESS;
9069 }
9070
9071 bm_read_from_buffer_locked(pheader, pc->read_pointer, pbuf->read_cache + pbuf->read_cache_wp, event_size);
9072
9073 pbuf->read_cache_wp += total_size;
9074
9075 /* update statistics */
9076 pheader->num_out_events++;
9077 pbuf->count_read++;
9078 pbuf->bytes_read += event_size;
9079 }
9080
9081 /* shift read pointer */
9082
9083 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9084 pc->read_pointer = new_read_pointer;
9085
9086 need_wakeup = TRUE;
9087 }
9088 /* NOT REACHED */
9089}
9090
9091static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9092 /* now convert event header */
9093 if (convert_flags) {
9094 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9095 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9096 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9097 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9098 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9099 }
9100}
9101
9103{
9104 // return values:
9105 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9106 // BM_CORRUPTED - shared memory is corrupted
9107 // BM_NO_MEMORY - asked for more than buffer size
9108 // BM_ASYNC_RETURN - timeout waiting for free space
9109 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9110 // SS_ABORT - we are told to shutdown (locks releases)
9111
9112 int status;
9113 BUFFER* pbuf = pbuf_guard.get_pbuf();
9114 BUFFER_HEADER *pheader = pbuf->buffer_header;
9115 char *pdata = (char *) (pheader + 1);
9116
9117 /* make sure the buffer never completely full:
9118 * read pointer and write pointer would coincide
9119 * and the code cannot tell if it means the
9120 * buffer is 100% full or 100% empty. It will explode
9121 * or lose events */
9122 requested_space += 100;
9123
9124 if (requested_space >= pheader->size)
9125 return BM_NO_MEMORY;
9126
9129
9130 //DWORD blocking_time = 0;
9131 //int blocking_loops = 0;
9132 int blocking_client_index = -1;
9134 blocking_client_name[0] = 0;
9135
9136 while (1) {
9137 while (1) {
9138 /* check if enough space in buffer */
9139
9140 int free = pheader->read_pointer - pheader->write_pointer;
9141 if (free <= 0)
9142 free += pheader->size;
9143
9144 //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);
9145
9146 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9147 //if (blocking_loops) {
9148 // DWORD wait_time = ss_millitime() - blocking_time;
9149 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9150 //}
9151
9152 if (pbuf->wait_start_time != 0) {
9154 DWORD wait_time = now - pbuf->wait_start_time;
9155 pbuf->time_write_wait += wait_time;
9156 pbuf->wait_start_time = 0;
9157 int iclient = pbuf->wait_client_index;
9158 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9159 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9160 pbuf->client_count_write_wait[iclient] += 1;
9161 pbuf->client_time_write_wait[iclient] += wait_time;
9162 }
9163 }
9164
9165 //if (blocking_loops > 0) {
9166 // 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);
9167 //}
9168
9169 return BM_SUCCESS;
9170 }
9171
9172 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9173 cm_msg(MERROR, "bm_wait_for_free_space",
9174 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9175 pheader->name,
9176 pheader->read_pointer,
9177 pheader->write_pointer,
9178 pheader->size,
9179 free,
9181 return BM_CORRUPTED;
9182 }
9183
9184 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9185 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9186 int total_size = ALIGN8(event_size);
9187
9188#if 0
9189 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);
9190#endif
9191
9192 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9193 cm_msg(MERROR, "bm_wait_for_free_space",
9194 "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",
9195 pheader->name,
9196 pheader->read_pointer,
9197 pheader->write_pointer,
9198 pheader->size,
9199 free,
9201 pevent->data_size,
9202 event_size,
9203 total_size);
9204 return BM_CORRUPTED;
9205 }
9206
9207 int blocking_client = -1;
9208
9209 int i;
9210 for (i = 0; i < pheader->max_client_index; i++) {
9211 BUFFER_CLIENT *pc = pheader->client + i;
9212 if (pc->pid) {
9213 if (pc->read_pointer == pheader->read_pointer) {
9214 /*
9215 First assume that the client with the "minimum" read pointer
9216 is not really blocking due to a GET_ALL request.
9217 */
9219 //int blocking_request_id = -1;
9220
9221 int j;
9222 for (j = 0; j < pc->max_request_index; j++) {
9223 const EVENT_REQUEST *prequest = pc->event_request + j;
9224 if (prequest->valid
9225 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9226 if (prequest->sampling_type & GET_ALL) {
9227 blocking = TRUE;
9228 //blocking_request_id = prequest->id;
9229 break;
9230 }
9231 }
9232 }
9233
9234 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9235
9236 if (blocking) {
9238 break;
9239 }
9240
9241 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9242 }
9243 }
9244 } /* client loop */
9245
9246 if (blocking_client >= 0) {
9249 //if (!blocking_time) {
9250 // blocking_time = ss_millitime();
9251 //}
9252
9253 //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);
9254
9255 // from this "break" we go into timeout check and sleep/wait.
9256 break;
9257 }
9258
9259 /* no blocking clients. move the read pointer and again check for free space */
9260
9261 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9262
9263 if (!moved) {
9264 cm_msg(MERROR, "bm_wait_for_free_space",
9265 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer did not move as expected",
9266 pheader->name,
9267 pheader->read_pointer,
9268 pheader->write_pointer,
9269 pheader->size,
9270 free,
9272 return BM_CORRUPTED;
9273 }
9274
9275 /* we freed one event, loop back to the check for free space */
9276 }
9277
9278 //blocking_loops++;
9279
9280 /* at least one client is blocking */
9281
9283 pc->write_wait = requested_space;
9284
9285 if (pbuf->wait_start_time == 0) {
9286 pbuf->wait_start_time = ss_millitime();
9287 pbuf->count_write_wait++;
9288 if (requested_space > pbuf->max_requested_space)
9289 pbuf->max_requested_space = requested_space;
9290 pbuf->wait_client_index = blocking_client_index;
9291 }
9292
9294
9295 //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);
9296
9297 int sleep_time_msec = 1000;
9298
9299 if (timeout_msec == BM_WAIT) {
9300 // wait forever
9301 } else if (timeout_msec == BM_NO_WAIT) {
9302 // no wait
9303 return BM_ASYNC_RETURN;
9304 } else {
9305 // check timeout
9306 if (now >= time_end) {
9307 // timeout!
9308 return BM_ASYNC_RETURN;
9309 }
9310
9312
9313 if (sleep_time_msec <= 0) {
9314 sleep_time_msec = 10;
9315 } else if (sleep_time_msec > 1000) {
9316 sleep_time_msec = 1000;
9317 }
9318 }
9319
9321
9322 /* before waiting, unlock everything in the correct order */
9323
9324 pbuf_guard.unlock();
9325
9327 pbuf->write_cache_mutex.unlock();
9328
9329 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9330
9331#ifdef DEBUG_MSG
9332 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9333#endif
9334
9336 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9337 //if (idx >= 0)
9338 // pheader->client[idx].write_wait = requested_space;
9339
9340 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9341
9343
9344 /* we are told to shutdown */
9345 if (status == SS_ABORT) {
9346 // NB: buffer is locked!
9347 return SS_ABORT;
9348 }
9349
9350 /* make sure we do sleep in this loop:
9351 * if we are the mserver receiving data on the event
9352 * socket and the data buffer is full, ss_suspend() will
9353 * never sleep: it will detect data on the event channel,
9354 * call rpc_server_receive() (recursively, we already *are* in
9355 * rpc_server_receive()) and return without sleeping. Result
9356 * is a busy loop waiting for free space in data buffer */
9357
9358 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9359 * the event socket, and should sleep now, so this sleep below
9360 * maybe is not needed now. but for safety, I keep it. K.O. */
9361
9362 if (status != SS_TIMEOUT) {
9363 //printf("ss_suspend: status %d\n", status);
9364 ss_sleep(1);
9365 }
9366
9367 /* we may be stuck in this loop for an arbitrary long time,
9368 * depending on how other buffer clients read the accumulated data
9369 * so we should update all the timeouts & etc. K.O. */
9370
9372
9373 /* lock things again in the correct order */
9374
9375 if (unlock_write_cache) {
9377
9378 if (status != BM_SUCCESS) {
9379 // bail out with all locks released
9380 return status;
9381 }
9382 }
9383
9384 if (!pbuf_guard.relock()) {
9385 if (unlock_write_cache) {
9386 pbuf->write_cache_mutex.unlock();
9387 }
9388
9389 // bail out with all locks released
9390 return pbuf_guard.get_status();
9391 }
9392
9393 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9395
9396 pc->write_wait = 0;
9397
9399 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9400 //if (idx >= 0)
9401 // pheader->client[idx].write_wait = 0;
9402 //else {
9403 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9404 // status = SS_ABORT;
9405 //}
9406
9407#ifdef DEBUG_MSG
9408 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);
9409#endif
9410
9411 }
9412}
9413
9415{
9416 BUFFER* pbuf = pbuf_guard.get_pbuf();
9417 BUFFER_HEADER* pheader = pbuf->buffer_header;
9418
9419 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9420
9421 if (pc->read_pointer != pheader->write_pointer) {
9422 // buffer has data
9423 return BM_SUCCESS;
9424 }
9425
9426 if (timeout_msec == BM_NO_WAIT) {
9427 /* event buffer is empty and we are told to not wait */
9428 if (!pc->read_wait) {
9429 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9430 pc->read_wait = TRUE;
9431 }
9432 return BM_ASYNC_RETURN;
9433 }
9434
9437 DWORD sleep_time = 1000;
9438 if (timeout_msec == BM_NO_WAIT) {
9439 // default sleep time
9440 } else if (timeout_msec == BM_WAIT) {
9441 // default sleep time
9442 } else {
9445 }
9446
9447 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9448
9449 while (pc->read_pointer == pheader->write_pointer) {
9450 /* wait until there is data in the buffer (write pointer moves) */
9451
9452 if (!pc->read_wait) {
9453 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9454 pc->read_wait = TRUE;
9455 }
9456
9457 pc->last_activity = ss_millitime();
9458
9460
9461 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9462
9463 pbuf_guard.unlock();
9464
9466 pbuf->read_cache_mutex.unlock();
9467
9469
9470 if (timeout_msec == BM_NO_WAIT) {
9471 // return immediately
9472 } else if (timeout_msec == BM_WAIT) {
9473 // wait forever
9474 } else {
9476 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9477 if (now >= time_wait) {
9478 timeout_msec = BM_NO_WAIT; // cause immediate return
9479 } else {
9481 if (sleep_time > 1000)
9482 sleep_time = 1000;
9483 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9484 }
9485 }
9486
9487 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9488
9489 if (unlock_read_cache) {
9491 if (status != BM_SUCCESS) {
9492 // bail out with all locks released
9493 return status;
9494 }
9495 }
9496
9497 if (!pbuf_guard.relock()) {
9498 if (unlock_read_cache) {
9499 pbuf->read_cache_mutex.unlock();
9500 }
9501 // bail out with all locks released
9502 return pbuf_guard.get_status();
9503 }
9504
9505 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9506 * because we may have been removed from the buffer by bm_cleanup() & co
9507 * due to a timeout or whatever. */
9509
9510 /* return if TCP connection broken */
9511 if (status == SS_ABORT)
9512 return SS_ABORT;
9513
9514 if (timeout_msec == BM_NO_WAIT)
9515 return BM_ASYNC_RETURN;
9516 }
9517
9518 if (pc->read_wait) {
9519 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9520 pc->read_wait = FALSE;
9521 }
9522
9523 return BM_SUCCESS;
9524}
9525
9526static 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)
9527{
9528 char *pdata = (char *) (pheader + 1);
9529
9530 //int old_write_pointer = pheader->write_pointer;
9531
9532 /* new event fits into the remaining space? */
9533 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9534 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9535 char* wptr = pdata + pheader->write_pointer;
9536 for (int i=0; i<sg_n; i++) {
9537 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9538 memcpy(wptr, sg_ptr[i], sg_len[i]);
9539 wptr += sg_len[i];
9540 }
9541 pheader->write_pointer = pheader->write_pointer + total_size;
9542 assert(pheader->write_pointer <= pheader->size);
9543 /* remaining space is smaller than size of an event header? */
9544 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9545 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9546 // equal to the event header size, we will write the next event header here,
9547 // then wrap the pointer and write the event data at the beginning of the buffer.
9548 //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);
9549 pheader->write_pointer = 0;
9550 }
9551 } else {
9552 /* split event */
9553 size_t size = pheader->size - pheader->write_pointer;
9554
9555 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9556
9557 //memcpy(pdata + pheader->write_pointer, pevent, size);
9558 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9559
9560 char* wptr = pdata + pheader->write_pointer;
9561 size_t count = 0;
9562
9563 // copy first part
9564
9565 int i = 0;
9566 for (; i<sg_n; i++) {
9567 if (count + sg_len[i] > size)
9568 break;
9569 memcpy(wptr, sg_ptr[i], sg_len[i]);
9570 wptr += sg_len[i];
9571 count += sg_len[i];
9572 }
9573
9574 //printf("wptr %d, count %d\n", wptr-pdata, count);
9575
9576 // split segment
9577
9578 size_t first = size - count;
9579 size_t second = sg_len[i] - first;
9580 assert(first + second == sg_len[i]);
9581 assert(count + first == size);
9582
9583 //printf("first %d, second %d\n", first, second);
9584
9585 memcpy(wptr, sg_ptr[i], first);
9586 wptr = pdata + 0;
9587 count += first;
9589 wptr += second;
9590 count += second;
9591 i++;
9592
9593 // copy remaining
9594
9595 for (; i<sg_n; i++) {
9596 memcpy(wptr, sg_ptr[i], sg_len[i]);
9597 wptr += sg_len[i];
9598 count += sg_len[i];
9599 }
9600
9601 //printf("wptr %d, count %d\n", wptr-pdata, count);
9602
9603 //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);
9604
9605 pheader->write_pointer = total_size - size;
9606 }
9607
9608 //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);
9609}
9610
9612 if (pc->pid) {
9613 int j;
9614 for (j = 0; j < pc->max_request_index; j++) {
9615 const EVENT_REQUEST *prequest = pc->event_request + j;
9616 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9617 return prequest->id;
9618 }
9619 }
9620 }
9621
9622 return -1;
9623}
9624
9625static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9626 if (request_id >= 0) {
9627 /* if that client has a request and is suspended, wake it up */
9628 if (pc->read_wait) {
9629 char str[80];
9630 sprintf(str, "B %s %d", pheader->name, request_id);
9631 ss_resume(pc->port, str);
9632 //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);
9633 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9634 pc->read_wait = FALSE;
9635 }
9636 }
9637}
9638
9639#endif // LOCAL_ROUTINES
9640
9641#if 0
9642INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9643{
9644 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9645
9648
9650
9651 while (1) {
9652 if (timeout_msec == BM_WAIT) {
9653 xtimeout_msec = 1000;
9654 } else if (timeout_msec == BM_NO_WAIT) {
9656 } else {
9657 if (xtimeout_msec > 1000) {
9658 xtimeout_msec = 1000;
9659 }
9660 }
9661
9662 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9663
9664 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9665
9666 if (status == BM_ASYNC_RETURN) {
9667 if (timeout_msec == BM_WAIT) {
9668 // BM_WAIT means wait forever
9669 continue;
9670 } else if (timeout_msec == BM_NO_WAIT) {
9671 // BM_NO_WAIT means do not wait
9672 return status;
9673 } else {
9675 if (now >= time_end) {
9676 // timeout, return BM_ASYNC_RETURN
9677 return status;
9678 }
9679
9681
9682 if (remain < xtimeout_msec) {
9684 }
9685
9686 // keep asking for event...
9687 continue;
9688 }
9689 } else if (status == BM_SUCCESS) {
9690 // success, return BM_SUCCESS
9691 return status;
9692 } else {
9693 // error
9694 return status;
9695 }
9696 }
9697}
9698#endif
9699
9700INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9701{
9702 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9703 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9704
9705 if (data_size == 0) {
9706 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9707 return BM_INVALID_SIZE;
9708 }
9709
9710 if (data_size > MAX_DATA_SIZE) {
9711 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);
9712 return BM_INVALID_SIZE;
9713 }
9714
9715 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9716
9717 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9718
9719 if (rpc_is_remote()) {
9720 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9721 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9722 } else {
9723 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9724 }
9725}
9726
9727int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9728{
9729 const char* cptr = event.data();
9730 size_t clen = event.size();
9731 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9732}
9733
9734int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9735{
9736 int sg_n = event.size();
9737 const char* sg_ptr[sg_n];
9738 size_t sg_len[sg_n];
9739 for (int i=0; i<sg_n; i++) {
9740 sg_ptr[i] = event[i].data();
9741 sg_len[i] = event[i].size();
9742 }
9743 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9744}
9745
9746#ifdef LOCAL_ROUTINES
9748#endif
9749
9750/********************************************************************/
9800int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9801{
9802 if (rpc_is_remote())
9803 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9804
9805 if (sg_n < 1) {
9806 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9807 return BM_INVALID_SIZE;
9808 }
9809
9810 if (sg_ptr[0] == NULL) {
9811 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9812 return BM_INVALID_SIZE;
9813 }
9814
9815 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9816 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));
9817 return BM_INVALID_SIZE;
9818 }
9819
9820 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9821
9822 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9823 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9824
9825 if (data_size == 0) {
9826 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9827 return BM_INVALID_SIZE;
9828 }
9829
9830 if (data_size > MAX_DATA_SIZE) {
9831 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);
9832 return BM_INVALID_SIZE;
9833 }
9834
9835 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9836
9837 size_t count = 0;
9838 for (int i=0; i<sg_n; i++) {
9839 count += sg_len[i];
9840 }
9841
9842 if (count != event_size) {
9843 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);
9844 return BM_INVALID_SIZE;
9845 }
9846
9847 //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);
9848
9849#ifdef LOCAL_ROUTINES
9850 {
9851 int status = 0;
9852 const size_t total_size = ALIGN8(event_size);
9853
9854 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9855
9856 if (!pbuf)
9857 return status;
9858
9859 /* round up total_size to next DWORD boundary */
9860 //int total_size = ALIGN8(event_size);
9861
9862 /* check if write cache is enabled */
9863 if (pbuf->write_cache_size) {
9865
9866 if (status != BM_SUCCESS)
9867 return status;
9868
9869 /* check if write cache is enabled */
9870 if (pbuf->write_cache_size) {
9871 size_t max_event_size = pbuf->write_cache_size/MAX_WRITE_CACHE_EVENT_SIZE_DIV;
9873
9874 //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);
9875
9876 /* if this event does not fit into the write cache, flush the write cache */
9877 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9878 //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);
9879
9881
9882 if (!pbuf_guard.is_locked()) {
9883 pbuf->write_cache_mutex.unlock();
9884 return pbuf_guard.get_status();
9885 }
9886
9888
9889 if (pbuf_guard.is_locked()) {
9890 // check if bm_wait_for_free_space() failed to relock the buffer
9891 pbuf_guard.unlock();
9892 }
9893
9894 if (status != BM_SUCCESS) {
9895 pbuf->write_cache_mutex.unlock();
9896 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9897 if (status == BM_NO_MEMORY)
9898 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9899 return status;
9900 }
9901
9902 // write cache must be empty here
9903 assert(pbuf->write_cache_wp == 0);
9904 }
9905
9906 /* write this event into the write cache, if it is not too big and if it fits */
9907 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9908 //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);
9909
9910 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9911
9912 for (int i=0; i<sg_n; i++) {
9913 memcpy(wptr, sg_ptr[i], sg_len[i]);
9914 wptr += sg_len[i];
9915 }
9916
9917 pbuf->write_cache_wp += total_size;
9918
9919 pbuf->write_cache_mutex.unlock();
9920 return BM_SUCCESS;
9921 }
9922 }
9923
9924 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9925 pbuf->write_cache_mutex.unlock();
9926 }
9927
9928 /* we come here only for events that are too big to fit into the cache */
9929
9930 /* lock the buffer */
9932
9933 if (!pbuf_guard.is_locked()) {
9934 return pbuf_guard.get_status();
9935 }
9936
9937 /* calculate some shorthands */
9938 BUFFER_HEADER *pheader = pbuf->buffer_header;
9939
9940#if 0
9942 if (status != BM_SUCCESS) {
9943 printf("bm_send_event: corrupted 111!\n");
9944 abort();
9945 }
9946#endif
9947
9948 /* check if buffer is large enough */
9949 if (total_size >= (size_t)pheader->size) {
9950 pbuf_guard.unlock(); // unlock before cm_msg()
9951 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9952 return BM_NO_MEMORY;
9953 }
9954
9956
9957 if (status != BM_SUCCESS) {
9958 // implicit unlock
9959 return status;
9960 }
9961
9962#if 0
9964 if (status != BM_SUCCESS) {
9965 printf("bm_send_event: corrupted 222!\n");
9966 abort();
9967 }
9968#endif
9969
9970 int old_write_pointer = pheader->write_pointer;
9971
9972 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9973
9974 /* write pointer was incremented, but there should
9975 * always be some free space in the buffer and the
9976 * write pointer should never cacth up to the read pointer:
9977 * the rest of the code gets confused this happens (buffer 100% full)
9978 * as it is write_pointer == read_pointer can be either
9979 * 100% full or 100% empty. My solution: never fill
9980 * the buffer to 100% */
9981 assert(pheader->write_pointer != pheader->read_pointer);
9982
9983 /* send wake up messages to all clients that want this event */
9984 int i;
9985 for (i = 0; i < pheader->max_client_index; i++) {
9986 BUFFER_CLIENT *pc = pheader->client + i;
9987 int request_id = bm_find_first_request_locked(pc, pevent);
9988 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9989 }
9990
9991#if 0
9993 if (status != BM_SUCCESS) {
9994 printf("bm_send_event: corrupted 333!\n");
9995 abort();
9996 }
9997#endif
9998
9999 /* update statistics */
10000 pheader->num_in_events++;
10001 pbuf->count_sent += 1;
10002 pbuf->bytes_sent += total_size;
10003 }
10004#endif /* LOCAL_ROUTINES */
10005
10006 return BM_SUCCESS;
10007}
10008
10009static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
10010{
10011 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10012
10015
10017
10018 while (1) {
10019 if (timeout_msec == BM_WAIT) {
10020 xtimeout_msec = 1000;
10021 } else if (timeout_msec == BM_NO_WAIT) {
10023 } else {
10024 if (xtimeout_msec > 1000) {
10025 xtimeout_msec = 1000;
10026 }
10027 }
10028
10029 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10030
10031 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10032
10033 if (status == BM_ASYNC_RETURN) {
10034 if (timeout_msec == BM_WAIT) {
10035 // BM_WAIT means wait forever
10036 continue;
10037 } else if (timeout_msec == BM_NO_WAIT) {
10038 // BM_NO_WAIT means do not wait
10039 return status;
10040 } else {
10042 if (now >= time_end) {
10043 // timeout, return BM_ASYNC_RETURN
10044 return status;
10045 }
10046
10048
10049 if (remain < (DWORD)xtimeout_msec) {
10051 }
10052
10053 // keep asking for event...
10054 continue;
10055 }
10056 } else if (status == BM_SUCCESS) {
10057 // success, return BM_SUCCESS
10058 return status;
10059 } else {
10060 // error
10061 return status;
10062 }
10063 }
10064}
10065
10066/********************************************************************/
10084#ifdef LOCAL_ROUTINES
10086{
10087 // NB we come here with write cache locked and buffer locked.
10088
10089 {
10090 INT status = 0;
10091
10092 //printf("bm_flush_cache_locked!\n");
10093
10094 BUFFER* pbuf = pbuf_guard.get_pbuf();
10095 BUFFER_HEADER* pheader = pbuf->buffer_header;
10096
10097 //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);
10098
10099 int old_write_pointer = pheader->write_pointer;
10100
10101 int request_id[MAX_CLIENTS];
10102 for (int i = 0; i < pheader->max_client_index; i++) {
10103 request_id[i] = -1;
10104 }
10105
10106 size_t ask_rp = pbuf->write_cache_rp;
10107 size_t ask_wp = pbuf->write_cache_wp;
10108
10109 if (ask_wp == 0) { // nothing to do
10110 return BM_SUCCESS;
10111 }
10112
10113 if (ask_rp == ask_wp) { // nothing to do
10114 return BM_SUCCESS;
10115 }
10116
10117 assert(ask_rp < ask_wp);
10118
10119 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10120
10121 if (ask_free == 0) { // nothing to do
10122 return BM_SUCCESS;
10123 }
10124
10125#if 0
10127 if (status != BM_SUCCESS) {
10128 printf("bm_flush_cache: corrupted 111!\n");
10129 abort();
10130 }
10131#endif
10132
10134
10135 if (status != BM_SUCCESS) {
10136 return status;
10137 }
10138
10139 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10140 //
10141 // wait_for_free_space() will sleep with all locks released,
10142 // during this time, another thread may call bm_send_event() that will
10143 // add one or more events to the write cache and after wait_for_free_space()
10144 // returns, size of data in cache will be bigger than the amount
10145 // of free space we requested. so we need to keep track of how
10146 // much data we write to the buffer and ask for more data
10147 // if we run short. This is the reason for the big loop
10148 // around wait_for_free_space(). We ask for slightly too little free
10149 // space to make sure all this code is always used and does work. K.O.
10150
10151 if (pbuf->write_cache_wp == 0) {
10152 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10153 return BM_SUCCESS;
10154 }
10155
10156 //size_t written = 0;
10157 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10158 /* loop over all events in cache */
10159
10160 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10161 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10162 size_t total_size = ALIGN8(event_size);
10163
10164#if 0
10165 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",
10166 int(pbuf->write_cache_size),
10167 int(pbuf->write_cache_wp),
10168 int(pbuf->write_cache_rp),
10169 int(pevent->data_size),
10170 int(event_size),
10171 int(total_size),
10172 int(ask_free),
10173 int(written));
10174#endif
10175
10176 // check for crazy event size
10177 assert(total_size >= sizeof(EVENT_HEADER));
10178 assert(total_size <= (size_t)pheader->size);
10179
10180 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10181
10182 /* update statistics */
10183 pheader->num_in_events++;
10184 pbuf->count_sent += 1;
10185 pbuf->bytes_sent += total_size;
10186
10187 /* see comment for the same code in bm_send_event().
10188 * We make sure the buffer is never 100% full */
10189 assert(pheader->write_pointer != pheader->read_pointer);
10190
10191 /* check if anybody has a request for this event */
10192 for (int i = 0; i < pheader->max_client_index; i++) {
10193 BUFFER_CLIENT *pc = pheader->client + i;
10194 int r = bm_find_first_request_locked(pc, pevent);
10195 if (r >= 0) {
10196 request_id[i] = r;
10197 }
10198 }
10199
10200 /* this loop does not loop forever because rp
10201 * is monotonously incremented here. write_cache_wp does
10202 * not change */
10203
10204 pbuf->write_cache_rp += total_size;
10205 //written += total_size;
10206
10207 assert(pbuf->write_cache_rp > 0);
10208 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10209 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10210 }
10211
10212 /* the write cache is now empty */
10213 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10214 pbuf->write_cache_wp = 0;
10215 pbuf->write_cache_rp = 0;
10216
10217 /* check which clients are waiting */
10218 for (int i = 0; i < pheader->max_client_index; i++) {
10219 BUFFER_CLIENT *pc = pheader->client + i;
10220 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10221 }
10222 }
10223
10224 return BM_SUCCESS;
10225}
10226
10227#endif /* LOCAL_ROUTINES */
10228
10229INT bm_flush_cache(int buffer_handle, int timeout_msec)
10230{
10231 if (rpc_is_remote()) {
10232 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10233 }
10234
10235#ifdef LOCAL_ROUTINES
10236 {
10237 INT status = 0;
10238
10239 //printf("bm_flush_cache!\n");
10240
10241 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10242
10243 if (!pbuf)
10244 return status;
10245
10246 if (pbuf->write_cache_size == 0)
10247 return BM_SUCCESS;
10248
10250
10251 if (status != BM_SUCCESS)
10252 return status;
10253
10254 /* check if anything needs to be flushed */
10255 if (pbuf->write_cache_wp == 0) {
10256 pbuf->write_cache_mutex.unlock();
10257 return BM_SUCCESS;
10258 }
10259
10260 /* lock the buffer */
10262
10263 if (!pbuf_guard.is_locked())
10264 return pbuf_guard.get_status();
10265
10267
10268 /* unlock in correct order */
10269
10270 if (pbuf_guard.is_locked()) {
10271 // check if bm_wait_for_free_space() failed to relock the buffer
10272 pbuf_guard.unlock();
10273 }
10274
10275 pbuf->write_cache_mutex.unlock();
10276
10277 return status;
10278 }
10279#endif /* LOCAL_ROUTINES */
10280
10281 return BM_SUCCESS;
10282}
10283
10284#ifdef LOCAL_ROUTINES
10285
10286static 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) {
10288
10289 int max_size = 0;
10290 if (buf_size) {
10291 max_size = *buf_size;
10292 *buf_size = 0;
10293 }
10294
10295 //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);
10296
10297 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10298
10299 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10300
10301 /* look if there is anything in the cache */
10302 if (pbuf->read_cache_size > 0) {
10303
10305
10306 if (status != BM_SUCCESS)
10307 return status;
10308
10309 if (pbuf->read_cache_wp == 0) {
10310
10311 // lock buffer for the first time
10312
10313 if (!pbuf_guard.relock()) {
10314 pbuf->read_cache_mutex.unlock();
10315 return pbuf_guard.get_status();
10316 }
10317
10319 if (status != BM_SUCCESS) {
10320 // unlock in correct order
10321 if (pbuf_guard.is_locked()) {
10322 // check if bm_wait_for_more_events() failed to relock the buffer
10323 pbuf_guard.unlock();
10324 }
10325 pbuf->read_cache_mutex.unlock();
10326 return status;
10327 }
10328
10329 // buffer remains locked here
10330 }
10331 EVENT_HEADER *pevent;
10332 int event_size;
10333 int total_size;
10334 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10335 if (pbuf_guard.is_locked()) {
10336 // do not need to keep the event buffer locked
10337 // when reading from the read cache
10338 pbuf_guard.unlock();
10339 }
10340 //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);
10342 if (buf) {
10343 if (event_size > max_size) {
10344 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);
10347 }
10348
10349 memcpy(buf, pevent, event_size);
10350
10351 if (buf_size) {
10352 *buf_size = event_size;
10353 }
10354 if (convert_flags) {
10355 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10356 }
10357 } else if (bufptr) {
10359 memcpy(*bufptr, pevent, event_size);
10361 } else if (vecptr) {
10362 vecptr->resize(0);
10363 char* cptr = (char*)pevent;
10364 vecptr->assign(cptr, cptr+event_size);
10365 }
10366 bm_incr_read_cache_locked(pbuf, total_size);
10367 pbuf->read_cache_mutex.unlock();
10368 if (dispatch) {
10369 // FIXME need to protect currently dispatched event against
10370 // another thread overwriting it by refilling the read cache
10371 bm_dispatch_event(buffer_handle, pevent);
10372 return BM_MORE_EVENTS;
10373 }
10374 // buffer is unlocked here
10375 return status;
10376 }
10377 pbuf->read_cache_mutex.unlock();
10378 }
10379
10380 /* we come here if the read cache is disabled */
10381 /* we come here if the next event is too big to fit into the read cache */
10382
10383 if (!pbuf_guard.is_locked()) {
10384 if (!pbuf_guard.relock())
10385 return pbuf_guard.get_status();
10386 }
10387
10389
10390 BUFFER_HEADER *pheader = pbuf->buffer_header;
10391
10393
10394 while (1) {
10395 /* loop over events in the event buffer */
10396
10398
10399 if (status != BM_SUCCESS) {
10400 // implicit unlock
10401 return status;
10402 }
10403
10404 /* check if event at current read pointer matches a request */
10405
10406 EVENT_HEADER *pevent;
10407 int event_size;
10408 int total_size;
10409
10410 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10411 if (status == BM_CORRUPTED) {
10412 // implicit unlock
10413 return status;
10414 } else if (status != BM_SUCCESS) {
10415 /* event buffer is empty */
10416 break;
10417 }
10418
10420
10421 if (is_requested) {
10422 //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);
10423
10425
10426 if (buf) {
10427 if (event_size > max_size) {
10428 cm_msg(MERROR, "bm_read_buffer",
10429 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10430 event_size, pheader->name);
10433 }
10434
10435 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10436
10437 if (buf_size) {
10438 *buf_size = event_size;
10439 }
10440
10441 if (convert_flags) {
10442 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10443 }
10444
10445 pbuf->count_read++;
10446 pbuf->bytes_read += event_size;
10447 } else if (dispatch || bufptr) {
10448 assert(event_buffer == NULL); // make sure we only come here once
10451 pbuf->count_read++;
10452 pbuf->bytes_read += event_size;
10453 } else if (vecptr) {
10455 pbuf->count_read++;
10456 pbuf->bytes_read += event_size;
10457 }
10458
10459 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10460 pc->read_pointer = new_read_pointer;
10461
10462 pheader->num_out_events++;
10463 /* exit loop over events */
10464 break;
10465 }
10466
10467 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10468 pc->read_pointer = new_read_pointer;
10469 pheader->num_out_events++;
10470 }
10471
10472 /*
10473 If read pointer has been changed, it may have freed up some space
10474 for waiting producers. So check if free space is now more than 50%
10475 of the buffer size and wake waiting producers.
10476 */
10477
10479
10480 pbuf_guard.unlock();
10481
10482 if (dispatch && event_buffer) {
10483 bm_dispatch_event(buffer_handle, event_buffer);
10484 free(event_buffer);
10486 return BM_MORE_EVENTS;
10487 }
10488
10489 if (bufptr && event_buffer) {
10493 }
10494
10495 if (event_buffer) {
10496 free(event_buffer);
10498 }
10499
10500 return status;
10501}
10502
10503#endif
10504
10505static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10506{
10507 //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);
10508
10509 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10510
10511 void *xbuf = NULL;
10512 int xbuf_size = 0;
10513
10514 if (buf) {
10515 xbuf = buf;
10516 xbuf_size = *buf_size;
10517 } else if (ppevent) {
10520 } else if (pvec) {
10521 pvec->resize(_bm_max_event_size);
10522 xbuf = pvec->data();
10523 xbuf_size = pvec->size();
10524 } else {
10525 assert(!"incorrect call to bm_receivent_event_rpc()");
10526 }
10527
10528 int status;
10531
10533
10534 int zbuf_size = xbuf_size;
10535
10536 while (1) {
10537 if (timeout_msec == BM_WAIT) {
10538 xtimeout_msec = 1000;
10539 } else if (timeout_msec == BM_NO_WAIT) {
10541 } else {
10542 if (xtimeout_msec > 1000) {
10543 xtimeout_msec = 1000;
10544 }
10545 }
10546
10548
10550
10551 //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);
10552
10553 if (status == BM_ASYNC_RETURN) {
10554 if (timeout_msec == BM_WAIT) {
10555 // BM_WAIT means wait forever
10556 continue;
10557 } else if (timeout_msec == BM_NO_WAIT) {
10558 // BM_NO_WAIT means do not wait
10559 break;
10560 } else {
10562 if (now >= time_end) {
10563 // timeout, return BM_ASYNC_RETURN
10564 break;
10565 }
10566
10568
10569 if (remain < (DWORD)xtimeout_msec) {
10571 }
10572
10573 // keep asking for event...
10574 continue;
10575 }
10576 } else if (status == BM_SUCCESS) {
10577 // success, return BM_SUCCESS
10578 break;
10579 }
10580
10581 // RPC error
10582
10583 if (buf) {
10584 *buf_size = 0;
10585 } else if (ppevent) {
10586 free(*ppevent);
10587 *ppevent = NULL;
10588 } else if (pvec) {
10589 pvec->resize(0);
10590 } else {
10591 assert(!"incorrect call to bm_receivent_event_rpc()");
10592 }
10593
10594 return status;
10595 }
10596
10597 // status is BM_SUCCESS or BM_ASYNC_RETURN
10598
10599 if (buf) {
10600 *buf_size = zbuf_size;
10601 } else if (ppevent) {
10602 // nothing to do
10603 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10604 } else if (pvec) {
10605 pvec->resize(zbuf_size);
10606 } else {
10607 assert(!"incorrect call to bm_receivent_event_rpc()");
10608 }
10609
10610 return status;
10611}
10612
10613/********************************************************************/
10672INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10673 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10674 if (rpc_is_remote()) {
10675 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10676 }
10677#ifdef LOCAL_ROUTINES
10678 {
10680
10681 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10682
10683 if (!pbuf)
10684 return status;
10685
10686 int convert_flags = rpc_get_convert_flags();
10687
10688 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10689 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10690 return status;
10691 }
10692#else /* LOCAL_ROUTINES */
10693
10694 return BM_SUCCESS;
10695#endif
10696}
10697
10698/********************************************************************/
10754 if (rpc_is_remote()) {
10755 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10756 }
10757#ifdef LOCAL_ROUTINES
10758 {
10760
10761 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10762
10763 if (!pbuf)
10764 return status;
10765
10766 int convert_flags = rpc_get_convert_flags();
10767
10768 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10769 }
10770#else /* LOCAL_ROUTINES */
10771
10772 return BM_SUCCESS;
10773#endif
10774}
10775
10776/********************************************************************/
10831INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10832 if (rpc_is_remote()) {
10833 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10834 }
10835#ifdef LOCAL_ROUTINES
10836 {
10838
10839 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10840
10841 if (!pbuf)
10842 return status;
10843
10844 int convert_flags = rpc_get_convert_flags();
10845
10846 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10847 }
10848#else /* LOCAL_ROUTINES */
10849 return BM_SUCCESS;
10850#endif
10851}
10852
10853#ifdef LOCAL_ROUTINES
10854
10856{
10857 /* clear read cache */
10858 if (pbuf->read_cache_size > 0) {
10859
10861
10862 if (status != BM_SUCCESS)
10863 return status;
10864
10865 pbuf->read_cache_rp = 0;
10866 pbuf->read_cache_wp = 0;
10867
10868 pbuf->read_cache_mutex.unlock();
10869 }
10870
10872
10873 if (!pbuf_guard.is_locked())
10874 return pbuf_guard.get_status();
10875
10876 BUFFER_HEADER *pheader = pbuf->buffer_header;
10877
10878 /* forward read pointer to global write pointer */
10880 pclient->read_pointer = pheader->write_pointer;
10881
10882 return BM_SUCCESS;
10883}
10884
10885#endif /* LOCAL_ROUTINES */
10886
10887/********************************************************************/
10896INT bm_skip_event(INT buffer_handle) {
10897 if (rpc_is_remote())
10898 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
10899
10900#ifdef LOCAL_ROUTINES
10901 {
10902 int status = 0;
10903
10904 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
10905
10906 if (!pbuf)
10907 return status;
10908
10909 return bm_skip_event(pbuf);
10910 }
10911#endif
10912
10913 return BM_SUCCESS;
10914}
10915
10916#ifdef LOCAL_ROUTINES
10917/********************************************************************/
10924static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
10925 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
10926
10927 /* return immediately if no callback routine is defined */
10928 if (!pbuf->callback)
10929 return BM_SUCCESS;
10930
10931 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
10932}
10933
10934/********************************************************************/
10940static INT bm_push_event(const char *buffer_name)
10941{
10942 std::vector<BUFFER*> mybuffers;
10943
10944 gBuffersMutex.lock();
10946 gBuffersMutex.unlock();
10947
10948 for (size_t i = 0; i < mybuffers.size(); i++) {
10949 BUFFER *pbuf = mybuffers[i];
10950 if (!pbuf || !pbuf->attached)
10951 continue;
10952 // FIXME: unlocked read access to pbuf->buffer_name!
10953 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
10954 return bm_push_buffer(pbuf, i + 1);
10955 }
10956 }
10957
10958 return BM_INVALID_HANDLE;
10959}
10960
10961#else
10962
10963static INT bm_push_event(const char *buffer_name)
10964{
10965 return BM_SUCCESS;
10966}
10967
10968#endif /* LOCAL_ROUTINES */
10969
10970/********************************************************************/
10977#ifdef LOCAL_ROUTINES
10978 {
10979 INT status = 0;
10980 BOOL bMore;
10981 DWORD start_time;
10982 //static DWORD last_time = 0;
10983
10984 /* if running as a server, buffer checking is done by client
10985 via ASYNC bm_receive_event */
10986 if (rpc_is_mserver()) {
10987 return FALSE;
10988 }
10989
10990 bMore = FALSE;
10991 start_time = ss_millitime();
10992
10993 std::vector<BUFFER*> mybuffers;
10994
10995 gBuffersMutex.lock();
10997 gBuffersMutex.unlock();
10998
10999 /* go through all buffers */
11000 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11002
11003 if (!pbuf || !pbuf->attached)
11004 continue;
11005
11006 //int count_loops = 0;
11007 while (1) {
11008 if (pbuf->attached) {
11009 /* one bm_push_event could cause a run stop and a buffer close, which
11010 * would crash the next call to bm_push_event(). So check for valid
11011 * buffer on each call */
11012
11013 /* this is what happens:
11014 * bm_push_buffer() may call a user callback function
11015 * user callback function may indirectly call bm_close() of this buffer,
11016 * i.e. if it stops the run,
11017 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11018 * here we will see pbuf->attched is false and quit this loop
11019 */
11020
11021 status = bm_push_buffer(pbuf, idx + 1);
11022
11023 if (status == BM_CORRUPTED) {
11024 return status;
11025 }
11026
11027 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11028
11029 if (status != BM_MORE_EVENTS) {
11030 //DWORD t = ss_millitime() - start_time;
11031 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11032 break;
11033 }
11034
11035 // count_loops++;
11036 }
11037
11038 // NB: this code has a logic error: if 2 buffers always have data,
11039 // this timeout will cause us to exit reading the 1st buffer
11040 // after 1000 msec, then we read the 2nd buffer exactly once,
11041 // and exit the loop because the timeout is still active -
11042 // we did not reset "start_time" when we started reading
11043 // from the 2nd buffer. Result is that we always read all
11044 // the data in a loop from the 1st buffer, but read just
11045 // one event from the 2nd buffer, resulting in severe unfairness.
11046
11047 /* stop after one second */
11048 DWORD t = ss_millitime() - start_time;
11049 if (t > 1000) {
11050 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11051 bMore = TRUE;
11052 break;
11053 }
11054 }
11055 }
11056
11057 //last_time = start_time;
11058
11059 return bMore;
11060
11061 }
11062#else /* LOCAL_ROUTINES */
11063
11064 return FALSE;
11065
11066#endif
11067}
11068
11069/********************************************************************/
11071/********************************************************************\
11072
11073 Routine: bm_notify_client
11074
11075 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11076 the connected client. Used by mserver to relay the BM_MSG
11077 buffer message from local UDP socket to the remote connected client.
11078
11079 Input:
11080 char *buffer_name Name of buffer
11081 int client_socket Network socket to client
11082
11083 Output:
11084 none
11085
11086 Function value:
11087 BM_SUCCESS Successful completion
11088
11089\********************************************************************/
11090{
11091 static DWORD last_time = 0;
11093
11094 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11095
11096 BUFFER* fbuf = NULL;
11097
11098 gBuffersMutex.lock();
11099
11100 for (size_t i = 0; i < gBuffers.size(); i++) {
11101 BUFFER* pbuf = gBuffers[i];
11102 if (!pbuf || !pbuf->attached)
11103 continue;
11104 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11105 fbuf = pbuf;
11106 break;
11107 }
11108 }
11109
11110 gBuffersMutex.unlock();
11111
11112 if (!fbuf)
11113 return BM_INVALID_HANDLE;
11114
11115 /* don't send notification if client has no callback defined
11116 to receive events -> client calls bm_receive_event manually */
11117 if (!fbuf->callback)
11118 return DB_SUCCESS;
11119
11120 int convert_flags = rpc_get_convert_flags();
11121
11122 /* only send notification once each 500ms */
11123 if (now - last_time < 500)
11124 return DB_SUCCESS;
11125
11126 last_time = now;
11127
11128 char buffer[32];
11129 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11130
11131 nc->header.routine_id = MSG_BM;
11132 nc->header.param_size = 0;
11133
11134 if (convert_flags) {
11137 }
11138
11139 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11140
11141 /* send the update notification to the client */
11142 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11143
11144 return BM_SUCCESS;
11145}
11146
11147/********************************************************************/
11149/********************************************************************\
11150
11151 Routine: bm_poll_event
11152
11153 Purpose: Poll an event from a remote server. Gets called by
11154 rpc_client_dispatch() and by cm_yield()
11155
11156 Function value:
11157 BM_SUCCESS At least one event was received and dispatched
11158 BM_ASYNC_RETURN No events received
11159 SS_ABORT Network connection broken
11160
11161\********************************************************************/
11162{
11164
11165 //printf("bm_poll_event!\n");
11166
11167 DWORD start_time = ss_millitime();
11168
11169 std::vector<char> vec;
11170
11171 /* loop over all requests */
11172 _request_list_mutex.lock();
11173 bool locked = true;
11174 size_t n = _request_list.size();
11175 for (size_t i = 0; i < n; i++) {
11176 if (!locked) {
11177 _request_list_mutex.lock();
11178 locked = true;
11179 }
11180 /* continue if no dispatcher set (manual bm_receive_event) */
11181 if (_request_list[i].dispatcher == NULL)
11182 continue;
11183
11184 int buffer_handle = _request_list[i].buffer_handle;
11185
11186 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11187 _request_list_mutex.unlock();
11188 locked = false;
11189
11190 do {
11191 /* receive event */
11192 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11193
11194 //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());
11195
11196 /* call user function if successful */
11197 if (status == BM_SUCCESS) {
11198 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11200 }
11201
11202 /* break if no more events */
11203 if (status == BM_ASYNC_RETURN)
11204 break;
11205
11206 /* break if corrupted event buffer */
11207 if (status == BM_TRUNCATED) {
11208 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());
11209 }
11210
11211 /* break if corrupted event buffer */
11212 if (status == BM_CORRUPTED)
11213 return SS_ABORT;
11214
11215 /* break if server died */
11216 if (status == RPC_NET_ERROR) {
11217 return SS_ABORT;
11218 }
11219
11220 /* stop after one second */
11221 if (ss_millitime() - start_time > 1000) {
11222 break;
11223 }
11224
11225 } while (TRUE);
11226 }
11227
11228 if (locked)
11229 _request_list_mutex.unlock();
11230
11232 return BM_SUCCESS;
11233 else
11234 return BM_ASYNC_RETURN;
11235}
11236
11237/********************************************************************/
11263 if (rpc_is_remote())
11265
11266#ifdef LOCAL_ROUTINES
11267 {
11268 std::vector<BUFFER*> mybuffers;
11269
11270 gBuffersMutex.lock();
11272 gBuffersMutex.unlock();
11273
11274 /* go through all buffers */
11275 for (BUFFER* pbuf : mybuffers) {
11276 if (!pbuf)
11277 continue;
11278 if (!pbuf->attached)
11279 continue;
11280
11281 int status = bm_skip_event(pbuf);
11282 if (status != BM_SUCCESS)
11283 return status;
11284 }
11285 }
11286#endif /* LOCAL_ROUTINES */
11287
11288 return BM_SUCCESS;
11289}
11290
11292#ifndef DOXYGEN_SHOULD_SKIP_THIS
11293
11294#define MAX_DEFRAG_EVENTS 10
11295
11302
11304
11305/********************************************************************/
11306static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11307 EVENT_HEADER *pevent, void *pdata,
11308 EVENT_HANDLER *dispatcher)
11309/********************************************************************\
11310
11311 Routine: bm_defragment_event
11312
11313 Purpose: Called internally from the event receiving routines
11314 bm_push_event and bm_poll_event to recombine event
11315 fragments and call the user callback routine upon
11316 completion.
11317
11318 Input:
11319 HNDLE buffer_handle Handle for the buffer containing event
11320 HNDLE request_id Handle for event request
11321 EVENT_HEADER *pevent Pointer to event header
11322 void *pata Pointer to event data
11323 dispatcher() User callback routine
11324
11325 Output:
11326 <calls dispatcher() after successfull recombination of event>
11327
11328 Function value:
11329 void
11330
11331\********************************************************************/
11332{
11333 INT i;
11334
11335 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11336 /*---- start new event ----*/
11337
11338 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11339
11340 /* check if fragments already stored */
11341 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11342 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11343 break;
11344
11345 if (i < MAX_DEFRAG_EVENTS) {
11346 free(defrag_buffer[i].pevent);
11349 cm_msg(MERROR, "bm_defragement_event",
11350 "Received new event with ID %d while old fragments were not completed",
11351 (pevent->event_id & 0x0FFF));
11352 }
11353
11354 /* search new slot */
11355 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11356 if (defrag_buffer[i].event_id == 0)
11357 break;
11358
11359 if (i == MAX_DEFRAG_EVENTS) {
11360 cm_msg(MERROR, "bm_defragment_event",
11361 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11362 return;
11363 }
11364
11365 /* check event size */
11366 if (pevent->data_size != sizeof(DWORD)) {
11367 cm_msg(MERROR, "bm_defragment_event",
11368 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11369 pevent->data_size, (int) sizeof(DWORD));
11370 return;
11371 }
11372
11373 /* setup defragment buffer */
11374 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11378
11379 if (defrag_buffer[i].pevent == NULL) {
11381 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11382 return;
11383 }
11384
11385 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11388
11389 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11390 // pevent->serial_number, defrag_buffer[i].data_size);
11391
11392 return;
11393 }
11394
11395 /* search buffer for that event */
11396 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11397 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11398 break;
11399
11400 if (i == MAX_DEFRAG_EVENTS) {
11401 /* no buffer available -> no first fragment received */
11402 cm_msg(MERROR, "bm_defragement_event",
11403 "Received fragment without first fragment (ID %d) Ser#:%d",
11404 pevent->event_id & 0x0FFF, pevent->serial_number);
11405 return;
11406 }
11407
11408 /* add fragment to buffer */
11410 free(defrag_buffer[i].pevent);
11413 cm_msg(MERROR, "bm_defragement_event",
11414 "Received fragments with more data (%d) than event size (%d)",
11415 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11416 return;
11417 }
11418
11419 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11420 defrag_buffer[i].received, pdata, pevent->data_size);
11421
11422 defrag_buffer[i].received += pevent->data_size;
11423
11424 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11425 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11426
11427 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11428 /* event complete */
11429 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11430 free(defrag_buffer[i].pevent);
11433 }
11434}
11435
11437#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11438
/* end of bmfunctionc */
11441
11447/********************************************************************\
11448* *
11449* RPC functions *
11450* *
11451\********************************************************************/
11452
11454{
11455public:
11456 std::atomic_bool connected{false}; /* socket is connected */
11457 std::string host_name; /* server name */
11458 int port = 0; /* server port */
11459 std::mutex mutex; /* connection lock */
11460 int index = 0; /* index in the connection array */
11461 std::string client_name; /* name of remote client */
11462 int send_sock = 0; /* tcp socket */
11463 int remote_hw_type = 0; /* remote hardware type */
11464 int rpc_timeout = 0; /* timeout in milliseconds */
11465
11466 void print() {
11467 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11468 index,
11469 client_name.c_str(),
11470 host_name.c_str(),
11471 port,
11472 send_sock,
11473 int(connected),
11474 rpc_timeout);
11475 }
11476
11478 if (send_sock > 0) {
11480 }
11481 connected = false;
11482 }
11483};
11484
11485/* globals */
11486
11487//
11488// locking rules for client connections:
11489//
11490// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11491// 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
11492// lock individual connection, check c->connected, work on the connection, unlock connection
11493//
11494// ok to access without locking client connection:
11495//
11496// - c->connected (std::atomic, but must recheck it after taking the lock)
11497// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11498//
11499// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11500// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11501//
11502// lifetime of client connections:
11503//
11504// - client connection slots are allocated by rpc_client_connect()
11505// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11506// - client slots marked NULL are free and will be reused by rpc_client_connect()
11507// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11508// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11509// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11510// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11511// client call will see an empty slot and return an error
11512// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11513// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11514// only when one is sure no other threads are running concurrent rpc client calls.
11515//
11516
11518static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11519
11520static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11521static bool _rpc_is_remote = false;
11522
11523//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11524static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11525static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11526
11528{
11529 assert(idx >= 0);
11530 assert(idx < (int)_server_acceptions.size());
11531 assert(_server_acceptions[idx] != NULL);
11532 return _server_acceptions[idx];
11533}
11534
11539
11541{
11542 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11543 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11544 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11545 return _server_acceptions[idx];
11546 }
11547 }
11548
11550
11551 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11552 if (_server_acceptions[idx] == NULL) {
11553 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11554 _server_acceptions[idx] = sa;
11555 return _server_acceptions[idx];
11556 }
11557 }
11558
11559 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11560 _server_acceptions.push_back(sa);
11561
11562 return sa;
11563}
11564
11566{
11567 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11568
11569 if (is_mserver) {
11570 assert(_mserver_acception == this);
11572 is_mserver = false;
11573 }
11574
11575 /* close server connection */
11576 if (recv_sock)
11578 if (send_sock)
11580 if (event_sock)
11582
11583 /* free TCP cache */
11584 if (net_buffer) {
11585 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11586 free(net_buffer);
11587 net_buffer = NULL;
11588 net_buffer_size = 0;
11589 }
11590
11591 /* mark this entry as invalid */
11592 clear();
11593}
11594
11595static std::vector<RPC_LIST> rpc_list;
11596static std::mutex rpc_list_mutex;
11597
11599
11600
11601/********************************************************************\
11602* conversion functions *
11603\********************************************************************/
11604
11605void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11606 *convert_flags = 0;
11607
11608 /* big/little endian conversion */
11609 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11610 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11611 && (hw_type & DRI_BIG_ENDIAN)))
11612 *convert_flags |= CF_ENDIAN;
11613
11614 /* float conversion between IEEE and VAX G */
11615 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11616 *convert_flags |= CF_VAX2IEEE;
11617
11618 /* float conversion between VAX G and IEEE */
11619 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11620 *convert_flags |= CF_IEEE2VAX;
11621
11623 //if (remote_hw_type & DR_ASCII)
11624 // *convert_flags |= CF_ASCII;
11625}
11626
11627/********************************************************************/
11631
11632/********************************************************************/
11634 unsigned short int lo, hi;
11635
11636 /* swap hi and lo word */
11637 lo = *((short int *) (var) + 1);
11638 hi = *((short int *) (var));
11639
11640 /* correct exponent */
11641 if (lo != 0)
11642 lo += 0x100;
11643
11644 *((short int *) (var) + 1) = hi;
11645 *((short int *) (var)) = lo;
11646}
11647
11649 unsigned short int lo, hi;
11650
11651 /* swap hi and lo word */
11652 lo = *((short int *) (var) + 1);
11653 hi = *((short int *) (var));
11654
11655 /* correct exponent */
11656 if (hi != 0)
11657 hi -= 0x100;
11658
11659 *((short int *) (var) + 1) = hi;
11660 *((short int *) (var)) = lo;
11661
11662}
11663
11665 unsigned short int i1, i2, i3, i4;
11666
11667 /* swap words */
11668 i1 = *((short int *) (var) + 3);
11669 i2 = *((short int *) (var) + 2);
11670 i3 = *((short int *) (var) + 1);
11671 i4 = *((short int *) (var));
11672
11673 /* correct exponent */
11674 if (i4 != 0)
11675 i4 -= 0x20;
11676
11677 *((short int *) (var) + 3) = i4;
11678 *((short int *) (var) + 2) = i3;
11679 *((short int *) (var) + 1) = i2;
11680 *((short int *) (var)) = i1;
11681}
11682
11684 unsigned short int i1, i2, i3, i4;
11685
11686 /* swap words */
11687 i1 = *((short int *) (var) + 3);
11688 i2 = *((short int *) (var) + 2);
11689 i3 = *((short int *) (var) + 1);
11690 i4 = *((short int *) (var));
11691
11692 /* correct exponent */
11693 if (i1 != 0)
11694 i1 += 0x20;
11695
11696 *((short int *) (var) + 3) = i4;
11697 *((short int *) (var) + 2) = i3;
11698 *((short int *) (var) + 1) = i2;
11699 *((short int *) (var)) = i1;
11700}
11701
11702/********************************************************************/
11703void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11704
11705 if (convert_flags & CF_ENDIAN) {
11706 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11707 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11708 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11709 }
11710
11711 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11712 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11713 if (tid == TID_FLOAT)
11714 rpc_ieee2vax_float((float *) data);
11715 if (tid == TID_DOUBLE)
11716 rpc_ieee2vax_double((double *) data);
11717 }
11718
11719 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11720 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11721 if (tid == TID_FLOAT)
11722 rpc_vax2ieee_float((float *) data);
11723 if (tid == TID_DOUBLE)
11724 rpc_vax2ieee_double((double *) data);
11725 }
11726}
11727
11728void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11729/********************************************************************\
11730
11731 Routine: rpc_convert_data
11732
11733 Purpose: Convert data format between differenct computers
11734
11735 Input:
11736 void *data Pointer to data
11737 INT tid Type ID of data, one of TID_xxx
11738 INT flags Combination of following flags:
11739 RPC_IN: data is input parameter
11740 RPC_OUT: data is output variable
11741 RPC_FIXARRAY, RPC_VARARRAY: data is array
11742 of "size" bytes (see next param.)
11743 RPC_OUTGOING: data is outgoing
11744 INT total_size Size of bytes of data. Used for variable
11745 length arrays.
11746 INT convert_flags Flags for data conversion
11747
11748 Output:
11749 void *data Is converted according to _convert_flag
11750 value
11751
11752 Function value:
11753 RPC_SUCCESS Successful completion
11754
11755\********************************************************************/
11756{
11757 /* convert array */
11758 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11759 int single_size = rpc_tid_size(tid);
11760 /* don't convert TID_ARRAY & TID_STRUCT */
11761 if (single_size == 0)
11762 return;
11763
11764 int n = total_size / single_size;
11765
11766 for (int i = 0; i < n; i++) {
11767 char* p = (char *) data + (i * single_size);
11768 rpc_convert_single(p, tid, flags, convert_flags);
11769 }
11770 } else {
11771 rpc_convert_single(data, tid, flags, convert_flags);
11772 }
11773}
11774
11775/********************************************************************\
11776* type ID functions *
11777\********************************************************************/
11778
11780 if (id >= 0 && id < TID_LAST)
11781 return tid_size[id];
11782
11783 return 0;
11784}
11785
11786const char *rpc_tid_name(INT id) {
11787 if (id >= 0 && id < TID_LAST)
11788 return tid_name[id];
11789 else
11790 return "<unknown>";
11791}
11792
11793const char *rpc_tid_name_old(INT id) {
11794 if (id >= 0 && id < TID_LAST)
11795 return tid_name_old[id];
11796 else
11797 return "<unknown>";
11798}
11799
11800int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11801{
11802 for (int i=0; i<TID_LAST; i++) {
11803 if (strcmp(name, tid_name[i]) == 0)
11804 return i;
11805 }
11806
11807 for (int i=0; i<TID_LAST; i++) {
11808 if (strcmp(name, tid_name_old[i]) == 0)
11809 return i;
11810 }
11811
11812 return 0;
11813}
11814
11815/********************************************************************\
11816* client functions *
11817\********************************************************************/
11818
11819/********************************************************************/
11837
11838/********************************************************************/
11850{
11851 for (int i = 0; new_list[i].id != 0; i++) {
11852 /* check valid ID for user functions */
11853 if (new_list != rpc_get_internal_list(0) &&
11855 || new_list[i].id > RPC_MAX_ID)) {
11856 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11857 }
11858 }
11859
11860 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11861
11862 /* check double defined functions */
11863 for (int i = 0; new_list[i].id != 0; i++) {
11864 for (size_t j = 0; j < rpc_list.size(); j++) {
11865 if (rpc_list[j].id == new_list[i].id) {
11866 return RPC_DOUBLE_DEFINED;
11867 }
11868 }
11869 }
11870
11871 /* append new functions */
11872 for (int i = 0; new_list[i].id != 0; i++) {
11873 RPC_LIST e = new_list[i];
11874
11875 /* set default dispatcher */
11876 if (e.dispatch == NULL) {
11877 e.dispatch = func;
11878 }
11879
11880 rpc_list.push_back(e);
11881 }
11882
11883 return RPC_SUCCESS;
11884}
11885
11886
11887
11889#ifndef DOXYGEN_SHOULD_SKIP_THIS
11890
11891/********************************************************************/
11893/********************************************************************\
11894
11895 Routine: rpc_deregister_functions
11896
11897 Purpose: Free memory of previously registered functions
11898
11899 Input:
11900 none
11901
11902 Output:
11903 none
11904
11905 Function value:
11906 RPC_SUCCESS Successful completion
11907
11908\********************************************************************/
11909{
11910 rpc_list_mutex.lock();
11911 rpc_list.clear();
11912 rpc_list_mutex.unlock();
11913
11914 return RPC_SUCCESS;
11915}
11916
11917
11918/********************************************************************/
11919INT rpc_register_function(INT id, INT(*func)(INT, void **))
11920/********************************************************************\
11921
11922 Routine: rpc_register_function
11923
11924 Purpose: Replace a dispatch function for a specific rpc routine
11925
11926 Input:
11927 INT id RPC ID
11928 INT *func New dispatch function
11929
11930 Output:
11931 <implicit: func gets copied to rpc_list>
11932
11933 Function value:
11934 RPC_SUCCESS Successful completion
11935 RPC_INVALID_ID RPC ID not found
11936
11937\********************************************************************/
11938{
11939 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11940
11941 for (size_t i = 0; i < rpc_list.size(); i++) {
11942 if (rpc_list[i].id == id) {
11943 rpc_list[i].dispatch = func;
11944 return RPC_SUCCESS;
11945 }
11946 }
11947
11948 return RPC_INVALID_ID;
11949}
11950
11951/********************************************************************/
11952
11953static int handle_msg_odb(int n, const NET_COMMAND *nc) {
11954 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
11955 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
11956 /* update a changed record */
11957 HNDLE hDB = *((INT *) nc->param);
11958 HNDLE hKeyRoot = *((INT *) nc->param + 1);
11959 HNDLE hKey = *((INT *) nc->param + 2);
11960 int index = *((INT *) nc->param + 3);
11962 }
11963 return CM_VERSION_MISMATCH;
11964}
11965
11966/********************************************************************/
11968/********************************************************************\
11969
11970 Routine: rpc_client_dispatch
11971
11972 Purpose: Receive data from the mserver: watchdog and buffer notification messages
11973
11974\********************************************************************/
11975{
11976 INT status = 0;
11977 char net_buffer[256];
11978
11979 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11980 if (n <= 0)
11981 return SS_ABORT;
11982
11983 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
11984
11985 if (nc->header.routine_id == MSG_ODB) {
11986 status = handle_msg_odb(n, nc);
11987 } else if (nc->header.routine_id == MSG_WATCHDOG) {
11988 nc->header.routine_id = 1;
11989 nc->header.param_size = 0;
11990 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11992 } else if (nc->header.routine_id == MSG_BM) {
11994 struct timeval timeout;
11995
11996 //printf("rpc_client_dispatch: received MSG_BM!\n");
11997
11998 /* receive further messages to empty TCP queue */
11999 do {
12000 FD_ZERO(&readfds);
12001 FD_SET(sock, &readfds);
12002
12003 timeout.tv_sec = 0;
12004 timeout.tv_usec = 0;
12005
12006 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12007
12008 if (FD_ISSET(sock, &readfds)) {
12009 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12010 if (n <= 0)
12011 return SS_ABORT;
12012
12013 if (nc->header.routine_id == MSG_ODB) {
12014 status = handle_msg_odb(n, nc);
12015 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12016 nc->header.routine_id = 1;
12017 nc->header.param_size = 0;
12018 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12020 }
12021 }
12022
12023 } while (FD_ISSET(sock, &readfds));
12024
12025 /* poll event from server */
12027 }
12028
12029 return status;
12030}
12031
12032
12033/********************************************************************/
12034INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12035/********************************************************************\
12036
12037 Routine: rpc_client_connect
12038
12039 Purpose: Establish a network connection to a remote client
12040
12041 Input:
12042 char *host_name IP address of host to connect to.
12043 INT port TPC port to connect to.
12044 char *clinet_name Client program name
12045
12046 Output:
12047 HNDLE *hConnection Handle for new connection which can be used
12048 in future rpc_call(hConnection....) calls
12049
12050 Function value:
12051 RPC_SUCCESS Successful completion
12052 RPC_NET_ERROR Error in socket call
12053 RPC_NO_CONNECTION Maximum number of connections reached
12054 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12055
12056\********************************************************************/
12057{
12058 INT i, status;
12059 bool debug = false;
12060
12061 /* check if cm_connect_experiment was called */
12062 if (_client_name.length() == 0) {
12063 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12064 return RPC_NOT_REGISTERED;
12065 }
12066
12067 /* refuse connection to port 0 */
12068 if (port == 0) {
12069 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12070 return RPC_NET_ERROR;
12071 }
12072
12074
12075 static std::mutex gHostnameMutex;
12076
12077 {
12078 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12079
12080 if (debug) {
12081 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12082 for (size_t i = 0; i < _client_connections.size(); i++) {
12083 if (_client_connections[i]) {
12084 printf("client connection %d: ", (int)i);
12085 _client_connections[i]->print();
12086 printf("\n");
12087 }
12088 }
12089 }
12090
12091 // slot with index 0 is not used, fill it with a NULL
12092
12093 if (_client_connections.empty()) {
12094 _client_connections.push_back(NULL);
12095 }
12096
12097 bool hostname_locked = false;
12098
12099 /* check if connection already exists */
12100 for (size_t i = 1; i < _client_connections.size(); i++) {
12102 if (c && c->connected) {
12103
12104 if (!hostname_locked) {
12105 gHostnameMutex.lock();
12106 hostname_locked = true;
12107 }
12108
12109 if ((c->host_name == host_name) && (c->port == port)) {
12110 // NB: we must release the hostname lock before taking
12111 // c->mutex to avoid a locking order inversion deadlock:
12112 // later on we lock the hostname mutex while holding the c->mutex
12113 gHostnameMutex.unlock();
12114 hostname_locked = false;
12115 std::lock_guard<std::mutex> cguard(c->mutex);
12116 // check if socket is still connected
12117 if (c->connected) {
12118 // found connection slot with matching hostname and port number
12119 status = ss_socket_wait(c->send_sock, 0);
12120 if (status == SS_TIMEOUT) { // yes, still connected and empty
12121 // so reuse it connection
12122 *hConnection = c->index;
12123 if (debug) {
12124 printf("already connected: ");
12125 c->print();
12126 printf("\n");
12127 }
12128 // implicit unlock of c->mutex
12129 // gHostnameLock is not locked here
12130 return RPC_SUCCESS;
12131 }
12132 //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);
12133 c->close_locked();
12134 }
12135 // implicit unlock of c->mutex
12136 }
12137 }
12138 }
12139
12140 if (hostname_locked) {
12141 gHostnameMutex.unlock();
12142 hostname_locked = false;
12143 }
12144
12145 // only start reusing connections once we have
12146 // a good number of slots allocated.
12147 if (_client_connections.size() > 10) {
12148 static int last_reused = 0;
12149
12150 int size = _client_connections.size();
12151 for (int j = 1; j < size; j++) {
12152 int i = (last_reused + j) % size;
12153 if (_client_connections[i] && !_client_connections[i]->connected) {
12155 if (debug) {
12156 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12157 c->print();
12158 printf("\n");
12159 }
12160 last_reused = i;
12161 break;
12162 }
12163 }
12164 }
12165
12166 // no slots to reuse, allocate a new slot.
12167 if (!c) {
12169
12170 // if empty slot not found, add to end of array
12171 c->index = _client_connections.size();
12172 _client_connections.push_back(c);
12173
12174 if (debug) {
12175 printf("new connection appended to array: ");
12176 c->print();
12177 printf("\n");
12178 }
12179 }
12180
12181 c->mutex.lock();
12182 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12183
12184 // done with the array of connections
12185 // implicit unlock of _client_connections_mutex
12186 }
12187
12188 // locked connection slot for new connection
12189 assert(c != NULL);
12190
12191 std::string errmsg;
12192
12193 /* create a new socket for connecting to remote server */
12194 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12195 if (status != SS_SUCCESS) {
12196 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12197 c->mutex.unlock();
12198 return RPC_NET_ERROR;
12199 }
12200
12201 gHostnameMutex.lock();
12202
12203 c->host_name = host_name;
12204 c->port = port;
12205
12206 gHostnameMutex.unlock();
12207
12208 c->client_name = client_name;
12209 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12210
12211 /* set TCP_NODELAY option for better performance */
12212 i = 1;
12213 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12214
12215 /* send local computer info */
12216 std::string local_prog_name = rpc_get_name();
12217 std::string local_host_name = ss_gethostname();
12218
12219 int hw_type = rpc_get_hw_type();
12220
12221 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12222
12223 int size = cstr.length() + 1;
12224 i = send(c->send_sock, cstr.c_str(), size, 0);
12225 if (i < 0 || i != size) {
12226 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12227 c->mutex.unlock();
12228 return RPC_NET_ERROR;
12229 }
12230
12231 bool restore_watchdog_timeout = false;
12233 DWORD watchdog_timeout;
12234 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12235
12236 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12237
12238 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12241 }
12242
12243 char str[256];
12244
12245 /* receive remote computer info */
12246 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12247
12249 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12250 }
12251
12252 if (i <= 0) {
12253 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12254 c->close_locked();
12255 c->mutex.unlock();
12256 return RPC_NET_ERROR;
12257 }
12258
12259 int remote_hw_type = 0;
12260 char remote_version[32];
12261 remote_version[0] = 0;
12262 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12263
12264 c->remote_hw_type = remote_hw_type;
12265
12266 /* print warning if version patch level doesn't agree */
12267 char v1[32];
12268 mstrlcpy(v1, remote_version, sizeof(v1));
12269 if (strchr(v1, '.'))
12270 if (strchr(strchr(v1, '.') + 1, '.'))
12271 *strchr(strchr(v1, '.') + 1, '.') = 0;
12272
12273 mstrlcpy(str, cm_get_version(), sizeof(str));
12274 if (strchr(str, '.'))
12275 if (strchr(strchr(str, '.') + 1, '.'))
12276 *strchr(strchr(str, '.') + 1, '.') = 0;
12277
12278 if (strcmp(v1, str) != 0) {
12279 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12280 }
12281
12282 c->connected = true;
12283
12284 *hConnection = c->index;
12285
12286 c->mutex.unlock();
12287
12288 return RPC_SUCCESS;
12289}
12290
12291/********************************************************************/
12293/********************************************************************\
12294
12295 Routine: rpc_client_check
12296
12297 Purpose: Check all client connections if remote client closed link
12298
12299\********************************************************************/
12300{
12301#if 0
12302 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12303 if (_client_connection[i].send_sock != 0)
12304 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);
12305#endif
12306
12307 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12308
12309 /* check for broken connections */
12310 for (unsigned i = 0; i < _client_connections.size(); i++) {
12312 if (c && c->connected) {
12313 std::lock_guard<std::mutex> cguard(c->mutex);
12314
12315 if (!c->connected) {
12316 // implicit unlock
12317 continue;
12318 }
12319
12320 //printf("rpc_client_check: connection %d: ", i);
12321 //c->print();
12322 //printf("\n");
12323
12324 int ok = 0;
12325
12327 FD_ZERO(&readfds);
12328 FD_SET(c->send_sock, &readfds);
12329
12330 struct timeval timeout;
12331 timeout.tv_sec = 0;
12332 timeout.tv_usec = 0;
12333
12334 int status;
12335
12336#ifdef OS_WINNT
12337 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12338#else
12339 do {
12340 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12341 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12342#endif
12343
12344 if (!FD_ISSET(c->send_sock, &readfds)) {
12345 // implicit unlock
12346 continue;
12347 }
12348
12349 char buffer[64];
12350
12351 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12352 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12353
12354 if (status < 0) {
12355#ifndef OS_WINNT
12356 if (errno == EAGAIN) { // still connected
12357 ok = 1;
12358 } else
12359#endif
12360 {
12361 // connection error
12362 cm_msg(MERROR, "rpc_client_check",
12363 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12364 c->client_name.c_str(),
12365 c->host_name.c_str(),
12366 errno, strerror(errno));
12367 ok = 0;
12368 }
12369 } else if (status == 0) {
12370 // connection closed by remote end without sending an EXIT message
12371 // this can happen if the remote end has crashed, so this message
12372 // is still necessary as a useful diagnostic for unexpected crashes
12373 // of midas programs. K.O.
12374 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());
12375 ok = 0;
12376 } else {
12377 // read some data
12378 ok = 1;
12379 if (equal_ustring(buffer, "EXIT")) {
12380 /* normal exit */
12381 ok = 0;
12382 }
12383 }
12384
12385 if (!ok) {
12386 //printf("rpc_client_check: closing connection %d: ", i);
12387 //c->print();
12388 //printf("\n");
12389
12390 // connection lost, close the socket
12391 c->close_locked();
12392 }
12393
12394 // implicit unlock
12395 }
12396 }
12397
12398 // implicit unlock of _client_connections_mutex
12399}
12400
12401
12402/********************************************************************/
12403INT rpc_server_connect(const char *host_name, const char *exp_name)
12404/********************************************************************\
12405
12406 Routine: rpc_server_connect
12407
12408 Purpose: Extablish a network connection to a remote MIDAS
12409 server using a callback scheme.
12410
12411 Input:
12412 char *host_name IP address of host to connect to.
12413
12414 INT port TPC port to connect to.
12415
12416 char *exp_name Name of experiment to connect to. By using
12417 this name, several experiments (e.g. online
12418 DAQ and offline analysis) can run simultan-
12419 eously on the same host.
12420
12421 Output:
12422 none
12423
12424 Function value:
12425 RPC_SUCCESS Successful completion
12426 RPC_NET_ERROR Error in socket call
12427 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12428 CM_UNDEF_EXP Undefined experiment on server
12429
12430\********************************************************************/
12431{
12432 INT i, status;
12433 INT remote_hw_type, hw_type;
12434 char str[200], version[32], v1[32];
12436 struct timeval timeout;
12437 int port = MIDAS_TCP_PORT;
12438 char *s;
12439
12440#ifdef OS_WINNT
12441 {
12443
12444 /* Start windows sockets */
12445 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12446 return RPC_NET_ERROR;
12447 }
12448#endif
12449
12450 /* check if local connection */
12451 if (host_name[0] == 0)
12452 return RPC_SUCCESS;
12453
12454 /* register system functions */
12456
12457 /* check if cm_connect_experiment was called */
12458 if (_client_name.length() == 0) {
12459 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12460 return RPC_NOT_REGISTERED;
12461 }
12462
12463 /* check if connection already exists */
12465 return RPC_SUCCESS;
12466
12470
12471 bool listen_localhost = false;
12472
12473 if (strcmp(host_name, "localhost") == 0)
12474 listen_localhost = true;
12475
12476 int lsock1, lport1;
12477 int lsock2, lport2;
12478 int lsock3, lport3;
12479
12480 std::string errmsg;
12481
12483
12484 if (status != SS_SUCCESS) {
12485 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12486 return RPC_NET_ERROR;
12487 }
12488
12490
12491 if (status != SS_SUCCESS) {
12492 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12493 return RPC_NET_ERROR;
12494 }
12495
12497
12498 if (status != SS_SUCCESS) {
12499 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12500 return RPC_NET_ERROR;
12501 }
12502
12503 /* extract port number from host_name */
12504 mstrlcpy(str, host_name, sizeof(str));
12505 s = strchr(str, ':');
12506 if (s) {
12507 *s = 0;
12508 port = strtoul(s + 1, NULL, 0);
12509 }
12510
12511 int sock;
12512
12513 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12514
12515 if (status != SS_SUCCESS) {
12516 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12517 return RPC_NET_ERROR;
12518 }
12519
12520 /* connect to experiment */
12521 if (exp_name[0] == 0)
12522 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12523 else
12524 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12525
12526 send(sock, str, strlen(str) + 1, 0);
12527 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12528 ss_socket_close(&sock);
12529 if (i <= 0) {
12530 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12531 return RPC_NET_ERROR;
12532 }
12533
12534 status = version[0] = 0;
12535 sscanf(str, "%d %s", &status, version);
12536
12537 if (status == 2) {
12538/* message "undefined experiment" should be displayed by application */
12539 return CM_UNDEF_EXP;
12540 }
12541
12542 /* print warning if version patch level doesn't agree */
12543 strcpy(v1, version);
12544 if (strchr(v1, '.'))
12545 if (strchr(strchr(v1, '.') + 1, '.'))
12546 *strchr(strchr(v1, '.') + 1, '.') = 0;
12547
12548 strcpy(str, cm_get_version());
12549 if (strchr(str, '.'))
12550 if (strchr(strchr(str, '.') + 1, '.'))
12551 *strchr(strchr(str, '.') + 1, '.') = 0;
12552
12553 if (strcmp(v1, str) != 0) {
12554 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12555 cm_get_version());
12556 }
12557
12558 /* wait for callback on send and recv socket with timeout */
12559 FD_ZERO(&readfds);
12563
12564 timeout.tv_sec = _rpc_connect_timeout / 1000;
12565 timeout.tv_usec = 0;
12566
12567 do {
12568 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12569
12570 /* if an alarm signal was cought, restart select with reduced timeout */
12571 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12572 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12573
12574 } while (status == -1); /* dont return if an alarm signal was cought */
12575
12576 if (!FD_ISSET(lsock1, &readfds)) {
12577 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12581 return RPC_NET_ERROR;
12582 }
12583
12587
12589 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12590 return RPC_NET_ERROR;
12591 }
12592
12596
12597 /* set TCP_NODELAY option for better performance */
12598 int flag = 1;
12601
12602 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12603 flag = 2 * 1024 * 1024;
12605 if (status != 0)
12606 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12607
12608 /* send local computer info */
12609 std::string local_prog_name = rpc_get_name();
12611 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12612
12614
12615 /* receive remote computer info */
12617 if (i <= 0) {
12618 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12619 return RPC_NET_ERROR;
12620 }
12621
12622 sscanf(str, "%d", &remote_hw_type);
12623 _server_connection.remote_hw_type = remote_hw_type;
12624
12626
12627 _rpc_is_remote = true;
12628
12629 return RPC_SUCCESS;
12630}
12631
12632/********************************************************************/
12633
12635{
12637 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12639 if (c && c->connected) {
12641 c->mutex.lock();
12642 if (!c->connected) {
12643 // disconnected while we were waiting for the lock
12644 c->mutex.unlock();
12645 return NULL;
12646 }
12647 return c;
12648 }
12649 }
12651 return NULL;
12652}
12653
12655{
12656 /* close all open connections */
12657
12659
12660 for (unsigned i = 0; i < _client_connections.size(); i++) {
12662 if (c && c->connected) {
12663 int index = c->index;
12664 // must unlock the array, otherwise we hang -
12665 // rpc_client_disconnect() will do rpc_call_client()
12666 // which needs to lock the array to convert handle
12667 // to connection pointer. Ouch! K.O. Dec 2020.
12671 }
12672 }
12673
12674 for (unsigned i = 0; i < _client_connections.size(); i++) {
12676 //printf("client connection %d %p\n", i, c);
12677 if (c) {
12678 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12679 if (!c->connected) {
12680 delete c;
12682 }
12683 }
12684 }
12685
12687
12688 /* close server connection from other clients */
12689 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12690 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12691 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12692 _server_acceptions[i]->close();
12693 }
12694 }
12695}
12696
12697/********************************************************************/
12699/********************************************************************\
12700
12701 Routine: rpc_client_disconnect
12702
12703 Purpose: Close a rpc connection to a MIDAS client
12704
12705 Input:
12706 HNDLE hConn Handle of connection
12707 BOOL bShutdown Shut down remote server if TRUE
12708
12709 Output:
12710 none
12711
12712 Function value:
12713 RPC_SUCCESS Successful completion
12714
12715\********************************************************************/
12716{
12717 /* notify server about exit */
12718
12719 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12720
12722
12723 return RPC_SUCCESS;
12724}
12725
12726/********************************************************************/
12728/********************************************************************\
12729
12730 Routine: rpc_server_disconnect
12731
12732 Purpose: Close a rpc connection to a MIDAS server and close all
12733 server connections from other clients
12734
12735 Input:
12736 none
12737
12738 Output:
12739 none
12740
12741 Function value:
12742 RPC_SUCCESS Successful completion
12743 RPC_NET_ERROR Error in socket call
12744 RPC_NO_CONNECTION Maximum number of connections reached
12745
12746\********************************************************************/
12747{
12749
12751 return RPC_SUCCESS;
12752
12754
12755 /* flush remaining events */
12757
12758 /* notify server about exit */
12759 if (rpc_is_connected()) {
12761 }
12762
12763 /* close sockets */
12770
12772
12773 /* remove semaphore */
12774 if (_mutex_rpc)
12776 _mutex_rpc = NULL;
12777
12779 return RPC_SUCCESS;
12780}
12781
12782/********************************************************************/
12784/********************************************************************\
12785
12786 Routine: rpc_is_remote
12787
12788 Purpose: Return true if program is connected to a remote server
12789
12790 Input:
12791 none
12792
12793 Output:
12794 none
12795
12796 Function value:
12797 INT true is remote client connected to mserver, false if local connection
12798
12799\********************************************************************/
12800{
12801 return _rpc_is_remote;
12802}
12803
12804/********************************************************************/
12806/********************************************************************\
12807
12808 Routine: rpc_is_connected
12809
12810 Purpose: Return true if connection to mserver is still open
12811
12812 Input:
12813 none
12814
12815 Output:
12816 none
12817
12818 Function value:
12819 INT true if connection to mserver is still open, false if connection to mserver is already closed
12820
12821\********************************************************************/
12822{
12823 return _server_connection.send_sock != 0;
12824}
12825
12826/********************************************************************/
12828/********************************************************************\
12829
12830 Routine: rpc_get_mserver_hostname
12831
12832 Purpose: Return the hostname of the mserver connection (host:port format)
12833
12834\********************************************************************/
12835{
12837}
12838
12839/********************************************************************/
12841/********************************************************************\
12842
12843 Routine: rpc_is_mserver
12844
12845 Purpose: Return true if we are the mserver
12846
12847 Function value:
12848 INT "true" if we are the mserver
12849
12850\********************************************************************/
12851{
12852 return _mserver_acception != NULL;
12853}
12854
12855/********************************************************************/
12857/********************************************************************\
12858
12859 Routine: rpc_get_hw_type
12860
12861 Purpose: get hardware information
12862
12863 Function value:
12864 INT combination of DRI_xxx bits
12865
12866\********************************************************************/
12867{
12868 {
12869 {
12870 INT tmp_type, size;
12871 DWORD dummy;
12872 unsigned char *p;
12873 float f;
12874 double d;
12875
12876 tmp_type = 0;
12877
12878 /* test pointer size */
12879 size = sizeof(p);
12880 if (size == 2)
12881 tmp_type |= DRI_16;
12882 if (size == 4)
12883 tmp_type |= DRI_32;
12884 if (size == 8)
12885 tmp_type |= DRI_64;
12886
12887 /* test if little or big endian machine */
12888 dummy = 0x12345678;
12889 p = (unsigned char *) &dummy;
12890 if (*p == 0x78)
12892 else if (*p == 0x12)
12894 else
12895 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
12896
12897 /* floating point format */
12898 f = (float) 1.2345;
12899 dummy = 0;
12900 memcpy(&dummy, &f, sizeof(f));
12901 if ((dummy & 0xFF) == 0x19 &&
12902 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
12903 && ((dummy >> 24) & 0xFF) == 0x3F)
12904 tmp_type |= DRF_IEEE;
12905 else if ((dummy & 0xFF) == 0x9E &&
12906 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
12907 && ((dummy >> 24) & 0xFF) == 0x04)
12909 else
12910 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12911
12912 d = (double) 1.2345;
12913 dummy = 0;
12914 memcpy(&dummy, &d, sizeof(f));
12915 if ((dummy & 0xFF) == 0x8D && /* little endian */
12916 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
12917 && ((dummy >> 24) & 0xFF) == 0x12)
12918 tmp_type |= DRF_IEEE;
12919 else if ((dummy & 0xFF) == 0x83 && /* big endian */
12920 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
12921 && ((dummy >> 24) & 0xFF) == 0x3F)
12922 tmp_type |= DRF_IEEE;
12923 else if ((dummy & 0xFF) == 0x13 &&
12924 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
12925 && ((dummy >> 24) & 0xFF) == 0xC0)
12927 else if ((dummy & 0xFF) == 0x9E &&
12928 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
12929 && ((dummy >> 24) & 0xFF) == 0x04)
12930 cm_msg(MERROR, "rpc_get_option",
12931 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
12932 else
12933 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12934
12935 return tmp_type;
12936 }
12937 }
12938}
12939
12941#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12942
12943/********************************************************************/
12951#if 0
12953 switch (item) {
12954 case RPC_OTIMEOUT:
12955 if (hConn == -1)
12957 else if (hConn == -2)
12959 else {
12961 if (c) {
12962 c->rpc_timeout = value;
12963 c->mutex.unlock();
12964 }
12965 }
12966 break;
12967
12968 case RPC_NODELAY:
12969 if (hConn == -1) {
12971 } else {
12973 if (c) {
12974 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12975 c->mutex.unlock();
12976 }
12977 }
12978 break;
12979
12980 default:
12981 cm_msg(MERROR, "rpc_set_option", "invalid argument");
12982 break;
12983 }
12984
12985 return 0;
12986}
12987#endif
12988
12989/********************************************************************/
12996{
12997 if (hConn == RPC_HNDLE_MSERVER) {
12999 } else if (hConn == RPC_HNDLE_CONNECT) {
13000 return _rpc_connect_timeout;
13001 } else {
13003 if (c) {
13004 int timeout = c->rpc_timeout;
13005 c->mutex.unlock();
13006 return timeout;
13007 }
13008 }
13009 return 0;
13010}
13011
13012/********************************************************************/
13021{
13022 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13023
13024 if (hConn == RPC_HNDLE_MSERVER) {
13025 if (old_timeout_msec)
13028 } else if (hConn == RPC_HNDLE_CONNECT) {
13029 if (old_timeout_msec)
13032 } else {
13034 if (c) {
13035 if (old_timeout_msec)
13036 *old_timeout_msec = c->rpc_timeout;
13037 c->rpc_timeout = timeout_msec;
13038 c->mutex.unlock();
13039 } else {
13040 if (old_timeout_msec)
13041 *old_timeout_msec = 0;
13042 }
13043 }
13044 return RPC_SUCCESS;
13045}
13046
13047
13049#ifndef DOXYGEN_SHOULD_SKIP_THIS
13050
13051/********************************************************************/
13053/********************************************************************\
13054
13055 Routine: rpc_get_convert_flags
13056
13057 Purpose: Get RPC convert_flags for the mserver connection
13058
13059 Function value:
13060 INT Actual option
13061
13062\********************************************************************/
13063{
13066 else
13067 return 0;
13068}
13069
13070static std::string _mserver_path;
13071
13072/********************************************************************/
13074/********************************************************************\
13075
13076 Routine: rpc_get_mserver_path()
13077
13078 Purpose: Get path of the mserver executable
13079
13080\********************************************************************/
13081{
13082 return _mserver_path.c_str();
13083}
13084
13085/********************************************************************/
13086INT rpc_set_mserver_path(const char *path)
13087/********************************************************************\
13088
13089 Routine: rpc_set_mserver_path
13090
13091 Purpose: Remember the path of the mserver executable
13092
13093 Input:
13094 char *path Full path of the mserver executable
13095
13096 Function value:
13097 RPC_SUCCESS Successful completion
13098
13099\********************************************************************/
13100{
13101 _mserver_path = path;
13102 return RPC_SUCCESS;
13103}
13104
13105/********************************************************************/
13106std::string rpc_get_name()
13107/********************************************************************\
13108
13109 Routine: rpc_get_name
13110
13111 Purpose: Get name set by rpc_set_name
13112
13113 Input:
13114 none
13115
13116 Output:
13117 char* name The location pointed by *name receives a
13118 copy of the _prog_name
13119
13120 Function value:
13121 RPC_SUCCESS Successful completion
13122
13123\********************************************************************/
13124{
13125 return _client_name;
13126}
13127
13128
13129/********************************************************************/
13131/********************************************************************\
13132
13133 Routine: rpc_set_name
13134
13135 Purpose: Set name of actual program for further rpc connections
13136
13137 Input:
13138 char *name Program name, up to NAME_LENGTH chars,
13139 no blanks
13140
13141 Output:
13142 none
13143
13144 Function value:
13145 RPC_SUCCESS Successful completion
13146
13147\********************************************************************/
13148{
13150
13151 return RPC_SUCCESS;
13152}
13153
13154
13155/********************************************************************/
13156INT rpc_set_debug(void (*func)(const char *), INT mode)
13157/********************************************************************\
13158
13159 Routine: rpc_set_debug
13160
13161 Purpose: Set a function which is called on every RPC call to
13162 display the function name and parameters of the RPC
13163 call.
13164
13165 Input:
13166 void *func(char*) Pointer to function.
13167 INT mode Debug mode
13168
13169 Output:
13170 none
13171
13172 Function value:
13173 RPC_SUCCESS Successful completion
13174
13175\********************************************************************/
13176{
13177 _debug_print = func;
13178 _debug_mode = mode;
13179 return RPC_SUCCESS;
13180}
13181
13182/********************************************************************/
13183void rpc_debug_printf(const char *format, ...)
13184/********************************************************************\
13185
13186 Routine: rpc_debug_print
13187
13188 Purpose: Calls function set via rpc_set_debug to output a string.
13189
13190 Input:
13191 char *str Debug string
13192
13193 Output:
13194 none
13195
13196\********************************************************************/
13197{
13199 char str[1000];
13200
13201 if (_debug_mode) {
13202 va_start(argptr, format);
13203 vsprintf(str, (char *) format, argptr);
13204 va_end(argptr);
13205
13206 if (_debug_print) {
13207 strcat(str, "\n");
13209 } else
13210 puts(str);
13211 }
13212}
13213
13214/********************************************************************/
13216 switch (arg_type) {
13217 /* On the stack, the minimum parameter size is sizeof(int).
13218 To avoid problems on little endian systems, treat all
13219 smaller parameters as int's */
13220 case TID_UINT8:
13221 case TID_INT8:
13222 case TID_CHAR:
13223 case TID_UINT16:
13224 case TID_INT16:
13225 *((int *) arg) = va_arg(*arg_ptr, int);
13226 break;
13227
13228 case TID_INT32:
13229 case TID_BOOL:
13230 *((INT *) arg) = va_arg(*arg_ptr, INT);
13231 break;
13232
13233 case TID_UINT32:
13234 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13235 break;
13236
13237 /* float variables are passed as double by the compiler */
13238 case TID_FLOAT:
13239 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13240 break;
13241
13242 case TID_DOUBLE:
13243 *((double *) arg) = va_arg(*arg_ptr, double);
13244 break;
13245
13246 case TID_ARRAY:
13247 *((char **) arg) = va_arg(*arg_ptr, char *);
13248 break;
13249 }
13250}
13251
13252/********************************************************************/
13254{
13255 bool debug = false;
13256
13257 if (debug) {
13258 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13259 for (int i=0; rl.param[i].tid != 0; i++) {
13260 int tid = rl.param[i].tid;
13261 int flags = rl.param[i].flags;
13262 int n = rl.param[i].n;
13263 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13264 }
13265 }
13266
13267 char args[MAX_RPC_PARAMS][8];
13268
13269 for (int i=0; rl.param[i].tid != 0; i++) {
13270 int tid = rl.param[i].tid;
13271 int flags = rl.param[i].flags;
13272 int arg_type = 0;
13273
13274 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13275 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13276 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13277
13278 if (bpointer)
13280 else
13281 arg_type = tid;
13282
13283 /* floats are passed as doubles, at least under NT */
13284 if (tid == TID_FLOAT && !bpointer)
13286
13287 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13288
13290 }
13291
13292 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13293 char* buf = (char *)malloc(buf_size);
13294 assert(buf);
13295
13296 (*nc) = (NET_COMMAND*) buf;
13297
13298 /* find out if we are on a big endian system */
13299 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13300
13301 char* param_ptr = (*nc)->param;
13302
13303 for (int i=0; rl.param[i].tid != 0; i++) {
13304 int tid = rl.param[i].tid;
13305 int flags = rl.param[i].flags;
13306 int arg_type = 0;
13307
13308 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13309 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13310 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13311
13312 if (bpointer)
13314 else
13315 arg_type = tid;
13316
13317 /* floats are passed as doubles, at least under NT */
13318 if (tid == TID_FLOAT && !bpointer)
13320
13321 /* get pointer to argument */
13322 //char arg[8];
13323 //rpc_va_arg(&ap, arg_type, arg);
13324
13325 char* arg = args[i];
13326
13327 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13328 if (bbig) {
13329 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13330 arg[0] = arg[3];
13331 }
13332 if (tid == TID_UINT16 || tid == TID_INT16) {
13333 arg[0] = arg[2];
13334 arg[1] = arg[3];
13335 }
13336 }
13337
13338 if (flags & RPC_IN) {
13339 int arg_size = 0;
13340
13341 if (bpointer)
13342 arg_size = rpc_tid_size(tid);
13343 else
13345
13346 /* for strings, the argument size depends on the string length */
13347 if (tid == TID_STRING || tid == TID_LINK) {
13348 arg_size = 1 + strlen((char *) *((char **) arg));
13349 }
13350
13351 /* for varibale length arrays, the size is given by
13352 the next parameter on the stack */
13353 if (flags & RPC_VARARRAY) {
13354 //va_list aptmp;
13356 //va_copy(aptmp, ap);
13357
13358 //char arg_tmp[8];
13359 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13360
13361 const char* arg_tmp = args[i+1];
13362
13363 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13364 if (flags & RPC_OUT)
13365 arg_size = *((INT *) *((void **) arg_tmp));
13366 else
13367 arg_size = *((INT *) arg_tmp);
13368
13369 *((INT *) param_ptr) = ALIGN8(arg_size);
13370 param_ptr += ALIGN8(sizeof(INT));
13371
13372 //va_end(aptmp);
13373 }
13374
13375 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13376 arg_size = rl.param[i].n;
13377
13378 /* always align parameter size */
13379 int param_size = ALIGN8(arg_size);
13380
13381 {
13382 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13383
13384 if (param_offset + param_size + 16 > buf_size) {
13385 size_t new_size = param_offset + param_size + 1024;
13386 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13387 buf = (char *) realloc(buf, new_size);
13388 assert(buf);
13389 buf_size = new_size;
13390 (*nc) = (NET_COMMAND*) buf;
13391 param_ptr = buf + param_offset;
13392 }
13393 }
13394
13395 if (bpointer) {
13396 if (debug) {
13397 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);
13398 }
13399 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13400 } else if (tid == TID_FLOAT) {
13401 if (debug) {
13402 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);
13403 }
13404 /* floats are passed as doubles on most systems */
13405 *((float *) param_ptr) = (float) *((double *) arg);
13406 } else {
13407 if (debug) {
13408 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);
13409 }
13410 memcpy(param_ptr, arg, arg_size);
13411 }
13412
13413 param_ptr += param_size;
13414 }
13415 }
13416
13417 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13418
13419 if (debug)
13420 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13421}
13422
13423/********************************************************************/
13424static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13425{
13426 bool debug = false;
13427
13428 if (debug)
13429 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13430
13431 /* extract result variables and place it to argument list */
13432
13433 const char* param_ptr = buf;
13434
13435 for (int i = 0; rl.param[i].tid != 0; i++) {
13436 int tid = rl.param[i].tid;
13437 int flags = rl.param[i].flags;
13438 int arg_type = 0;
13439
13440 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13441 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13442 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13443
13444 if (bpointer)
13446 else
13447 arg_type = rl.param[i].tid;
13448
13449 if (tid == TID_FLOAT && !bpointer)
13451
13452 char arg[8];
13453 rpc_va_arg(&ap, arg_type, arg);
13454
13455 if (rl.param[i].flags & RPC_OUT) {
13456
13457 if (param_ptr == NULL) {
13458 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);
13459 return RPC_NET_ERROR;
13460 }
13461
13462 tid = rl.param[i].tid;
13463 int arg_size = rpc_tid_size(tid);
13464
13465 if (tid == TID_STRING || tid == TID_LINK)
13466 arg_size = strlen((char *) (param_ptr)) + 1;
13467
13468 if (flags & RPC_VARARRAY) {
13469 arg_size = *((INT *) param_ptr);
13470 param_ptr += ALIGN8(sizeof(INT));
13471 }
13472
13473 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13474 arg_size = rl.param[i].n;
13475
13476 /* parameter size is always aligned */
13477 int param_size = ALIGN8(arg_size);
13478
13479 /* return parameters are always pointers */
13480 if (*((char **) arg)) {
13481 if (debug)
13482 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);
13483 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13484 }
13485
13486 param_ptr += param_size;
13487 }
13488 }
13489
13490 return RPC_SUCCESS;
13491}
13492
13493/********************************************************************/
13495/********************************************************************\
13496
13497 Routine: rpc_client_call
13498
13499 Purpose: Call a function on a MIDAS client
13500
13501 Input:
13502 INT hConn Client connection
13503 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13504
13505 ... variable argument list
13506
13507 Output:
13508 (depends on argument list)
13509
13510 Function value:
13511 RPC_SUCCESS Successful completion
13512 RPC_NET_ERROR Error in socket call
13513 RPC_NO_CONNECTION No active connection
13514 RPC_TIMEOUT Timeout in RPC call
13515 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13516 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13517
13518\********************************************************************/
13519{
13521
13522 if (!c) {
13523 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13524 return RPC_NO_CONNECTION;
13525 }
13526
13527 //printf("rpc_client_call: handle %d, connection: ", hConn);
13528 //c->print();
13529 //printf("\n");
13530
13531 INT i, status;
13532
13533 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13534 routine_id &= ~RPC_NO_REPLY;
13535
13536 //if (rpc_no_reply)
13537 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13538
13539 // make local copy of the client name just in case _client_connection is erased by another thread
13540
13541 /* find rpc_index */
13542
13543 int rpc_index = -1;
13544 const char *rpc_name = NULL;
13546
13547 rpc_list_mutex.lock();
13548 for (size_t i = 0; i < rpc_list.size(); i++) {
13549 if (rpc_list[i].id == (int) routine_id) {
13550 rpc_index = i;
13553 break;
13554 }
13555 }
13556 rpc_list_mutex.unlock();
13557
13558 if (rpc_index < 0) {
13559 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);
13560 c->mutex.unlock();
13561 return RPC_INVALID_ID;
13562 }
13563
13564 NET_COMMAND *nc = NULL;
13565
13566 /* examine variable argument list and convert it to parameter array */
13567 va_list ap;
13568 va_start(ap, routine_id);
13569
13571
13572 va_end(ap);
13573
13574 nc->header.routine_id = routine_id;
13575
13576 if (rpc_no_reply)
13578
13579 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13580
13581 /* in FAST TCP mode, only send call and return immediately */
13582 if (rpc_no_reply) {
13583 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13584
13585 if (i != send_size) {
13586 cm_msg(MERROR, "rpc_client_call", "call to \"%s\" on \"%s\" RPC \"%s\": send_tcp() failed", c->client_name.c_str(), c->host_name.c_str(), rpc_name);
13587 free(nc);
13588 c->mutex.unlock();
13589 return RPC_NET_ERROR;
13590 }
13591
13592 free(nc);
13593
13594 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
13595 //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);
13596 //c->print();
13597 //printf("\n");
13598 c->close_locked();
13599 }
13600
13601 c->mutex.unlock();
13602 return RPC_SUCCESS;
13603 }
13604
13605 /* in TCP mode, send and wait for reply on send socket */
13606 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13607 if (i != send_size) {
13608 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);
13609 c->mutex.unlock();
13610 return RPC_NET_ERROR;
13611 }
13612
13613 free(nc);
13614 nc = NULL;
13615
13616 bool restore_watchdog_timeout = false;
13618 DWORD watchdog_timeout;
13619 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13620
13621 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
13622
13623 if (c->rpc_timeout >= (int) watchdog_timeout) {
13625 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
13626 }
13627
13628 DWORD rpc_status = 0;
13629 DWORD buf_size = 0;
13630 char* buf = NULL;
13631
13632 /* receive result on send socket */
13633 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
13634
13636 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
13637 }
13638
13639 if (status == SS_TIMEOUT) {
13640 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);
13641 if (buf)
13642 free(buf);
13643 c->mutex.unlock();
13644 return RPC_TIMEOUT;
13645 }
13646
13647 if (status != SS_SUCCESS) {
13648 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);
13649 if (buf)
13650 free(buf);
13651 c->mutex.unlock();
13652 return RPC_NET_ERROR;
13653 }
13654
13655 c->mutex.unlock();
13656
13657 if (rpc_status == RPC_INVALID_ID) {
13658 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);
13659 if (buf)
13660 free(buf);
13661 return rpc_status;
13662 }
13663
13664 /* extract result variables and place it to argument list */
13665
13666 va_start(ap, routine_id);
13667
13668 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13669
13670 if (status != RPC_SUCCESS) {
13672 }
13673
13674 va_end(ap);
13675
13676 if (buf)
13677 free(buf);
13678 buf = NULL;
13679 buf_size = 0;
13680
13681 return rpc_status;
13682}
13683
13684/********************************************************************/
13685INT rpc_call(DWORD routine_id, ...)
13686/********************************************************************\
13687
13688 Routine: rpc_call
13689
13690 Purpose: Call a function on a MIDAS server
13691
13692 Input:
13693 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13694
13695 ... variable argument list
13696
13697 Output:
13698 (depends on argument list)
13699
13700 Function value:
13701 RPC_SUCCESS Successful completion
13702 RPC_NET_ERROR Error in socket call
13703 RPC_NO_CONNECTION No active connection
13704 RPC_TIMEOUT Timeout in RPC call
13705 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13706 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13707
13708\********************************************************************/
13709{
13710 va_list ap;
13711 INT i, status;
13712
13713 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13714 routine_id &= ~RPC_NO_REPLY;
13715
13716 //if (rpc_no_reply)
13717 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13718
13719 int send_sock = _server_connection.send_sock;
13720 int rpc_timeout = _server_connection.rpc_timeout;
13721
13722 if (!send_sock) {
13723 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
13724 return RPC_NET_ERROR;
13725 }
13726
13727 if (!_mutex_rpc) {
13728 /* create a local mutex for multi-threaded applications */
13730 }
13731
13732 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
13733 if (status != SS_SUCCESS) {
13734 cm_msg(MERROR, "rpc_call", "Mutex timeout");
13735 return RPC_MUTEX_TIMEOUT;
13736 }
13737
13738 /* find rpc definition */
13739
13740 int idx = -1;
13741 const char* rpc_name = NULL;
13743
13744 rpc_list_mutex.lock();
13745
13746 for (size_t i = 0; i < rpc_list.size(); i++) {
13747 if (rpc_list[i].id == (int) routine_id) {
13748 idx = i;
13751 break;
13752 }
13753 }
13754
13755 rpc_list_mutex.unlock();
13756
13757 if (idx < 0) {
13759 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
13760 return RPC_INVALID_ID;
13761 }
13762
13763 /* prepare output buffer */
13764
13765 NET_COMMAND* nc = NULL;
13766
13767 /* examine variable argument list and convert it to parameter array */
13768 va_start(ap, routine_id);
13769
13771
13772 va_end(ap);
13773
13774 nc->header.routine_id = routine_id;
13775
13776 if (rpc_no_reply)
13778
13779 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13780
13781 /* do not wait for reply if requested RPC_NO_REPLY */
13782 if (rpc_no_reply) {
13783 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13784
13785 if (i != send_size) {
13787 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13788 free(nc);
13789 return RPC_NET_ERROR;
13790 }
13791
13793 free(nc);
13794 return RPC_SUCCESS;
13795 }
13796
13797 /* in TCP mode, send and wait for reply on send socket */
13798 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13799 if (i != send_size) {
13801 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13802 free(nc);
13803 return RPC_NET_ERROR;
13804 }
13805
13806 free(nc);
13807 nc = NULL;
13808
13809 bool restore_watchdog_timeout = false;
13811 DWORD watchdog_timeout;
13812 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13813
13814 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
13815
13816 if (!rpc_is_remote()) {
13817 // if RPC is remote, we are connected to an mserver,
13818 // the mserver takes care of watchdog timeouts.
13819 // otherwise we should make sure the watchdog timeout
13820 // is longer than the RPC timeout. K.O.
13821 if (rpc_timeout >= (int) watchdog_timeout) {
13823 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
13824 }
13825 }
13826
13827 DWORD rpc_status = 0;
13828 DWORD buf_size = 0;
13829 char* buf = NULL;
13830
13831 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
13832
13835 }
13836
13837 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
13838
13840
13841 /* check for reply errors */
13842
13843 if (status == SS_TIMEOUT) {
13844 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
13845 if (buf)
13846 free(buf);
13847 abort(); // cannot continue - our mserver is not talking to us!
13848 return RPC_TIMEOUT;
13849 }
13850
13851 if (status != SS_SUCCESS) {
13852 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
13853 if (buf)
13854 free(buf);
13855 abort(); // cannot continue - something is wrong with our mserver connection
13856 return RPC_NET_ERROR;
13857 }
13858
13859 if (rpc_status == RPC_INVALID_ID) {
13860 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
13861 if (buf)
13862 free(buf);
13863 return rpc_status;
13864 }
13865
13866 /* extract result variables and place it to argument list */
13867
13868 va_start(ap, routine_id);
13869
13870 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13871
13872 if (status != RPC_SUCCESS) {
13874 }
13875
13876 va_end(ap);
13877
13878 if (buf)
13879 free(buf);
13880
13881 return rpc_status;
13882}
13883
13884
13885/********************************************************************/
13887 INT old;
13888
13891 return old;
13892}
13893
13895 return _opt_tcp_size;
13896}
13897
13899#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13900
13901/********************************************************************/
13923INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
13924{
13925 if (rpc_is_remote()) {
13926 return rpc_send_event1(buffer_handle, pevent);
13927 } else {
13928 return bm_send_event(buffer_handle, pevent, unused, async_flag);
13929 }
13930}
13931
13932/********************************************************************/
13941INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
13942{
13943 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
13944 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
13945}
13946
13947INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
13948{
13949 if (sg_n < 1) {
13950 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
13951 return BM_INVALID_SIZE;
13952 }
13953
13954 if (sg_ptr[0] == NULL) {
13955 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
13956 return BM_INVALID_SIZE;
13957 }
13958
13959 if (sg_len[0] < sizeof(EVENT_HEADER)) {
13960 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));
13961 return BM_INVALID_SIZE;
13962 }
13963
13964 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
13965
13966 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
13967 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
13968
13969 if (data_size == 0) {
13970 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
13971 return BM_INVALID_SIZE;
13972 }
13973
13974 if (data_size > MAX_DATA_SIZE) {
13975 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);
13976 return BM_INVALID_SIZE;
13977 }
13978
13979 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
13980 const size_t total_size = ALIGN8(event_size);
13981
13982 size_t count = 0;
13983 for (int i=0; i<sg_n; i++) {
13984 count += sg_len[i];
13985 }
13986
13987 if (count != event_size) {
13988 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);
13989 return BM_INVALID_SIZE;
13990 }
13991
13992 // protect non-atomic access to _server_connection.event_sock. K.O.
13993
13994 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
13995
13996 //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);
13997
13998 if (_server_connection.event_sock == 0) {
13999 return RPC_NO_CONNECTION;
14000 }
14001
14002 //
14003 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14004 //
14005 // 4 bytes of buffer handle
14006 // 16 bytes of event header, includes data_size
14007 // ALIGN8(data_size) bytes of event data
14008 //
14009
14010 int status;
14011
14012 /* send buffer handle */
14013
14014 assert(sizeof(DWORD) == 4);
14015 DWORD bh_buf = buffer_handle;
14016
14017 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14018 if (status != SS_SUCCESS) {
14020 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14021 return RPC_NET_ERROR;
14022 }
14023
14024 /* send data */
14025
14026 for (int i=0; i<sg_n; i++) {
14028 if (status != SS_SUCCESS) {
14030 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14031 return RPC_NET_ERROR;
14032 }
14033 }
14034
14035 /* send padding */
14036
14037 if (count < total_size) {
14038 char padding[8] = { 0,0,0,0,0,0,0,0 };
14039 size_t padlen = total_size - count;
14040 assert(padlen < 8);
14042 if (status != SS_SUCCESS) {
14044 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14045 return RPC_NET_ERROR;
14046 }
14047 }
14048
14049 return RPC_SUCCESS;
14050}
14051
14052/********************************************************************/
14061 return RPC_SUCCESS;
14062}
14063
14064/********************************************************************/
14065
14066struct TR_FIFO {
14067 int transition = 0;
14068 int run_number = 0;
14071};
14072
14073static std::mutex _tr_fifo_mutex;
14075static int _tr_fifo_wp = 0;
14076static int _tr_fifo_rp = 0;
14077
14079/********************************************************************\
14080
14081 Routine: rpc_transition_dispatch
14082
14083 Purpose: Gets called when a transition function was registered and
14084 a transition occured. Internal use only.
14085
14086 Input:
14087 INT idx RPC function ID
14088 void *prpc_param RPC parameters
14089
14090 Output:
14091 none
14092
14093 Function value:
14094 INT return value from called user routine
14095
14096\********************************************************************/
14097{
14098 /* erase error string */
14099 *(CSTRING(2)) = 0;
14100
14101 if (idx == RPC_RC_TRANSITION) {
14102 // find registered handler
14103 // NB: this code should match same code in cm_transition_call_direct()
14104 // NB: only use the first handler, this is how MIDAS always worked
14105 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14106 _trans_table_mutex.lock();
14107 size_t n = _trans_table.size();
14108 _trans_table_mutex.unlock();
14109
14110 for (size_t i = 0; i < n; i++) {
14111 _trans_table_mutex.lock();
14113 _trans_table_mutex.unlock();
14114
14115 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14116 if (tt.func) {
14117 /* execute callback if defined */
14118 return tt.func(CINT(1), CSTRING(2));
14119 } else {
14120 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14121 /* store transition in FIFO */
14126 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14127 // implicit unlock
14128 return RPC_SUCCESS;
14129 }
14130 }
14131 }
14132 // no handler for this transition
14133 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14134 return CM_SUCCESS;
14135 } else {
14136 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14137 return RPC_INVALID_ID;
14138 }
14139}
14140
14141/********************************************************************/
14142int cm_query_transition(int *transition, int *run_number, int *trans_time)
14143/********************************************************************\
14144
14145 Routine: cm_query_transition
14146
14147 Purpose: Query system if transition has occured. Normally, one
14148 registers callbacks for transitions via
14149 cm_register_transition. In some environments however,
14150 callbacks are not possible. In that case one spciefies
14151 a NULL pointer as the callback routine and can query
14152 transitions "manually" by calling this functions. A small
14153 FIFO takes care that no transition is lost if this functions
14154 did not get called between some transitions.
14155
14156 Output:
14157 INT *transition Type of transition, one of TR_xxx
14158 INT *run_nuber Run number for transition
14159 time_t *trans_time Time (in UNIX time) of transition
14160
14161 Function value:
14162 FALSE No transition occured since last call
14163 TRUE Transition occured
14164
14165\********************************************************************/
14166{
14167 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14168
14169 if (_tr_fifo_wp == _tr_fifo_rp)
14170 return FALSE;
14171
14172 if (transition)
14174
14175 if (run_number)
14177
14178 if (trans_time)
14179 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14180
14181 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14182
14183 // implicit unlock
14184 return TRUE;
14185}
14186
14187/********************************************************************\
14188* server functions *
14189\********************************************************************/
14190
14191#if 0
14192void debug_dump(unsigned char *p, int size)
14193{
14194 int i, j;
14195 unsigned char c;
14196
14197 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14198 printf("%p ", p + i * 16);
14199 for (j = 0; j < 16; j++)
14200 if (i * 16 + j < size)
14201 printf("%02X ", p[i * 16 + j]);
14202 else
14203 printf(" ");
14204 printf(" ");
14205
14206 for (j = 0; j < 16; j++) {
14207 c = p[i * 16 + j];
14208 if (i * 16 + j < size)
14209 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14210 }
14211 printf("\n");
14212 }
14213
14214 printf("\n");
14215}
14216#endif
14217
14218/********************************************************************/
14220/********************************************************************\
14221
14222 Routine: recv_net_command
14223
14224 Purpose: TCP receive routine with local cache. To speed up network
14225 performance, a 64k buffer is read in at once and split into
14226 several RPC command on successive calls to recv_net_command.
14227 Therefore, the number of recv() calls is minimized.
14228
14229 This routine is ment to be called by the server process.
14230 Clients should call recv_tcp instead.
14231
14232 Input:
14233 INT idx Index of server connection
14234 DWORD buffer_size Size of the buffer in bytes.
14235 INT flags Flags passed to recv()
14236 INT convert_flags Convert flags needed for big/little
14237 endian conversion
14238
14239 Output:
14240 char *buffer Network receive buffer.
14241 INT *remaining Remaining data in cache
14242
14243 Function value:
14244 INT Same as recv()
14245
14246\********************************************************************/
14247{
14248 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14249
14251
14252 int sock = sa->recv_sock;
14253
14254 if (!sa->net_buffer) {
14255 if (sa->is_mserver)
14257 else
14259
14260 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14261 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14262 sa->write_ptr = 0;
14263 sa->read_ptr = 0;
14264 sa->misalign = 0;
14265 }
14266 if (!sa->net_buffer) {
14267 cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14268 return -1;
14269 }
14270
14271 int copied = 0;
14272 int param_size = -1;
14273
14274 int write_ptr = sa->write_ptr;
14275 int read_ptr = sa->read_ptr;
14276 int misalign = sa->misalign;
14277 char *net_buffer = sa->net_buffer;
14278
14279 do {
14280 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14281 if (param_size == -1) {
14282 if (copied > 0) {
14283 /* assemble split header */
14284 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14285 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14286 param_size = (INT) nc->header.param_size;
14287 } else {
14288 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14289 param_size = (INT) nc->header.param_size;
14290 }
14291
14292 if (sa->convert_flags)
14293 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14294 }
14295
14296 //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14297
14298 /* check if parameters fit in buffer */
14299 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14300 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14301 char *p = (char *) realloc(*pbuf, new_size);
14302 //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14303 if (p == NULL) {
14304 cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14305 sa->read_ptr = 0;
14306 sa->write_ptr = 0;
14307 return -1;
14308 }
14309 *pbuf = p;
14310 *pbufsize = new_size;
14311 }
14312
14313 buffer = *pbuf;
14314
14315 /* check if we have all parameters in buffer */
14316 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14317 break;
14318 }
14319
14320 /* not enough data, so copy partially and get new */
14321 int size = write_ptr - read_ptr;
14322
14323 if (size > 0) {
14324 memcpy(buffer + copied, net_buffer + read_ptr, size);
14325 copied += size;
14326 read_ptr = write_ptr;
14327 }
14328#ifdef OS_UNIX
14329 do {
14330 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14331
14332 /* don't return if an alarm signal was cought */
14333 } while (write_ptr == -1 && errno == EINTR);
14334#else
14335 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14336#endif
14337
14338 /* abort if connection broken */
14339 if (write_ptr <= 0) {
14340 if (write_ptr == 0)
14341 cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14342 else
14343 cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14344
14345 if (remaining)
14346 *remaining = 0;
14347
14348 return write_ptr;
14349 }
14350
14351 read_ptr = misalign;
14352 write_ptr += misalign;
14353
14354 misalign = write_ptr % 8;
14355 } while (TRUE);
14356
14357 /* copy rest of parameters */
14358 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14359 memcpy(buffer + copied, net_buffer + read_ptr, size);
14360 read_ptr += size;
14361
14362 if (remaining) {
14363 /* don't keep rpc_server_receive in an infinite loop */
14364 if (write_ptr - read_ptr < param_size)
14365 *remaining = 0;
14366 else
14367 *remaining = write_ptr - read_ptr;
14368 }
14369
14370 sa->write_ptr = write_ptr;
14371 sa->read_ptr = read_ptr;
14372 sa->misalign = misalign;
14373
14374 return size + copied;
14375}
14376
14377
14378/********************************************************************/
14380/********************************************************************\
14381
14382 Routine: recv_tcp_check
14383
14384 Purpose: Check if in TCP receive buffer associated with sock is
14385 some data. Called by ss_suspend.
14386
14387 Input:
14388 INT sock TCP receive socket
14389
14390 Output:
14391 none
14392
14393 Function value:
14394 INT count Number of bytes remaining in TCP buffer
14395
14396\********************************************************************/
14397{
14398 /* figure out to which connection socket belongs */
14399 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14400 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14401 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14402 }
14403
14404 return 0;
14405}
14406
14407
14408/********************************************************************/
14410/********************************************************************\
14411
14412 Routine: recv_event_server_realloc
14413
14414 Purpose: receive events sent by rpc_send_event()
14415
14416 Input:
14417 INT idx Index of server connection
14418 DWORD buffer_size Size of the buffer in bytes.
14419 INT flags Flags passed to recv()
14420 INT convert_flags Convert flags needed for big/little
14421 endian conversion
14422
14423 Output:
14424 char *buffer Network receive buffer.
14425 INT *remaining Remaining data in cache
14426
14427 Function value:
14428 INT Same as recv()
14429
14430\********************************************************************/
14431{
14432 int sock = psa->event_sock;
14433
14434 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14435
14436 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14437
14438 char header_buf[header_size];
14439
14440 // First read the header.
14441 //
14442 // Data format is:
14443 // INT buffer handle (4 bytes)
14444 // EVENT_HEADER (16 bytes)
14445 // event data
14446 // ALIGN8() padding
14447 // ...next event
14448
14449 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14450
14451 if (hrd == 0) {
14452 // timeout waiting for data
14453 return 0;
14454 }
14455
14456 /* abort if connection broken */
14457 if (hrd < 0) {
14458 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14459 return -1;
14460 }
14461
14462 if (hrd < (int) header_size) {
14463 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14464
14465 /* abort if connection broken */
14466 if (hrd1 <= 0) {
14467 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14468 return -1;
14469 }
14470
14471 hrd += hrd1;
14472 }
14473
14474 /* abort if connection broken */
14475 if (hrd != (int) header_size) {
14476 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14477 return -1;
14478 }
14479
14480 INT *pbh = (INT *) header_buf;
14481 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14482
14483 /* convert header little endian/big endian */
14484 if (psa->convert_flags) {
14485 rpc_convert_single(&pbh, TID_INT32, 0, psa->convert_flags);
14486 rpc_convert_single(&pevent->event_id, TID_INT16, 0, psa->convert_flags);
14487 rpc_convert_single(&pevent->trigger_mask, TID_INT16, 0, psa->convert_flags);
14488 rpc_convert_single(&pevent->serial_number, TID_UINT32, 0, psa->convert_flags);
14489 rpc_convert_single(&pevent->time_stamp, TID_UINT32, 0, psa->convert_flags);
14490 rpc_convert_single(&pevent->data_size, TID_UINT32, 0, psa->convert_flags);
14491 }
14492
14493 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14494 int total_size = ALIGN8(event_size);
14495
14496 //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);
14497
14498 if (pevent->data_size == 0) {
14499 for (int i=0; i<5; i++) {
14500 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14501 }
14502 abort();
14503 }
14504
14505 /* check for sane event size */
14506 if (event_size <= 0 || total_size <= 0) {
14507 cm_msg(MERROR, "recv_event_server",
14508 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14509 event_size, total_size);
14510 return -1;
14511 }
14512
14513 //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);
14514
14515
14516 int bufsize = sizeof(INT) + total_size;
14517
14518 // Second, check that output buffer is big enough
14519
14520 /* check if data part fits in buffer */
14521 if (*pbuffer_size < bufsize) {
14522 int newsize = 1024 + ALIGN8(bufsize);
14523
14524 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14525
14526 char *newbuf = (char *) realloc(*pbuffer, newsize);
14527 if (newbuf == NULL) {
14528 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14529 newsize);
14530 return -1;
14531 }
14532 *pbuffer = newbuf;
14534 }
14535
14536 // Third, copy header into output buffer
14537
14539
14540 // Forth, read the event data
14541
14542 int to_read = sizeof(INT) + total_size - header_size;
14543 int rptr = header_size;
14544
14545 if (to_read > 0) {
14546 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14547
14548 /* abort if connection broken */
14549 if (drd <= 0) {
14550 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14551 return -1;
14552 }
14553 }
14554
14555 return bufsize;
14556}
14557
14558
14559/********************************************************************/
14561/********************************************************************\
14562
14563 Routine: rpc_register_listener
14564
14565 Purpose: Register the calling process as a MIDAS RPC server. Note
14566 that cm_connnect_experiment must be called prior to any call of
14567 rpc_register_server.
14568
14569 Input:
14570 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14571
14572 Output:
14573 int *plsock Listener socket, can be NULL
14574 int *pport Port under which server is listening, can be NULL
14575
14576 Function value:
14577 RPC_SUCCESS Successful completion
14578 RPC_NET_ERROR Error in socket call
14579 RPC_NOT_REGISTERED cm_connect_experiment was not called
14580
14581\********************************************************************/
14582{
14583 int status;
14584 int lsock;
14585
14587 if (status != RPC_SUCCESS)
14588 return status;
14589
14591 if (status != SS_SUCCESS)
14592 return status;
14593
14594 if (plsock)
14595 *plsock = lsock;
14596
14597 return RPC_SUCCESS;
14598}
14599
14600/********************************************************************/
14602/********************************************************************\
14603
14604 Routine: rpc_register_listener
14605
14606 Purpose: Register the calling process as a MIDAS RPC server. Note
14607 that cm_connnect_experiment must be called prior to any call of
14608 rpc_register_listener.
14609
14610 Input:
14611 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14612 INT *func Default dispatch function
14613
14614 Output:
14615 int *plsock Listener socket, should not be NULL
14616 int *pport Port under which server is listening, can be NULL
14617
14618 Function value:
14619 RPC_SUCCESS Successful completion
14620 RPC_NET_ERROR Error in socket call
14621 RPC_NOT_REGISTERED cm_connect_experiment was not called
14622
14623\********************************************************************/
14624{
14625 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
14627
14628 /* create a socket for listening */
14629 int lsock = 0;
14630 int lport = 0;
14631 std::string errmsg;
14632
14634
14635 if (status != SS_SUCCESS) {
14636 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
14637 return RPC_NET_ERROR;
14638 }
14639
14640 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
14641#if defined(F_SETFD) && defined(FD_CLOEXEC)
14643 if (status < 0) {
14644 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
14645 return RPC_NET_ERROR;
14646 }
14647#endif
14648
14649 /* return port wich OS has choosen */
14650 if (pport) {
14651 *pport = lport;
14652 }
14653
14654 if (plsock)
14655 *plsock = lsock;
14656
14657 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
14658
14659 return RPC_SUCCESS;
14660}
14661
14667
14669static int tls_size = 0;
14670
14671/********************************************************************/
14672INT rpc_execute(INT sock, char *buffer, INT convert_flags)
14673/********************************************************************\
14674
14675 Routine: rpc_execute
14676
14677 Purpose: Execute a RPC command received over the network
14678
14679 Input:
14680 INT sock TCP socket to which the result should be
14681 send back
14682
14683 char *buffer Command buffer
14684 INT convert_flags Flags for data conversion
14685
14686 Output:
14687 none
14688
14689 Function value:
14690 RPC_SUCCESS Successful completion
14691 RPC_INVALID_ID Invalid routine_id received
14692 RPC_NET_ERROR Error in socket call
14693 RPC_EXCEED_BUFFER Not enough memory for network buffer
14694 RPC_SHUTDOWN Shutdown requested
14695 SS_ABORT TCP connection broken
14696 SS_EXIT TCP connection closed
14697
14698\********************************************************************/
14699{
14700 INT i, routine_id, status;
14702 INT tid, flags;
14704 INT param_size, max_size;
14705 void *prpc_param[20];
14706 char debug_line[1024], *return_buffer;
14709#ifdef FIXED_BUFFER
14711#else
14712 int initial_buffer_size = 1024;
14713#endif
14714
14715 /* return buffer must must use thread local storage multi-thread servers */
14716 if (!tls_size) {
14720 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14721 tls_size = 1;
14722 }
14723 for (i = 0; i < tls_size; i++)
14724 if (tls_buffer[i].thread_id == ss_gettid())
14725 break;
14726 if (i == tls_size) {
14727 /* new thread -> allocate new buffer */
14731 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14732 tls_size++;
14733 }
14734
14737 return_buffer = tls_buffer[i].buffer;
14738 assert(return_buffer);
14739
14740 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
14741 // and valgrind complains that we transmit uninitialized data
14742 //memset(return_buffer, 0, return_buffer_size);
14743
14744 /* extract pointer array to parameters */
14745 nc_in = (NET_COMMAND *) buffer;
14746
14747 /* convert header format (byte swapping) */
14748 if (convert_flags) {
14749 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
14750 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
14751 }
14752
14753 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
14754 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
14755 //}
14756
14757 /* no result return as requested */
14758 if (nc_in->header.routine_id & RPC_NO_REPLY)
14759 sock = 0;
14760
14761 /* find entry in rpc_list */
14762 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
14763
14764 int idx = -1;
14765 RPC_LIST rl;
14766
14767 rpc_list_mutex.lock();
14768
14769 for (size_t i = 0; i < rpc_list.size(); i++) {
14770 if (rpc_list[i].id == routine_id) {
14771 idx = i;
14772 rl = rpc_list[idx];
14773 break;
14774 }
14775 }
14776
14777 rpc_list_mutex.unlock();
14778
14779 if (idx < 0) {
14780 cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
14781 return RPC_INVALID_ID;
14782 }
14783
14784 again:
14785
14786 in_param_ptr = nc_in->param;
14787
14788 nc_out = (NET_COMMAND *) return_buffer;
14789 out_param_ptr = nc_out->param;
14790
14791 sprintf(debug_line, "%s(", rl.name);
14792
14793 for (i = 0; rl.param[i].tid != 0; i++) {
14794 tid = rl.param[i].tid;
14795 flags = rl.param[i].flags;
14796
14797 if (flags & RPC_IN) {
14798 param_size = ALIGN8(rpc_tid_size(tid));
14799
14800 if (tid == TID_STRING || tid == TID_LINK)
14801 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
14802
14803 if (flags & RPC_VARARRAY) {
14804 /* for arrays, the size is stored as a INT in front of the array */
14805 param_size = *((INT *) in_param_ptr);
14806 if (convert_flags)
14807 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
14808 param_size = ALIGN8(param_size);
14809
14810 in_param_ptr += ALIGN8(sizeof(INT));
14811 }
14812
14813 if (tid == TID_STRUCT)
14814 param_size = ALIGN8(rl.param[i].n);
14815
14817
14818 /* convert data format */
14819 if (convert_flags) {
14820 if (flags & RPC_VARARRAY)
14821 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
14822 else
14823 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
14824 convert_flags);
14825 }
14826
14827 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
14828 if (rl.param[i].tid == TID_STRING) {
14829 /* check for long strings (db_create_record...) */
14830 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
14831 strcat(debug_line, "\"");
14832 strcat(debug_line, str.c_str());
14833 strcat(debug_line, "\"");
14834 } else
14835 strcat(debug_line, "...");
14836 } else
14837 strcat(debug_line, str.c_str());
14838
14839 in_param_ptr += param_size;
14840 }
14841
14842 if (flags & RPC_OUT) {
14843 param_size = ALIGN8(rpc_tid_size(tid));
14844
14845 if (flags & RPC_VARARRAY || tid == TID_STRING) {
14846
14847 /* save maximum array length from the value of the next argument.
14848 * this means RPC_OUT arrays and strings should always be passed like this:
14849 * rpc_call(..., array_ptr, array_max_size, ...); */
14850
14851 max_size = *((INT *) in_param_ptr);
14852
14853 if (convert_flags)
14854 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
14856
14857 *((INT *) out_param_ptr) = max_size;
14858
14859 /* save space for return array length */
14860 out_param_ptr += ALIGN8(sizeof(INT));
14861
14862 /* use maximum array length from input */
14863 param_size = max_size;
14864 }
14865
14866 if (rl.param[i].tid == TID_STRUCT)
14867 param_size = ALIGN8(rl.param[i].n);
14868
14869 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
14870#ifdef FIXED_BUFFER
14871 cm_msg(MERROR, "rpc_execute",
14872 "return parameters (%d) too large for network buffer (%d)",
14874
14875 return RPC_EXCEED_BUFFER;
14876#else
14877 int itls;
14878 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
14879
14880#if 0
14881 cm_msg(MINFO, "rpc_execute",
14882 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
14884#endif
14885
14887
14889 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
14890
14891 if (!tls_buffer[itls].buffer) {
14892 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
14893 return RPC_EXCEED_BUFFER;
14894 }
14895
14897 return_buffer = tls_buffer[itls].buffer;
14898 assert(return_buffer);
14899
14900 goto again;
14901#endif
14902 }
14903
14904 /* if parameter goes both directions, copy input to output */
14905 if (rl.param[i].flags & RPC_IN)
14906 memcpy(out_param_ptr, prpc_param[i], param_size);
14907
14908 if (_debug_print && !(flags & RPC_IN))
14909 strcat(debug_line, "-");
14910
14912 out_param_ptr += param_size;
14913 }
14914
14915 if (rl.param[i + 1].tid)
14916 strcat(debug_line, ", ");
14917 }
14918
14919 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
14920
14921 strcat(debug_line, ")");
14923
14925
14926 /*********************************\
14927 * call dispatch function *
14928 \*********************************/
14929 if (rl.dispatch)
14930 status = rl.dispatch(routine_id, prpc_param);
14931 else
14933
14934 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
14936
14937 /* return immediately for closed down client connections */
14938 if (!sock && routine_id == RPC_ID_EXIT)
14939 return SS_EXIT;
14940
14941 if (!sock && routine_id == RPC_ID_SHUTDOWN)
14942 return RPC_SHUTDOWN;
14943
14944 /* Return if TCP connection broken */
14945 if (status == SS_ABORT)
14946 return SS_ABORT;
14947
14948 /* if sock == 0, we are in FTCP mode and may not sent results */
14949 if (!sock)
14950 return RPC_SUCCESS;
14951
14952 /* compress variable length arrays */
14953 out_param_ptr = nc_out->param;
14954 for (i = 0; rl.param[i].tid != 0; i++)
14955 if (rl.param[i].flags & RPC_OUT) {
14956 tid = rl.param[i].tid;
14957 flags = rl.param[i].flags;
14958 param_size = ALIGN8(rpc_tid_size(tid));
14959
14960 if (tid == TID_STRING) {
14961 max_size = *((INT *) out_param_ptr);
14962 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14963 // and prpc_param() is now pointing to the wrong place. here we know our string data
14964 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
14965 //const char* param_ptr = (char *) prpc_param[i];
14966 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
14967 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
14968 param_size = strlen(param_ptr) + 1;
14969 param_size = ALIGN8(param_size);
14970
14971 /* move string ALIGN8(sizeof(INT)) left */
14972 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
14973
14974 /* move remaining parameters to end of string */
14975 memmove(out_param_ptr + param_size,
14976 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
14978 }
14979
14980 if (flags & RPC_VARARRAY) {
14981 /* store array length at current out_param_ptr */
14982 max_size = *((INT *) out_param_ptr);
14983 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14984 // and prpc_param() is now pointing to the wrong place. instead, compute location
14985 // of next parameter using max_size. K.O.
14986 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
14987 if (rl.param[i+1].flags & RPC_OUT)
14988 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
14989 else
14990 param_size = *((INT *) prpc_param[i + 1]);
14991 *((INT *) out_param_ptr) = param_size; // store new array size
14992 if (convert_flags)
14994
14995 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
14996
14997 param_size = ALIGN8(param_size);
14998
14999 /* move remaining parameters to end of array */
15000 memmove(out_param_ptr + param_size,
15003 }
15004
15005 if (tid == TID_STRUCT)
15006 param_size = ALIGN8(rl.param[i].n);
15007
15008 /* convert data format */
15009 if (convert_flags) {
15010 if (flags & RPC_VARARRAY)
15012 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15013 else
15015 rl.param[i].flags | RPC_OUTGOING,
15016 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15017 }
15018
15019 out_param_ptr += param_size;
15020 }
15021
15022 /* send return parameters */
15023 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15024 nc_out->header.routine_id = status;
15025 nc_out->header.param_size = param_size;
15026
15027 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15028
15029 /* convert header format (byte swapping) if necessary */
15030 if (convert_flags) {
15031 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15032 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15033 }
15034
15035 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15036 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15037
15038 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15039
15040 if (status < 0) {
15041 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15042 return RPC_NET_ERROR;
15043 }
15044
15045 /* print return buffer */
15046/*
15047 printf("Return buffer, ID %d:\n", routine_id);
15048 for (i=0; i<param_size ; i++)
15049 {
15050 status = (char) nc_out->param[i];
15051 printf("%02X ", status);
15052 if (i%8 == 7)
15053 printf("\n");
15054 }
15055*/
15056 /* return SS_EXIT if RPC_EXIT is called */
15057 if (routine_id == RPC_ID_EXIT)
15058 return SS_EXIT;
15059
15060 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15061 if (routine_id == RPC_ID_SHUTDOWN)
15062 return RPC_SHUTDOWN;
15063
15064 return RPC_SUCCESS;
15065}
15066
15067/********************************************************************/
15069/********************************************************************\
15070 Routine: rpc_test_rpc
15071
15072 Purpose: Test RPC parameters encoding and decoding
15073
15074 Input:
15075 none
15076
15077 Output:
15078 none
15079
15080 Function value:
15081 RPC_SUCCESS Successful completion
15082
15083\********************************************************************/
15084{
15085 int status = RPC_SUCCESS;
15086
15087 printf("rpc_test_rpc!\n");
15088
15089 int int_out = 0;
15090 int int_inout = 456;
15091
15092 char string_out[32];
15093 char string2_out[48];
15094
15095 char string_inout[25];
15096 strcpy(string_inout, "string_inout");
15097
15098 KEY struct_in;
15099
15100 struct_in.type = 111;
15101 struct_in.num_values = 222;
15102 strcpy(struct_in.name, "name");
15103 struct_in.last_written = 333;
15104
15107
15108 struct_inout.type = 111111;
15109 struct_inout.num_values = 222222;
15110 strcpy(struct_inout.name, "name_name");
15111 struct_inout.last_written = 333333;
15112
15114 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15115
15116 for (int i=0; i<10; i++) {
15117 dwordarray_inout[i] = i*10;
15118 }
15119
15120 char array_in[10];
15121
15122 for (size_t i=0; i<sizeof(array_in); i++) {
15123 array_in[i] = 'a' + i;
15124 }
15125
15126 char array_out[16];
15127 size_t array_out_size = sizeof(array_out);
15128
15130 123,
15131 &int_out,
15132 &int_inout,
15133 "test string",
15134 string_out, sizeof(string_out),
15135 string2_out, sizeof(string2_out),
15136 string_inout, sizeof(string_inout),
15137 &struct_in,
15138 &struct_out,
15139 &struct_inout,
15141 array_in, sizeof(array_in),
15143 );
15144
15145 printf("rpc_call(RPC_TEST2) status %d\n", status);
15146
15147 if (int_out != 789) {
15148 printf("int_out mismatch!\n");
15149 status = 0;
15150 }
15151
15152 if (int_inout != 456*2) {
15153 printf("int_inout mismatch!\n");
15154 status = 0;
15155 }
15156
15157 if (strcmp(string_out, "string_out") != 0) {
15158 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
15159 status = 0;
15160 }
15161
15162 if (strcmp(string2_out, "second string_out") != 0) {
15163 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
15164 status = 0;
15165 }
15166
15167 if (strcmp(string_inout, "return string_inout") != 0) {
15168 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
15169 status = 0;
15170 }
15171
15172 KEY* pkey;
15173
15174 pkey = &struct_in;
15175
15176 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15177
15178 pkey = &struct_out;
15179
15180 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
15181 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);
15182 status = 0;
15183 }
15184
15185 pkey = &struct_inout;
15186
15187 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
15188 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);
15189 status = 0;
15190 }
15191
15192#if 0
15193 if (dwordarray_inout_size != 4*5) {
15194 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
15195 status = 0;
15196 }
15197
15198 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
15199 printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
15200 }
15201#endif
15202
15203#if 0
15204 {RPC_TEST_CXX, "test_cxx",
15205 {{TID_INT32, RPC_IN},
15207 {TID_STRING, RPC_IN},
15211 {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
15212 {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
15213 {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
15217 {0}}},
15218#endif
15219
15220#if 0
15222#endif
15223
15224 return status;
15225}
15226
15227static std::atomic_bool gAllowedHostsEnabled(false);
15228static std::vector<std::string> gAllowedHosts;
15229static std::mutex gAllowedHostsMutex;
15230
15231/********************************************************************/
15233/********************************************************************\
15234 Routine: rpc_clear_allowed_hosts
15235
15236 Purpose: Clear list of allowed hosts and permit connections from anybody
15237
15238 Input:
15239 none
15240
15241 Output:
15242 none
15243
15244 Function value:
15245 RPC_SUCCESS Successful completion
15246
15247\********************************************************************/
15248{
15249 gAllowedHostsMutex.lock();
15250 gAllowedHosts.clear();
15251 gAllowedHostsEnabled = false;
15252 gAllowedHostsMutex.unlock();
15253 return RPC_SUCCESS;
15254}
15255
15256/********************************************************************/
15257INT rpc_add_allowed_host(const char *hostname)
15258/********************************************************************\
15259 Routine: rpc_add_allowed_host
15260
15261 Purpose: Permit connections from listed hosts only
15262
15263 Input:
15264 none
15265
15266 Output:
15267 none
15268
15269 Function value:
15270 RPC_SUCCESS Successful completion
15271 RPC_NO_MEMORY Too many allowed hosts
15272
15273\********************************************************************/
15274{
15275 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
15276
15277 gAllowedHostsMutex.lock();
15278 gAllowedHosts.push_back(hostname);
15279 gAllowedHostsEnabled = true;
15280 gAllowedHostsMutex.unlock();
15281
15282 return RPC_SUCCESS;
15283}
15284
15285/********************************************************************/
15286INT rpc_check_allowed_host(const char *hostname)
15287/********************************************************************\
15288 Routine: rpc_check_allowed_host
15289
15290 Purpose: Check if hostname is permitted to connect
15291
15292 Function value:
15293 RPC_SUCCESS hostname is permitted to connect
15294 RPC_NOT_REGISTERED hostname not permitted to connect
15295
15296\********************************************************************/
15297{
15298 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
15299
15301 return RPC_SUCCESS;
15302
15303 if (strcmp(hostname, "localhost") == 0)
15304 return RPC_SUCCESS;
15305
15306 if (strcmp(hostname, "localhost.localdomain") == 0)
15307 return RPC_SUCCESS;
15308
15309 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
15310 return RPC_SUCCESS;
15311
15312 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
15313 return RPC_SUCCESS;
15314
15316
15317 gAllowedHostsMutex.lock();
15318
15319 for (const auto& h: gAllowedHosts) {
15320 if (h == hostname) {
15322 break;
15323 }
15324 }
15325
15326 gAllowedHostsMutex.unlock();
15327
15328 //if (status != RPC_SUCCESS)
15329 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
15330
15331 return status;
15332}
15333
15334/*------------------------------------------------------------------*/
15336{
15337 std::string hostname;
15338
15339 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
15340
15341 if (status != SS_SUCCESS)
15342 return status;
15343
15344 status = rpc_check_allowed_host(hostname.c_str());
15345
15346 if (status == RPC_SUCCESS)
15347 return RPC_SUCCESS;
15348
15349 static std::atomic_int max_report(10);
15350 if (max_report > 0) {
15351 max_report--;
15352 if (max_report == 0) {
15353 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
15354 } else {
15355 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());
15356 }
15357 }
15358
15359 return RPC_NET_ERROR;
15360}
15361
15362/********************************************************************/
15364/********************************************************************\
15365
15366 Routine: rpc_server_accept
15367
15368 Purpose: Accept new incoming connections
15369
15370 Input:
15371 INT lscok Listen socket
15372
15373 Output:
15374 none
15375
15376 Function value:
15377 RPC_SUCCESS Successful completion
15378 RPC_NET_ERROR Error in socket call
15379 RPC_CONNCLOSED Connection was closed
15380 RPC_SHUTDOWN Listener shutdown
15381 RPC_EXCEED_BUFFER Not enough memory for network buffer
15382
15383\********************************************************************/
15384{
15385 INT i;
15386 INT sock;
15387 char version[NAME_LENGTH], v1[32];
15388 char experiment[NAME_LENGTH];
15389 INT port1, port2, port3;
15390 char *ptr;
15391 char net_buffer[256];
15392 struct linger ling;
15393
15394 static struct callback_addr callback;
15395
15396 if (lsock > 0) {
15397 sock = accept(lsock, NULL, NULL);
15398
15399 if (sock == -1)
15400 return RPC_NET_ERROR;
15401 } else {
15402 /* lsock is stdin -> already connected from inetd */
15403
15404 sock = lsock;
15405 }
15406
15407 /* check access control list */
15410
15411 if (status != RPC_SUCCESS) {
15412 ss_socket_close(&sock);
15413 return RPC_NET_ERROR;
15414 }
15415 }
15416
15417 /* receive string with timeout */
15418 i = recv_string(sock, net_buffer, 256, 10000);
15419 rpc_debug_printf("Received command: %s", net_buffer);
15420
15421 if (i > 0) {
15422 char command = (char) toupper(net_buffer[0]);
15423
15424 //printf("rpc_server_accept: command [%c]\n", command);
15425
15426 switch (command) {
15427 case 'S': {
15428
15429 /*----------- shutdown listener ----------------------*/
15430 ss_socket_close(&sock);
15431 return RPC_SHUTDOWN;
15432 }
15433 case 'I': {
15434
15435 /*----------- return available experiments -----------*/
15436#ifdef LOCAL_ROUTINES
15437 exptab_struct exptab;
15438 cm_read_exptab(&exptab); // thread safe!
15439 for (unsigned i=0; i<exptab.exptab.size(); i++) {
15440 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
15441 const char* str = exptab.exptab[i].name.c_str();
15442 send(sock, str, strlen(str) + 1, 0);
15443 }
15444 send(sock, "", 1, 0);
15445#endif
15446 ss_socket_close(&sock);
15447 break;
15448 }
15449 case 'C': {
15450
15451 /*----------- connect to experiment -----------*/
15452
15453 /* get callback information */
15454 callback.experiment[0] = 0;
15455 port1 = port2 = version[0] = 0;
15456
15457 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
15458
15459 /* parse string in format "C port1 port2 port3 version expt" */
15460 /* example: C 51046 45838 56832 2.0.0 alpha */
15461
15462 port1 = strtoul(net_buffer + 2, &ptr, 0);
15463 port2 = strtoul(ptr, &ptr, 0);
15464 port3 = strtoul(ptr, &ptr, 0);
15465
15466 while (*ptr == ' ')
15467 ptr++;
15468
15469 i = 0;
15470 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
15471 version[i++] = *ptr++;
15472
15473 // ensure that we do not overwrite buffer "version"
15474 assert(i < (int) sizeof(version));
15475 version[i] = 0;
15476
15477 // skip wjatever is left from the "version" string
15478 for (; *ptr != 0 && *ptr != ' ';)
15479 ptr++;
15480
15481 while (*ptr == ' ')
15482 ptr++;
15483
15484 i = 0;
15485 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
15486 experiment[i++] = *ptr++;
15487
15488 // ensure that we do not overwrite buffer "experiment"
15489 assert(i < (int) sizeof(experiment));
15490 experiment[i] = 0;
15491
15493
15494 /* print warning if version patch level doesn't agree */
15495 mstrlcpy(v1, version, sizeof(v1));
15496 if (strchr(v1, '.'))
15497 if (strchr(strchr(v1, '.') + 1, '.'))
15498 *strchr(strchr(v1, '.') + 1, '.') = 0;
15499
15500 char str[100];
15501 mstrlcpy(str, cm_get_version(), sizeof(str));
15502 if (strchr(str, '.'))
15503 if (strchr(strchr(str, '.') + 1, '.'))
15504 *strchr(strchr(str, '.') + 1, '.') = 0;
15505
15506 if (strcmp(v1, str) != 0) {
15507 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
15508 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
15509 }
15510
15515
15517
15518 if (status != SS_SUCCESS) {
15519 ss_socket_close(&sock);
15520 break;
15521 }
15522
15523#ifdef LOCAL_ROUTINES
15524 /* update experiment definition */
15525 exptab_struct exptab;
15526 cm_read_exptab(&exptab); // thread safe!
15527
15528 unsigned idx = 0;
15529 bool found = false;
15530 /* lookup experiment */
15531 if (equal_ustring(callback.experiment.c_str(), "Default")) {
15532 found = true;
15533 idx = 0;
15534 } else {
15535 for (idx = 0; idx < exptab.exptab.size(); idx++) {
15536 if (exptab.exptab[idx].name == callback.experiment) {
15537 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
15538 found = true;
15539 break;
15540 }
15541 }
15542 }
15543 }
15544
15545 if (!found) {
15546 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
15547
15548 send(sock, "2", 2, 0); /* 2 means exp. not found */
15549 ss_socket_close(&sock);
15550 break;
15551 }
15552
15553 callback.directory = exptab.exptab[idx].directory;
15554 callback.user = exptab.exptab[idx].user;
15555
15556 /* create a new process */
15557 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
15558 char debug_str[30];
15559
15564
15565 const char *mserver_path = rpc_get_mserver_path();
15566
15567 const char *argv[10];
15568 argv[0] = mserver_path;
15569 argv[1] = callback.host_name.c_str();
15570 argv[2] = host_port1_str;
15571 argv[3] = host_port2_str;
15572 argv[4] = host_port3_str;
15573 argv[5] = debug_str;
15574 argv[6] = callback.experiment.c_str();
15575 argv[7] = callback.directory.c_str();
15576 argv[8] = callback.user.c_str();
15577 argv[9] = NULL;
15578
15579 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
15580 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
15581 argv[9]);
15582
15584
15585 if (status != SS_SUCCESS) {
15586 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
15587
15588 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
15589 send(sock, str, strlen(str) + 1, 0);
15590 ss_socket_close(&sock);
15591 break;
15592 }
15593
15594 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
15595 send(sock, str, strlen(str) + 1, 0);
15596#endif // LOCAL_ROUTINES
15597 ss_socket_close(&sock);
15598
15599 break;
15600 }
15601 default: {
15602 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
15603 ss_socket_close(&sock);
15604 break;
15605 }
15606 }
15607 } else { /* if i>0 */
15608
15609 /* lingering needed for PCTCP */
15610 ling.l_onoff = 1;
15611 ling.l_linger = 0;
15612 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
15613 ss_socket_close(&sock);
15614 }
15615
15616 return RPC_SUCCESS;
15617}
15618
15619/********************************************************************/
15621/********************************************************************\
15622
15623 Routine: rpc_client_accept
15624
15625 Purpose: midas program accept new RPC connection (run transitions, etc)
15626
15627 Input:
15628 INT lsock Listen socket
15629
15630 Output:
15631 none
15632
15633 Function value:
15634 RPC_SUCCESS Successful completion
15635 RPC_NET_ERROR Error in socket call
15636 RPC_CONNCLOSED Connection was closed
15637 RPC_SHUTDOWN Listener shutdown
15638 RPC_EXCEED_BUFFER Not enough memory for network buffer
15639
15640\********************************************************************/
15641{
15642 INT i, status;
15644 std::string client_program;
15645 std::string host_name;
15646 INT convert_flags;
15647 char net_buffer[256], *p;
15648
15649 int sock = accept(lsock, NULL, NULL);
15650
15651 if (sock == -1)
15652 return RPC_NET_ERROR;
15653
15654 /* check access control list */
15657
15658 if (status != RPC_SUCCESS) {
15659 ss_socket_close(&sock);
15660 return RPC_NET_ERROR;
15661 }
15662 }
15663
15664 host_name = "(unknown)";
15665 client_program = "(unknown)";
15666
15667 /* receive string with timeout */
15668 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
15669 if (i <= 0) {
15670 ss_socket_close(&sock);
15671 return RPC_NET_ERROR;
15672 }
15673
15674 /* get remote computer info */
15675 p = strtok(net_buffer, " ");
15676 if (p != NULL) {
15677 client_hw_type = atoi(p);
15678 p = strtok(NULL, " ");
15679 }
15680 if (p != NULL) {
15681 //version = atoi(p);
15682 p = strtok(NULL, " ");
15683 }
15684 if (p != NULL) {
15685 client_program = p;
15686 p = strtok(NULL, " ");
15687 }
15688 if (p != NULL) {
15689 host_name = p;
15690 p = strtok(NULL, " ");
15691 }
15692
15693 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
15694
15696
15697 /* save information in _server_acception structure */
15698 sa->recv_sock = sock;
15699 sa->send_sock = 0;
15700 sa->event_sock = 0;
15702 sa->host_name = host_name;
15705 sa->watchdog_timeout = 0;
15706 sa->is_mserver = FALSE;
15707
15708 /* send my own computer id */
15710 std::string str = msprintf("%d %s", hw_type, cm_get_version());
15711 status = send(sock, str.c_str(), str.length() + 1, 0);
15712 if (status != (INT) str.length() + 1)
15713 return RPC_NET_ERROR;
15714
15716 sa->convert_flags = convert_flags;
15717
15719
15720 return RPC_SUCCESS;
15721}
15722
15723/********************************************************************/
15725/********************************************************************\
15726
15727 Routine: rpc_server_callback
15728
15729 Purpose: Callback a remote client. Setup _server_acception entry
15730 with optional conversion flags and establish two-way
15731 TCP connection.
15732
15733 Input:
15734 callback_addr pcallback Pointer to a callback structure
15735
15736 Output:
15737 none
15738
15739 Function value:
15740 RPC_SUCCESS Successful completion
15741
15742\********************************************************************/
15743{
15744 INT status;
15745 int recv_sock, send_sock, event_sock;
15746 char str[100];
15747 std::string client_program;
15749 INT convert_flags;
15750 char net_buffer[256];
15751 char *p;
15752 int flag;
15753
15754 /* copy callback information */
15756 //idx = callback.index;
15757
15758 std::string errmsg;
15759
15760 /* create new sockets for TCP */
15762
15763 if (status != SS_SUCCESS) {
15764 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());
15765 ss_socket_close(&recv_sock);
15766 //ss_socket_close(&send_sock);
15767 //ss_socket_close(&event_sock);
15768 return RPC_NET_ERROR;
15769 }
15770
15772
15773 if (status != SS_SUCCESS) {
15774 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());
15775 ss_socket_close(&recv_sock);
15776 ss_socket_close(&send_sock);
15777 //ss_socket_close(&event_sock);
15778 return RPC_NET_ERROR;
15779 }
15780
15782
15783 if (status != SS_SUCCESS) {
15784 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());
15785 ss_socket_close(&recv_sock);
15786 ss_socket_close(&send_sock);
15787 ss_socket_close(&event_sock);
15788 return RPC_NET_ERROR;
15789 }
15790#ifndef OS_ULTRIX /* crashes ULTRIX... */
15791 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
15792 flag = 2 * 1024 * 1024;
15793 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
15794 if (status != 0)
15795 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
15796 strerror(errno));
15797#endif
15798
15799 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
15800 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
15801 ss_socket_close(&recv_sock);
15802 ss_socket_close(&send_sock);
15803 ss_socket_close(&event_sock);
15804 return RPC_NET_ERROR;
15805 }
15806 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
15807
15808 /* get remote computer info */
15809 client_hw_type = strtoul(net_buffer, &p, 0);
15810
15811 while (*p == ' ')
15812 p++;
15813
15814 client_program = p;
15815
15816 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
15817
15818 std::string host_name;
15819
15821
15822 if (status != SS_SUCCESS)
15823 host_name = "unknown";
15824
15825 //printf("rpc_server_callback: mserver acception\n");
15826
15828
15829 /* save information in _server_acception structure */
15830 sa->recv_sock = recv_sock;
15831 sa->send_sock = send_sock;
15832 sa->event_sock = event_sock;
15834 sa->host_name = host_name;
15837 sa->watchdog_timeout = 0;
15838 sa->is_mserver = TRUE;
15839
15840 assert(_mserver_acception == NULL);
15841
15842 _mserver_acception = sa;
15843
15844 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
15845
15846 /* send my own computer id */
15848 sprintf(str, "%d", hw_type);
15849 send(recv_sock, str, strlen(str) + 1, 0);
15850
15852 sa->convert_flags = convert_flags;
15853
15855
15856 if (rpc_is_mserver()) {
15857 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
15858 }
15859
15860 return RPC_SUCCESS;
15861}
15862
15863
15864/********************************************************************/
15866/********************************************************************\
15867
15868 Routine: rpc_server_loop
15869
15870 Purpose: mserver main event loop
15871
15872\********************************************************************/
15873{
15874 while (1) {
15875 int status = ss_suspend(1000, 0);
15876
15877 if (status == SS_ABORT || status == SS_EXIT)
15878 break;
15879
15881 break;
15882
15883 /* check alarms, etc */
15885
15887 }
15888
15889 return RPC_SUCCESS;
15890}
15891
15892/********************************************************************/
15894/********************************************************************\
15895
15896 Routine: rpc_server_receive_rpc
15897
15898 Purpose: Receive rpc commands and execute them. Close the connection
15899 if client has broken TCP pipe.
15900
15901 Function value:
15902 RPC_SUCCESS Successful completion
15903 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15904 SS_EXIT Server connection was closed
15905 SS_ABORT Server connection was broken
15906
15907\********************************************************************/
15908{
15909 int status = 0;
15910 int remaining = 0;
15911
15912 char *buf = NULL;
15913 int bufsize = 0;
15914
15915 do {
15917
15918 if (n_received <= 0) {
15919 status = SS_ABORT;
15920 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
15921 goto error;
15922 }
15923
15924 status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
15925
15926 if (status == SS_ABORT) {
15927 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
15928 goto error;
15929 }
15930
15931 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
15932 if (rpc_is_mserver())
15933 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
15934 goto exit;
15935 }
15936
15937 } while (remaining);
15938
15939 if (buf) {
15940 free(buf);
15941 buf = NULL;
15942 bufsize = 0;
15943 }
15944
15945 return RPC_SUCCESS;
15946
15947 error:
15948
15949 {
15950 char str[80];
15951 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
15952 if (strchr(str, '.'))
15953 *strchr(str, '.') = 0;
15954 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
15955 }
15956
15957 exit:
15958
15960
15961 if (buf) {
15962 free(buf);
15963 buf = NULL;
15964 bufsize = 0;
15965 }
15966
15967 /* disconnect from experiment as MIDAS server */
15968 if (rpc_is_mserver()) {
15969 HNDLE hDB, hKey;
15970
15972
15973 /* only disconnect from experiment if previously connected.
15974 Necessary for pure RPC servers (RPC_SRVR) */
15975 if (hDB) {
15979
15981
15983 }
15984 }
15985
15986 bool is_mserver = sa->is_mserver;
15987
15988 sa->close();
15989
15990 /* signal caller a shutdonw */
15991 if (status == RPC_SHUTDOWN)
15992 return status;
15993
15994 /* only the mserver should stop on server connection closure */
15995 if (!is_mserver) {
15996 return SS_SUCCESS;
15997 }
15998
15999 return status;
16000}
16001
16002/********************************************************************/
16004/********************************************************************\
16005
16006 Routine: rpc_server_receive_event
16007
16008 Purpose: Receive event and dispatch it
16009
16010 Function value:
16011 RPC_SUCCESS Successful completion
16012 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
16013 SS_EXIT Server connection was closed
16014 SS_ABORT Server connection was broken
16015
16016\********************************************************************/
16017{
16018 int status = 0;
16019
16020 DWORD start_time = ss_millitime();
16021
16022 //
16023 // THIS IS NOT THREAD SAFE!!!
16024 //
16025 // IT IS ONLY USED BY THE MSERVER
16026 // MSERVER IS SINGLE-THREADED!!!
16027 //
16028
16029 static char *xbuf = NULL;
16030 static int xbufsize = 0;
16031 static bool xbufempty = true;
16032
16033 // short cut
16034 if (sa == NULL && xbufempty)
16035 return RPC_SUCCESS;
16036
16037 static bool recurse = false;
16038
16039 if (recurse) {
16040 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
16041 // do not do anything if we are called recursively
16042 // via recursive ss_suspend() or otherwise. K.O.
16043 if (xbufempty)
16044 return RPC_SUCCESS;
16045 else
16046 return BM_ASYNC_RETURN;
16047 }
16048
16049 recurse = true;
16050
16051 do {
16052 if (xbufempty && sa) {
16054
16055 if (n_received < 0) {
16056 status = SS_ABORT;
16057 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
16058 goto error;
16059 }
16060
16061 if (n_received == 0) {
16062 // no more data in the tcp socket
16063 recurse = false;
16064 return RPC_SUCCESS;
16065 }
16066
16067 xbufempty = false;
16068 }
16069
16070 if (xbufempty) {
16071 // no event in xbuf buffer
16072 recurse = false;
16073 return RPC_SUCCESS;
16074 }
16075
16076 /* send event to buffer */
16077 INT *pbh = (INT *) xbuf;
16078 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
16079
16080 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
16081
16082 //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);
16083
16084 if (status == SS_ABORT) {
16085 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
16086 goto error;
16087 }
16088
16089 if (status == BM_ASYNC_RETURN) {
16090 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
16091 recurse = false;
16092 return status;
16093 }
16094
16095 if (status != BM_SUCCESS) {
16096 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
16097 }
16098
16099 xbufempty = true;
16100
16101 /* repeat for maximum 0.5 sec */
16102 } while (ss_millitime() - start_time < 500);
16103
16104 recurse = false;
16105 return RPC_SUCCESS;
16106
16107 error:
16108
16109 {
16110 char str[80];
16111 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
16112 if (strchr(str, '.'))
16113 *strchr(str, '.') = 0;
16114 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
16115 }
16116
16117 //exit:
16118
16120
16121 /* disconnect from experiment as MIDAS server */
16122 if (rpc_is_mserver()) {
16123 HNDLE hDB, hKey;
16124
16126
16127 /* only disconnect from experiment if previously connected.
16128 Necessary for pure RPC servers (RPC_SRVR) */
16129 if (hDB) {
16133
16135
16137 }
16138 }
16139
16140 bool is_mserver = sa->is_mserver;
16141
16142 sa->close();
16143
16144 /* signal caller a shutdonw */
16145 if (status == RPC_SHUTDOWN)
16146 return status;
16147
16148 /* only the mserver should stop on server connection closure */
16149 if (!is_mserver) {
16150 return SS_SUCCESS;
16151 }
16152
16153 return status;
16154}
16155
16156
16157/********************************************************************/
16159/********************************************************************\
16160
16161 Routine: rpc_flush_event_socket
16162
16163 Purpose: Receive and en-buffer events from the mserver event socket
16164
16165 Function value:
16166 BM_SUCCESS Event socket is empty, all data was read an en-buffered
16167 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
16168 SS_EXIT Server connection was closed
16169 SS_ABORT Server connection was broken
16170
16171\********************************************************************/
16172{
16174
16175 //printf("ss_event_socket_has_data() returned %d\n", has_data);
16176
16177 if (has_data) {
16178 if (timeout_msec == BM_NO_WAIT) {
16179 return BM_ASYNC_RETURN;
16180 } else if (timeout_msec == BM_WAIT) {
16181 return BM_ASYNC_RETURN;
16182 } else {
16184 if (status == SS_ABORT || status == SS_EXIT)
16185 return status;
16186 return BM_ASYNC_RETURN;
16187 }
16188 }
16189
16191
16192 //printf("rpc_server_receive_event() status %d\n", status);
16193
16194 if (status == BM_ASYNC_RETURN) {
16195 return BM_ASYNC_RETURN;
16196 }
16197
16198 if (status == SS_ABORT || status == SS_EXIT)
16199 return status;
16200
16201 return BM_SUCCESS;
16202}
16203
16204/********************************************************************/
16206/********************************************************************\
16207
16208 Routine: rpc_server_shutdown
16209
16210 Purpose: Shutdown RPC server, abort all connections
16211
16212 Input:
16213 none
16214
16215 Output:
16216 none
16217
16218 Function value:
16219 RPC_SUCCESS Successful completion
16220
16221\********************************************************************/
16222{
16223 //printf("rpc_server_shutdown!\n");
16224
16225 struct linger ling;
16226
16227 /* close all open connections */
16228 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16229 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
16231 /* lingering needed for PCTCP */
16232 ling.l_onoff = 1;
16233 ling.l_linger = 0;
16234 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16236
16237 if (sa->send_sock) {
16238 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16240 }
16241
16242 if (sa->event_sock) {
16243 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16245 }
16246 }
16247 }
16248
16249 /* avoid memory leak */
16250 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16252 if (sa) {
16253 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
16254 if (sa == _mserver_acception) {
16255 // do not leave behind a stale pointer!
16257 }
16258 delete sa;
16260 }
16261 }
16262
16263 if (_rpc_registered) {
16266 }
16267
16268 /* free suspend structures */
16270
16271 return RPC_SUCCESS;
16272}
16273
16274
16275/********************************************************************/
16277/********************************************************************\
16278
16279 Routine: rpc_check_channels
16280
16281 Purpose: Check open rpc channels by sending watchdog messages
16282
16283 Input:
16284 none
16285
16286 Output:
16287 none
16288
16289 Function value:
16290 RPC_SUCCESS Channel is still alive
16291 RPC_NET_ERROR Connection is broken
16292
16293\********************************************************************/
16294{
16295 INT status;
16296 NET_COMMAND nc;
16298 struct timeval timeout;
16299
16300 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16301 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
16303 if (sa == NULL)
16304 continue;
16305
16306 if (sa->watchdog_timeout == 0) {
16307 continue;
16308 }
16309
16311 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
16312
16313 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
16314
16315 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
16316
16317 /* send a watchdog message */
16319 nc.header.param_size = 0;
16320
16321 int convert_flags = sa->convert_flags;
16322 if (convert_flags) {
16325 }
16326
16327 /* send the header to the client */
16328 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
16329
16330 if (i < 0) {
16331 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
16332 sa->prog_name.c_str(),
16333 sa->host_name.c_str(),
16334 sa->watchdog_timeout / 1000,
16335 i);
16336
16337 /* disconnect from experiment */
16338 if (rpc_is_mserver()) {
16340 return RPC_NET_ERROR;
16341 }
16342
16343 sa->close();
16344 return RPC_NET_ERROR;
16345 }
16346
16347 /* make some timeout checking */
16348 FD_ZERO(&readfds);
16349 FD_SET(sa->send_sock, &readfds);
16350 FD_SET(sa->recv_sock, &readfds);
16351
16352 timeout.tv_sec = sa->watchdog_timeout / 1000;
16353 timeout.tv_usec = (sa->watchdog_timeout % 1000) * 1000;
16354
16355 do {
16356 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
16357
16358 /* if an alarm signal was cought, restart select with reduced timeout */
16359 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
16360 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
16361
16362 } while (status == -1); /* dont return if an alarm signal was cought */
16363
16364 if (!FD_ISSET(sa->send_sock, &readfds) &&
16365 !FD_ISSET(sa->recv_sock, &readfds)) {
16366
16367 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
16368 sa->prog_name.c_str(),
16369 sa->host_name.c_str(),
16370 sa->watchdog_timeout / 1000);
16371
16372 /* disconnect from experiment */
16373 if (rpc_is_mserver()) {
16375 return RPC_NET_ERROR;
16376 }
16377
16378 sa->close();
16379 return RPC_NET_ERROR;
16380 }
16381
16382 /* receive result on send socket */
16383 if (FD_ISSET(sa->send_sock, &readfds)) {
16384 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
16385 if (i <= 0) {
16386 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
16387 sa->prog_name.c_str(),
16388 sa->host_name.c_str(),
16389 sa->watchdog_timeout / 1000,
16390 i);
16391
16392 /* disconnect from experiment */
16393 if (rpc_is_mserver()) {
16395 return RPC_NET_ERROR;
16396 }
16397
16398 sa->close();
16399 return RPC_NET_ERROR;
16400 }
16401 }
16402 }
16403 }
16404 }
16405
16406 return RPC_SUCCESS;
16407}
16408
16416/********************************************************************\
16417* *
16418* Bank functions *
16419* *
16420\********************************************************************/
16421
16422/********************************************************************/
16428void bk_init(void *event) {
16429 ((BANK_HEADER *) event)->data_size = 0;
16430 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
16431}
16432
16434#ifndef DOXYGEN_SHOULD_SKIP_THIS
16435
16436/********************************************************************/
16437BOOL bk_is32(const void *event)
16438/********************************************************************\
16439
16440 Routine: bk_is32
16441
16442 Purpose: Return true if banks inside event are 32-bit banks
16443
16444 Input:
16445 void *event pointer to the event
16446
16447 Output:
16448 none
16449
16450 Function value:
16451 none
16452
16453\********************************************************************/
16454{
16455 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
16456}
16457
16458/********************************************************************/
16459BOOL bk_is32a(const void *event)
16460/********************************************************************\
16461
16462 Routine: bk_is32a
16463
16464 Purpose: Return true if banks inside event are 32-bit banks
16465 and banks are 64-bit aligned
16466
16467 Input:
16468 void *event pointer to the event
16469
16470 Output:
16471 none
16472
16473 Function value:
16474 none
16475
16476\********************************************************************/
16477{
16478 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
16479}
16480
16482#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16483
16484/********************************************************************/
16491void bk_init32(void *event) {
16492 ((BANK_HEADER *) event)->data_size = 0;
16493 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
16494}
16495
16496/********************************************************************/
16504void bk_init32a(void *event) {
16505 ((BANK_HEADER *) event)->data_size = 0;
16507}
16508
16509/********************************************************************/
16517INT bk_size(const void *event) {
16518 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
16519}
16520
16521static void copy_bk_name(char* dst, const char* src)
16522{
16523 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
16524
16525 if (src[0] == 0) {
16526 // invalid empty name
16527 dst[0] = 0;
16528 dst[1] = 0;
16529 dst[2] = 0;
16530 dst[3] = 0;
16531 return;
16532 }
16533
16534 dst[0] = src[0];
16535
16536 if (src[1] == 0) {
16537 dst[1] = 0;
16538 dst[2] = 0;
16539 dst[3] = 0;
16540 return;
16541 }
16542
16543 dst[1] = src[1];
16544
16545 if (src[2] == 0) {
16546 dst[2] = 0;
16547 dst[3] = 0;
16548 return;
16549 }
16550
16551 dst[2] = src[2];
16552
16553 if (src[3] == 0) {
16554 dst[3] = 0;
16555 return;
16556 }
16557
16558 dst[3] = src[3];
16559}
16560
16561/********************************************************************/
16583void bk_create(void *event, const char *name, WORD type, void **pdata) {
16584 if (bk_is32a((BANK_HEADER *) event)) {
16585 if (((PTYPE) event & 0x07) != 0) {
16586 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
16587 return;
16588 }
16589 BANK32A *pbk32a;
16590
16591 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16592 copy_bk_name(pbk32a->name, name);
16593 pbk32a->type = type;
16594 pbk32a->data_size = 0;
16595 *pdata = pbk32a + 1;
16596 } else if (bk_is32((BANK_HEADER *) event)) {
16597 BANK32 *pbk32;
16598
16599 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16600 copy_bk_name(pbk32->name, name);
16601 pbk32->type = type;
16602 pbk32->data_size = 0;
16603 *pdata = pbk32 + 1;
16604 } else {
16605 BANK *pbk;
16606
16607 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16608 copy_bk_name(pbk->name, name);
16609 pbk->type = type;
16610 pbk->data_size = 0;
16611 *pdata = pbk + 1;
16612 }
16613}
16614
16616#ifndef DOXYGEN_SHOULD_SKIP_THIS
16617
16618/********************************************************************/
16626INT bk_copy(char *pevent, char *psrce, const char *bkname) {
16627
16628 INT status;
16631 BANK *psbkh;
16632 char *pdest;
16633 void *psdata;
16634
16635 // source pointing on the BANKxx
16636 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
16637 // Find requested bank
16639 // Return 0 if not found
16640 if (status != SUCCESS) return 0;
16641
16642 // Check bank type...
16643 // You cannot mix BANK and BANK32 so make sure all the FE use either
16644 // bk_init(pevent) or bk_init32(pevent).
16645 if (bk_is32a(psBkh)) {
16646
16647 // pointer to the source bank header
16648 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
16649 // Data size in the bank
16651
16652 // Get to the end of the event
16653 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16654 // Copy from BANK32 to end of Data
16655 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
16656 // Bring pointer to the next free location
16657 pdest += ALIGN8(bksze) + sizeof(BANK32A);
16658
16659 } else if (bk_is32(psBkh)) {
16660
16661 // pointer to the source bank header
16662 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
16663 // Data size in the bank
16665
16666 // Get to the end of the event
16667 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16668 // Copy from BANK32 to end of Data
16669 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
16670 // Bring pointer to the next free location
16671 pdest += ALIGN8(bksze) + sizeof(BANK32);
16672
16673 } else {
16674
16675 // pointer to the source bank header
16676 psbkh = ((BANK *) psdata - 1);
16677 // Data size in the bank
16678 bksze = psbkh->data_size;
16679
16680 // Get to the end of the event
16681 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16682 // Copy from BANK to end of Data
16683 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
16684 // Bring pointer to the next free location
16685 pdest += ALIGN8(bksze) + sizeof(BANK);
16686 }
16687
16688 // Close bank (adjust BANK_HEADER size)
16689 bk_close(pevent, pdest);
16690 // Adjust EVENT_HEADER size
16691 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
16692 return SUCCESS;
16693}
16694
16695/********************************************************************/
16696int bk_delete(void *event, const char *name)
16697/********************************************************************\
16698
16699 Routine: bk_delete
16700
16701 Purpose: Delete a MIDAS bank inside an event
16702
16703 Input:
16704 void *event pointer to the event
16705 char *name Name of bank (exactly four letters)
16706
16707 Function value:
16708 CM_SUCCESS Bank has been deleted
16709 0 Bank has not been found
16710
16711\********************************************************************/
16712{
16713 BANK *pbk;
16714 DWORD dname;
16715 int remaining;
16716
16717 if (bk_is32a((BANK_HEADER *) event)) {
16718 /* locate bank */
16719 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16720 copy_bk_name((char *) &dname, name);
16721 do {
16722 if (*((DWORD *) pbk32a->name) == dname) {
16723 /* bank found, delete it */
16724 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16725 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16726
16727 /* reduce total event size */
16728 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
16729
16730 /* copy remaining bytes */
16731 if (remaining > 0)
16732 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
16733 return CM_SUCCESS;
16734 }
16735
16736 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16737 } while ((DWORD) ((char *) pbk32a - (char *) event) <
16738 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16739 } else if (bk_is32((BANK_HEADER *) event)) {
16740 /* locate bank */
16741 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16742 copy_bk_name((char *) &dname, name);
16743 do {
16744 if (*((DWORD *) pbk32->name) == dname) {
16745 /* bank found, delete it */
16746 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16747 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16748
16749 /* reduce total event size */
16750 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
16751
16752 /* copy remaining bytes */
16753 if (remaining > 0)
16754 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
16755 return CM_SUCCESS;
16756 }
16757
16758 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16759 } while ((DWORD) ((char *) pbk32 - (char *) event) <
16760 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16761 } else {
16762 /* locate bank */
16763 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16764 copy_bk_name((char *) &dname, name);
16765 do {
16766 if (*((DWORD *) pbk->name) == dname) {
16767 /* bank found, delete it */
16768 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16769 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16770
16771 /* reduce total event size */
16772 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
16773
16774 /* copy remaining bytes */
16775 if (remaining > 0)
16776 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
16777 return CM_SUCCESS;
16778 }
16779
16780 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16781 } while ((DWORD) ((char *) pbk - (char *) event) <
16782 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16783 }
16784
16785 return 0;
16786}
16787
16789#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16790
16791/********************************************************************/
16802INT bk_close(void *event, void *pdata) {
16803 if (bk_is32a((BANK_HEADER *) event)) {
16804 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16805 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
16806 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
16807 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]);
16808 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
16809 return pbk32a->data_size;
16810 } else if (bk_is32((BANK_HEADER *) event)) {
16811 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16812 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
16813 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
16814 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]);
16815 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
16816 return pbk32->data_size;
16817 } else {
16818 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16819 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
16820 if (size > 0xFFFF) {
16821 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16822 size = 0;
16823 }
16824 pbk->data_size = (WORD) (size);
16825 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
16826 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]);
16827 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
16828 if (size > 0xFFFF) {
16829 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16830 size = 0;
16831 }
16832 ((BANK_HEADER *) event)->data_size = size;
16833 return pbk->data_size;
16834 }
16835}
16836
16837/********************************************************************/
16862INT bk_list(const void *event, char *bklist) { /* Full event */
16863 INT nbk;
16864 BANK *pmbk = NULL;
16865 BANK32 *pmbk32 = NULL;
16866 BANK32A *pmbk32a = NULL;
16867 char *pdata;
16868
16869 /* compose bank list */
16870 bklist[0] = 0;
16871 nbk = 0;
16872 do {
16873 /* scan all banks for bank name only */
16874 if (bk_is32a(event)) {
16875 bk_iterate32a(event, &pmbk32a, &pdata);
16876 if (pmbk32a == NULL)
16877 break;
16878 } else if (bk_is32(event)) {
16879 bk_iterate32(event, &pmbk32, &pdata);
16880 if (pmbk32 == NULL)
16881 break;
16882 } else {
16883 bk_iterate(event, &pmbk, &pdata);
16884 if (pmbk == NULL)
16885 break;
16886 }
16887 nbk++;
16888
16889 if (nbk > BANKLIST_MAX) {
16890 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
16891 return (nbk - 1);
16892 }
16893 if (bk_is32a(event))
16894 strncat(bklist, (char *) pmbk32a->name, 4);
16895 else if (bk_is32(event))
16896 strncat(bklist, (char *) pmbk32->name, 4);
16897 else
16898 strncat(bklist, (char *) pmbk->name, 4);
16899 } while (1);
16900 return (nbk);
16901}
16902
16903/********************************************************************/
16911INT bk_locate(const void *event, const char *name, void *pdata) {
16912 BANK *pbk;
16913 BANK32 *pbk32;
16914 BANK32A *pbk32a;
16915 DWORD dname;
16916
16917 if (bk_is32a(event)) {
16918 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16919 copy_bk_name((char *) &dname, name);
16920 while ((DWORD) ((char *) pbk32a - (char *) event) <
16921 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16922 if (*((DWORD *) pbk32a->name) == dname) {
16923 *((void **) pdata) = pbk32a + 1;
16924 if (tid_size[pbk32a->type & 0xFF] == 0)
16925 return pbk32a->data_size;
16926 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16927 }
16928 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16929 }
16930 } else if (bk_is32(event)) {
16931 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16932 copy_bk_name((char *) &dname, name);
16933 while ((DWORD) ((char *) pbk32 - (char *) event) <
16934 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16935 if (*((DWORD *) pbk32->name) == dname) {
16936 *((void **) pdata) = pbk32 + 1;
16937 if (tid_size[pbk32->type & 0xFF] == 0)
16938 return pbk32->data_size;
16939 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
16940 }
16941 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16942 }
16943 } else {
16944 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16945 copy_bk_name((char *) &dname, name);
16946 while ((DWORD) ((char *) pbk - (char *) event) <
16947 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16948 if (*((DWORD *) pbk->name) == dname) {
16949 *((void **) pdata) = pbk + 1;
16950 if (tid_size[pbk->type & 0xFF] == 0)
16951 return pbk->data_size;
16952 return pbk->data_size / tid_size[pbk->type & 0xFF];
16953 }
16954 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16955 }
16956
16957 }
16958
16959 /* bank not found */
16960 *((void **) pdata) = NULL;
16961 return 0;
16962}
16963
16964/********************************************************************/
16974INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
16975 DWORD dname;
16976
16977 if (bk_is32a(pbkh)) {
16978 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
16979 copy_bk_name((char *) &dname, name);
16980 do {
16981 if (*((DWORD *) pbk32a->name) == dname) {
16982 *((void **) pdata) = pbk32a + 1;
16983 if (tid_size[pbk32a->type & 0xFF] == 0)
16984 *bklen = pbk32a->data_size;
16985 else
16986 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16987
16988 *bktype = pbk32a->type;
16989 return 1;
16990 }
16991 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16992 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
16993 } else if (bk_is32(pbkh)) {
16994 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
16995 copy_bk_name((char *) &dname, name);
16996 do {
16997 if (*((DWORD *) pbk32->name) == dname) {
16998 *((void **) pdata) = pbk32 + 1;
16999 if (tid_size[pbk32->type & 0xFF] == 0)
17000 *bklen = pbk32->data_size;
17001 else
17002 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
17003
17004 *bktype = pbk32->type;
17005 return 1;
17006 }
17007 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17008 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17009 } else {
17010 BANK *pbk = (BANK *) (pbkh + 1);
17011 copy_bk_name((char *) &dname, name);
17012 do {
17013 if (*((DWORD *) pbk->name) == dname) {
17014 *((void **) pdata) = pbk + 1;
17015 if (tid_size[pbk->type & 0xFF] == 0)
17016 *bklen = pbk->data_size;
17017 else
17018 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
17019
17020 *bktype = pbk->type;
17021 return 1;
17022 }
17023 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17024 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17025 }
17026
17027 /* bank not found */
17028 *((void **) pdata) = NULL;
17029 return 0;
17030}
17031
17032/********************************************************************/
17068INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
17069 if (*pbk == NULL)
17070 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
17071 else
17072 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17073
17074 *((void **) pdata) = (*pbk) + 1;
17075
17076 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17077 *pbk = *((BANK **) pdata) = NULL;
17078 return 0;
17079 }
17080
17081 return (*pbk)->data_size;
17082}
17083
17084
17086#ifndef DOXYGEN_SHOULD_SKIP_THIS
17087
17088/********************************************************************/
17089INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
17090/********************************************************************\
17091
17092 Routine: bk_iterate32
17093
17094 Purpose: Iterate through 32 bit MIDAS banks inside an event
17095
17096 Input:
17097 void *event pointer to the event
17098 BANK32 **pbk32 must be NULL for the first call to bk_iterate
17099
17100 Output:
17101 BANK32 **pbk32 pointer to the bank header
17102 void *pdata pointer to data area of the bank
17103
17104 Function value:
17105 INT size of the bank in bytes
17106
17107\********************************************************************/
17108{
17109 if (*pbk == NULL)
17110 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
17111 else
17112 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17113
17114 *((void **) pdata) = (*pbk) + 1;
17115
17116 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17117 *pbk = NULL;
17118 pdata = NULL;
17119 return 0;
17120 }
17121
17122 return (*pbk)->data_size;
17123}
17124
17125INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
17126/********************************************************************\
17127
17128 Routine: bk_iterate32a
17129
17130 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
17131
17132 Input:
17133 void *event pointer to the event
17134 BANK32A **pbk32a must be NULL for the first call to bk_iterate
17135
17136 Output:
17137 BANK32A **pbk32 pointer to the bank header
17138 void *pdata pointer to data area of the bank
17139
17140 Function value:
17141 INT size of the bank in bytes
17142
17143\********************************************************************/
17144{
17145 if (*pbk32a == NULL)
17146 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
17147 else
17148 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
17149
17150 *((void **) pdata) = (*pbk32a) + 1;
17151
17152 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17153 *pbk32a = NULL;
17154 pdata = NULL;
17155 return 0;
17156 }
17157
17158 return (*pbk32a)->data_size;
17159}
17160
17162#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17163
17164/********************************************************************/
17179INT bk_swap(void *event, BOOL force) {
17181 BANK *pbk;
17182 BANK32 *pbk32;
17183 BANK32A *pbk32a;
17184 void *pdata;
17185 WORD type;
17186
17187 pbh = (BANK_HEADER *) event;
17188
17189 /* only swap if flags in high 16-bit */
17190 if (pbh->flags < 0x10000 && !force)
17191 return 0;
17192
17193 /* swap bank header */
17194 DWORD_SWAP(&pbh->data_size);
17195 DWORD_SWAP(&pbh->flags);
17196
17197 pbk = (BANK *) (pbh + 1);
17198 pbk32 = (BANK32 *) pbk;
17199 pbk32a = (BANK32A *) pbk;
17200
17201 /* scan event */
17202 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
17203 /* swap bank header */
17204 if (bk_is32a(event)) {
17205 DWORD_SWAP(&pbk32a->type);
17206 DWORD_SWAP(&pbk32a->data_size);
17207 pdata = pbk32a + 1;
17208 type = (WORD) pbk32a->type;
17209 } else if (bk_is32(event)) {
17210 DWORD_SWAP(&pbk32->type);
17211 DWORD_SWAP(&pbk32->data_size);
17212 pdata = pbk32 + 1;
17213 type = (WORD) pbk32->type;
17214 } else {
17215 WORD_SWAP(&pbk->type);
17216 WORD_SWAP(&pbk->data_size);
17217 pdata = pbk + 1;
17218 type = pbk->type;
17219 }
17220
17221 /* pbk points to next bank */
17222 if (bk_is32a(event)) {
17223 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17224 pbk = (BANK *) pbk32a;
17225 } else if (bk_is32(event)) {
17226 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17227 pbk = (BANK *) pbk32;
17228 } else {
17229 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17230 pbk32 = (BANK32 *) pbk;
17231 }
17232
17233 switch (type) {
17234 case TID_UINT16:
17235 case TID_INT16:
17236 while ((char *) pdata < (char *) pbk) {
17238 pdata = (void *) (((WORD *) pdata) + 1);
17239 }
17240 break;
17241
17242 case TID_UINT32:
17243 case TID_INT32:
17244 case TID_BOOL:
17245 case TID_FLOAT:
17246 while ((char *) pdata < (char *) pbk) {
17248 pdata = (void *) (((DWORD *) pdata) + 1);
17249 }
17250 break;
17251
17252 case TID_DOUBLE:
17253 case TID_INT64:
17254 case TID_UINT64:
17255 while ((char *) pdata < (char *) pbk) {
17257 pdata = (void *) (((double *) pdata) + 1);
17258 }
17259 break;
17260 }
17261 }
17262
17263 return CM_SUCCESS;
17264}
17265
/* end of bkfunctionc */
17269
17270
17277#ifndef DOXYGEN_SHOULD_SKIP_THIS
17278/********************************************************************/
17279
17280/********************************************************************\
17281* *
17282* Ring buffer functions *
17283* *
17284* Provide an inter-thread buffer scheme for handling front-end *
17285* events. This code allows concurrent data acquisition, calibration *
17286* and network transfer on a multi-CPU machine. One thread reads *
17287* out the data, passes it vis the ring buffer functions *
17288* to another thread running on the other CPU, which can then *
17289* calibrate and/or send the data over the network. *
17290* *
17291\********************************************************************/
17292
17293typedef struct {
17294 unsigned char *buffer;
17295 unsigned int size;
17296 unsigned int max_event_size;
17297 unsigned char *rp;
17298 unsigned char *wp;
17299 unsigned char *ep;
17300} RING_BUFFER;
17301
17302#define MAX_RING_BUFFER 100
17303
17305
17306static volatile int _rb_nonblocking = 0;
17307
17309#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17310
17311/********************************************************************/
17318/********************************************************************\
17319
17320 Routine: rb_set_nonblocking
17321
17322 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17323 environments for stopping all theads without deadlock
17324
17325 Input:
17326 NONE
17327
17328 Output:
17329 NONE
17330
17331 Function value:
17332 DB_SUCCESS Successful completion
17333
17334\********************************************************************/
17335{
17336 _rb_nonblocking = 1;
17337
17338 return DB_SUCCESS;
17339}
17340
17341/********************************************************************/
17358int rb_create(int size, int max_event_size, int *handle)
17359/********************************************************************\
17360
17361 Routine: rb_create
17362
17363 Purpose: Create a ring buffer with a given size
17364
17365 Input:
17366 int size Size of ring buffer, must be larger than
17367 2*max_event_size
17368 int max_event_size Maximum event size to be placed into
17369 ring buffer
17370 Output:
17371 int *handle Handle to ring buffer
17372
17373 Function value:
17374 DB_SUCCESS Successful completion
17375 DB_NO_MEMORY Maximum number of ring buffers exceeded
17376 DB_INVALID_PARAM Invalid event size specified
17377
17378\********************************************************************/
17379{
17380 int i;
17381
17382 for (i = 0; i < MAX_RING_BUFFER; i++)
17383 if (rb[i].buffer == NULL)
17384 break;
17385
17386 if (i == MAX_RING_BUFFER)
17387 return DB_NO_MEMORY;
17388
17389 if (size < max_event_size * 2)
17390 return DB_INVALID_PARAM;
17391
17392 memset(&rb[i], 0, sizeof(RING_BUFFER));
17393 rb[i].buffer = (unsigned char *) M_MALLOC(size);
17394 assert(rb[i].buffer);
17395 rb[i].size = size;
17397 rb[i].rp = rb[i].buffer;
17398 rb[i].wp = rb[i].buffer;
17399 rb[i].ep = rb[i].buffer;
17400
17401 *handle = i + 1;
17402
17403 return DB_SUCCESS;
17404}
17405
17406/********************************************************************/
17412int rb_delete(int handle)
17413/********************************************************************\
17414
17415 Routine: rb_delete
17416
17417 Purpose: Delete a ring buffer
17418
17419 Input:
17420 none
17421 Output:
17422 int handle Handle to ring buffer
17423
17424 Function value:
17425 DB_SUCCESS Successful completion
17426
17427\********************************************************************/
17428{
17429 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17430 return DB_INVALID_HANDLE;
17431
17432 M_FREE(rb[handle - 1].buffer);
17433 rb[handle - 1].buffer = NULL;
17434 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
17435
17436 return DB_SUCCESS;
17437}
17438
17439/********************************************************************/
17449int rb_get_wp(int handle, void **p, int millisec)
17450/********************************************************************\
17451
17452Routine: rb_get_wp
17453
17454 Purpose: Retrieve write pointer where new data can be written
17455
17456 Input:
17457 int handle Ring buffer handle
17458 int millisec Optional timeout in milliseconds if
17459 buffer is full. Zero to not wait at
17460 all (non-blocking)
17461
17462 Output:
17463 char **p Write pointer
17464
17465 Function value:
17466 DB_SUCCESS Successful completion
17467
17468\********************************************************************/
17469{
17470 int h, i;
17471 unsigned char *rp;
17472
17473 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17474 return DB_INVALID_HANDLE;
17475
17476 h = handle - 1;
17477
17478 for (i = 0; i <= millisec / 10; i++) {
17479
17480 rp = rb[h].rp; // keep local copy for convenience
17481
17482 /* check if enough size for wp >= rp without wrap-around */
17483 if (rb[h].wp >= rp
17484 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17485 *p = rb[h].wp;
17486 return DB_SUCCESS;
17487 }
17488
17489 /* check if enough size for wp >= rp with wrap-around */
17490 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17491 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
17492 *p = rb[h].wp;
17493 return DB_SUCCESS;
17494 }
17495
17496 /* check if enough size for wp < rp */
17497 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17498 *p = rb[h].wp;
17499 return DB_SUCCESS;
17500 }
17501
17502 if (millisec == 0)
17503 return DB_TIMEOUT;
17504
17505 if (_rb_nonblocking)
17506 return DB_TIMEOUT;
17507
17508 /* wait one time slice */
17509 ss_sleep(10);
17510 }
17511
17512 return DB_TIMEOUT;
17513}
17514
17515/********************************************************************/
17524int rb_increment_wp(int handle, int size)
17525/********************************************************************\
17526
17527 Routine: rb_increment_wp
17528
17529 Purpose: Increment current write pointer, making the data at
17530 the write pointer available to the receiving thread
17531
17532 Input:
17533 int handle Ring buffer handle
17534 int size Number of bytes placed at the WP
17535
17536 Output:
17537 NONE
17538
17539 Function value:
17540 DB_SUCCESS Successful completion
17541 DB_INVALID_PARAM Event size too large or invalid handle
17542\********************************************************************/
17543{
17544 int h;
17545 unsigned char *new_wp;
17546
17547 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17548 return DB_INVALID_HANDLE;
17549
17550 h = handle - 1;
17551
17552 if ((DWORD) size > rb[h].max_event_size) {
17553 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
17554 size/1024/1024, rb[h].max_event_size/1024/1024);
17555 abort();
17556 }
17557
17558 new_wp = rb[h].wp + size;
17559
17560 /* wrap around wp if not enough space */
17561 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17562 rb[h].ep = new_wp;
17563 new_wp = rb[h].buffer;
17564 assert(rb[h].rp != rb[h].buffer);
17565 } else
17566 if (new_wp > rb[h].ep)
17567 rb[h].ep = new_wp;
17568
17569 rb[h].wp = new_wp;
17570
17571 return DB_SUCCESS;
17572}
17573
17574/********************************************************************/
17590int rb_get_rp(int handle, void **p, int millisec)
17591/********************************************************************\
17592
17593 Routine: rb_get_rp
17594
17595 Purpose: Obtain the current read pointer at which new data is
17596 available with optional timeout
17597
17598 Input:
17599 int handle Ring buffer handle
17600 int millisec Optional timeout in milliseconds if
17601 buffer is full. Zero to not wait at
17602 all (non-blocking)
17603
17604 Output:
17605 char **p Address of pointer pointing to newly
17606 available data. If p == NULL, only
17607 return status.
17608
17609 Function value:
17610 DB_SUCCESS Successful completion
17611
17612\********************************************************************/
17613{
17614 int i, h;
17615
17616 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17617 return DB_INVALID_HANDLE;
17618
17619 h = handle - 1;
17620
17621 for (i = 0; i <= millisec / 10; i++) {
17622
17623 if (rb[h].wp != rb[h].rp) {
17624 if (p != NULL)
17625 *p = rb[handle - 1].rp;
17626 return DB_SUCCESS;
17627 }
17628
17629 if (millisec == 0)
17630 return DB_TIMEOUT;
17631
17632 if (_rb_nonblocking)
17633 return DB_TIMEOUT;
17634
17635 /* wait one time slice */
17636 ss_sleep(10);
17637 }
17638
17639 return DB_TIMEOUT;
17640}
17641
17642/********************************************************************/
17652int rb_increment_rp(int handle, int size)
17653/********************************************************************\
17654
17655 Routine: rb_increment_rp
17656
17657 Purpose: Increment current read pointer, freeing up space for
17658 the writing thread.
17659
17660 Input:
17661 int handle Ring buffer handle
17662 int size Number of bytes to free up at current
17663 read pointer
17664
17665 Output:
17666 NONE
17667
17668 Function value:
17669 DB_SUCCESS Successful completion
17670 DB_INVALID_PARAM Event size too large or invalid handle
17671
17672\********************************************************************/
17673{
17674 int h;
17675
17676 unsigned char *new_rp;
17677 unsigned char *ep;
17678
17679 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17680 return DB_INVALID_HANDLE;
17681
17682 h = handle - 1;
17683
17684 if ((DWORD) size > rb[h].max_event_size)
17685 return DB_INVALID_PARAM;
17686
17687 new_rp = rb[h].rp + size;
17688 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
17689
17690 /* wrap around if end pointer reached */
17691 if (new_rp >= ep && rb[h].wp < ep)
17692 new_rp = rb[h].buffer;
17693
17694 rb[handle - 1].rp = new_rp;
17695
17696 return DB_SUCCESS;
17697}
17698
17699/********************************************************************/
17707int rb_get_buffer_level(int handle, int *n_bytes)
17708/********************************************************************\
17709
17710 Routine: rb_get_buffer_level
17711
17712 Purpose: Return number of bytes in a ring buffer
17713
17714 Input:
17715 int handle Handle of the buffer to get the info
17716
17717 Output:
17718 int *n_bytes Number of bytes in buffer
17719
17720 Function value:
17721 DB_SUCCESS Successful completion
17722 DB_INVALID_HANDLE Buffer handle is invalid
17723
17724\********************************************************************/
17725{
17726 int h;
17727
17728 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17729 return DB_INVALID_HANDLE;
17730
17731 h = handle - 1;
17732
17733 if (rb[h].wp >= rb[h].rp)
17734 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
17735 else
17736 *n_bytes =
17737 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
17738
17739 return DB_SUCCESS;
17740}
17741
/* end of rbfunctionc */
17743
17744
17746{
17747 if (format == FORMAT_FIXED) {
17748 int status;
17749 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
17750 if (status != DB_SUCCESS) {
17751 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
17752 return status;
17753 }
17754 return SUCCESS;
17755 } else if (format == FORMAT_MIDAS) {
17756 INT size, i, status, n_data;
17757 int n;
17758 char *pdata, *pdata0;
17759
17760 char name[5];
17762 BANK *pbk;
17763 BANK32 *pbk32;
17764 BANK32A *pbk32a;
17765 DWORD bkname;
17766 WORD bktype;
17768 KEY key;
17769
17770 pbh = (BANK_HEADER *) (pevent + 1);
17771 pbk = NULL;
17772 pbk32 = NULL;
17773 pbk32a = NULL;
17774
17775 /* count number of banks */
17776 for (n=0 ; ; n++) {
17777 if (bk_is32a(pbh)) {
17779 if (pbk32a == NULL)
17780 break;
17781 } else if (bk_is32(pbh)) {
17783 if (pbk32 == NULL)
17784 break;
17785 } else {
17786 bk_iterate(pbh, &pbk, &pdata);
17787 if (pbk == NULL)
17788 break;
17789 }
17790 }
17791
17792 /* build array of keys */
17793 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
17794
17795 pbk = NULL;
17796 pbk32 = NULL;
17797 n = 0;
17798 do {
17799 /* scan all banks */
17800 if (bk_is32a(pbh)) {
17801 size = bk_iterate32a(pbh, &pbk32a, &pdata);
17802 if (pbk32a == NULL)
17803 break;
17804 bkname = *((DWORD *) pbk32a->name);
17805 bktype = (WORD) pbk32a->type;
17806 } else if (bk_is32(pbh)) {
17807 size = bk_iterate32(pbh, &pbk32, &pdata);
17808 if (pbk32 == NULL)
17809 break;
17810 bkname = *((DWORD *) pbk32->name);
17811 bktype = (WORD) pbk32->type;
17812 } else {
17813 size = bk_iterate(pbh, &pbk, &pdata);
17814 if (pbk == NULL)
17815 break;
17816 bkname = *((DWORD *) pbk->name);
17817 bktype = (WORD) pbk->type;
17818 }
17819
17820 n_data = size;
17821 if (rpc_tid_size(bktype & 0xFF))
17822 n_data /= rpc_tid_size(bktype & 0xFF);
17823
17824 /* get bank key */
17825 *((DWORD *) name) = bkname;
17826 name[4] = 0;
17827 /* record the start of the data in case it is struct */
17828 pdata0 = pdata;
17829 if (bktype == TID_STRUCT) {
17831 if (status != DB_SUCCESS) {
17832 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
17833 continue;
17834 }
17835
17836 /* write structured bank */
17837 for (i = 0;; i++) {
17840 break;
17841
17842 db_get_key(hDB, hKeyl, &key);
17843
17844 /* adjust for alignment */
17845 if (key.type != TID_STRING && key.type != TID_LINK)
17847
17849 if (status != DB_SUCCESS) {
17850 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17851 continue;
17852 }
17853 hKeys[n++] = hKeyl;
17854
17855 /* shift data pointer to next item */
17857 }
17858 } else {
17859 /* write variable length bank */
17861 if (status != DB_SUCCESS) {
17863 if (status != DB_SUCCESS) {
17864 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);
17865 continue;
17866 }
17868 if (status != DB_SUCCESS) {
17869 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);
17870 continue;
17871 }
17872 }
17873 if (n_data > 0) {
17874 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
17875 if (status != DB_SUCCESS) {
17876 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17877 }
17878 hKeys[n++] = hKeyRoot;
17879 }
17880 }
17881 } while (1);
17882
17883 /* notify all hot-lined clients in one go */
17885
17886 free(hKeys);
17887
17888 return SUCCESS;
17889 } else {
17890 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
17891 return CM_DB_ERROR;
17892 }
17893}
17894
17895/* emacs
17896 * Local Variables:
17897 * tab-width: 8
17898 * c-basic-offset: 3
17899 * indent-tabs-mode: nil
17900 * End:
17901 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11457
std::atomic_bool connected
Definition midas.cxx:11456
std::string client_name
Definition midas.cxx:11461
BUFFER * get_pbuf() const
Definition midas.cxx:3195
bool is_error() const
Definition midas.cxx:3185
int get_status() const
Definition midas.cxx:3190
bool is_locked() const
Definition midas.cxx:3180
bm_lock_buffer_guard(BUFFER *pbuf, bool do_not_lock=false)
Definition midas.cxx:3099
bm_lock_buffer_guard & operator=(const bm_lock_buffer_guard &)=delete
bm_lock_buffer_guard(const bm_lock_buffer_guard &)=delete
static bool exists(const std::string &name)
Definition odbxx.cxx:75
INT transition(INT run_number, char *error)
Definition consume.cxx:35
#define EXPRT
Definition esone.h:28
TRIGGER_SETTINGS ts
INT al_get_alarms(std::string *presult)
Definition alarm.cxx:853
INT al_check()
Definition alarm.cxx:614
void bk_init32a(void *event)
Definition midas.cxx:16504
INT bk_close(void *event, void *pdata)
Definition midas.cxx:16802
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:17125
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:16521
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:17179
BOOL bk_is32a(const void *event)
Definition midas.cxx:16459
int bk_delete(void *event, const char *name)
Definition midas.cxx:16696
BOOL bk_is32(const void *event)
Definition midas.cxx:16437
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:17089
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:16911
void bk_init(void *event)
Definition midas.cxx:16428
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:16862
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:16626
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:17068
void bk_init32(void *event)
Definition midas.cxx:16491
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:16583
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:16974
INT bk_size(const void *event)
Definition midas.cxx:16517
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8809
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:10505
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6739
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6211
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9700
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:10009
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:6486
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:10855
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10085
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7302
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11294
INT bm_delete_request(INT request_id)
Definition midas.cxx:8606
INT bm_close_all_buffers(void)
Definition midas.cxx:7265
INT bm_poll_event()
Definition midas.cxx:11148
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:8336
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6608
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6245
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10831
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9625
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9611
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8988
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6088
INT bm_empty_buffers()
Definition midas.cxx:11262
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8742
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9091
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:8487
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5944
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8162
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6329
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10672
INT bm_check_buffers()
Definition midas.cxx:10976
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9014
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:8314
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8540
static double _bm_mutex_timeout_sec
Definition midas.cxx:5942
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7118
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:9800
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8303
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:9526
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11303
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6278
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6037
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:8958
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8887
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6139
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8877
static DWORD _bm_max_event_size
Definition midas.cxx:5936
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6430
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10229
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8845
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8644
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:10286
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:9102
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7097
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9727
static int _bm_lock_timeout
Definition midas.cxx:5941
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:8913
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10753
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6413
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6057
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:10924
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:9414
INT cm_set_path(const char *path)
Definition midas.cxx:1511
INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number)
Definition midas.cxx:3615
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7422
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4187
INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:2847
static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition midas.cxx:3375
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7345
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4104
INT cm_yield(INT millisec)
Definition midas.cxx:5664
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3025
INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names)
Definition midas.cxx:2626
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3339
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11306
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2780
INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void(*func)(char *))
Definition midas.cxx:2292
static BUFFER_CLIENT * bm_get_my_client_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:6021
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2600
INT cm_start_watchdog_thread()
Definition midas.cxx:7377
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:10940
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
Definition midas.cxx:3047
static int tr_finish(HNDLE hDB, TrState *tr, int transition, int status, const char *errorstr)
Definition midas.cxx:4018
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3805
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:7926
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4051
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5308
INT cm_stop_watchdog_thread()
Definition midas.cxx:7392
std::string cm_asctime()
Definition midas.cxx:1426
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5812
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:2311
INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:1883
static int xbm_lock_buffer(BUFFER *pbuf)
Definition midas.cxx:7997
INT cm_periodic_tasks()
Definition midas.cxx:5601
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8059
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5407
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5260
INT cm_select_experiment_remote(const char *host_name, std::string *exp_name)
Definition midas.cxx:2733
INT cm_register_server(void)
Definition midas.cxx:3474
static BOOL _ctrlc_pressed
Definition midas.cxx:5461
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3426
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5478
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5745
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3358
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7632
void cm_check_connect(void)
Definition midas.cxx:2215
static BUFFER * bm_get_buffer(const char *who, INT buffer_handle, int *pstatus)
Definition midas.cxx:6644
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2684
std::string cm_expand_env(const char *str)
Definition midas.cxx:7732
std::string cm_get_client_name()
Definition midas.cxx:2073
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7968
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5483
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1559
static void rpc_client_shutdown()
Definition midas.cxx:12654
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7344
static exptab_struct _exptab
Definition midas.cxx:1619
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:1907
INT cm_disconnect_experiment(void)
Definition midas.cxx:2860
static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition midas.cxx:6174
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11070
int cm_get_exptab(const char *expname, std::string *dir, std::string *user)
Definition midas.cxx:1813
static void xcm_watchdog_thread()
Definition midas.cxx:7371
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7764
INT cm_synchronize(DWORD *seconds)
Definition midas.cxx:1383
std::string cm_get_exptab_filename()
Definition midas.cxx:1802
std::string cm_get_path()
Definition midas.cxx:1551
static DWORD _deferred_transition_mask
Definition midas.cxx:3841
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5865
void cm_test_expand_env()
Definition midas.cxx:7779
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3859
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2180
static INT _requested_transition
Definition midas.cxx:3840
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2148
const char * cm_get_version()
Definition midas.cxx:1490
INT cm_read_exptab(exptab_struct *exptab)
Definition midas.cxx:1628
static int bm_lock_buffer_write_cache(BUFFER *pbuf)
Definition midas.cxx:7947
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3691
INT cm_check_deferred_transition()
Definition midas.cxx:3911
std::string cm_get_experiment_name()
Definition midas.cxx:1594
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3745
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:3998
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1866
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7343
const char * cm_get_revision()
Definition midas.cxx:1498
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7351
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:2953
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5474
static INT tr_main_thread(void *param)
Definition midas.cxx:5276
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5463
INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3258
INT cm_time(DWORD *t)
Definition midas.cxx:1448
INT cm_transition_cleanup()
Definition midas.cxx:5289
INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3297
static int cm_transition_call_direct(TrClient *tr_client)
Definition midas.cxx:4422
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7542
INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
Definition midas.cxx:2972
static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4545
INT cm_set_experiment_name(const char *name)
Definition midas.cxx:1572
#define CM_SUCCESS
Definition midas.h:582
#define CM_UNDEF_EXP
Definition midas.h:586
#define CM_INVALID_TRANSITION
Definition midas.h:594
#define CM_SET_ERROR
Definition midas.h:583
#define CM_TRUNCATED
Definition midas.h:596
#define CM_DEFERRED_TRANSITION
Definition midas.h:591
#define CM_TRANSITION_IN_PROGRESS
Definition midas.h:592
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define CM_NO_CLIENT
Definition midas.h:584
#define CM_TRANSITION_CANCELED
Definition midas.h:597
#define CM_DB_ERROR
Definition midas.h:585
#define CM_VERSION_MISMATCH
Definition midas.h:587
#define CM_UNDEF_ENVIRON
Definition midas.h:590
#define BM_INVALID_PARAM
Definition midas.h:619
#define BM_MORE_EVENTS
Definition midas.h:620
#define BM_TRUNCATED
Definition midas.h:614
#define BM_NOT_FOUND
Definition midas.h:612
#define BM_NO_MEMORY
Definition midas.h:607
#define BM_INVALID_MIXING
Definition midas.h:621
#define BM_NO_SLOT
Definition midas.h:610
#define BM_ASYNC_RETURN
Definition midas.h:613
#define BM_INVALID_HANDLE
Definition midas.h:609
#define BM_INVALID_SIZE
Definition midas.h:624
#define BM_SUCCESS
Definition midas.h:605
#define BM_CORRUPTED
Definition midas.h:623
#define BM_NO_SHM
Definition midas.h:622
#define BM_CREATED
Definition midas.h:606
#define BM_NO_SEMAPHORE
Definition midas.h:611
#define DB_INVALID_HANDLE
Definition midas.h:635
#define DB_INVALID_PARAM
Definition midas.h:639
#define DB_SUCCESS
Definition midas.h:631
#define DB_NO_MEMORY
Definition midas.h:633
#define DB_NO_KEY
Definition midas.h:642
#define DB_TYPE_MISMATCH
Definition midas.h:645
#define DB_CREATED
Definition midas.h:632
#define DB_TIMEOUT
Definition midas.h:655
#define DB_NO_MORE_SUBKEYS
Definition midas.h:646
#define SS_SUCCESS
Definition midas.h:663
#define SS_ABORT
Definition midas.h:677
#define SS_FILE_ERROR
Definition midas.h:669
#define SS_TIMEOUT
Definition midas.h:674
#define SS_CREATED
Definition midas.h:664
#define SS_EXIT
Definition midas.h:678
#define RPC_SHUTDOWN
Definition midas.h:707
#define RPC_SUCCESS
Definition midas.h:698
#define RPC_EXCEED_BUFFER
Definition midas.h:703
#define RPC_INVALID_ID
Definition midas.h:706
#define RPC_DOUBLE_DEFINED
Definition midas.h:709
#define RPC_NOT_REGISTERED
Definition midas.h:704
#define RPC_MUTEX_TIMEOUT
Definition midas.h:710
#define RPC_TIMEOUT
Definition midas.h:702
#define RPC_NO_CONNECTION
Definition midas.h:700
#define RPC_NET_ERROR
Definition midas.h:701
#define AL_TRIGGERED
Definition midas.h:758
#define FE_ERR_HW
Definition midas.h:719
unsigned short int WORD
Definition mcstd.h:49
unsigned int DWORD
Definition mcstd.h:51
#define SUCCESS
Definition mcstd.h:54
#define TR_RESUME
Definition midas.h:408
#define GET_NONBLOCKING
Definition midas.h:322
#define TR_PAUSE
Definition midas.h:407
#define TID_DOUBLE
Definition midas.h:343
#define MT_LOG_STR
Definition midas.h:555
#define TID_KEY
Definition midas.h:349
#define GET_ALL
Definition midas.h:321
#define TID_BOOL
Definition midas.h:340
#define TRIGGER_ALL
Definition midas.h:538
#define TR_START
Definition midas.h:405
#define TR_SYNC
Definition midas.h:358
#define GET_RECENT
Definition midas.h:323
#define MT_ALL
Definition midas.h:549
#define BM_NO_WAIT
Definition midas.h:366
#define TID_UINT64
Definition midas.h:352
#define FORMAT_FIXED
Definition midas.h:314
#define MT_INFO_STR
Definition midas.h:552
#define TID_INT64
Definition midas.h:351
#define TR_MTHREAD
Definition midas.h:361
#define MT_INFO
Definition midas.h:543
#define TR_STARTABORT
Definition midas.h:409
#define RPC_HNDLE_CONNECT
Definition midas.h:394
#define STATE_STOPPED
Definition midas.h:305
#define MINFO
Definition midas.h:560
#define MODE_DELETE
Definition midas.h:372
#define MT_DEBUG_STR
Definition midas.h:553
#define MT_TALK
Definition midas.h:547
#define RPC_NO_REPLY
Definition midas.h:396
#define TID_STRUCT
Definition midas.h:348
#define MT_USER
Definition midas.h:545
#define RPC_HNDLE_MSERVER
Definition midas.h:393
#define MLOG
Definition midas.h:563
#define MT_USER_STR
Definition midas.h:554
#define TID_INT32
Definition midas.h:339
#define TR_DETACH
Definition midas.h:360
#define MT_LOG
Definition midas.h:546
#define TID_UINT8
Definition midas.h:328
#define TID_LINK
Definition midas.h:350
#define STATE_PAUSED
Definition midas.h:306
#define MT_TALK_STR
Definition midas.h:556
#define TID_STRING
Definition midas.h:346
#define MODE_WRITE
Definition midas.h:371
#define EVENTID_ALL
Definition midas.h:537
#define TR_DEFERRED
Definition midas.h:410
#define MERROR
Definition midas.h:559
#define STATE_RUNNING
Definition midas.h:307
#define MODE_READ
Definition midas.h:370
#define TID_INT8
Definition midas.h:330
#define FORMAT_MIDAS
Definition midas.h:311
#define TID_ARRAY
Definition midas.h:347
#define MT_DEBUG
Definition midas.h:544
#define TID_CHAR
Definition midas.h:331
#define MTALK
Definition midas.h:564
#define MDEBUG
Definition midas.h:561
#define TID_UINT32
Definition midas.h:337
#define TID_UINT16
Definition midas.h:333
#define TR_STOP
Definition midas.h:406
#define TID_INT16
Definition midas.h:335
#define BM_WAIT
Definition midas.h:365
#define TID_FLOAT
Definition midas.h:341
#define TID_LAST
Definition midas.h:354
#define MT_ERROR
Definition midas.h:542
#define MT_ERROR_STR
Definition midas.h:551
#define VALIGN(adr, align)
Definition midas.h:526
#define MIN(a, b)
Definition midas.h:515
#define ALIGN8(x)
Definition midas.h:522
#define MAX(a, b)
Definition midas.h:509
RPC_LIST * rpc_get_internal_list(INT flag)
Definition mrpc.cxx:716
#define DRI_32
Definition msystem.h:46
#define MESSAGE_BUFFER_NAME
Definition msystem.h:111
#define DRI_LITTLE_ENDIAN
Definition msystem.h:48
#define DRF_G_FLOAT
Definition msystem.h:51
#define MAX_STRING_LENGTH
Definition msystem.h:113
#define MESSAGE_BUFFER_SIZE
Definition msystem.h:110
#define MSG_BM
Definition msystem.h: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:918
INT() RPC_HANDLER(INT index, void *prpc_param[])
Definition midas.h:922
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:1110
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:987
int cm_msg_early_init(void)
Definition midas.cxx:479
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:516
int cm_msg_open_buffer(void)
Definition midas.cxx:486
int cm_msg_close_buffer(void)
Definition midas.cxx:499
static std::mutex gMsgBufMutex
Definition midas.cxx:871
static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message)
Definition midas.cxx:1079
INT cm_msg_register(EVENT_HANDLER *func)
Definition midas.cxx:1065
INT cm_msg_log(INT message_type, const char *facility, const char *message)
Definition midas.cxx:676
INT cm_msg_flush_buffer()
Definition midas.cxx:879
static std::deque< msg_buffer_entry > gMsgBuf
Definition midas.cxx:870
static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message)
Definition midas.cxx:838
std::string cm_get_error(INT code)
Definition midas.cxx:467
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:929
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:763
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1348
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1278
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:551
INT cm_set_msg_print(INT system_mask, INT user_mask, int(*func)(const char *))
Definition midas.cxx:659
#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:3206
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2273
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6898
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3861
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3064
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5420
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:13300
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1789
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2832
INT db_lock_database(HNDLE hDB)
Definition odb.cxx:2460
std::string strcomb1(const char **list)
Definition odb.cxx:600
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6544
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3313
INT db_unlock_database(HNDLE hDB)
Definition odb.cxx:2582
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8032
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6024
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:13945
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3019
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7653
INT db_close_all_records()
Definition odb.cxx:13524
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13823
INT db_close_all_databases(void)
Definition odb.cxx:2365
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7220
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10849
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2697
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7318
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:5266
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4084
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13562
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:2981
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13609
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2902
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2796
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2407
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12631
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12299
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5591
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12808
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14016
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2669
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7507
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3172
int rb_get_rp(int handle, void **p, int millisec)
Definition midas.cxx:17590
#define MAX_RING_BUFFER
Definition midas.cxx:17302
int rb_delete(int handle)
Definition midas.cxx:17412
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:17449
int rb_increment_rp(int handle, int size)
Definition midas.cxx:17652
int rb_set_nonblocking()
Definition midas.cxx:17317
static volatile int _rb_nonblocking
Definition midas.cxx:17306
int rb_increment_wp(int handle, int size)
Definition midas.cxx:17524
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:17358
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:17707
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:17304
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:15257
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11728
#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:12034
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14560
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:14669
#define RPC_TEST2
Definition mrpc.h:123
#define RPC_CM_CHECK_CLIENT
Definition mrpc.h:34
#define RPC_CM_ASCTIME
Definition mrpc.h:28
static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION *psa, char **pbuffer, int *pbuffer_size)
Definition midas.cxx:14409
INT rpc_get_opt_tcp_size()
Definition midas.cxx:13894
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12698
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11598
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13494
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11849
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:15724
static std::mutex _client_connections_mutex
Definition midas.cxx:11517
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13020
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
static std::mutex gAllowedHostsMutex
Definition midas.cxx:15229
INT recv_tcp_check(int sock)
Definition midas.cxx:14379
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15893
#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:13073
static RPC_SERVER_ACCEPTION * rpc_get_server_acception(int idx)
Definition midas.cxx:11527
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11535
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11605
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12403
std::string rpc_get_name()
Definition midas.cxx:13106
#define RPC_ID_WATCHDOG
Definition mrpc.h:133
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11540
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
bool rpc_is_remote(void)
Definition midas.cxx:12783
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13183
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11793
static int _tr_fifo_rp
Definition midas.cxx:14076
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14142
#define RPC_RC_TRANSITION
Definition mrpc.h:116
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13215
#define RPC_ID_EXIT
Definition mrpc.h:135
INT rpc_server_loop(void)
Definition midas.cxx:15865
INT rpc_clear_allowed_hosts()
Definition midas.cxx:15232
int rpc_test_rpc()
Definition midas.cxx:15068
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12827
INT rpc_get_hw_type()
Definition midas.cxx:12856
INT rpc_deregister_functions()
Definition midas.cxx:11892
bool rpc_is_connected(void)
Definition midas.cxx:12805
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13086
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11595
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:14668
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11518
#define RPC_ID_SHUTDOWN
Definition mrpc.h:134
static void rpc_call_encode(va_list &ap, const RPC_LIST &rl, NET_COMMAND **nc)
Definition midas.cxx:13253
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12634
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:15228
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:13685
static std::mutex rpc_list_mutex
Definition midas.cxx:11596
void rpc_client_check()
Definition midas.cxx:12292
const char * rpc_tid_name(INT id)
Definition midas.cxx:11786
INT rpc_flush_event()
Definition midas.cxx:14060
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11830
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11524
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:15335
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:16205
static int _tr_fifo_wp
Definition midas.cxx:14075
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:16158
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:13923
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14074
static std::string _mserver_path
Definition midas.cxx:13070
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:12995
INT rpc_server_disconnect()
Definition midas.cxx:12727
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13156
INT rpc_client_accept(int lsock)
Definition midas.cxx:15620
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11648
INT rpc_execute(INT sock, char *buffer, INT convert_flags)
Definition midas.cxx:14672
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:13886
#define RPC_BM_GET_BUFFER_LEVEL
Definition mrpc.h:40
int rpc_name_tid(const char *name)
Definition midas.cxx:11800
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:14601
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:16003
#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:15363
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14073
bool rpc_is_mserver(void)
Definition midas.cxx:12840
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11525
static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
Definition midas.cxx:14219
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11633
#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:13941
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11521
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13424
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:13947
INT rpc_set_name(const char *name)
Definition midas.cxx:13130
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:11919
#define RPC_CM_MSG_RETRIEVE
Definition mrpc.h:32
#define RPC_BM_SKIP_EVENT
Definition mrpc.h:50
INT rpc_client_dispatch(int sock)
Definition midas.cxx:11967
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11520
INT rpc_check_channels(void)
Definition midas.cxx:16276
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14078
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:11953
#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:11683
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11664
#define RPC_CM_GET_WATCHDOG_INFO
Definition mrpc.h:24
INT rpc_get_convert_flags(void)
Definition midas.cxx:13052
INT rpc_tid_size(INT id)
Definition midas.cxx:11779
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:15286
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11703
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
char exp_name[NAME_LENGTH]
Definition mana.cxx:243
INT run_number[2]
Definition mana.cxx:246
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
DWORD last_time
Definition mana.cxx:3070
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
INT odb_size
Definition analyzer.cxx:46
BOOL debug
debug printouts
Definition mana.cxx:254
INT type
Definition mana.cxx:269
HNDLE hDB
main ODB handle
Definition mana.cxx:207
char host_name[HOST_NAME_LENGTH]
Definition mana.cxx:242
double count
Definition mdump.cxx:33
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
HNDLE hSubkey
Definition mdump.cxx:35
char expt_name[NAME_LENGTH]
Definition mevb.cxx:44
char buffer_name[NAME_LENGTH]
Definition mevb.cxx:45
DWORD actual_time
Definition mfe.cxx:37
void * event_buffer
Definition mfe.cxx:65
INT max_event_size
Definition mfed.cxx:30
#define DWORD
Definition mhdump.cxx:31
static const int tid_size[]
Definition midas.cxx:66
static INT _n_mem
Definition midas.cxx:287
static std::string join(const char *sep, const std::vector< std::string > &v)
Definition midas.cxx:398
static std::vector< TRANS_TABLE > _trans_table
Definition midas.cxx:248
static std::atomic_int _message_mask_system
Definition midas.cxx:447
INT _semaphore_alarm
Definition midas.cxx:1476
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:1470
int(* MessagePrintCallback)(const char *)
Definition midas.cxx:443
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:1472
static std::string _path_name
Definition midas.cxx:1474
INT _database_entries
static std::string _client_name
Definition midas.cxx:1473
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:1475
INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
Definition midas.cxx:7813
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:445
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:448
static std::mutex _request_list_mutex
Definition midas.cxx:219
INT _semaphore_elog
Definition midas.cxx:1477
INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
Definition midas.cxx:7860
INT _semaphore_history
Definition midas.cxx:1478
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:1471
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:17745
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8085
#define DIR_SEPARATOR
Definition midas.h:193
#define MAX_CLIENTS
Definition midas.h:274
INT HNDLE
Definition midas.h:132
#define O_LARGEFILE
Definition midas.h:210
#define CF_ENDIAN
Definition midas.h:1609
#define M_MALLOC(x)
Definition midas.h:1550
#define CINT(_i)
Definition midas.h:1620
#define RPC_OUT
Definition midas.h:1579
#define HOST_NAME_LENGTH
Definition midas.h:273
INT midas_thread_t
Definition midas.h:179
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define RPC_IN
Definition midas.h:1578
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1604
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1605
#define NET_TCP_SIZE
Definition midas.h:266
int INT
Definition midas.h:129
#define DEFAULT_RPC_TIMEOUT
Definition midas.h:287
#define MAX_RPC_PARAMS
Definition midas.h:1593
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1446
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:907
#define RPC_OUTGOING
Definition midas.h:1583
#define MAX_WRITE_CACHE_EVENT_SIZE_DIV
Definition midas.h:259
#define MIDAS_TCP_PORT
Definition midas.h:283
#define CSTRING(_i)
Definition midas.h:1644
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1580
#define EVENTID_MESSAGE
Definition midas.h:902
#define MIDAS_VERSION
Definition midas.h:37
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define M_FREE(x)
Definition midas.h:1552
#define TRUE
Definition midas.h:182
#define BANKLIST_MAX
Definition midas.h:278
#define EVENTID_FRAG1
Definition midas.h:906
#define CF_IEEE2VAX
Definition midas.h:1610
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1582
#define POINTER_T
Definition midas.h:166
#define BANK_FORMAT_64BIT_ALIGNED
Definition midas.h:1203
#define BANK_FORMAT_32BIT
Definition midas.h:1202
#define CF_VAX2IEEE
Definition midas.h:1611
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define MAX_WRITE_CACHE_SIZE_DIV
Definition midas.h:258
#define TRANSITION_ERROR_STRING_LENGTH
Definition midas.h:280
#define RPC_FIXARRAY
Definition midas.h:1581
#define BANK_FORMAT_VERSION
Definition midas.h:1201
#define NAME_LENGTH
Definition midas.h:272
#define trigger_mask
#define message(type, str)
#define read(n, a, f)
#define event_id
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
INT serial
Definition minife.c:20
static std::string remove(const std::string s, char c)
Definition mjsonrpc.cxx:253
int gettimeofday(struct timeval *tp, void *tzp)
struct callback_addr callback
Definition mserver.cxx:22
timeval tv
Definition msysmon.cxx:1095
int event_size
Definition msysmon.cxx:527
MUTEX_T * tm
Definition odbedit.cxx:39
char pwd[256]
Definition odbedit.cxx:24
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
DWORD status
Definition odbhist.cxx:39
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
DWORD data_size
Definition midas.h:1228
DWORD data_size
Definition midas.h:1222
Definition midas.h:1213
DWORD watchdog_timeout
Definition midas.h:951
DWORD last_activity
Definition midas.h:950
char name[NAME_LENGTH]
Definition midas.h:935
INT read_pointer
Definition midas.h:940
INT num_in_events
Definition midas.h:964
INT write_pointer
Definition midas.h:963
INT num_clients
Definition midas.h:959
char name[NAME_LENGTH]
Definition midas.h:958
INT max_client_index
Definition midas.h:960
BUFFER_CLIENT client[MAX_CLIENTS]
Definition midas.h:967
INT num_out_events
Definition midas.h:965
INT read_pointer
Definition midas.h:962
int count_sent
Definition midas.cxx:6449
int last_count_lock
Definition midas.cxx:6453
BOOL get_all_flag
Definition midas.cxx:6445
int count_lock
Definition midas.cxx:6448
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6462
int count_write_wait
Definition midas.cxx:6451
double bytes_read
Definition midas.cxx:6458
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6459
int wait_client_index
Definition midas.cxx:6455
DWORD time_write_wait
Definition midas.cxx:6452
int count_read
Definition midas.cxx:6457
double bytes_sent
Definition midas.cxx:6450
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6460
DWORD wait_start_time
Definition midas.cxx:6454
int max_requested_space
Definition midas.cxx:6456
INT client_index
Definition midas.h:989
int client_count_write_wait[MAX_CLIENTS]
Definition midas.h:1022
char buffer_name[NAME_LENGTH]
Definition midas.h:991
void * adr
Definition midas.cxx:280
const char * string
Definition midas.cxx:267
EVENT_HEADER * pevent
Definition midas.cxx:11300
short int event_id
Definition midas.h:852
DWORD data_size
Definition midas.h:856
DWORD serial_number
Definition midas.h:854
DWORD time_stamp
Definition midas.h:855
short int trigger_mask
Definition midas.h:853
INT sampling_type
Definition midas.h:931
BOOL valid
Definition midas.h:928
short int event_id
Definition midas.cxx:206
INT buffer_handle
Definition midas.cxx:205
short int trigger_mask
Definition midas.cxx:207
EVENT_HANDLER * dispatcher
Definition midas.cxx:208
void clear()
Definition midas.cxx:210
Definition midas.h:1026
INT num_values
Definition midas.h:1028
DWORD type
Definition midas.h:1027
char name[NAME_LENGTH]
Definition midas.h:1029
INT item_size
Definition midas.h:1032
NET_COMMAND_HEADER header
Definition msystem.h:293
char param[32]
Definition msystem.h:294
unsigned char * wp
Definition midas.cxx:17298
unsigned char * buffer
Definition midas.cxx:17294
unsigned int max_event_size
Definition midas.cxx:17296
unsigned int size
Definition midas.cxx:17295
unsigned char * rp
Definition midas.cxx:17297
unsigned char * ep
Definition midas.cxx:17299
RPC_HANDLER * dispatch
Definition midas.h:1599
const char * name
Definition midas.h:1597
midas_thread_t thread_id
Definition midas.cxx:14663
int buffer_size
Definition midas.cxx:14664
char * buffer
Definition midas.cxx:14665
int transition
Definition midas.cxx:14067
time_t trans_time
Definition midas.cxx:14069
int run_number
Definition midas.cxx:14068
int sequence_number
Definition midas.cxx:14070
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:3967
int transition
Definition midas.cxx:3952
std::string host_name
Definition midas.cxx:3958
DWORD connect_end_time
Definition midas.cxx:3969
std::atomic_int status
Definition midas.cxx:3962
~TrClient()
Definition midas.cxx:3980
std::vector< int > wait_for_index
Definition midas.cxx:3957
DWORD init_time
Definition midas.cxx:3965
std::string key_name
Definition midas.cxx:3961
DWORD rpc_end_time
Definition midas.cxx:3972
std::string waiting_for_client
Definition midas.cxx:3966
DWORD rpc_timeout
Definition midas.cxx:3970
int async_flag
Definition midas.cxx:3954
int port
Definition midas.cxx:3960
DWORD rpc_start_time
Definition midas.cxx:3971
int sequence_number
Definition midas.cxx:3956
int debug_flag
Definition midas.cxx:3955
int run_number
Definition midas.cxx:3953
DWORD end_time
Definition midas.cxx:3973
DWORD connect_start_time
Definition midas.cxx:3968
std::string client_name
Definition midas.cxx:3959
std::string errorstr
Definition midas.cxx:3964
void Print() const
Definition midas.cxx:3986
std::thread * thread
Definition midas.cxx:3963
int transition
Definition midas.cxx:4005
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:4013
int async_flag
Definition midas.cxx:4007
DWORD end_time
Definition midas.cxx:4012
int run_number
Definition midas.cxx:4006
int status
Definition midas.cxx:4009
DWORD start_time
Definition midas.cxx:4011
std::string errorstr
Definition midas.cxx:4010
int debug_flag
Definition midas.cxx:4008
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:1608
std::string name
Definition midas.cxx:1609
std::string directory
Definition midas.cxx:1610
std::string user
Definition midas.cxx:1611
std::string filename
Definition midas.cxx:1615
std::vector< exptab_entry > exptab
Definition midas.cxx:1616
Definition midas.cxx:864
DWORD ts
Definition midas.cxx:865
int message_type
Definition midas.cxx:866
std::string message
Definition midas.cxx:867
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