MIDAS
Loading...
Searching...
No Matches
midas.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: MIDAS.C
4 Created by: Stefan Ritt
5
6 Contents: MIDAS main library funcitons
7
8 $Id$
9
10\********************************************************************/
11
12#undef NDEBUG // midas required assert() to be always enabled
13
14#include "midas.h"
15#include "msystem.h"
16#include "git-revision.h"
17#include "mstrlcpy.h"
18#include "odbxx.h"
19
20#include <assert.h>
21#include <signal.h>
22#include <sys/resource.h>
23
24#include <mutex>
25#include <deque>
26#include <thread>
27#include <atomic>
28#include <algorithm>
29
62#ifndef DOXYGEN_SHOULD_SKIP_THIS
63
64/********************************************************************/
65/* data type sizes */
66static const int tid_size[] = {
67 0, /* tid == 0 not defined */
68 1, /* TID_UINT8 unsigned byte 0 255 */
69 1, /* TID_INT8 signed byte -128 127 */
70 1, /* TID_CHAR single character 0 255 */
71 2, /* TID_UINT16 two bytes 0 65535 */
72 2, /* TID_INT16 signed word -32768 32767 */
73 4, /* TID_UINT32 four bytes 0 2^32-1 */
74 4, /* TID_INT32 signed dword -2^31 2^31-1 */
75 4, /* TID_BOOL four bytes bool 0 1 */
76 4, /* TID_FLOAT 4 Byte float format */
77 8, /* TID_DOUBLE 8 Byte float format */
78 4, /* TID_BITFIELD 32 Bits Bitfield 0000... 11111... */
79 0, /* TID_STRING zero terminated string */
80 0, /* TID_ARRAY variable length array of unkown type */
81 0, /* TID_STRUCT C structure */
82 0, /* TID_KEY key in online database */
83 0, /* TID_LINK link in online database */
84 8, /* TID_INT64 8 bytes int -2^63 2^63-1 */
85 8 /* TID_UINT64 8 bytes unsigned int 0 2^64-1 */
86};
87
88/* data type names */
89static const char *tid_name_old[] = {
90 "NULL",
91 "BYTE",
92 "SBYTE",
93 "CHAR",
94 "WORD",
95 "SHORT",
96 "DWORD",
97 "INT",
98 "BOOL",
99 "FLOAT",
100 "DOUBLE",
101 "BITFIELD",
102 "STRING",
103 "ARRAY",
104 "STRUCT",
105 "KEY",
106 "LINK",
107 "INT64",
108 "UINT64"
109};
110
111static const char *tid_name[] = {
112 "NULL",
113 "UINT8",
114 "INT8",
115 "CHAR",
116 "UINT16",
117 "INT16",
118 "UINT32",
119 "INT32",
120 "BOOL",
121 "FLOAT",
122 "DOUBLE",
123 "BITFIELD",
124 "STRING",
125 "ARRAY",
126 "STRUCT",
127 "KEY",
128 "LINK",
129 "INT64",
130 "UINT64"
131};
132
134{
135 if (transition == TR_START) return "START";
136 if (transition == TR_STOP) return "STOP";
137 if (transition == TR_PAUSE) return "PAUSE";
138 if (transition == TR_RESUME) return "RESUME";
139 if (transition == TR_STARTABORT) return "STARTABORT";
140 if (transition == TR_DEFERRED) return "DEFERRED";
141 return msprintf("UNKNOWN TRANSITION %d", transition);
142}
143
144const char *mname[] = {
145 "January",
146 "February",
147 "March",
148 "April",
149 "May",
150 "June",
151 "July",
152 "August",
153 "September",
154 "October",
155 "November",
156 "December"
157};
158
159/* Globals */
160#ifdef OS_MSDOS
161extern unsigned _stklen = 60000U;
162#endif
163
164extern DATABASE *_database;
166
167//
168// locking rules for gBuffers and gBuffersMutex:
169//
170// - all access to gBuffers must be done while holding gBufferMutex
171// - while holding gBufferMutex:
172// - taking additional locks not permitted (no calling odb, no locking event buffers, etc)
173// - calling functions that can take additional locks not permitted (no calling db_xxx(), bm_xxx(), etc)
174// - calling functions that can come back recursively not permitted
175//
176// after obtaining a BUFFER*pbuf pointer from gBuffers:
177//
178// - holding gBuffersMutex is not required
179// - to access pbuf data, must hold buffer_mutex or call bm_lock_buffer()
180// - except for:
181// pbuf->attached - no need to hold a lock (std::atomic)
182// pbuf->buffer_name - no need to hold a lock (constant data, only changed by bm_open_buffer())
183//
184// object life time:
185//
186// - gBuffers never shrinks
187// - new BUFFER objects are created by bm_open_buffer(), added to gBuffers when ready for use, pbuf->attached set to true
188// - bm_close_buffer() sets pbuf->attached to false
189// - BUFFER objects are never deleted to avoid race between delete and bm_send_event() & co
190// - BUFFER objects are never reused, bm_open_buffer() always creates a new object
191// - gBuffers[i] set to NULL are empty slots available for reuse
192// - closed buffers have corresponding gBuffers[i]->attached set to false
193//
194
195static std::mutex gBuffersMutex; // protects gBuffers vector itself, but not it's contents!
196static std::vector<BUFFER*> gBuffers;
197
198static INT _msg_buffer = 0;
200
201/* Event request descriptor */
202
204{
205 INT buffer_handle = 0; /* Buffer handle */
206 short int event_id = 0; /* same as in EVENT_HEADER */
207 short int trigger_mask = 0; /* same as in EVENT_HEADER */
208 EVENT_HANDLER* dispatcher = NULL; /* Dispatcher func. */
209
210 void clear()
211 {
212 buffer_handle = 0;
213 event_id = 0;
214 trigger_mask = 0;
215 dispatcher = NULL;
216 }
217};
218
219static std::mutex _request_list_mutex;
220static std::vector<EventRequest> _request_list;
221
222//static char *_tcp_buffer = NULL;
223//static INT _tcp_wp = 0;
224//static INT _tcp_rp = 0;
225//static INT _tcp_sock = 0;
226
227static MUTEX_T *_mutex_rpc = NULL; // mutex to protect RPC calls
228
229static void (*_debug_print)(const char *) = NULL;
230
231static INT _debug_mode = 0;
232
233static int _rpc_connect_timeout = 10000;
234
235// for use on a single machine it is best to restrict RPC access to localhost
236// by binding the RPC listener socket to the localhost IP address.
238
239/* table for transition functions */
240
246
247static std::mutex _trans_table_mutex;
248static std::vector<TRANS_TABLE> _trans_table;
249
251 {TR_START, 0, NULL},
252 {TR_STOP, 0, NULL},
253 {TR_PAUSE, 0, NULL},
254 {TR_RESUME, 0, NULL},
255 {0, 0, NULL}
256};
257
259static int _rpc_listen_socket = 0;
260
261static INT rpc_transition_dispatch(INT idx, void *prpc_param[]);
262
263void cm_ctrlc_handler(int sig);
264
265typedef struct {
267 const char *string;
269
270static const ERROR_TABLE _error_table[] = {
271 {CM_WRONG_PASSWORD, "Wrong password"},
272 {CM_UNDEF_EXP, "Experiment not defined"},
274 "\"exptab\" file not found and MIDAS_DIR or MIDAS_EXPTAB environment variable is not defined"},
275 {RPC_NET_ERROR, "Cannot connect to remote host"},
276 {0, NULL}
277};
278
279typedef struct {
280 void *adr;
281 int size;
282 char file[80];
283 int line;
285
286static DBG_MEM_LOC *_mem_loc = NULL;
287static INT _n_mem = 0;
288
290{
293 char *errstr;
297 std::atomic_int status{0};
298 std::atomic_bool finished{false};
299 std::atomic<std::thread*> thread{NULL};
300};
301
303
304/*------------------------------------------------------------------*/
305
306void *dbg_malloc(unsigned int size, char *file, int line) {
307 FILE *f;
308 void *adr;
309 int i;
310
311 adr = malloc(size);
312
313 /* search for deleted entry */
314 for (i = 0; i < _n_mem; i++)
315 if (_mem_loc[i].adr == NULL)
316 break;
317
318 if (i == _n_mem) {
319 _n_mem++;
320 if (!_mem_loc)
321 _mem_loc = (DBG_MEM_LOC *) malloc(sizeof(DBG_MEM_LOC));
322 else
323 _mem_loc = (DBG_MEM_LOC *) realloc(_mem_loc, sizeof(DBG_MEM_LOC) * _n_mem);
324 }
325
326 assert(_mem_loc != NULL);
327
328 _mem_loc[i].adr = adr;
329 _mem_loc[i].size = size;
330 strcpy(_mem_loc[i].file, file);
331 _mem_loc[i].line = line;
332
333 f = fopen("mem.txt", "w");
334
335 assert(f != NULL);
336
337 for (i = 0; i < _n_mem; i++)
338 if (_mem_loc[i].adr)
339 fprintf(f, "%s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line, _mem_loc[i].size, _mem_loc[i].adr);
340
341 fclose(f);
342
343 return adr;
344}
345
346void *dbg_calloc(unsigned int size, unsigned int count, char *file, int line) {
347 void *adr;
348
349 adr = dbg_malloc(size * count, file, line);
350 if (adr)
351 memset(adr, 0, size * count);
352
353 return adr;
354}
355
356void dbg_free(void *adr, char *file, int line) {
357 FILE *f;
358 int i;
359
360 free(adr);
361
362 for (i = 0; i < _n_mem; i++)
363 if (_mem_loc[i].adr == adr)
364 break;
365
366 if (i < _n_mem)
367 _mem_loc[i].adr = NULL;
368
369 f = fopen("mem.txt", "w");
370
371 assert(f != NULL);
372
373 for (i = 0; i < _n_mem; i++)
374 if (_mem_loc[i].adr)
375 fprintf(f, "%s:%d %s:%d size=%d adr=%p\n", _mem_loc[i].file, _mem_loc[i].line,
376 file, line, _mem_loc[i].size, _mem_loc[i].adr);
377
378 fclose(f);
379}
380
381static std::vector<std::string> split(const char* sep, const std::string& s)
382{
383 unsigned sep_len = strlen(sep);
384 std::vector<std::string> v;
385 std::string::size_type pos = 0;
386 while (1) {
387 std::string::size_type next = s.find(sep, pos);
388 if (next == std::string::npos) {
389 v.push_back(s.substr(pos));
390 break;
391 }
392 v.push_back(s.substr(pos, next-pos));
393 pos = next+sep_len;
394 }
395 return v;
396}
397
398static std::string join(const char* sep, const std::vector<std::string>& v)
399{
400 std::string s;
401
402 for (unsigned i=0; i<v.size(); i++) {
403 if (i>0) {
404 s += sep;
405 }
406 s += v[i];
407 }
408
409 return s;
410}
411
412bool ends_with_char(const std::string& s, char c)
413{
414 if (s.length() < 1)
415 return false;
416 return s[s.length()-1] == c;
417}
418
419std::string msprintf(const char *format, ...) {
420 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 va_end(ap1);
433 std::string s(buffer);
434 free(buffer);
435 return s;
436}
437
438/********************************************************************\
439* *
440* Common message functions *
441* *
442\********************************************************************/
443
444typedef int (*MessagePrintCallback)(const char *);
445
446static std::atomic<MessagePrintCallback> _message_print{puts};
447
448static std::atomic_int _message_mask_system{MT_ALL};
449static std::atomic_int _message_mask_user{MT_ALL};
450
451
453#endif /* DOXYGEN_SHOULD_SKIP_THIS */
454
460/********************************************************************/
468std::string cm_get_error(INT code)
469{
470 for (int i = 0; _error_table[i].code; i++) {
471 if (_error_table[i].code == code) {
472 return _error_table[i].string;
473 }
474 }
475
476 return msprintf("unlisted status code %d", code);
477}
478
479/********************************************************************/
481
482 return CM_SUCCESS;
483}
484
485/********************************************************************/
486
488 //printf("cm_msg_open_buffer!\n");
489 if (_msg_buffer == 0) {
491 if (status != BM_SUCCESS && status != BM_CREATED) {
492 return status;
493 }
494 }
495 return CM_SUCCESS;
496}
497
498/********************************************************************/
499
501 //printf("cm_msg_close_buffer!\n");
502 if (_msg_buffer) {
504 _msg_buffer = 0;
505 }
506 return CM_SUCCESS;
507}
508
509/********************************************************************/
510
518 std::string path;
519
520 cm_msg_get_logfile("midas", 0, &path, NULL, NULL);
521
522 /* extract directory name from full path name of midas.log */
523 size_t pos = path.rfind(DIR_SEPARATOR);
524 if (pos != std::string::npos) {
525 path.resize(pos);
526 } else {
527 path = "";
528 }
529
530 //printf("cm_msg_facilities: path [%s]\n", path.c_str());
531
532 STRING_LIST flist;
533
534 ss_file_find(path.c_str(), "*.log", &flist);
535
536 for (size_t i = 0; i < flist.size(); i++) {
537 const char *p = flist[i].c_str();
538 if (strchr(p, '_') == NULL && !(p[0] >= '0' && p[0] <= '9')) {
539 size_t pos = flist[i].rfind('.');
540 if (pos != std::string::npos) {
541 flist[i].resize(pos);
542 }
543 list->push_back(flist[i]);
544 }
545 }
546
547 return SUCCESS;
548}
549
550/********************************************************************/
551
552void cm_msg_get_logfile(const char *fac, time_t t, std::string* filename, std::string* linkname, std::string* linktarget) {
553 HNDLE hDB;
554 int status;
555
557
558 // check for call to cm_msg() before MIDAS is fully initialized
559 // or after MIDAS is partially shutdown.
560 if (status != CM_SUCCESS) {
561 if (filename)
562 *filename = std::string(fac) + ".log";
563 if (linkname)
564 *linkname = "";
565 if (linktarget)
566 *linktarget = "";
567 return;
568 }
569
570 if (filename)
571 *filename = "";
572 if (linkname)
573 *linkname = "";
574 if (linktarget)
575 *linktarget = "";
576
577 std::string facility;
578 if (fac && fac[0])
579 facility = fac;
580 else
581 facility = "midas";
582
583 std::string message_format;
584 db_get_value_string(hDB, 0, "/Logger/Message file date format", 0, &message_format, TRUE);
585 if (message_format.find('%') != std::string::npos) {
586 /* replace stings such as %y%m%d with current date */
587 struct tm tms;
588
589 ss_tzset();
590 if (t == 0)
591 time(&t);
592 localtime_r(&t, &tms);
593
594 char de[256];
595 de[0] = '_';
596 strftime(de + 1, sizeof(de)-1, strchr(message_format.c_str(), '%'), &tms);
597 message_format = de;
598 }
599
600 std::string message_dir;
601 db_get_value_string(hDB, 0, "/Logger/Message dir", 0, &message_dir, TRUE);
602 if (message_dir.empty()) {
603 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &message_dir, FALSE);
604 if (message_dir.empty()) {
605 message_dir = cm_get_path();
606 if (message_dir.empty()) {
607 message_dir = ss_getcwd();
608 }
609 }
610 }
611
612 // prepend experiment directory
613 if (message_dir[0] != DIR_SEPARATOR)
614 message_dir = cm_get_path() + message_dir;
615
616 if (message_dir.back() != DIR_SEPARATOR)
617 message_dir.push_back(DIR_SEPARATOR);
618
619 if (filename)
620 *filename = message_dir + facility + message_format + ".log";
621 if (!message_format.empty()) {
622 if (linkname)
623 *linkname = message_dir + facility + ".log";
624 if (linktarget)
625 *linktarget = facility + message_format + ".log";
626 }
627}
628
629/********************************************************************/
660INT cm_set_msg_print(INT system_mask, INT user_mask, int (*func)(const char *)) {
661 _message_mask_system = system_mask;
662 _message_mask_user = user_mask;
663 _message_print = func;
664
665 return BM_SUCCESS;
666}
667
668/********************************************************************/
677INT cm_msg_log(INT message_type, const char *facility, const char *message) {
678 INT status;
679
680 if (rpc_is_remote()) {
681 if (rpc_is_connected()) {
682 status = rpc_call(RPC_CM_MSG_LOG, message_type, facility, message);
683 if (status != RPC_SUCCESS) {
684 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);
685 }
686 return status;
687 } else {
688 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to midas.log, no connection to mserver\n", message);
689 return RPC_NET_ERROR;
690 }
691 }
692
693 if (message_type != MT_DEBUG) {
694 std::string filename, linkname, linktarget;
695
696 cm_msg_get_logfile(facility, 0, &filename, &linkname, &linktarget);
697
698#ifdef OS_LINUX
699 if (!linkname.empty()) {
700 //printf("cm_msg_log: filename [%s] linkname [%s] linktarget [%s]\n", filename.c_str(), linkname.c_str(), linktarget.c_str());
701 // If filename does not exist, user just switched from non-date format to date format.
702 // In that case we must copy linkname to filename, otherwise messages might get lost.
703 if (ss_file_exist(linkname.c_str()) && !ss_file_link_exist(linkname.c_str())) {
704 ss_file_copy(linkname.c_str(), filename.c_str(), true);
705 }
706
707 unlink(linkname.c_str());
708 status = symlink(linktarget.c_str(), linkname.c_str());
709 if (status != 0) {
710 fprintf(stderr,
711 "cm_msg_log: Error: Cannot symlink message log file \'%s' to \'%s\', symlink() errno: %d (%s)\n",
712 linktarget.c_str(), linkname.c_str(), errno, strerror(errno));
713 }
714 }
715#endif
716
717 int fh = open(filename.c_str(), O_WRONLY | O_CREAT | O_APPEND | O_LARGEFILE, 0644);
718 if (fh < 0) {
719 fprintf(stderr,
720 "cm_msg_log: Message \"%s\" not written to midas.log because open(%s) failed with errno %d (%s)\n",
721 message, filename.c_str(), errno, strerror(errno));
722 } else {
723
724 struct timeval tv;
725 struct tm tms;
726
727 ss_tzset();
728 gettimeofday(&tv, NULL);
729 localtime_r(&tv.tv_sec, &tms);
730
731 char str[256];
732 strftime(str, sizeof(str), "%H:%M:%S", &tms);
733 sprintf(str + strlen(str), ".%03d ", (int) (tv.tv_usec / 1000));
734 strftime(str + strlen(str), sizeof(str), "%G/%m/%d", &tms);
735
736 std::string msg;
737 msg += str;
738 msg += " ";
739 msg += message;
740 msg += "\n";
741
742 /* avoid c++ complaint about comparison between
743 unsigned size_t returned by msg.length() and
744 signed ssize_t returned by write() */
745 ssize_t len = msg.length();
746
747 /* atomic write, no need to take a semaphore */
748 ssize_t wr = write(fh, msg.c_str(), len);
749
750 if (wr < 0) {
751 fprintf(stderr, "cm_msg_log: Message \"%s\" not written to \"%s\", write() error, errno %d (%s)\n", message, filename.c_str(), errno, strerror(errno));
752 } else if (wr != len) {
753 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);
754 }
755
756 close(fh);
757 }
758 }
759
760 return CM_SUCCESS;
761}
762
763
764static std::string cm_msg_format(INT message_type, const char *filename, INT line, const char *routine, const char *format, va_list *argptr)
765{
766 /* strip path */
767 const char* pc = filename + strlen(filename);
768 while (*pc != '\\' && *pc != '/' && pc != filename)
769 pc--;
770 if (pc != filename)
771 pc++;
772
773 /* convert type to string */
774 std::string type_str;
775 if (message_type & MT_ERROR)
776 type_str += MT_ERROR_STR;
777 if (message_type & MT_INFO)
778 type_str += MT_INFO_STR;
779 if (message_type & MT_DEBUG)
780 type_str += MT_DEBUG_STR;
781 if (message_type & MT_USER)
782 type_str += MT_USER_STR;
783 if (message_type & MT_LOG)
784 type_str += MT_LOG_STR;
785 if (message_type & MT_TALK)
786 type_str += MT_TALK_STR;
787
788 std::string message;
789
790 /* print client name into string */
791 if (message_type == MT_USER)
792 message = msprintf("[%s] ", routine);
793 else {
794 std::string name = rpc_get_name();
795 if (name.length() > 0)
796 message = msprintf("[%s,%s] ", name.c_str(), type_str.c_str());
797 else
798 message = "";
799 }
800
801 /* preceed error messages with file and line info */
802 if (message_type == MT_ERROR) {
803 message += msprintf("[%s:%d:%s,%s] ", pc, line, routine, type_str.c_str());
804 } else if (message_type == MT_USER) {
805 message = msprintf("[%s,%s] ", routine, type_str.c_str());
806 }
807
808 int bufsize = 1024;
809 char* buf = (char*)malloc(bufsize);
810 assert(buf);
811
812 for (int i=0; i<10; i++) {
813 va_list ap;
814 va_copy(ap, *argptr);
815
816 /* print argument list into message */
817 int n = vsnprintf(buf, bufsize-1, format, ap);
818
819 //printf("vsnprintf [%s] %d %d\n", format, bufsize, n);
820
821 va_end(ap);
822
823 if (n < bufsize) {
824 break;
825 }
826
827 bufsize += 100;
828 bufsize *= 2;
829 buf = (char*)realloc(buf, bufsize);
830 assert(buf);
831 }
832
833 message += buf;
834 free(buf);
835
836 return message;
837}
838
839static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message) {
840 //printf("cm_msg_send: ts %d, type %d, message [%s]\n", ts, message_type, send_message);
841
842 /* send event if not of type MLOG */
843 if (message_type != MT_LOG) {
844 if (_msg_buffer) {
845 /* copy message to event */
846 size_t len = strlen(send_message);
847 int event_length = sizeof(EVENT_HEADER) + len + 1;
848 char event[event_length];
849 EVENT_HEADER *pevent = (EVENT_HEADER *) event;
850
851 memcpy(event + sizeof(EVENT_HEADER), send_message, len + 1);
852
853 /* setup the event header and send the message */
854 bm_compose_event(pevent, EVENTID_MESSAGE, (WORD) message_type, len + 1, 0);
855 if (ts)
856 pevent->time_stamp = ts;
857 //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)));
858 bm_send_event(_msg_buffer, pevent, 0, BM_WAIT);
859 }
860 }
861
862 return CM_SUCCESS;
863}
864
868 std::string message;
869};
870
871static std::deque<msg_buffer_entry> gMsgBuf;
872static std::mutex gMsgBufMutex;
873
874/********************************************************************/
881 int i;
882
883 //printf("cm_msg_flush_buffer!\n");
884
885 for (i = 0; i < 100; i++) {
887 {
888 std::lock_guard<std::mutex> lock(gMsgBufMutex);
889 if (gMsgBuf.empty())
890 break;
891 e = gMsgBuf.front();
892 gMsgBuf.pop_front();
893 // implicit unlock
894 }
895
896 /* log message */
897 cm_msg_log(e.message_type, "midas", e.message.c_str());
898
899 /* send message to SYSMSG */
900 int status = cm_msg_send_event(e.ts, e.message_type, e.message.c_str());
901 if (status != CM_SUCCESS)
902 return status;
903 }
904
905 return CM_SUCCESS;
906}
907
908/********************************************************************/
930INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
931{
932 DWORD ts = ss_time();
933
934 /* print argument list into message */
935 std::string message;
936 va_list argptr;
937 va_start(argptr, format);
938 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
939 va_end(argptr);
940
941 //printf("message [%s]\n", message.c_str());
942
943 /* call user function if set via cm_set_msg_print */
945 if (f != NULL && (message_type & _message_mask_user) != 0) {
946 if (message_type != MT_LOG) { // do not print MLOG messages
947 (*f)(message.c_str());
948 }
949 }
950
951 /* return if system mask is not set */
952 if ((message_type & _message_mask_system) == 0) {
953 return CM_SUCCESS;
954 }
955
956 gMsgBufMutex.lock();
957 gMsgBuf.push_back(msg_buffer_entry{ts, message_type, message});
958 gMsgBufMutex.unlock();
959
960 return CM_SUCCESS;
961}
962
963/********************************************************************/
988INT cm_msg1(INT message_type, const char *filename, INT line,
989 const char *facility, const char *routine, const char *format, ...) {
990 va_list argptr;
991 std::string message;
992 static BOOL in_routine = FALSE;
993
994 /* avoid recursive calles */
995 if (in_routine)
996 return 0;
997
998 in_routine = TRUE;
999
1000 /* print argument list into message */
1001 va_start(argptr, format);
1002 message = cm_msg_format(message_type, filename, line, routine, format, &argptr);
1003 va_end(argptr);
1004
1005 /* call user function if set via cm_set_msg_print */
1007 if (f != NULL && (message_type & _message_mask_user) != 0)
1008 (*f)(message.c_str());
1009
1010 /* return if system mask is not set */
1011 if ((message_type & _message_mask_system) == 0) {
1012 in_routine = FALSE;
1013 return CM_SUCCESS;
1014 }
1015
1016 /* send message to SYSMSG */
1017 cm_msg_send_event(0, message_type, message.c_str());
1018
1019 /* log message */
1020 cm_msg_log(message_type, facility, message.c_str());
1021
1022 in_routine = FALSE;
1023
1024 return CM_SUCCESS;
1025}
1026
1027/********************************************************************/
1067 INT status, id;
1068
1069 // we should only come here after the message buffer
1070 // was opened by cm_connect_experiment()
1071 assert(_msg_buffer);
1072
1073 _msg_dispatch = func;
1074
1076
1077 return status;
1078}
1079
1080static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message) {
1081 int new_message_length = strlen(new_message);
1082 int new_allocated = 1024 + 2 * ((*allocated) + new_message_length);
1083 char buf[100];
1084 int buf_length;
1085
1086 //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);
1087
1088 if (*length + new_message_length + 100 > *allocated) {
1089 *messages = (char *) realloc(*messages, new_allocated);
1090 assert(*messages != NULL);
1091 *allocated = new_allocated;
1092 }
1093
1094 if (*length > 0)
1095 if ((*messages)[(*length) - 1] != '\n') {
1096 (*messages)[*length] = '\n'; // separator between messages
1097 (*length) += 1;
1098 }
1099
1100 sprintf(buf, "%ld ", tstamp);
1101 buf_length = strlen(buf);
1102 memcpy(&((*messages)[*length]), buf, buf_length);
1103 (*length) += buf_length;
1104
1105 memcpy(&((*messages)[*length]), new_message, new_message_length);
1106 (*length) += new_message_length;
1107 (*messages)[*length] = 0; // make sure string is NUL terminated
1108}
1109
1110/* Retrieve message from an individual file. Internal use only */
1111static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated,
1112 int *num_messages) {
1113 BOOL stop;
1114 int fh;
1115 char *p, str[1000];
1116 struct stat stat_buf;
1117 time_t tstamp, tstamp_valid, tstamp_last;
1118
1119 ss_tzset(); // required by localtime_r()
1120
1121 *num_messages = 0;
1122
1123 fh = open(filename, O_RDONLY | O_TEXT, 0644);
1124 if (fh < 0) {
1125 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot open log file \"%s\", errno %d (%s)", filename, errno,
1126 strerror(errno));
1127 return SS_FILE_ERROR;
1128 }
1129
1130 /* read whole file into memory */
1131 fstat(fh, &stat_buf);
1132 ssize_t size = stat_buf.st_size;
1133
1134 /* if file is too big, only read tail of file */
1135 ssize_t maxsize = 10 * 1024 * 1024;
1136 if (size > maxsize) {
1137 lseek(fh, -maxsize, SEEK_END);
1138 //printf("lseek status %d, errno %d (%s)\n", status, errno, strerror(errno));
1139 size = maxsize;
1140 }
1141
1142 char *buffer = (char *) malloc(size + 1);
1143
1144 if (buffer == NULL) {
1145 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot malloc %d bytes to read log file \"%s\", errno %d (%s)", (int) size,
1146 filename, errno, strerror(errno));
1147 close(fh);
1148 return SS_FILE_ERROR;
1149 }
1150
1151 ssize_t rd = read(fh, buffer, size);
1152
1153 if (rd != size) {
1154 cm_msg(MERROR, "cm_msg_retrieve1", "Cannot read %d bytes from log file \"%s\", read() returned %d, errno %d (%s)",
1155 (int) size, filename, (int) rd, errno, strerror(errno));
1156 close(fh);
1157 return SS_FILE_ERROR;
1158 }
1159
1160 buffer[size] = 0;
1161 close(fh);
1162
1163 p = buffer + size - 1;
1164 tstamp_last = tstamp_valid = 0;
1165 stop = FALSE;
1166
1167 while (*p == '\n' || *p == '\r')
1168 p--;
1169
1170 int n;
1171 for (n = 0; !stop && p > buffer;) {
1172
1173 /* go to beginning of line */
1174 int i;
1175 for (i = 0; p != buffer && (*p != '\n' && *p != '\r'); i++)
1176 p--;
1177
1178 /* limit line length to sizeof(str) */
1179 if (i >= (int) sizeof(str))
1180 i = sizeof(str) - 1;
1181
1182 if (p == buffer) {
1183 i++;
1184 memcpy(str, p, i);
1185 } else
1186 memcpy(str, p + 1, i);
1187 str[i] = 0;
1188 if (strchr(str, '\n'))
1189 *strchr(str, '\n') = 0;
1190 if (strchr(str, '\r'))
1191 *strchr(str, '\r') = 0;
1192 mstrlcat(str, "\n", sizeof(str));
1193
1194 // extract time tag
1195 time_t now;
1196 time(&now);
1197
1198 struct tm tms;
1199 localtime_r(&now, &tms); // must call tzset() beforehand!
1200
1201 if (str[0] >= '0' && str[0] <= '9') {
1202 // new format
1203 tms.tm_hour = atoi(str);
1204 tms.tm_min = atoi(str + 3);
1205 tms.tm_sec = atoi(str + 6);
1206 tms.tm_year = atoi(str + 13) - 1900;
1207 tms.tm_mon = atoi(str + 18) - 1;
1208 tms.tm_mday = atoi(str + 21);
1209 } else {
1210 // old format
1211 tms.tm_hour = atoi(str + 11);
1212 tms.tm_min = atoi(str + 14);
1213 tms.tm_sec = atoi(str + 17);
1214 tms.tm_year = atoi(str + 20) - 1900;
1215 for (i = 0; i < 12; i++)
1216 if (strncmp(str + 4, mname[i], 3) == 0)
1217 break;
1218 tms.tm_mon = i;
1219 tms.tm_mday = atoi(str + 8);
1220 }
1221 tstamp = ss_mktime(&tms);
1222 if (tstamp != -1)
1223 tstamp_valid = tstamp;
1224
1225 // for new messages (n=0!), stop when t reached
1226 if (n_messages == 0) {
1227 if (tstamp_valid < t)
1228 break;
1229 }
1230
1231 // for old messages, stop when all messages belonging to tstamp_last are sent
1232 if (n_messages != 0) {
1233 if (tstamp_last > 0 && tstamp_valid < tstamp_last)
1234 break;
1235 }
1236
1237 if (t == 0 || tstamp == -1 ||
1238 (n_messages > 0 && tstamp <= t) ||
1239 (n_messages == 0 && tstamp >= t)) {
1240
1241 n++;
1242
1243 add_message(messages, length, allocated, tstamp, str);
1244 }
1245
1246 while (*p == '\n' || *p == '\r')
1247 p--;
1248
1249 if (n_messages == 1)
1250 stop = TRUE;
1251 else if (n_messages > 1) {
1252 // continue collecting messages until time stamp differs from current one
1253 if (n == n_messages)
1254 tstamp_last = tstamp_valid;
1255
1256 // if all messages without time tags, just return after n
1257 if (n == n_messages && tstamp_valid == 0)
1258 break;
1259 }
1260 }
1261
1262 free(buffer);
1263
1264 *num_messages = n;
1265
1266 return CM_SUCCESS;
1267}
1268
1269/********************************************************************/
1279INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages) {
1280 std::string filename, linkname;
1281 INT n, i;
1282 time_t filedate;
1283 int length = 0;
1284 int allocated = 0;
1285
1286 time(&filedate);
1287 cm_msg_get_logfile(facility, filedate, &filename, &linkname, NULL);
1288
1289 //printf("facility %s, filename \"%s\" \"%s\"\n", facility, filename, linkname);
1290
1291 // see if file exists, use linkname if not
1292 if (!linkname.empty()) {
1293 if (!ss_file_exist(filename.c_str()))
1294 filename = linkname;
1295 }
1296
1297 if (ss_file_exist(filename.c_str())) {
1298 cm_msg_retrieve1(filename.c_str(), t, n_message, messages, &length, &allocated, &n);
1299 } else {
1300 n = 0;
1301 }
1302
1303 /* if there is no symlink, then there is no additional log files to read */
1304 if (linkname.empty()) {
1305 *num_messages = n;
1306 return CM_SUCCESS;
1307 }
1308
1309 //printf("read more messages %d %d!\n", n, n_message);
1310
1311 int missing = 0;
1312 while (n < n_message) {
1313 filedate -= 3600 * 24; // go one day back
1314
1315 cm_msg_get_logfile(facility, filedate, &filename, NULL, NULL);
1316
1317 //printf("read [%s] for time %d!\n", filename.c_str(), filedate);
1318
1319 if (ss_file_exist(filename.c_str())) {
1320 cm_msg_retrieve1(filename.c_str(), t, n_message - n, messages, &length, &allocated, &i);
1321 n += i;
1322 missing = 0;
1323 } else {
1324 missing++;
1325 }
1326
1327 // stop if ten consecutive files are not found
1328 if (missing > 10)
1329 break;
1330 }
1331
1332 *num_messages = n;
1333
1334 return CM_SUCCESS;
1335}
1336
1337/********************************************************************/
1349INT cm_msg_retrieve(INT n_message, char *message, INT buf_size) {
1350 int status;
1351 char *messages = NULL;
1352 int num_messages = 0;
1353
1354 if (rpc_is_remote())
1355 return rpc_call(RPC_CM_MSG_RETRIEVE, n_message, message, buf_size);
1356
1357 status = cm_msg_retrieve2("midas", 0, n_message, &messages, &num_messages);
1358
1359 if (messages) {
1360 mstrlcpy(message, messages, buf_size);
1361 int len = strlen(messages);
1362 if (len > buf_size)
1364 free(messages);
1365 }
1366
1367 return status;
1368}
1369
/* end of msgfunctionc */
1372
1378/********************************************************************/
1385 INT sec, status;
1386
1387 /* if connected to server, get time from there */
1388 if (rpc_is_remote()) {
1390
1391 /* set local time */
1392 if (status == CM_SUCCESS)
1393 ss_settime(sec);
1394 }
1395
1396 /* return time to caller */
1397 if (seconds != NULL) {
1398 *seconds = ss_time();
1399 }
1400
1401 return CM_SUCCESS;
1402}
1403
1404/********************************************************************/
1411INT cm_asctime(char *str, INT buf_size) {
1412 /* if connected to server, get time from there */
1413 if (rpc_is_remote())
1414 return rpc_call(RPC_CM_ASCTIME, str, buf_size);
1415
1416 /* return local time */
1417 mstrlcpy(str, ss_asctime().c_str(), buf_size);
1418
1419 return CM_SUCCESS;
1420}
1421
1422/********************************************************************/
1427std::string cm_asctime() {
1428 /* if connected to server, get time from there */
1429 if (rpc_is_remote()) {
1430 char buf[256];
1431 int status = rpc_call(RPC_CM_ASCTIME, buf, sizeof(buf));
1432 if (status == CM_SUCCESS) {
1433 return buf;
1434 } else {
1435 return "";
1436 }
1437 }
1438
1439 /* return local time */
1440 return ss_asctime();
1441}
1442
1443/********************************************************************/
1450 /* if connected to server, get time from there */
1451 if (rpc_is_remote())
1452 return rpc_call(RPC_CM_TIME, t);
1453
1454 /* return local time */
1455 *t = ss_time();
1456
1457 return CM_SUCCESS;
1458}
1459
/* end of cmfunctionc */
1462
1463/********************************************************************\
1464* *
1465* cm_xxx - Common Functions to buffer & database *
1466* *
1467\********************************************************************/
1468
1469/* Globals */
1470
1471static HNDLE _hKeyClient = 0; /* key handle for client in ODB */
1472static HNDLE _hDB = 0; /* Database handle */
1473static std::string _experiment_name;
1474static std::string _client_name;
1475static std::string _path_name;
1480//INT _semaphore_msg = -1;
1481
1491const char *cm_get_version() {
1492 return MIDAS_VERSION;
1493}
1494
1499const char *cm_get_revision() {
1500 return GIT_REVISION;
1501}
1502
1503/********************************************************************/
1512INT cm_set_path(const char *path) {
1513 assert(path);
1514 assert(path[0] != 0);
1515
1516 _path_name = path;
1517
1518 if (_path_name.back() != DIR_SEPARATOR) {
1520 }
1521
1522 //printf("cm_set_path [%s]\n", _path_name.c_str());
1523
1524 return CM_SUCCESS;
1525}
1526
1527/********************************************************************/
1533INT cm_get_path(char *path, int path_size) {
1534 // check that we were not accidentally called
1535 // with the size of the pointer to a string
1536 // instead of the size of the string buffer
1537 assert(path_size != sizeof(char *));
1538 assert(path);
1539 assert(_path_name.length() > 0);
1540
1541 mstrlcpy(path, _path_name.c_str(), path_size);
1542
1543 return CM_SUCCESS;
1544}
1545
1546/********************************************************************/
1552std::string cm_get_path() {
1553 assert(_path_name.length() > 0);
1554 return _path_name;
1555}
1556
1557/********************************************************************/
1558/* C++ wrapper for cm_get_path */
1559
1560INT EXPRT cm_get_path_string(std::string *path) {
1561 assert(path != NULL);
1562 assert(_path_name.length() > 0);
1563 *path = _path_name;
1564 return CM_SUCCESS;
1565}
1566
1567/********************************************************************/
1575 return CM_SUCCESS;
1576}
1577
1578/********************************************************************/
1585INT cm_get_experiment_name(char *name, int name_length) {
1586 mstrlcpy(name, _experiment_name.c_str(), name_length);
1587 return CM_SUCCESS;
1588}
1589
1590/********************************************************************/
1596 return _experiment_name;
1597}
1598
/* end of cmfunctionc */
1601
1607#ifdef LOCAL_ROUTINES
1608
1610 std::string name;
1611 std::string directory;
1612 std::string user;
1613};
1614
1616 std::string filename;
1617 std::vector<exptab_entry> exptab;
1618};
1619
1620static exptab_struct _exptab; // contents of exptab file
1621
1630{
1631 exptab->exptab.clear();
1632
1633 /* MIDAS_DIR overrides exptab */
1634 if (getenv("MIDAS_DIR")) {
1635 exptab->filename = "MIDAS_DIR";
1636
1638
1639 if (getenv("MIDAS_EXPT_NAME")) {
1640 e.name = getenv("MIDAS_EXPT_NAME");
1641 } else {
1642 e.name = "Default";
1643 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());
1644 }
1645
1646 e.directory = getenv("MIDAS_DIR");
1647 e.user = "";
1648
1649 exptab->exptab.push_back(e);
1650
1651 return CM_SUCCESS;
1652 }
1653
1654 /* default directory for different OSes */
1655#if defined (OS_WINNT)
1656 std::string str;
1657 if (getenv("SystemRoot"))
1658 str = getenv("SystemRoot");
1659 else if (getenv("windir"))
1660 str = getenv("windir");
1661 else
1662 str = "";
1663
1664 std::string alt_str = str;
1665 str += "\\system32\\exptab";
1666 alt_str += "\\system\\exptab";
1667#elif defined (OS_UNIX)
1668 std::string str = "/etc/exptab";
1669 std::string alt_str = "/exptab";
1670#else
1671 std::strint str = "exptab";
1672 std::string alt_str = "exptab";
1673#endif
1674
1675 /* MIDAS_EXPTAB overrides default directory */
1676 if (getenv("MIDAS_EXPTAB")) {
1677 str = getenv("MIDAS_EXPTAB");
1678 alt_str = getenv("MIDAS_EXPTAB");
1679 }
1680
1681 exptab->filename = str;
1682
1683 /* read list of available experiments */
1684 FILE* f = fopen(str.c_str(), "r");
1685 if (f == NULL) {
1686 f = fopen(alt_str.c_str(), "r");
1687 if (f == NULL)
1688 return CM_UNDEF_ENVIRON;
1689 exptab->filename = alt_str;
1690 }
1691
1692 if (f != NULL) {
1693 do {
1694 char buf[256];
1695 memset(buf, 0, sizeof(buf));
1696 char* str = fgets(buf, sizeof(buf)-1, f);
1697 if (str == NULL)
1698 break;
1699 if (str[0] == 0) continue; // empty line
1700 if (str[0] == '#') continue; // comment line
1701
1703
1704 // following code emulates the function of this sprintf():
1705 //sscanf(str, "%s %s %s", exptab[i].name, exptab[i].directory, exptab[i].user);
1706
1707 // skip leading spaces
1708 while (*str && isspace(*str))
1709 str++;
1710
1711 char* p1 = str;
1712 char* p2 = str;
1713
1714 while (*p2 && !isspace(*p2))
1715 p2++;
1716
1717 ssize_t len = p2-p1;
1718
1719 if (len<1)
1720 continue;
1721
1722 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1723
1724 e.name = std::string(p1, len);
1725
1726 if (*p2 == 0)
1727 continue;
1728
1729 str = p2;
1730
1731 // skip leading spaces
1732 while (*str && isspace(*str))
1733 str++;
1734
1735 p1 = str;
1736 p2 = str;
1737
1738 while (*p2 && !isspace(*p2))
1739 p2++;
1740
1741 len = p2-p1;
1742
1743 if (len<1)
1744 continue;
1745
1746 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1747
1748 e.directory = std::string(p1, len);
1749
1750 if (*p2 == 0)
1751 continue;
1752
1753 str = p2;
1754
1755 // skip leading spaces
1756 while (*str && isspace(*str))
1757 str++;
1758
1759 p1 = str;
1760 p2 = str;
1761
1762 while (*p2 && !isspace(*p2))
1763 p2++;
1764
1765 len = p2-p1;
1766
1767 //printf("str %d [%s] p1 [%s] p2 %d [%s] len %d\n", *str, str, p1, *p2, p2, (int)len);
1768
1769 e.user = std::string(p1, len);
1770
1771 /* check for trailing directory separator */
1772 if (!ends_with_char(e.directory, DIR_SEPARATOR)) {
1773 e.directory += DIR_SEPARATOR_STR;
1774 }
1775
1776 exptab->exptab.push_back(e);
1777 } while (!feof(f));
1778 fclose(f);
1779 }
1780
1781#if 0
1782 cm_msg(MINFO, "cm_read_exptab", "Read exptab \"%s\":", exptab->filename.c_str());
1783 for (unsigned j=0; j<exptab->exptab.size(); j++) {
1784 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());
1785 }
1786#endif
1787
1788 return CM_SUCCESS;
1789}
1790
1791/********************************************************************/
1798int cm_get_exptab_filename(char *s, int size) {
1799 mstrlcpy(s, _exptab.filename.c_str(), size);
1800 return CM_SUCCESS;
1801}
1802
1804 return _exptab.filename;
1805}
1806
1807/********************************************************************/
1814int cm_get_exptab(const char *expname, std::string* dir, std::string* user) {
1815
1816 if (_exptab.exptab.size() == 0) {
1818 if (status != CM_SUCCESS)
1819 return status;
1820 }
1821
1822 for (unsigned i = 0; i < _exptab.exptab.size(); i++) {
1823 if (_exptab.exptab[i].name == expname) {
1824 if (dir)
1825 *dir = _exptab.exptab[i].directory;
1826 if (user)
1827 *user = _exptab.exptab[i].user;
1828 return CM_SUCCESS;
1829 }
1830 }
1831 if (dir)
1832 *dir = "";
1833 if (user)
1834 *user = "";
1835 return CM_UNDEF_EXP;
1836}
1837
1838/********************************************************************/
1845int cm_get_exptab(const char *expname, char *dir, int dir_size, char *user, int user_size) {
1846 std::string sdir, suser;
1847 int status = cm_get_exptab(expname, &sdir, &suser);
1848 if (status == CM_SUCCESS) {
1849 if (dir)
1850 mstrlcpy(dir, sdir.c_str(), dir_size);
1851 if (user)
1852 mstrlcpy(user, suser.c_str(), user_size);
1853 return CM_SUCCESS;
1854 }
1855 return CM_UNDEF_EXP;
1856}
1857
1858#endif // LOCAL_ROUTINES
1859
1860/********************************************************************/
1868 /* only do it if local */
1869 if (!rpc_is_remote()) {
1871 }
1872 return CM_SUCCESS;
1873}
1874
1875/********************************************************************/
1885 if (rpc_is_remote())
1886 return rpc_call(RPC_CM_CHECK_CLIENT, hDB, hKeyClient);
1887
1888#ifdef LOCAL_ROUTINES
1889 return db_check_client(hDB, hKeyClient);
1890#endif /*LOCAL_ROUTINES */
1891 return CM_SUCCESS;
1892}
1893
1894/********************************************************************/
1908INT cm_set_client_info(HNDLE hDB, HNDLE *hKeyClient, const char *host_name,
1909 char *client_name, INT hw_type, const char *password, DWORD watchdog_timeout) {
1910 if (rpc_is_remote())
1911 return rpc_call(RPC_CM_SET_CLIENT_INFO, hDB, hKeyClient,
1912 host_name, client_name, hw_type, password, watchdog_timeout);
1913
1914#ifdef LOCAL_ROUTINES
1915 {
1916 INT status, pid, data, i, idx, size;
1918 char str[256], name[NAME_LENGTH], orig_name[NAME_LENGTH], pwd[NAME_LENGTH];
1919 BOOL call_watchdog, allow;
1920 PROGRAM_INFO_STR(program_info_str);
1921
1922 /* check security if password is present */
1923 status = db_find_key(hDB, 0, "/Experiment/Security/Password", &hKey);
1924 if (hKey) {
1925 /* get password */
1926 size = sizeof(pwd);
1927 db_get_data(hDB, hKey, pwd, &size, TID_STRING);
1928
1929 /* first check allowed hosts list */
1930 allow = FALSE;
1931 db_find_key(hDB, 0, "/Experiment/Security/Allowed hosts", &hKey);
1933 allow = TRUE;
1934
1935 /* check allowed programs list */
1936 db_find_key(hDB, 0, "/Experiment/Security/Allowed programs", &hKey);
1937 if (hKey && db_find_key(hDB, hKey, client_name, &hKey) == DB_SUCCESS)
1938 allow = TRUE;
1939
1940 /* now check password */
1941 if (!allow && strcmp(password, pwd) != 0) {
1942 if (password[0])
1943 cm_msg(MINFO, "cm_set_client_info", "Wrong password for host %s", host_name);
1944 return CM_WRONG_PASSWORD;
1945 }
1946 }
1947
1948 /* make following operation atomic by locking database */
1950
1951 /* check if entry with this pid exists already */
1952 pid = ss_getpid();
1953
1954 sprintf(str, "System/Clients/%0d", pid);
1955 status = db_find_key(hDB, 0, str, &hKey);
1956 if (status == DB_SUCCESS) {
1959 }
1960
1961 if (strlen(client_name) >= NAME_LENGTH)
1962 client_name[NAME_LENGTH] = 0;
1963
1964 strcpy(name, client_name);
1965 strcpy(orig_name, client_name);
1966
1967 /* check if client name already exists */
1968 status = db_find_key(hDB, 0, "System/Clients", &hKey);
1969
1970 for (idx = 1; status != DB_NO_MORE_SUBKEYS; idx++) {
1971 for (i = 0;; i++) {
1974 break;
1975
1976 if (status == DB_SUCCESS) {
1977 size = sizeof(str);
1978 status = db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, FALSE);
1979 if (status != DB_SUCCESS)
1980 continue;
1981 }
1982
1983 /* check if client is living */
1985 continue;
1986
1987 if (equal_ustring(str, name)) {
1988 sprintf(name, "%s%d", client_name, idx);
1989 break;
1990 }
1991 }
1992 }
1993
1994 /* set name */
1995 sprintf(str, "System/Clients/%0d/Name", pid);
1997 if (status != DB_SUCCESS) {
1999 cm_msg(MERROR, "cm_set_client_info", "cannot set client name, db_set_value(%s) status %d", str, status);
2000 return status;
2001 }
2002
2003 /* copy new client name */
2004 strcpy(client_name, name);
2005 db_set_client_name(hDB, client_name);
2006
2007 /* set also as rpc name */
2008 rpc_set_name(client_name);
2009
2010 /* use /system/clients/PID as root */
2011 sprintf(str, "System/Clients/%0d", pid);
2012 db_find_key(hDB, 0, str, &hKey);
2013
2014 /* set host name */
2016 if (status != DB_SUCCESS) {
2018 return status;
2019 }
2020
2021 /* set computer id */
2022 status = db_set_value(hDB, hKey, "Hardware type", &hw_type, sizeof(hw_type), 1, TID_INT32);
2023 if (status != DB_SUCCESS) {
2025 return status;
2026 }
2027
2028 /* set server port */
2029 data = 0;
2030 status = db_set_value(hDB, hKey, "Server Port", &data, sizeof(INT), 1, TID_INT32);
2031 if (status != DB_SUCCESS) {
2033 return status;
2034 }
2035
2036 /* lock client entry */
2038
2039 /* get (set) default watchdog timeout */
2040 size = sizeof(watchdog_timeout);
2041 sprintf(str, "/Programs/%s/Watchdog Timeout", orig_name);
2042 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2043
2044 /* define /programs entry */
2045 sprintf(str, "/Programs/%s", orig_name);
2046 db_create_record(hDB, 0, str, strcomb1(program_info_str).c_str());
2047
2048 /* save handle for ODB and client */
2050
2051 /* save watchdog timeout */
2052 cm_get_watchdog_params(&call_watchdog, NULL);
2053 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2054
2055 /* end of atomic operations */
2057
2058 /* touch notify key to inform others */
2059 data = 0;
2060 db_set_value(hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32);
2061
2062 *hKeyClient = hKey;
2063 }
2064#endif /* LOCAL_ROUTINES */
2065
2066 return CM_SUCCESS;
2067}
2068
2069/********************************************************************/
2075{
2076 INT status;
2077 HNDLE hDB, hKey;
2078
2079 /* get root key of client */
2081 if (!hDB) {
2082 return "unknown";
2083 }
2084
2085 std::string name;
2086
2087 status = db_get_value_string(hDB, hKey, "Name", 0, &name);
2088 if (status != DB_SUCCESS) {
2089 return "unknown";
2090 }
2091
2092 //printf("get client name: [%s]\n", name.c_str());
2093
2094 return name;
2095}
2096
2097/********************************************************************/
2149INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size) {
2150 if (host_name)
2151 host_name[0] = 0;
2152 if (exp_name)
2153 exp_name[0] = 0;
2154
2155 if (host_name && getenv("MIDAS_SERVER_HOST"))
2156 mstrlcpy(host_name, getenv("MIDAS_SERVER_HOST"), host_name_size);
2157
2158 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2159 mstrlcpy(exp_name, getenv("MIDAS_EXPT_NAME"), exp_name_size);
2160
2161 return CM_SUCCESS;
2162}
2163
2164INT cm_get_environment(std::string *host_name, std::string *exp_name) {
2165 if (host_name)
2166 *host_name = "";
2167 if (exp_name)
2168 *exp_name = "";
2169
2170 if (host_name && getenv("MIDAS_SERVER_HOST"))
2171 *host_name = getenv("MIDAS_SERVER_HOST");
2172
2173 if (exp_name && getenv("MIDAS_EXPT_NAME"))
2174 *exp_name = getenv("MIDAS_EXPT_NAME");
2175
2176 return CM_SUCCESS;
2177}
2178
2179#ifdef LOCAL_ROUTINES
2180
2182{
2183 std::string exp_name1;
2184
2185 if ((exp_name != NULL) && (strlen(exp_name) > 0)) {
2186 exp_name1 = exp_name;
2187 } else {
2188 int status = cm_select_experiment_local(&exp_name1);
2189 if (status != CM_SUCCESS)
2190 return status;
2191 }
2192
2193 std::string expdir, expuser;
2194
2195 int status = cm_get_exptab(exp_name1.c_str(), &expdir, &expuser);
2196
2197 if (status != CM_SUCCESS) {
2198 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());
2199 return CM_UNDEF_EXP;
2200 }
2201
2202 if (!ss_dir_exist(expdir.c_str())) {
2203 cm_msg(MERROR, "cm_set_experiment_local", "Experiment \"%s\" directory \"%s\" does not exist", exp_name1.c_str(), expdir.c_str());
2204 return CM_UNDEF_EXP;
2205 }
2206
2207 cm_set_experiment_name(exp_name1.c_str());
2208 cm_set_path(expdir.c_str());
2209
2210 return CM_SUCCESS;
2211}
2212
2213#endif // LOCAL_ROUTINES
2214
2215/********************************************************************/
2217 if (_hKeyClient) {
2218 cm_msg(MERROR, "cm_check_connect", "cm_disconnect_experiment not called at end of program");
2220 }
2221}
2222
2223/********************************************************************/
2293INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void (*func)(char *)) {
2294 INT status;
2295
2298 if (status != CM_SUCCESS) {
2299 std::string s = cm_get_error(status);
2300 puts(s.c_str());
2301 }
2302
2303 return status;
2304}
2305
2306/********************************************************************/
2312INT cm_connect_experiment1(const char *host_name, const char *default_exp_name,
2313 const char *client_name, void (*func)(char *), INT odb_size, DWORD watchdog_timeout) {
2314 INT status, size;
2315 char client_name1[NAME_LENGTH];
2316 char password[NAME_LENGTH], str[256];
2317 HNDLE hDB = 0, hKeyClient = 0;
2318 BOOL call_watchdog;
2319
2320 ss_tzset(); // required for localtime_r()
2321
2322 if (_hKeyClient)
2324
2326
2327 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before connecting to experiment");
2328 //cm_msg_flush_buffer();
2329
2330 rpc_set_name(client_name);
2331
2332 /* check for local host */
2333 if (equal_ustring(host_name, "local"))
2334 host_name = NULL;
2335
2336#ifdef OS_WINNT
2337 {
2338 WSADATA WSAData;
2339
2340 /* Start windows sockets */
2341 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
2342 return RPC_NET_ERROR;
2343 }
2344#endif
2345
2346 std::string default_exp_name1;
2347 if (default_exp_name)
2348 default_exp_name1 = default_exp_name;
2349
2350 /* connect to MIDAS server */
2351 if (host_name && host_name[0]) {
2352 if (default_exp_name1.length() == 0) {
2353 status = cm_select_experiment_remote(host_name, &default_exp_name1);
2354 if (status != CM_SUCCESS)
2355 return status;
2356 }
2357
2358 cm_set_experiment_name(default_exp_name1.c_str());
2359
2360 status = rpc_server_connect(host_name, default_exp_name1.c_str());
2361 if (status != RPC_SUCCESS)
2362 return status;
2363
2364 /* register MIDAS library functions */
2366 if (status != RPC_SUCCESS)
2367 return status;
2368 } else {
2369 /* lookup path for *SHM files and save it */
2370
2371#ifdef LOCAL_ROUTINES
2372 status = cm_set_experiment_local(default_exp_name1.c_str());
2373 if (status != CM_SUCCESS)
2374 return status;
2375
2376 default_exp_name1 = cm_get_experiment_name();
2377
2379
2380 INT semaphore_elog, semaphore_alarm, semaphore_history, semaphore_msg;
2381
2382 /* create alarm and elog semaphores */
2383 status = ss_semaphore_create("ALARM", &semaphore_alarm);
2384 if (status != SS_CREATED && status != SS_SUCCESS) {
2385 cm_msg(MERROR, "cm_connect_experiment", "Cannot create alarm semaphore");
2386 return status;
2387 }
2388 status = ss_semaphore_create("ELOG", &semaphore_elog);
2389 if (status != SS_CREATED && status != SS_SUCCESS) {
2390 cm_msg(MERROR, "cm_connect_experiment", "Cannot create elog semaphore");
2391 return status;
2392 }
2393 status = ss_semaphore_create("HISTORY", &semaphore_history);
2394 if (status != SS_CREATED && status != SS_SUCCESS) {
2395 cm_msg(MERROR, "cm_connect_experiment", "Cannot create history semaphore");
2396 return status;
2397 }
2398 status = ss_semaphore_create("MSG", &semaphore_msg);
2399 if (status != SS_CREATED && status != SS_SUCCESS) {
2400 cm_msg(MERROR, "cm_connect_experiment", "Cannot create message semaphore");
2401 return status;
2402 }
2403
2404 cm_set_experiment_semaphore(semaphore_alarm, semaphore_elog, semaphore_history, semaphore_msg);
2405#else
2406 return CM_UNDEF_EXP;
2407#endif
2408 }
2409
2410 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg before open ODB");
2411 //cm_msg_flush_buffer();
2412
2413 /* open ODB */
2414 if (odb_size == 0)
2416
2417 status = db_open_database("ODB", odb_size, &hDB, client_name);
2418 if (status != DB_SUCCESS && status != DB_CREATED) {
2419 cm_msg(MERROR, "cm_connect_experiment1", "cannot open database, db_open_database() status %d", status);
2420 return status;
2421 }
2422
2423 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after open ODB");
2424 //cm_msg_flush_buffer();
2425
2426 int odb_timeout = db_set_lock_timeout(hDB, 0);
2427 size = sizeof(odb_timeout);
2428 status = db_get_value(hDB, 0, "/Experiment/ODB timeout", &odb_timeout, &size, TID_INT32, TRUE);
2429 if (status != DB_SUCCESS) {
2430 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/ODB timeout, status %d", status);
2431 }
2432
2433 if (odb_timeout > 0) {
2434 db_set_lock_timeout(hDB, odb_timeout);
2435 }
2436
2437 BOOL protect_odb = FALSE;
2438 size = sizeof(protect_odb);
2439 status = db_get_value(hDB, 0, "/Experiment/Protect ODB", &protect_odb, &size, TID_BOOL, TRUE);
2440 if (status != DB_SUCCESS) {
2441 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Protect ODB, status %d", status);
2442 }
2443
2444 if (protect_odb) {
2446 }
2447
2448 BOOL enable_core_dumps = FALSE;
2449 size = sizeof(enable_core_dumps);
2450 status = db_get_value(hDB, 0, "/Experiment/Enable core dumps", &enable_core_dumps, &size, TID_BOOL, TRUE);
2451 if (status != DB_SUCCESS) {
2452 cm_msg(MERROR, "cm_connect_experiment1", "cannot get ODB /Experiment/Enable core dumps, status %d", status);
2453 }
2454
2455 if (enable_core_dumps) {
2456#ifdef RLIMIT_CORE
2457 struct rlimit limit;
2458 limit.rlim_cur = RLIM_INFINITY;
2459 limit.rlim_max = RLIM_INFINITY;
2460 status = setrlimit(RLIMIT_CORE, &limit);
2461 if (status != 0) {
2462 cm_msg(MERROR, "cm_connect_experiment", "Cannot setrlimit(RLIMIT_CORE, RLIM_INFINITY), errno %d (%s)", errno,
2463 strerror(errno));
2464 }
2465#else
2466#warning setrlimit(RLIMIT_CORE) is not available
2467#endif
2468 }
2469
2470 size = sizeof(disable_bind_rpc_to_localhost);
2471 status = db_get_value(hDB, 0, "/Experiment/Security/Enable non-localhost RPC", &disable_bind_rpc_to_localhost, &size,
2472 TID_BOOL, TRUE);
2473 if (status != DB_SUCCESS) {
2474 cm_msg(MERROR, "cm_connect_experiment1",
2475 "cannot get ODB /Experiment/Security/Enable non-localhost RPC, status %d", status);
2476 }
2477
2478 std::string local_host_name;
2479
2480 /* now setup client info */
2482 local_host_name = "localhost";
2483 else
2484 local_host_name = ss_gethostname();
2485
2486 /* check watchdog timeout */
2487 if (watchdog_timeout == 0)
2488 watchdog_timeout = DEFAULT_WATCHDOG_TIMEOUT;
2489
2490 strcpy(client_name1, client_name);
2491 password[0] = 0;
2492 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2493
2494 if (status == CM_WRONG_PASSWORD) {
2495 if (func == NULL)
2496 strcpy(str, ss_getpass("Password: "));
2497 else
2498 func(str);
2499
2500 strcpy(password, ss_crypt(str, "mi"));
2501 status = cm_set_client_info(hDB, &hKeyClient, local_host_name.c_str(), client_name1, rpc_get_hw_type(), password, watchdog_timeout);
2502 if (status != CM_SUCCESS) {
2503 /* disconnect */
2504 if (rpc_is_remote())
2507
2508 return status;
2509 }
2510 }
2511
2512 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set client info");
2513 //cm_msg_flush_buffer();
2514
2515 /* tell the rest of MIDAS that ODB is open for business */
2516
2517 cm_set_experiment_database(hDB, hKeyClient);
2518
2519 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after set experiment database");
2520 //cm_msg_flush_buffer();
2521
2522 /* cm_msg_open_buffer() calls bm_open_buffer() calls ODB function
2523 * to get event buffer size, etc */
2524
2526 if (status != CM_SUCCESS) {
2527 cm_msg(MERROR, "cm_connect_experiment1", "cannot open message buffer, cm_msg_open_buffer() status %d", status);
2528 return status;
2529 }
2530
2531 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after message system is ready");
2532 //cm_msg_flush_buffer();
2533
2534 /* set experiment name in ODB if not present */
2535 std::string current_name;
2536 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &current_name, TRUE);
2537 if (current_name.length() == 0 || current_name == "Default") {
2538 db_set_value_string(hDB, 0, "/Experiment/Name", &default_exp_name1);
2539 }
2540
2541 if (!rpc_is_remote()) {
2542 /* experiment path is only set for local connections */
2543 /* set data dir in ODB */
2544 std::string path = cm_get_path();
2545 db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, TRUE);
2546 }
2547
2548 /* register server to be able to be called by other clients */
2550 if (status != CM_SUCCESS) {
2551 cm_msg(MERROR, "cm_connect_experiment", "Cannot register RPC server, cm_register_server() status %d", status);
2552 if (!equal_ustring(client_name, "odbedit")) {
2553 return status;
2554 }
2555 }
2556
2557 /* set watchdog timeout */
2558 cm_get_watchdog_params(&call_watchdog, &watchdog_timeout);
2559 size = sizeof(watchdog_timeout);
2560 sprintf(str, "/Programs/%s/Watchdog Timeout", client_name);
2561 db_get_value(hDB, 0, str, &watchdog_timeout, &size, TID_INT32, TRUE);
2562 cm_set_watchdog_params(call_watchdog, watchdog_timeout);
2563
2564 /* set command line */
2565 std::string cmdline = ss_get_cmdline();
2566 std::string path = "/Programs/" + std::string(client_name);
2567 midas::odb prog(path);
2568 if (!midas::odb::exists(path + "/Start command") ||
2569 prog["Start command"] == std::string(""))
2570 prog["Start command"].set_string_size(cmdline, 256);
2571
2572 /* get final client name */
2573 std::string xclient_name = rpc_get_name();
2574
2575 /* startup message is not displayed */
2576 cm_msg(MLOG, "cm_connect_experiment", "Program %s on host %s started", xclient_name.c_str(), local_host_name.c_str());
2577
2578 /* enable system and user messages to stdout as default */
2580
2581 /* call cm_check_connect when exiting */
2582 atexit((void (*)(void)) cm_check_connect);
2583
2584 /* register ctrl-c handler */
2586
2587 //cm_msg(MERROR, "cm_connect_experiment", "test cm_msg after connect to experiment is complete");
2588 //cm_msg_flush_buffer();
2589
2590 return CM_SUCCESS;
2591}
2592
2593#ifdef LOCAL_ROUTINES
2594/********************************************************************/
2602 assert(exp_names != NULL);
2603 exp_names->clear();
2604
2605 if (_exptab.exptab.size() == 0) {
2607 if (status != CM_SUCCESS)
2608 return status;
2609 }
2610
2611 for (unsigned i=0; i<_exptab.exptab.size(); i++) {
2612 exp_names->push_back(_exptab.exptab[i].name);
2613 }
2614
2615 return CM_SUCCESS;
2616}
2617#endif // LOCAL_ROUTINES
2618
2619/********************************************************************/
2628 INT status;
2629 INT sock;
2630 int port = MIDAS_TCP_PORT;
2631 char hname[256];
2632 char *s;
2633
2634 assert(exp_names != NULL);
2635 exp_names->clear();
2636
2637 /* extract port number from host_name */
2638 mstrlcpy(hname, host_name, sizeof(hname));
2639 s = strchr(hname, ':');
2640 if (s) {
2641 *s = 0;
2642 port = strtoul(s + 1, NULL, 0);
2643 }
2644
2645 std::string errmsg;
2646
2647 status = ss_socket_connect_tcp(hname, port, &sock, &errmsg);
2648
2649 if (status != SS_SUCCESS) {
2650 cm_msg(MERROR, "cm_list_experiments_remote", "Cannot connect to \"%s\" port %d: %s", hname, port, errmsg.c_str());
2651 return RPC_NET_ERROR;
2652 }
2653
2654 /* request experiment list */
2655 send(sock, "I", 2, 0);
2656
2657 while (1) {
2658 char str[256];
2659
2660 status = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
2661
2662 if (status < 0)
2663 return RPC_NET_ERROR;
2664
2665 if (status == 0)
2666 break;
2667
2668 exp_names->push_back(str);
2669 }
2670
2671 ss_socket_close(&sock);
2672
2673 return CM_SUCCESS;
2674}
2675
2676#ifdef LOCAL_ROUTINES
2677/********************************************************************/
2686 INT status;
2687 STRING_LIST expts;
2688
2689 assert(exp_name != NULL);
2690
2691 /* retrieve list of experiments and make selection */
2693 if (status != CM_SUCCESS)
2694 return status;
2695
2696 if (expts.size() == 1) {
2697 *exp_name = expts[0];
2698 } else if (expts.size() > 1) {
2699 printf("Available experiments on local computer:\n");
2700
2701 for (unsigned i = 0; i < expts.size(); i++) {
2702 printf("%d : %s\n", i, expts[i].c_str());
2703 }
2704
2705 while (1) {
2706 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2707 char str[32];
2708 ss_gets(str, 32);
2709 int isel = atoi(str);
2710 if (isel < 0)
2711 continue;
2712 if (isel >= (int)expts.size())
2713 continue;
2714 *exp_name = expts[isel];
2715 break;
2716 }
2717 } else {
2718 return CM_UNDEF_EXP;
2719 }
2720
2721 return CM_SUCCESS;
2722}
2723#endif // LOCAL_ROUTINES
2724
2725/********************************************************************/
2735 INT status;
2736 STRING_LIST expts;
2737
2738 assert(exp_name != NULL);
2739
2740 /* retrieve list of experiments and make selection */
2742 if (status != CM_SUCCESS)
2743 return status;
2744
2745 if (expts.size() > 1) {
2746 printf("Available experiments on server %s:\n", host_name);
2747
2748 for (unsigned i = 0; i < expts.size(); i++) {
2749 printf("%d : %s\n", i, expts[i].c_str());
2750 }
2751
2752 while (1) {
2753 printf("Select number from 0 to %d: ", ((int)expts.size())-1);
2754 char str[32];
2755 ss_gets(str, 32);
2756 int isel = atoi(str);
2757 if (isel < 0)
2758 continue;
2759 if (isel >= (int)expts.size())
2760 continue;
2761 *exp_name = expts[isel];
2762 break;
2763 }
2764 } else {
2765 *exp_name = expts[0];
2766 }
2767
2768 return CM_SUCCESS;
2769}
2770
2771/********************************************************************/
2781INT cm_connect_client(const char *client_name, HNDLE *hConn) {
2782 HNDLE hDB, hKeyRoot, hSubkey, hKey;
2783 INT status, i, length, port;
2785
2786 /* find client entry in ODB */
2788
2789 status = db_find_key(hDB, 0, "System/Clients", &hKeyRoot);
2790 if (status != DB_SUCCESS)
2791 return status;
2792
2793 i = 0;
2794 do {
2795 /* search for client with specific name */
2796 status = db_enum_key(hDB, hKeyRoot, i++, &hSubkey);
2798 return CM_NO_CLIENT;
2799
2800 status = db_find_key(hDB, hSubkey, "Name", &hKey);
2801 if (status != DB_SUCCESS)
2802 return status;
2803
2804 length = NAME_LENGTH;
2805 status = db_get_data(hDB, hKey, name, &length, TID_STRING);
2806 if (status != DB_SUCCESS)
2807 return status;
2808
2809 if (equal_ustring(name, client_name)) {
2810 status = db_find_key(hDB, hSubkey, "Server Port", &hKey);
2811 if (status != DB_SUCCESS)
2812 return status;
2813
2814 length = sizeof(INT);
2815 status = db_get_data(hDB, hKey, &port, &length, TID_INT32);
2816 if (status != DB_SUCCESS)
2817 return status;
2818
2819 status = db_find_key(hDB, hSubkey, "Host", &hKey);
2820 if (status != DB_SUCCESS)
2821 return status;
2822
2823 length = sizeof(host_name);
2825 if (status != DB_SUCCESS)
2826 return status;
2827
2828 /* client found -> connect to its server port */
2829 return rpc_client_connect(host_name, port, client_name, hConn);
2830 }
2831
2832
2833 } while (TRUE);
2834}
2835
2836static void rpc_client_shutdown();
2837
2838/********************************************************************/
2849 return rpc_client_disconnect(hConn, bShutdown);
2850}
2851
2852/********************************************************************/
2862 HNDLE hDB, hKey;
2863
2864 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before disconnect from experiment");
2865 //cm_msg_flush_buffer();
2866
2867 /* wait on any transition thread */
2868 if (_trp.transition && !_trp.finished) {
2869 printf("Waiting for transition to finish...\n");
2870 do {
2871 ss_sleep(10);
2872 } while (!_trp.finished);
2873 }
2874
2875 /* stop the watchdog thread */
2877
2878 /* send shutdown notification */
2879 std::string client_name = rpc_get_name();
2880
2881 std::string local_host_name;
2882
2884 local_host_name = "localhost";
2885 else {
2886 local_host_name = ss_gethostname();
2887 //if (strchr(local_host_name, '.'))
2888 // *strchr(local_host_name, '.') = 0;
2889 }
2890
2891 /* disconnect message not displayed */
2892 cm_msg(MLOG, "cm_disconnect_experiment", "Program %s on host %s stopped", client_name.c_str(), local_host_name.c_str());
2894
2895 if (rpc_is_remote()) {
2896 if (rpc_is_connected()) {
2897 /* close open records */
2899
2901 }
2902
2905
2907 } else {
2909
2910 /* delete client info */
2912
2913 if (hDB)
2915
2916 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before close all buffers, close all databases");
2917 //cm_msg_flush_buffer();
2918
2922
2924
2925 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after close all buffers, close all databases");
2926 //cm_msg_flush_buffer();
2927 }
2928
2929 if (!rpc_is_mserver())
2931
2932 /* free RPC list */
2934
2935 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg before deleting the message ring buffer");
2936 //cm_msg_flush_buffer();
2937
2938 /* last flush before we delete the message ring buffer */
2940
2941 //cm_msg(MERROR, "cm_disconnect_experiment", "test cm_msg after disconnect is completed");
2942 //cm_msg_flush_buffer();
2943
2944 return CM_SUCCESS;
2945}
2946
2947/********************************************************************/
2955 //printf("cm_set_experiment_database: hDB %d, hKeyClient %d\n", hDB, hKeyClient);
2956
2957 _hDB = hDB;
2958 _hKeyClient = hKeyClient;
2959
2960 //if (hDB == 0) {
2961 // rpc_set_server_option(RPC_ODB_HANDLE, 0);
2962 //}
2963
2964 return CM_SUCCESS;
2965}
2966
2967
2968
2970#ifndef DOXYGEN_SHOULD_SKIP_THIS
2971
2972/********************************************************************/
2973INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
2974/********************************************************************\
2975
2976 Routine: cm_set_experiment_semaphore
2977
2978 Purpose: Set the handle to the experiment wide semaphorees
2979
2980 Input:
2981 INT semaphore_alarm Alarm semaphore
2982 INT semaphore_elog Elog semaphore
2983 INT semaphore_history History semaphore
2984 INT semaphore_msg Message semaphore
2985
2986 Output:
2987 none
2988
2989 Function value:
2990 CM_SUCCESS Successful completion
2991
2992\********************************************************************/
2993{
2994 _semaphore_alarm = semaphore_alarm;
2995 _semaphore_elog = semaphore_elog;
2996 _semaphore_history = semaphore_history;
2997 //_semaphore_msg = semaphore_msg;
2998
2999 return CM_SUCCESS;
3000}
3001
3003#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3004
3005/********************************************************************/
3027 if (_hDB) {
3028 //printf("cm_get_experiment_database %d %d\n", _hDB, _hKeyClient);
3029 if (hDB != NULL)
3030 *hDB = _hDB;
3031 if (hKeyClient != NULL)
3032 *hKeyClient = _hKeyClient;
3033 return CM_SUCCESS;
3034 } else {
3035 //printf("cm_get_experiment_database no init\n");
3036 if (hDB != NULL)
3037 *hDB = 0;
3038 if (hKeyClient != NULL)
3039 *hKeyClient = 0;
3040 return CM_DB_ERROR;
3041 }
3042}
3043
3045#ifndef DOXYGEN_SHOULD_SKIP_THIS
3046
3047/********************************************************************/
3048INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
3049/********************************************************************\
3050
3051 Routine: cm_get_experiment_semaphore
3052
3053 Purpose: Get the handle to the experiment wide semaphores
3054
3055 Input:
3056 none
3057
3058 Output:
3059 INT semaphore_alarm Alarm semaphore
3060 INT semaphore_elog Elog semaphore
3061 INT semaphore_history History semaphore
3062 INT semaphore_msg Message semaphore
3063
3064 Function value:
3065 CM_SUCCESS Successful completion
3066
3067\********************************************************************/
3068{
3069 if (semaphore_alarm)
3070 *semaphore_alarm = _semaphore_alarm;
3071 if (semaphore_elog)
3072 *semaphore_elog = _semaphore_elog;
3073 if (semaphore_history)
3074 *semaphore_history = _semaphore_history;
3075 //if (semaphore_msg)
3076 // *semaphore_msg = _semaphore_msg;
3077 if (semaphore_msg)
3078 *semaphore_msg = -1;
3079
3080 return CM_SUCCESS;
3081}
3082
3084#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3085
3086#ifdef LOCAL_ROUTINES
3087static BUFFER* bm_get_buffer(const char *who, INT buffer_handle, int *pstatus);
3088static int bm_lock_buffer_read_cache(BUFFER *pbuf);
3089static int bm_lock_buffer_write_cache(BUFFER *pbuf);
3090static int bm_lock_buffer_mutex(BUFFER *pbuf);
3091static int xbm_lock_buffer(BUFFER *pbuf);
3092static void xbm_unlock_buffer(BUFFER *pbuf);
3093
3095{
3096public:
3097 bool fDebug = false;
3098
3099public:
3100 bm_lock_buffer_guard(BUFFER* pbuf, bool do_not_lock=false) // ctor
3101 {
3102 assert(pbuf != NULL);
3103 fBuf = pbuf;
3104 if (do_not_lock) {
3105 if (fDebug)
3106 printf("lock_buffer_guard(%s) ctor without lock\n", fBuf->buffer_name);
3107 return;
3108 }
3109 if (fDebug)
3110 printf("lock_buffer_guard(%s) ctor\n", fBuf->buffer_name);
3112 if (status != BM_SUCCESS) {
3113 fLocked = false;
3114 fError = true;
3115 fStatus = status;
3116 } else {
3117 fLocked = true;
3118 }
3119 }
3120
3122 {
3123 if (fInvalid) {
3124 if (fDebug)
3125 printf("lock_buffer_guard(invalid) dtor\n");
3126 } else {
3127 assert(fBuf != NULL);
3128 if (fDebug)
3129 printf("lock_buffer_guard(%s) dtor, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3130 if (fLocked) {
3132 fLocked = false;
3133 fError = false;
3134 }
3135 fBuf = NULL;
3136 }
3137 }
3138
3139 // make object uncopyable
3142
3143 void unlock()
3144 {
3145 assert(fBuf != NULL);
3146 if (fDebug)
3147 printf("lock_buffer_guard(%s) unlock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3148 assert(fLocked);
3150 fLocked = false;
3151 fError = false;
3152 }
3153
3154 bool relock()
3155 {
3156 assert(fBuf != NULL);
3157 if (fDebug)
3158 printf("lock_buffer_guard(%s) relock, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3159 assert(!fLocked);
3161 if (status != BM_SUCCESS) {
3162 fLocked = false;
3163 fError = true;
3164 fStatus = status;
3165 } else {
3166 fLocked = true;
3167 }
3168 return fLocked;
3169 }
3170
3172 {
3173 assert(fBuf != NULL);
3174 if (fDebug)
3175 printf("lock_buffer_guard(%s) invalidate, locked %d, error %d\n", fBuf->buffer_name, fLocked, fError);
3176 assert(!fLocked);
3177 fInvalid = true;
3178 fBuf = NULL;
3179 }
3180
3181 bool is_locked() const
3182 {
3183 return fLocked;
3184 }
3185
3186 bool is_error() const
3187 {
3188 return fError;
3189 }
3190
3191 int get_status() const
3192 {
3193 return fStatus;
3194 }
3195
3197 {
3198 assert(!fInvalid); // pbuf was deleted
3199 assert(fBuf); // we do not return NULL
3200 return fBuf;
3201 }
3202
3203private:
3204 BUFFER* fBuf = NULL;
3205 bool fLocked = false;
3206 bool fError = false;
3207 bool fInvalid = false;
3208 int fStatus = 0;
3209};
3210
3212
3213#endif
3214
3215static INT bm_notify_client(const char *buffer_name, int s);
3216
3217static INT bm_push_event(const char *buffer_name);
3218
3219static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
3220 EVENT_HEADER *pevent, void *pdata,
3221 EVENT_HANDLER *dispatcher);
3222
3223/********************************************************************/
3260{
3261#ifdef LOCAL_ROUTINES
3262 _watchdog_timeout = timeout;
3263
3264 std::vector<BUFFER*> mybuffers;
3265
3266 gBuffersMutex.lock();
3267 mybuffers = gBuffers;
3268 gBuffersMutex.unlock();
3269
3270 /* set watchdog timeout of all open buffers */
3271 for (BUFFER* pbuf : mybuffers) {
3272
3273 if (!pbuf || !pbuf->attached)
3274 continue;
3275
3276 bm_lock_buffer_guard pbuf_guard(pbuf);
3277
3278 if (!pbuf_guard.is_locked())
3279 continue;
3280
3281 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
3282
3283 /* clear entry from client structure in buffer header */
3284 pclient->watchdog_timeout = timeout;
3285
3286 /* show activity */
3287 pclient->last_activity = ss_millitime();
3288 }
3289
3290 /* set watchdog timeout for ODB */
3291 db_set_watchdog_params(timeout);
3292
3293#endif /* LOCAL_ROUTINES */
3294
3295 return CM_SUCCESS;
3296}
3297
3298INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
3299{
3300 /* set also local timeout to requested value (needed by cm_enable_watchdog()) */
3301 _watchdog_timeout = timeout;
3302
3303 if (rpc_is_remote()) { // we are connected remotely
3304
3305 return rpc_call(RPC_CM_SET_WATCHDOG_PARAMS, call_watchdog, timeout);
3306
3307 } else if (rpc_is_mserver()) { // we are the mserver
3308
3310 if (sa)
3311 sa->watchdog_timeout = timeout;
3312
3313 /* write timeout value to client entry in ODB */
3314 HNDLE hDB, hKey;
3316
3317 if (hDB) {
3319 db_set_value(hDB, hKey, "Link timeout", &timeout, sizeof(timeout), 1, TID_INT32);
3321 }
3322
3323 /* set the watchdog for the local mserver program */
3324 return cm_set_watchdog_params_local(call_watchdog, timeout);
3325
3326 } else { // only running locally
3327
3328 return cm_set_watchdog_params_local(call_watchdog, timeout);
3329
3330 }
3331}
3332
3333/********************************************************************/
3340INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout) {
3341 if (call_watchdog)
3342 *call_watchdog = FALSE;
3343 if (timeout)
3344 *timeout = _watchdog_timeout;
3345
3346 return CM_SUCCESS;
3347}
3348
3349/********************************************************************/
3359INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last) {
3360 if (rpc_is_remote())
3361 return rpc_call(RPC_CM_GET_WATCHDOG_INFO, hDB, client_name, timeout, last);
3362
3363#ifdef LOCAL_ROUTINES
3364 return db_get_watchdog_info(hDB, client_name, timeout, last);
3365#else /* LOCAL_ROUTINES */
3366 return CM_SUCCESS;
3367#endif /* LOCAL_ROUTINES */
3368}
3369
3370
3372#ifndef DOXYGEN_SHOULD_SKIP_THIS
3373
3374/********************************************************************/
3375
3376static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info) {
3377 int status;
3378 int i, last;
3379 KEY key;
3380 int max_size;
3381 char *str;
3382
3383// if (index != -99)
3384// cm_msg(MINFO, "load_rpc_hosts", "Reloading RPC hosts access control list via hotlink callback");
3385
3387
3388 if (status != DB_SUCCESS)
3389 return;
3390
3391 //printf("clear rpc hosts!\n");
3393
3394 max_size = key.item_size;
3395 str = (char *) malloc(max_size);
3396
3397 last = 0;
3398 for (i = 0; i < key.num_values; i++) {
3399 int size = max_size;
3401 if (status != DB_SUCCESS)
3402 break;
3403
3404 if (strlen(str) < 1) // skip emties
3405 continue;
3406
3407 if (str[0] == '#') // skip commented-out entries
3408 continue;
3409
3410 //printf("add rpc hosts %d [%s]\n", i, str);
3412 last = i;
3413 }
3414
3415 if (key.num_values - last < 10) {
3416 int new_size = last + 10;
3417 status = db_set_num_values(hDB, hKey, new_size);
3418 if (status != DB_SUCCESS) {
3419 cm_msg(MERROR, "load_rpc_hosts",
3420 "Cannot resize the RPC hosts access control list, db_set_num_values(%d) status %d", new_size, status);
3421 }
3422 }
3423
3424 free(str);
3425}
3426
3428 int status;
3429 char buf[256];
3430 int size, i;
3431 HNDLE hKey;
3432
3433 strcpy(buf, "localhost");
3434 size = sizeof(buf);
3435
3436 status = db_get_value(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts[0]", buf, &size, TID_STRING, TRUE);
3437
3438 if (status != DB_SUCCESS) {
3439 cm_msg(MERROR, "init_rpc_hosts", "Cannot create the RPC hosts access control list, db_get_value() status %d",
3440 status);
3441 return;
3442 }
3443
3444 size = sizeof(i);
3445 i = 0;
3446 status = db_get_value(hDB, 0, "/Experiment/Security/Disable RPC hosts check", &i, &size, TID_BOOL, TRUE);
3447
3448 if (status != DB_SUCCESS) {
3449 cm_msg(MERROR, "init_rpc_hosts", "Cannot create \"Disable RPC hosts check\", db_get_value() status %d", status);
3450 return;
3451 }
3452
3453 if (i != 0) // RPC hosts check is disabled
3454 return;
3455
3456 status = db_find_key(hDB, 0, "/Experiment/Security/RPC hosts/Allowed hosts", &hKey);
3457
3458 if (status != DB_SUCCESS || hKey == 0) {
3459 cm_msg(MERROR, "init_rpc_hosts", "Cannot find the RPC hosts access control list, db_find_key() status %d",
3460 status);
3461 return;
3462 }
3463
3464 load_rpc_hosts(hDB, hKey, -99, NULL);
3465
3467
3468 if (status != DB_SUCCESS) {
3469 cm_msg(MERROR, "init_rpc_hosts", "Cannot watch the RPC hosts access control list, db_watch() status %d", status);
3470 return;
3471 }
3472}
3473
3474/********************************************************************/
3476/********************************************************************\
3477
3478 Routine: cm_register_server
3479
3480 Purpose: Register a server which can be called from other clients
3481 of a specific experiment.
3482
3483 Input:
3484 none
3485
3486 Output:
3487 none
3488
3489 Function value:
3490 CM_SUCCESS Successful completion
3491
3492\********************************************************************/
3493{
3494 if (!_rpc_registered) {
3495 INT status;
3496 int size;
3497 HNDLE hDB, hKey;
3498 char name[NAME_LENGTH];
3499 char str[256];
3500 int port = 0;
3501
3503
3504 size = sizeof(name);
3505 status = db_get_value(hDB, hKey, "Name", &name, &size, TID_STRING, FALSE);
3506
3507 if (status != DB_SUCCESS) {
3508 cm_msg(MERROR, "cm_register_server", "cannot get client name, db_get_value() status %d", status);
3509 return status;
3510 }
3511
3512 mstrlcpy(str, "/Experiment/Security/RPC ports/", sizeof(str));
3513 mstrlcat(str, name, sizeof(str));
3514
3515 size = sizeof(port);
3516 status = db_get_value(hDB, 0, str, &port, &size, TID_UINT32, TRUE);
3517
3518 if (status != DB_SUCCESS) {
3519 cm_msg(MERROR, "cm_register_server", "cannot get RPC port number, db_get_value(%s) status %d", str, status);
3520 return status;
3521 }
3522
3523 int lport = 0; // actual port number assigned to us by the OS
3524
3526 if (status != RPC_SUCCESS) {
3527 cm_msg(MERROR, "cm_register_server", "error, rpc_register_server(port=%d) status %d", port, status);
3528 return status;
3529 }
3530
3532
3533 /* register MIDAS library functions */
3535
3536 /* store port number in ODB */
3537
3538 status = db_find_key(hDB, hKey, "Server Port", &hKey);
3539 if (status != DB_SUCCESS) {
3540 cm_msg(MERROR, "cm_register_server", "error, db_find_key(\"Server Port\") status %d", status);
3541 return status;
3542 }
3543
3544 /* unlock database */
3546
3547 /* set value */
3548 status = db_set_data(hDB, hKey, &lport, sizeof(INT), 1, TID_INT32);
3549 if (status != DB_SUCCESS) {
3550 cm_msg(MERROR, "cm_register_server", "error, db_set_data(\"Server Port\"=%d) status %d", port, status);
3551 return status;
3552 }
3553
3554 /* lock database */
3556
3558 }
3559
3560 return CM_SUCCESS;
3561}
3562
3564#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3565
3566/********************************************************************/
3616INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number) {
3617 INT status;
3618 HNDLE hDB, hKey, hKeyTrans;
3619 KEY key;
3620 char str[256];
3621
3622 /* check for valid transition */
3624 cm_msg(MERROR, "cm_register_transition", "Invalid transition request \"%d\"", transition);
3625 return CM_INVALID_TRANSITION;
3626 }
3627
3629
3631
3632 /* register new transition request */
3633
3634 {
3635 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3636
3637 for (size_t i = 0; i < _trans_table.size(); i++) {
3638 if (_trans_table[i].transition == transition && _trans_table[i].sequence_number == sequence_number) {
3639 cm_msg(MERROR, "cm_register_transition", "transition %s with sequence number %d is already registered", cm_transition_name(transition).c_str(), sequence_number);
3640 return CM_INVALID_TRANSITION;
3641 }
3642 }
3643
3644 bool found = false;
3645 for (size_t i = 0; i < _trans_table.size(); i++) {
3646 if (!_trans_table[i].transition) {
3647 _trans_table[i].transition = transition;
3648 _trans_table[i].sequence_number = sequence_number;
3649 _trans_table[i].func = func;
3650 found = true;
3651 break;
3652 }
3653 }
3654
3655 if (!found) {
3656 TRANS_TABLE tt;
3658 tt.sequence_number = sequence_number;
3659 tt.func = func;
3660 _trans_table.push_back(tt);
3661 }
3662
3663 // implicit unlock
3664 }
3665
3666 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3667
3668 /* unlock database */
3670
3671 /* set value */
3672 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3673 if (!hKeyTrans) {
3674 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3675 if (status != DB_SUCCESS)
3676 return status;
3677 } else {
3678 status = db_get_key(hDB, hKeyTrans, &key);
3679 if (status != DB_SUCCESS)
3680 return status;
3681 status = db_set_data_index(hDB, hKeyTrans, &sequence_number, sizeof(INT), key.num_values, TID_INT32);
3682 if (status != DB_SUCCESS)
3683 return status;
3684 }
3685
3686 /* re-lock database */
3688
3689 return CM_SUCCESS;
3690}
3691
3693 INT status;
3694 HNDLE hDB, hKey, hKeyTrans;
3695 char str[256];
3696
3697 /* check for valid transition */
3699 cm_msg(MERROR, "cm_deregister_transition", "Invalid transition request \"%d\"", transition);
3700 return CM_INVALID_TRANSITION;
3701 }
3702
3704
3705 {
3706 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3707
3708 /* remove existing transition request */
3709 for (size_t i = 0; i < _trans_table.size(); i++) {
3711 _trans_table[i].transition = 0;
3712 _trans_table[i].sequence_number = 0;
3713 _trans_table[i].func = NULL;
3714 }
3715 }
3716
3717 // implicit unlock
3718 }
3719
3720 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3721
3722 /* unlock database */
3724
3725 /* set value */
3726 status = db_find_key(hDB, hKey, str, &hKeyTrans);
3727 if (hKeyTrans) {
3728 status = db_delete_key(hDB, hKeyTrans);
3729 if (status != DB_SUCCESS)
3730 return status;
3731 }
3732
3733 /* re-lock database */
3735
3736 return CM_SUCCESS;
3737}
3738
3739/********************************************************************/
3747 INT status;
3748 HNDLE hDB, hKey;
3749 char str[256];
3750
3751 /* check for valid transition */
3753 cm_msg(MERROR, "cm_set_transition_sequence", "Invalid transition request \"%d\"", transition);
3754 return CM_INVALID_TRANSITION;
3755 }
3756
3757 {
3758 std::lock_guard<std::mutex> guard(_trans_table_mutex);
3759
3760 int count = 0;
3761 for (size_t i = 0; i < _trans_table.size(); i++) {
3763 _trans_table[i].sequence_number = sequence_number;
3764 count++;
3765 }
3766 }
3767
3768 if (count == 0) {
3769 cm_msg(MERROR, "cm_set_transition_sequence", "transition %s is not registered", cm_transition_name(transition).c_str());
3770 return CM_INVALID_TRANSITION;
3771 } else if (count > 1) {
3772 cm_msg(MERROR, "cm_set_transition_sequence", "cannot change sequence number, transition %s is registered %d times", cm_transition_name(transition).c_str(), count);
3773 return CM_INVALID_TRANSITION;
3774 }
3775
3776 /* Change local sequence number for this transition type */
3777
3778 for (size_t i = 0; i < _trans_table.size(); i++) {
3780 _trans_table[i].sequence_number = sequence_number;
3781 }
3782 }
3783
3784 // implicit unlock
3785 }
3786
3788
3789 /* unlock database */
3791
3792 sprintf(str, "Transition %s", cm_transition_name(transition).c_str());
3793
3794 /* set value */
3795 status = db_set_value(hDB, hKey, str, &sequence_number, sizeof(INT), 1, TID_INT32);
3796 if (status != DB_SUCCESS)
3797 return status;
3798
3799 /* re-lock database */
3801
3802 return CM_SUCCESS;
3803
3804}
3805
3807 INT status;
3808 HNDLE hDB, hKey;
3809 KEY key;
3810
3812
3813 /* check that hKey is still valid */
3815
3816 if (status != DB_SUCCESS) {
3817 cm_msg(MERROR, "cm_set_client_run_state",
3818 "Cannot set client run state, client hKey %d into /System/Clients is not valid, maybe this client was removed by a watchdog timeout",
3819 hKey);
3820 return status;
3821 }
3822
3823 /* unlock database */
3825
3826 /* set value */
3827 status = db_set_value(hDB, hKey, "Run state", &state, sizeof(INT), 1, TID_INT32);
3828 if (status != DB_SUCCESS)
3829 return status;
3830
3831 /* re-lock database */
3833
3834 return CM_SUCCESS;
3835
3836}
3837
3839#ifndef DOXYGEN_SHOULD_SKIP_THIS
3840
3843
3845#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3846
3847/********************************************************************/
3861 INT status, size;
3862 char tr_key_name[256];
3863 HNDLE hDB, hKey;
3864
3866
3867 for (int i = 0; _deferred_trans_table[i].transition; i++)
3869 _deferred_trans_table[i].func = (int (*)(int, char *)) func;
3870
3871 /* set new transition mask */
3873
3874 sprintf(tr_key_name, "Transition %s DEFERRED", cm_transition_name(transition).c_str());
3875
3876 /* unlock database */
3878
3879 /* set value */
3880 int i = 0;
3881 status = db_set_value(hDB, hKey, tr_key_name, &i, sizeof(INT), 1, TID_INT32);
3882 if (status != DB_SUCCESS)
3883 return status;
3884
3885 /* re-lock database */
3887
3888 /* hot link requested transition */
3889 size = sizeof(_requested_transition);
3890 db_get_value(hDB, 0, "/Runinfo/Requested Transition", &_requested_transition, &size, TID_INT32, TRUE);
3891 db_find_key(hDB, 0, "/Runinfo/Requested Transition", &hKey);
3892 status = db_open_record(hDB, hKey, &_requested_transition, sizeof(INT), MODE_READ, NULL, NULL);
3893 if (status != DB_SUCCESS) {
3894 cm_msg(MERROR, "cm_register_deferred_transition", "Cannot hotlink /Runinfo/Requested Transition");
3895 return status;
3896 }
3897
3898 return CM_SUCCESS;
3899}
3900
3901/********************************************************************/
3913 INT i, status;
3914 char str[256];
3915 static BOOL first;
3916
3917 if (_requested_transition == 0)
3918 first = TRUE;
3919
3921 for (i = 0; _deferred_trans_table[i].transition; i++)
3923 break;
3924
3926 if (((BOOL(*)(INT, BOOL)) _deferred_trans_table[i].func)(_requested_transition, first)) {
3928 if (status != CM_SUCCESS)
3929 cm_msg(MERROR, "cm_check_deferred_transition", "Cannot perform deferred transition: %s", str);
3930
3931 /* bypass hotlink and set _requested_transition directly to zero */
3933
3934 return status;
3935 }
3936 first = FALSE;
3937 }
3938 }
3939
3940 return SUCCESS;
3941}
3942
3943
3945#ifndef DOXYGEN_SHOULD_SKIP_THIS
3946
3947/********************************************************************/
3948
3950#endif /* DOXYGEN_SHOULD_SKIP_THIS */
3951
3952struct TrClient {
3953 int transition = 0;
3954 int run_number = 0;
3955 int async_flag = 0;
3956 int debug_flag = 0;
3958 std::vector<int> wait_for_index;
3959 std::string host_name;
3960 std::string client_name;
3961 int port = 0;
3962 std::string key_name; /* this client key name in /System/Clients */
3963 std::atomic_int status{0};
3964 std::thread* thread = NULL;
3965 std::string errorstr;
3966 DWORD init_time = 0; // time when tr_client created
3967 std::string waiting_for_client; // name of client we are waiting for
3969 DWORD connect_start_time = 0; // time when client rpc connection is started
3970 DWORD connect_end_time = 0; // time when client rpc connection is finished
3972 DWORD rpc_start_time = 0; // time client rpc call is started
3973 DWORD rpc_end_time = 0; // time client rpc call is finished
3974 DWORD end_time = 0; // time client thread is finished
3975
3976 TrClient() // ctor
3977 {
3978 // empty
3979 }
3980
3981 ~TrClient() // dtor
3982 {
3983 //printf("TrClient::dtor: client \"%s\"\n", client_name);
3984 assert(thread == NULL);
3985 }
3986
3987 void Print() const
3988 {
3989 printf("client \"%s\", transition %d, seqno %d, status %d", client_name.c_str(), transition, sequence_number, int(status));
3990 if (wait_for_index.size() > 0) {
3991 printf(", wait for:");
3992 for (size_t i=0; i<wait_for_index.size(); i++) {
3993 printf(" %d", wait_for_index[i]);
3994 }
3995 }
3996 }
3997};
3998
3999static bool tr_compare(const std::unique_ptr<TrClient>& arg1, const std::unique_ptr<TrClient>& arg2) {
4000 return arg1->sequence_number < arg2->sequence_number;
4001}
4002
4003/*------------------------------------------------------------------*/
4004
4005struct TrState {
4006 int transition = 0;
4007 int run_number = 0;
4008 int async_flag = 0;
4009 int debug_flag = 0;
4010 int status = 0;
4011 std::string errorstr;
4014 std::vector<std::unique_ptr<TrClient>> clients;
4015};
4016
4017/*------------------------------------------------------------------*/
4018
4019static int tr_finish(HNDLE hDB, TrState* tr, int transition, int status, const char *errorstr)
4020{
4021 DWORD end_time = ss_millitime();
4022
4023 if (transition != TR_STARTABORT) {
4024 db_set_value(hDB, 0, "/System/Transition/end_time", &end_time, sizeof(DWORD), 1, TID_UINT32);
4025 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4026
4027 if (errorstr) {
4028 db_set_value(hDB, 0, "/System/Transition/error", errorstr, strlen(errorstr) + 1, 1, TID_STRING);
4029 } else if (status == CM_SUCCESS) {
4030 const char *buf = "Success";
4031 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4032 } else {
4033 char buf[256];
4034 sprintf(buf, "status %d", status);
4035 db_set_value(hDB, 0, "/System/Transition/error", buf, strlen(buf) + 1, 1, TID_STRING);
4036 }
4037 }
4038
4039 tr->status = status;
4040 tr->end_time = end_time;
4041 if (errorstr) {
4042 tr->errorstr = errorstr;
4043 } else {
4044 tr->errorstr = "(null)";
4045 }
4046
4047 return status;
4048}
4049
4050/*------------------------------------------------------------------*/
4051
4052static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client) {
4053 //printf("Writing client [%s] to ODB\n", tr_client->client_name.c_str());
4054
4055 int status;
4056 HNDLE hKey;
4057
4058 if (tr_client->transition == TR_STARTABORT) {
4059 status = db_create_key(hDB, 0, "/System/Transition/TR_STARTABORT", TID_KEY);
4060 status = db_find_key(hDB, 0, "/System/Transition/TR_STARTABORT", &hKey);
4061 if (status != DB_SUCCESS)
4062 return;
4063 } else {
4064 status = db_create_key(hDB, 0, "/System/Transition/Clients", TID_KEY);
4065 status = db_find_key(hDB, 0, "/System/Transition/Clients", &hKey);
4066 if (status != DB_SUCCESS)
4067 return;
4068 }
4069
4070 // same client_name can exist with different sequence numbers!
4071 std::string keyname = msprintf("%s_%d", tr_client->client_name.c_str(), tr_client->sequence_number);
4072
4073 status = db_create_key(hDB, hKey, keyname.c_str(), TID_KEY);
4074 status = db_find_key(hDB, hKey, keyname.c_str(), &hKey);
4075 if (status != DB_SUCCESS)
4076 return;
4077
4078 DWORD now = ss_millitime();
4079
4080 //int transition;
4081 //int run_number;
4082 //int async_flag;
4083 //int debug_flag;
4084 status = db_set_value(hDB, hKey, "sequence_number", &tr_client->sequence_number, sizeof(INT), 1, TID_INT32);
4085 status = db_set_value(hDB, hKey, "client_name", tr_client->client_name.c_str(), tr_client->client_name.length() + 1, 1, TID_STRING);
4086 status = db_set_value(hDB, hKey, "host_name", tr_client->host_name.c_str(), tr_client->host_name.length() + 1, 1, TID_STRING);
4087 status = db_set_value(hDB, hKey, "port", &tr_client->port, sizeof(INT), 1, TID_INT32);
4088 status = db_set_value(hDB, hKey, "init_time", &tr_client->init_time, sizeof(DWORD), 1, TID_UINT32);
4089 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);
4090 status = db_set_value(hDB, hKey, "connect_timeout", &tr_client->connect_timeout, sizeof(DWORD), 1, TID_UINT32);
4091 status = db_set_value(hDB, hKey, "connect_start_time", &tr_client->connect_start_time, sizeof(DWORD), 1, TID_UINT32);
4092 status = db_set_value(hDB, hKey, "connect_end_time", &tr_client->connect_end_time, sizeof(DWORD), 1, TID_UINT32);
4093 status = db_set_value(hDB, hKey, "rpc_timeout", &tr_client->rpc_timeout, sizeof(DWORD), 1, TID_UINT32);
4094 status = db_set_value(hDB, hKey, "rpc_start_time", &tr_client->rpc_start_time, sizeof(DWORD), 1, TID_UINT32);
4095 status = db_set_value(hDB, hKey, "rpc_end_time", &tr_client->rpc_end_time, sizeof(DWORD), 1, TID_UINT32);
4096 status = db_set_value(hDB, hKey, "end_time", &tr_client->end_time, sizeof(DWORD), 1, TID_UINT32);
4097 status = db_set_value(hDB, hKey, "status", &tr_client->status, sizeof(INT), 1, TID_INT32);
4098 status = db_set_value(hDB, hKey, "error", tr_client->errorstr.c_str(), tr_client->errorstr.length() + 1, 1, TID_STRING);
4099 status = db_set_value(hDB, hKey, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
4100}
4101
4102/*------------------------------------------------------------------*/
4103
4104/* Perform a detached transition through the external "mtransition" program */
4105static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
4106 HNDLE hDB;
4107 int status;
4108 const char *args[100];
4109 std::string path;
4110 char debug_arg[256];
4111 char start_arg[256];
4112 std::string expt_name;
4113 std::string mserver_hostname;
4114
4115 int iarg = 0;
4116
4118
4119 const char *midassys = getenv("MIDASSYS");
4120 if (midassys) {
4121 path += midassys;
4122 path += DIR_SEPARATOR_STR;
4123 path += "bin";
4124 path += DIR_SEPARATOR_STR;
4125 }
4126 path += "mtransition";
4127
4128 args[iarg++] = path.c_str();
4129
4130 if (rpc_is_remote()) {
4131 /* if connected to mserver, pass connection info to mtransition */
4132 mserver_hostname = rpc_get_mserver_hostname();
4133 args[iarg++] = "-h";
4134 args[iarg++] = mserver_hostname.c_str();
4135 }
4136
4137 /* get experiment name from ODB */
4138 db_get_value_string(hDB, 0, "/Experiment/Name", 0, &expt_name, FALSE);
4139
4140 if (expt_name.length() > 0) {
4141 args[iarg++] = "-e";
4142 args[iarg++] = expt_name.c_str();
4143 }
4144
4145 if (debug_flag) {
4146 args[iarg++] = "-d";
4147
4148 sprintf(debug_arg, "%d", debug_flag);
4149 args[iarg++] = debug_arg;
4150 }
4151
4152 if (transition == TR_STOP)
4153 args[iarg++] = "STOP";
4154 else if (transition == TR_PAUSE)
4155 args[iarg++] = "PAUSE";
4156 else if (transition == TR_RESUME)
4157 args[iarg++] = "RESUME";
4158 else if (transition == TR_START) {
4159 args[iarg++] = "START";
4160
4161 sprintf(start_arg, "%d", run_number);
4162 args[iarg++] = start_arg;
4163 }
4164
4165 args[iarg++] = NULL;
4166
4167#if 0
4168 for (iarg = 0; args[iarg] != NULL; iarg++) {
4169 printf("arg[%d] [%s]\n", iarg, args[iarg]);
4170 }
4171#endif
4172
4173 status = ss_spawnv(P_DETACH, args[0], args);
4174
4175 if (status != SS_SUCCESS) {
4176 if (errstr != NULL) {
4177 sprintf(errstr, "Cannot execute mtransition, ss_spawnv() returned %d", status);
4178 }
4179 return CM_SET_ERROR;
4180 }
4181
4182 return CM_SUCCESS;
4183}
4184
4185/*------------------------------------------------------------------*/
4186
4187/* contact a client via RPC and execute the remote transition */
4188static int cm_transition_call(TrState* s, int idx) {
4189 INT old_timeout, status, i, t1, t0, size;
4190 HNDLE hDB;
4191 HNDLE hConn = -1;
4192 int connect_timeout = 10000;
4193 int timeout = 120000;
4194
4196 assert(hDB);
4197
4198 TrClient *tr_client = s->clients[idx].get();
4199
4200 tr_client->errorstr = "";
4201 //tr_client->init_time = ss_millitime();
4202 tr_client->waiting_for_client = "";
4203 tr_client->connect_timeout = 0;
4204 tr_client->connect_start_time = 0;
4205 tr_client->connect_end_time = 0;
4206 tr_client->rpc_timeout = 0;
4207 tr_client->rpc_start_time = 0;
4208 tr_client->rpc_end_time = 0;
4209 tr_client->end_time = 0;
4210
4211 write_tr_client_to_odb(hDB, tr_client);
4212
4213 /* wait for predecessor if set */
4214 if (tr_client->async_flag & TR_MTHREAD && !tr_client->wait_for_index.empty()) {
4215 while (1) {
4216 TrClient* wait_for = NULL;
4217
4218 for (size_t i = 0; i < tr_client->wait_for_index.size(); i++) {
4219 int wait_for_index = tr_client->wait_for_index[i];
4220
4221 assert(wait_for_index >= 0);
4222 assert(wait_for_index < (int)s->clients.size());
4223
4224 TrClient *t = s->clients[wait_for_index].get();
4225
4226 if (!t)
4227 continue;
4228
4229 if (t->status == 0) {
4230 wait_for = t;
4231 break;
4232 }
4233
4234 if (t->status != SUCCESS && tr_client->transition != TR_STOP) {
4235 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));
4236 tr_client->status = -1;
4237 tr_client->errorstr = msprintf("Aborted by failure of client \"%s\"", t->client_name.c_str());
4238 tr_client->end_time = ss_millitime();
4239 write_tr_client_to_odb(hDB, tr_client);
4240 return CM_SUCCESS;
4241 }
4242 }
4243
4244 if (wait_for == NULL)
4245 break;
4246
4247 tr_client->waiting_for_client = wait_for->client_name;
4248 write_tr_client_to_odb(hDB, tr_client);
4249
4250 if (tr_client->debug_flag == 1)
4251 printf("Client \"%s\" waits for client \"%s\"\n", tr_client->client_name.c_str(), wait_for->client_name.c_str());
4252
4253 i = 0;
4254 size = sizeof(i);
4255 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
4256
4257 if (status == DB_SUCCESS && i == 0) {
4258 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());
4259 tr_client->status = -1;
4260 tr_client->errorstr = "Canceled";
4261 tr_client->end_time = ss_millitime();
4262 write_tr_client_to_odb(hDB, tr_client);
4263 return CM_SUCCESS;
4264 }
4265
4266 ss_sleep(100);
4267 };
4268 }
4269
4270 tr_client->waiting_for_client[0] = 0;
4271
4272 /* contact client if transition mask set */
4273 if (tr_client->debug_flag == 1)
4274 printf("Connecting to client \"%s\" on host %s...\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4275 if (tr_client->debug_flag == 2)
4276 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());
4277
4278 /* get transition timeout for rpc connect */
4279 size = sizeof(timeout);
4280 db_get_value(hDB, 0, "/Experiment/Transition connect timeout", &connect_timeout, &size, TID_INT32, TRUE);
4281
4282 if (connect_timeout < 1000)
4283 connect_timeout = 1000;
4284
4285 /* get transition timeout */
4286 size = sizeof(timeout);
4287 db_get_value(hDB, 0, "/Experiment/Transition timeout", &timeout, &size, TID_INT32, TRUE);
4288
4289 if (timeout < 1000)
4290 timeout = 1000;
4291
4292 /* set our timeout for rpc_client_connect() */
4293 //old_timeout = rpc_get_timeout(RPC_HNDLE_CONNECT);
4294 rpc_set_timeout(RPC_HNDLE_CONNECT, connect_timeout, &old_timeout);
4295
4296 tr_client->connect_timeout = connect_timeout;
4297 tr_client->connect_start_time = ss_millitime();
4298
4299 write_tr_client_to_odb(hDB, tr_client);
4300
4301 /* client found -> connect to its server port */
4302 status = rpc_client_connect(tr_client->host_name.c_str(), tr_client->port, tr_client->client_name.c_str(), &hConn);
4303
4304 rpc_set_timeout(RPC_HNDLE_CONNECT, old_timeout);
4305
4306 tr_client->connect_end_time = ss_millitime();
4307 write_tr_client_to_odb(hDB, tr_client);
4308
4309 if (status != RPC_SUCCESS) {
4310 cm_msg(MERROR, "cm_transition_call",
4311 "cannot connect to client \"%s\" on host %s, port %d, status %d",
4312 tr_client->client_name.c_str(), tr_client->host_name.c_str(), tr_client->port, status);
4313 tr_client->errorstr = msprintf("Cannot connect to client \"%s\"", tr_client->client_name.c_str());
4314
4315 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4316 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4317 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4318
4319 if (tr_client->transition != TR_STOP) {
4320 /* indicate abort */
4321 i = 1;
4322 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4323 i = 0;
4324 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4325 }
4326
4327 tr_client->status = status;
4328 tr_client->end_time = ss_millitime();
4329
4330 write_tr_client_to_odb(hDB, tr_client);
4331 return status;
4332 }
4333
4334 if (tr_client->debug_flag == 1)
4335 printf("Connection established to client \"%s\" on host %s\n", tr_client->client_name.c_str(), tr_client->host_name.c_str());
4336 if (tr_client->debug_flag == 2)
4337 cm_msg(MINFO, "cm_transition_call",
4338 "cm_transition: Connection established to client \"%s\" on host %s",
4339 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4340
4341 /* call RC_TRANSITION on remote client with increased timeout */
4342 //old_timeout = rpc_get_timeout(hConn);
4343 rpc_set_timeout(hConn, timeout, &old_timeout);
4344
4345 tr_client->rpc_timeout = timeout;
4346 tr_client->rpc_start_time = ss_millitime();
4347 write_tr_client_to_odb(hDB, tr_client);
4348
4349 if (tr_client->debug_flag == 1)
4350 printf("Executing RPC transition client \"%s\" on host %s...\n",
4351 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4352 if (tr_client->debug_flag == 2)
4353 cm_msg(MINFO, "cm_transition_call",
4354 "cm_transition: Executing RPC transition client \"%s\" on host %s...",
4355 tr_client->client_name.c_str(), tr_client->host_name.c_str());
4356
4357 t0 = ss_millitime();
4358
4359 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4360 errorstr[0] = 0;
4361
4362 status = rpc_client_call(hConn, RPC_RC_TRANSITION, tr_client->transition, tr_client->run_number, errorstr, sizeof(errorstr), tr_client->sequence_number);
4363
4364 tr_client->errorstr = errorstr;
4365
4366 t1 = ss_millitime();
4367
4368 tr_client->rpc_end_time = ss_millitime();
4369
4370 write_tr_client_to_odb(hDB, tr_client);
4371
4372 /* fix for clients returning 0 as error code */
4373 if (status == 0)
4374 status = FE_ERR_HW;
4375
4376 /* reset timeout */
4377 rpc_set_timeout(hConn, old_timeout);
4378
4379 //DWORD t2 = ss_millitime();
4380
4381 if (tr_client->debug_flag == 1)
4382 printf("RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d\n",
4383 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4384 if (tr_client->debug_flag == 2)
4385 cm_msg(MINFO, "cm_transition_call",
4386 "cm_transition: RPC transition finished client \"%s\" on host \"%s\" in %d ms with status %d",
4387 tr_client->client_name.c_str(), tr_client->host_name.c_str(), t1 - t0, status);
4388
4389 if (status == RPC_NET_ERROR || status == RPC_TIMEOUT) {
4390 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());
4391 /* clients that do not respond to transitions are dead or defective, get rid of them. K.O. */
4392 cm_shutdown(tr_client->client_name.c_str(), TRUE);
4393 cm_cleanup(tr_client->client_name.c_str(), TRUE);
4394 } else if (status != CM_SUCCESS && tr_client->errorstr.empty()) {
4395 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());
4396 }
4397
4398 tr_client->status = status;
4399 tr_client->end_time = ss_millitime();
4400
4401 // write updated status and end_time to ODB
4402
4403 write_tr_client_to_odb(hDB, tr_client);
4404
4405#if 0
4406 printf("hconn %d cm_transition_call(%s) finished init %d connect %d end %d rpc %d end %d xxx %d end %d\n",
4407 hConn,
4408 tr_client->client_name.c_str(),
4409 tr_client->init_time - tr_client->init_time,
4410 tr_client->connect_start_time - tr_client->init_time,
4411 tr_client->connect_end_time - tr_client->init_time,
4412 tr_client->rpc_start_time - tr_client->init_time,
4413 tr_client->rpc_end_time - tr_client->init_time,
4414 t2 - tr_client->init_time,
4415 tr_client->end_time - tr_client->init_time);
4416#endif
4417
4418 return CM_SUCCESS;
4419}
4420
4421/*------------------------------------------------------------------*/
4422
4424{
4425 HNDLE hDB;
4426
4428
4429 DWORD now = ss_millitime();
4430
4431 tr_client->errorstr = "";
4432 //tr_client->init_time = now;
4433 tr_client->waiting_for_client = "";
4434 tr_client->connect_timeout = 0;
4435 tr_client->connect_start_time = now;
4436 tr_client->connect_end_time = now;
4437 tr_client->rpc_timeout = 0;
4438 tr_client->rpc_start_time = 0;
4439 tr_client->rpc_end_time = 0;
4440 tr_client->end_time = 0;
4441
4442 write_tr_client_to_odb(hDB, tr_client);
4443
4444 // find registered handler
4445 // NB: this code should match same code in rpc_transition_dispatch()
4446 // NB: only use the first handler, this is how MIDAS always worked
4447 // NB: we could run all handlers, but we can return the status and error string of only one of them.
4448
4449 _trans_table_mutex.lock();
4450 size_t n = _trans_table.size();
4451 _trans_table_mutex.unlock();
4452
4453 for (size_t i = 0; i < n; i++) {
4454 _trans_table_mutex.lock();
4456 _trans_table_mutex.unlock();
4457 if (tt.transition == tr_client->transition && tt.sequence_number == tr_client->sequence_number) {
4458 /* call registered function */
4459 if (tt.func) {
4460 if (tr_client->debug_flag == 1)
4461 printf("Calling local transition callback\n");
4462 if (tr_client->debug_flag == 2)
4463 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Calling local transition callback");
4464
4465 tr_client->rpc_start_time = ss_millitime();
4466
4467 write_tr_client_to_odb(hDB, tr_client);
4468
4469 char errorstr[TRANSITION_ERROR_STRING_LENGTH];
4470 errorstr[0] = 0;
4471
4472 tr_client->status = tt.func(tr_client->run_number, errorstr);
4473
4474 tr_client->errorstr = errorstr;
4475
4476 tr_client->rpc_end_time = ss_millitime();
4477
4478 if (tr_client->debug_flag == 1)
4479 printf("Local transition callback finished, status %d\n", int(tr_client->status));
4480 if (tr_client->debug_flag == 2)
4481 cm_msg(MINFO, "cm_transition_call_direct", "cm_transition: Local transition callback finished, status %d", int(tr_client->status));
4482
4483 tr_client->end_time = ss_millitime();
4484
4485 // write status and end_time to ODB
4486
4487 write_tr_client_to_odb(hDB, tr_client);
4488
4489 return tr_client->status;
4490 }
4491 }
4492 }
4493
4494 cm_msg(MERROR, "cm_transition_call_direct", "no handler for transition %d with sequence number %d", tr_client->transition, tr_client->sequence_number);
4495
4496 tr_client->status = CM_SUCCESS;
4497 tr_client->end_time = ss_millitime();
4498
4499 // write status and end_time to ODB
4500
4501 write_tr_client_to_odb(hDB, tr_client);
4502
4503 return CM_SUCCESS;
4504}
4505
4506/********************************************************************/
4546static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
4547{
4548 INT i, status, size, sequence_number, port, state;
4549 HNDLE hDB, hRootKey, hSubkey, hKey, hKeylocal, hKeyTrans;
4550 DWORD seconds;
4551 char tr_key_name[256];
4552 KEY key;
4553 BOOL deferred;
4554 char xerrstr[TRANSITION_ERROR_STRING_LENGTH];
4555
4556 //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);
4557
4558 /* if needed, use internal error string */
4559 if (!errstr) {
4560 errstr = xerrstr;
4561 errstr_size = sizeof(xerrstr);
4562 }
4563
4564 /* erase error string */
4565 errstr[0] = 0;
4566
4567 /* get key of local client */
4568 cm_get_experiment_database(&hDB, &hKeylocal);
4569
4570 deferred = (transition & TR_DEFERRED) > 0;
4571 transition &= ~TR_DEFERRED;
4572
4573 /* check for valid transition */
4575 && transition != TR_STARTABORT) {
4576 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
4577 mstrlcpy(errstr, "Invalid transition request", errstr_size);
4578 return CM_INVALID_TRANSITION;
4579 }
4580
4581 /* check if transition in progress */
4582 if (!deferred) {
4583 i = 0;
4584 size = sizeof(i);
4585 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
4586 if (i == 1) {
4587 if (errstr) {
4588 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
4589 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
4590 }
4591 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
4593 }
4594 }
4595
4596 /* indicate transition in progress */
4597 i = transition;
4598 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
4599
4600 /* clear run abort flag */
4601 i = 0;
4602 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
4603
4604 /* construct new transition state */
4605
4606 TrState s;
4607
4610 s.async_flag = async_flag;
4611 s.debug_flag = debug_flag;
4612 s.status = 0;
4613 s.errorstr[0] = 0;
4615 s.end_time = 0;
4616
4617 /* construct the ODB tree /System/Transition */
4618
4619 status = db_delete(hDB, 0, "/System/Transition/TR_STARTABORT");
4620
4621 if (transition != TR_STARTABORT) {
4622 status = db_delete(hDB, 0, "/System/Transition/Clients");
4623 }
4624
4625 if (transition != TR_STARTABORT) {
4626 db_set_value(hDB, 0, "/System/Transition/transition", &transition, sizeof(INT), 1, TID_INT32);
4627 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4628 db_set_value(hDB, 0, "/System/Transition/start_time", &s.start_time, sizeof(DWORD), 1, TID_UINT32);
4629 db_set_value(hDB, 0, "/System/Transition/end_time", &s.end_time, sizeof(DWORD), 1, TID_UINT32);
4630 status = 0;
4631 db_set_value(hDB, 0, "/System/Transition/status", &status, sizeof(INT), 1, TID_INT32);
4632 db_set_value(hDB, 0, "/System/Transition/error", "", 1, 1, TID_STRING);
4633 db_set_value(hDB, 0, "/System/Transition/deferred", "", 1, 1, TID_STRING);
4634 }
4635
4636 /* check for alarms */
4637 i = 0;
4638 size = sizeof(i);
4639 db_get_value(hDB, 0, "/Experiment/Prevent start on alarms", &i, &size, TID_BOOL, TRUE);
4640 if (i == TRUE && transition == TR_START) {
4641 al_check();
4642 std::string alarms;
4643 if (al_get_alarms(&alarms) > 0) {
4644 cm_msg(MERROR, "cm_transition", "Run start abort due to alarms: %s", alarms.c_str());
4645 mstrlcpy(errstr, "Cannot start run due to alarms: ", errstr_size);
4646 mstrlcat(errstr, alarms.c_str(), errstr_size);
4647 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4648 }
4649 }
4650
4651 /* check for required programs */
4652 i = 0;
4653 size = sizeof(i);
4654 db_get_value(hDB, 0, "/Experiment/Prevent start on required progs", &i, &size, TID_BOOL, TRUE);
4655 if (i == TRUE && transition == TR_START) {
4656
4657 HNDLE hkeyroot, hkey;
4658
4659 /* check /programs alarms */
4660 db_find_key(hDB, 0, "/Programs", &hkeyroot);
4661 if (hkeyroot) {
4662 for (i = 0;; i++) {
4663 BOOL program_info_required = FALSE;
4664 status = db_enum_key(hDB, hkeyroot, i, &hkey);
4666 break;
4667
4668 db_get_key(hDB, hkey, &key);
4669
4670 /* don't check "execute on xxx" */
4671 if (key.type != TID_KEY)
4672 continue;
4673
4674 size = sizeof(program_info_required);
4675 status = db_get_value(hDB, hkey, "Required", &program_info_required, &size, TID_BOOL, TRUE);
4676 if (status != DB_SUCCESS) {
4677 cm_msg(MERROR, "cm_transition", "Cannot get program info required, status %d", status);
4678 continue;
4679 }
4680
4681 if (program_info_required) {
4682 std::string name = rpc_get_name();
4683 std::string str = name;
4684 str.resize(strlen(key.name));
4685 if (!equal_ustring(str.c_str(), key.name) && cm_exist(key.name, FALSE) == CM_NO_CLIENT) {
4686 cm_msg(MERROR, "cm_transition", "Run start abort due to program \"%s\" not running", key.name);
4687 std::string serrstr = msprintf("Run start abort due to program \"%s\" not running", key.name);
4688 mstrlcpy(errstr, serrstr.c_str(), errstr_size);
4689 return tr_finish(hDB, &s, transition, AL_TRIGGERED, errstr);
4690 }
4691 }
4692 }
4693 }
4694 }
4695
4696 /* do detached transition via mtransition tool */
4697 if (async_flag & TR_DETACH) {
4698 status = cm_transition_detach(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
4699 return tr_finish(hDB, &s, transition, status, errstr);
4700 }
4701
4702 mstrlcpy(errstr, "Unknown error", errstr_size);
4703
4704 if (debug_flag == 0) {
4705 size = sizeof(i);
4706 db_get_value(hDB, 0, "/Experiment/Transition debug flag", &debug_flag, &size, TID_INT32, TRUE);
4707 }
4708
4709 /* if no run number is given, get it from ODB and increment it */
4710 if (run_number == 0) {
4711 size = sizeof(run_number);
4712 status = db_get_value(hDB, 0, "Runinfo/Run number", &run_number, &size, TID_INT32, TRUE);
4713 assert(status == SUCCESS);
4714 if (transition == TR_START) {
4715 run_number++;
4716 }
4718
4719 if (transition != TR_STARTABORT) {
4720 db_set_value(hDB, 0, "/System/Transition/run_number", &run_number, sizeof(INT), 1, TID_INT32);
4721 }
4722 }
4723
4724 if (run_number <= 0) {
4725 cm_msg(MERROR, "cm_transition", "aborting on attempt to use invalid run number %d", run_number);
4726 abort();
4727 }
4728
4729 /* Set new run number in ODB */
4730 if (transition == TR_START) {
4731 if (debug_flag == 1)
4732 printf("Setting run number %d in ODB\n", run_number);
4733 if (debug_flag == 2)
4734 cm_msg(MINFO, "cm_transition", "cm_transition: Setting run number %d in ODB", run_number);
4735
4736 status = db_set_value(hDB, 0, "Runinfo/Run number", &run_number, sizeof(run_number), 1, TID_INT32);
4737 if (status != DB_SUCCESS) {
4738 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/Run number in database, status %d", status);
4739 abort();
4740 }
4741 }
4742
4743 if (deferred) {
4744 if (debug_flag == 1)
4745 printf("Clearing /Runinfo/Requested transition\n");
4746 if (debug_flag == 2)
4747 cm_msg(MINFO, "cm_transition", "cm_transition: Clearing /Runinfo/Requested transition");
4748
4749 /* remove transition request */
4750 i = 0;
4751 db_set_value(hDB, 0, "/Runinfo/Requested transition", &i, sizeof(int), 1, TID_INT32);
4752 } else {
4753 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4754 if (status != DB_SUCCESS) {
4755 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4756 if (errstr)
4757 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4758 return tr_finish(hDB, &s, transition, status, errstr);
4759 }
4760
4761 /* check if deferred transition already in progress */
4762 size = sizeof(i);
4763 db_get_value(hDB, 0, "/Runinfo/Requested transition", &i, &size, TID_INT32, TRUE);
4764 if (i) {
4765 if (errstr) {
4766 mstrlcpy(errstr, "Deferred transition already in progress", errstr_size);
4767 mstrlcat(errstr, ", to cancel, set \"/Runinfo/Requested transition\" to zero", errstr_size);
4768 }
4769 return tr_finish(hDB, &s, transition, CM_TRANSITION_IN_PROGRESS, errstr);
4770 }
4771
4772 std::string trname = cm_transition_name(transition);
4773
4774 sprintf(tr_key_name, "Transition %s DEFERRED", trname.c_str());
4775
4776 /* search database for clients with deferred transition request */
4777 for (i = 0, status = 0;; i++) {
4778 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4780 break;
4781
4782 if (status == DB_SUCCESS) {
4783 size = sizeof(sequence_number);
4784 status = db_get_value(hDB, hSubkey, tr_key_name, &sequence_number, &size, TID_INT32, FALSE);
4785
4786 /* if registered for deferred transition, set flag in ODB and return */
4787 if (status == DB_SUCCESS) {
4788 char str[256];
4789 size = NAME_LENGTH;
4790 db_get_value(hDB, hSubkey, "Name", str, &size, TID_STRING, TRUE);
4791
4792 if (debug_flag == 1)
4793 printf("---- Transition %s deferred by client \"%s\" ----\n", trname.c_str(), str);
4794 if (debug_flag == 2)
4795 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s deferred by client \"%s\" ----", trname.c_str(), str);
4796
4797 if (debug_flag == 1)
4798 printf("Setting /Runinfo/Requested transition\n");
4799 if (debug_flag == 2)
4800 cm_msg(MINFO, "cm_transition", "cm_transition: Setting /Runinfo/Requested transition");
4801
4802 /* /Runinfo/Requested transition is hot-linked by mfe.c and writing to it
4803 * will activate the deferred transition code in the frontend.
4804 * the transition itself will be run from the frontend via cm_transition(TR_DEFERRED) */
4805
4806 db_set_value(hDB, 0, "/Runinfo/Requested transition", &transition, sizeof(int), 1, TID_INT32);
4807
4808 db_set_value(hDB, 0, "/System/Transition/deferred", str, strlen(str) + 1, 1, TID_STRING);
4809
4810 if (errstr)
4811 sprintf(errstr, "Transition %s deferred by client \"%s\"", trname.c_str(), str);
4812
4813 return tr_finish(hDB, &s, transition, CM_DEFERRED_TRANSITION, errstr);
4814 }
4815 }
4816 }
4817 }
4818
4819 /* execute programs on start */
4820 if (transition == TR_START) {
4821 char str[256];
4822 str[0] = 0;
4823 size = sizeof(str);
4824 db_get_value(hDB, 0, "/Programs/Execute on start run", str, &size, TID_STRING, TRUE);
4825 if (str[0])
4826 ss_system(str);
4827
4828 db_find_key(hDB, 0, "/Programs", &hRootKey);
4829 if (hRootKey) {
4830 for (i = 0;; i++) {
4831 BOOL program_info_auto_start = FALSE;
4832 status = db_enum_key(hDB, hRootKey, i, &hKey);
4834 break;
4835
4836 db_get_key(hDB, hKey, &key);
4837
4838 /* don't check "execute on xxx" */
4839 if (key.type != TID_KEY)
4840 continue;
4841
4842 size = sizeof(program_info_auto_start);
4843 status = db_get_value(hDB, hKey, "Auto start", &program_info_auto_start, &size, TID_BOOL, TRUE);
4844 if (status != DB_SUCCESS) {
4845 cm_msg(MERROR, "cm_transition", "Cannot get program info auto start, status %d", status);
4846 continue;
4847 }
4848
4849 if (program_info_auto_start) {
4850 char start_command[MAX_STRING_LENGTH];
4851 start_command[0] = 0;
4852
4853 size = sizeof(start_command);
4854 status = db_get_value(hDB, hKey, "Start command", &start_command, &size, TID_STRING, TRUE);
4855 if (status != DB_SUCCESS) {
4856 cm_msg(MERROR, "cm_transition", "Cannot get program info start command, status %d", status);
4857 continue;
4858 }
4859
4860 if (start_command[0]) {
4861 cm_msg(MINFO, "cm_transition", "Auto Starting program \"%s\", command \"%s\"", key.name,
4862 start_command);
4863 ss_system(start_command);
4864 }
4865 }
4866 }
4867 }
4868 }
4869
4870 /* execute programs on startabort */
4871 if (transition == TR_STARTABORT) {
4872 /* make sure odb entry is always created, otherwise we only see it after the first aborted run start, maybe never */
4873 std::string cmd;
4874 db_get_value_string(hDB, 0, "/Programs/Execute on start abort", 0, &cmd, TRUE, 256);
4875
4876 if (!cmd.empty())
4877 ss_system(cmd.c_str());
4878 }
4879
4880 /* set new start time in database */
4881 if (transition == TR_START) {
4882 /* ASCII format */
4883 std::string now = cm_asctime();
4884 now.reserve(32);
4885 db_set_value(hDB, 0, "Runinfo/Start Time", now.c_str(), 32, 1, TID_STRING);
4886
4887 /* reset stop time */
4888 seconds = 0;
4889 db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4890
4891 /* Seconds since 1.1.1970 */
4892 cm_time(&seconds);
4893 db_set_value(hDB, 0, "Runinfo/Start Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4894 }
4895
4896 size = sizeof(state);
4897 status = db_get_value(hDB, 0, "Runinfo/State", &state, &size, TID_INT32, TRUE);
4898
4899 /* set stop time in database */
4900 if (transition == TR_STOP) {
4901 if (status != DB_SUCCESS)
4902 cm_msg(MERROR, "cm_transition", "cannot get Runinfo/State in database");
4903
4904 if (state != STATE_STOPPED) {
4905 /* stop time binary */
4906 cm_time(&seconds);
4907 status = db_set_value(hDB, 0, "Runinfo/Stop Time binary", &seconds, sizeof(seconds), 1, TID_UINT32);
4908 if (status != DB_SUCCESS)
4909 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time binary\" in database");
4910
4911 /* stop time ascii */
4912 std::string now = cm_asctime();
4913 now.reserve(32);
4914 status = db_set_value(hDB, 0, "Runinfo/Stop Time", now.c_str(), 32, 1, TID_STRING);
4915 if (status != DB_SUCCESS)
4916 cm_msg(MERROR, "cm_transition", "cannot set \"Runinfo/Stop Time\" in database");
4917 }
4918 }
4919
4920 status = db_find_key(hDB, 0, "System/Clients", &hRootKey);
4921 if (status != DB_SUCCESS) {
4922 cm_msg(MERROR, "cm_transition", "cannot find System/Clients entry in database");
4923 if (errstr)
4924 mstrlcpy(errstr, "Cannot find /System/Clients in ODB", errstr_size);
4925 return tr_finish(hDB, &s, transition, status, errstr);
4926 }
4927
4928 std::string trname = cm_transition_name(transition);
4929
4930 /* check that all transition clients are alive */
4931 for (int i = 0;;) {
4932 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4933 if (status != DB_SUCCESS)
4934 break;
4935
4937
4938 if (status == DB_SUCCESS) {
4939 /* this client is alive. Check next one! */
4940 i++;
4941 continue;
4942 }
4943
4944 assert(status == CM_NO_CLIENT);
4945
4946 /* start from scratch: removing odb entries as we iterate over them
4947 * does strange things to db_enum_key() */
4948 i = 0;
4949 }
4950
4951 /* check for broken RPC connections */
4953
4954 if (debug_flag == 1)
4955 printf("---- Transition %s started ----\n", trname.c_str());
4956 if (debug_flag == 2)
4957 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s started ----", trname.c_str());
4958
4959 sprintf(tr_key_name, "Transition %s", trname.c_str());
4960
4961 /* search database for clients which registered for transition */
4962
4963 for (int i = 0, status = 0;; i++) {
4964 KEY subkey;
4965 status = db_enum_key(hDB, hRootKey, i, &hSubkey);
4967 break;
4968
4969 status = db_get_key(hDB, hSubkey, &subkey);
4970 assert(status == DB_SUCCESS);
4971
4972 if (status == DB_SUCCESS) {
4973 status = db_find_key(hDB, hSubkey, tr_key_name, &hKeyTrans);
4974
4975 if (status == DB_SUCCESS) {
4976
4977 db_get_key(hDB, hKeyTrans, &key);
4978
4979 for (int j = 0; j < key.num_values; j++) {
4980 size = sizeof(sequence_number);
4981 status = db_get_data_index(hDB, hKeyTrans, &sequence_number, &size, j, TID_INT32);
4982 assert(status == DB_SUCCESS);
4983
4984 TrClient *c = new TrClient;
4985
4987 c->transition = transition;
4988 c->run_number = run_number;
4989 c->async_flag = async_flag;
4990 c->debug_flag = debug_flag;
4991 c->sequence_number = sequence_number;
4992 c->status = 0;
4993 c->key_name = subkey.name;
4994
4995 /* get client info */
4996 char client_name[NAME_LENGTH];
4997 size = sizeof(client_name);
4998 db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, TRUE);
4999 c->client_name = client_name;
5000
5002 size = sizeof(host_name);
5003 db_get_value(hDB, hSubkey, "Host", host_name, &size, TID_STRING, TRUE);
5004 c->host_name = host_name;
5005
5006 //printf("Found client [%s] name [%s] transition [%s], i=%d, j=%d\n", subkey.name, client_name, tr_key_name, i, j);
5007
5008 if (hSubkey == hKeylocal && ((async_flag & TR_MTHREAD) == 0)) {
5009 /* remember own client */
5010 c->port = 0;
5011 } else {
5012 size = sizeof(port);
5013 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
5014 c->port = port;
5015 }
5016
5017 /* check for duplicates */
5018
5019 bool found = false;
5020 for (size_t k=0; k<s.clients.size(); k++) {
5021 TrClient* cc = s.clients[k].get();
5022 if (cc->client_name == c->client_name)
5023 if (cc->host_name == c->host_name)
5024 if (cc->port == c->port)
5025 if (cc->sequence_number == c->sequence_number)
5026 found = true;
5027 }
5028
5029 if (!found) {
5030 s.clients.push_back(std::unique_ptr<TrClient>(c));
5031 c = NULL;
5032 } else {
5033 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);
5034 delete c;
5035 c = NULL;
5036 }
5037 }
5038 }
5039 }
5040 }
5041
5042 std::sort(s.clients.begin(), s.clients.end(), tr_compare);
5043
5044 /* set predecessor for multi-threaded transitions */
5045 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5046 if (s.clients[idx]->sequence_number == 0) {
5047 // sequence number 0 means "don't care"
5048 } else {
5049 /* find clients with smaller sequence number */
5050 if (idx > 0) {
5051 for (size_t i = idx - 1; ; i--) {
5052 if (s.clients[i]->sequence_number < s.clients[idx]->sequence_number) {
5053 if (s.clients[i]->sequence_number > 0) {
5054 s.clients[idx]->wait_for_index.push_back(i);
5055 }
5056 }
5057 if (i==0)
5058 break;
5059 }
5060 }
5061 }
5062 }
5063
5064 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5065 write_tr_client_to_odb(hDB, s.clients[idx].get());
5066 }
5067
5068#if 0
5069 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5070 printf("TrClient[%d]: ", int(idx));
5071 s.clients[idx]->Print();
5072 printf("\n");
5073 }
5074#endif
5075
5076 /* contact ordered clients for transition -----------------------*/
5078 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5079 if (debug_flag == 1)
5080 printf("\n==== Found client \"%s\" with sequence number %d\n",
5081 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5082 if (debug_flag == 2)
5083 cm_msg(MINFO, "cm_transition",
5084 "cm_transition: ==== Found client \"%s\" with sequence number %d",
5085 s.clients[idx]->client_name.c_str(), s.clients[idx]->sequence_number);
5086
5087 if (async_flag & TR_MTHREAD) {
5089 assert(s.clients[idx]->thread == NULL);
5090 s.clients[idx]->thread = new std::thread(cm_transition_call, &s, idx);
5091 } else {
5092 if (s.clients[idx]->port == 0) {
5093 /* if own client call transition callback directly */
5095 } else {
5096 /* if other client call transition via RPC layer */
5097 status = cm_transition_call(&s, idx);
5098 }
5099
5100 if (status == CM_SUCCESS && transition != TR_STOP)
5101 if (s.clients[idx]->status != SUCCESS) {
5102 cm_msg(MERROR, "cm_transition", "transition %s aborted: client \"%s\" returned status %d", trname.c_str(),
5103 s.clients[idx]->client_name.c_str(), int(s.clients[idx]->status));
5104 break;
5105 }
5106 }
5107
5108 if (status != CM_SUCCESS)
5109 break;
5110 }
5111
5112 /* wait until all threads have finished */
5113 for (size_t idx = 0; idx < s.clients.size(); idx++) {
5114 if (s.clients[idx]->thread) {
5115 // join() will wait forever until thread finishes
5116 s.clients[idx]->thread->join();
5117 delete s.clients[idx]->thread;
5118 s.clients[idx]->thread = NULL;
5119 }
5120 }
5121
5122 /* at this point, all per-client threads have stopped and it is safe to delete TrState and return */
5123
5124 i = 0;
5125 size = sizeof(i);
5126 status = db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, FALSE);
5127
5128 if (status == DB_SUCCESS && i == 0) {
5129 cm_msg(MERROR, "cm_transition", "transition %s aborted: \"/Runinfo/Transition in progress\" was cleared", trname.c_str());
5130
5131 if (errstr != NULL)
5132 mstrlcpy(errstr, "Canceled", errstr_size);
5133
5134 return tr_finish(hDB, &s, transition, CM_TRANSITION_CANCELED, "Canceled");
5135 }
5136
5137 /* search for any error */
5138 for (size_t idx = 0; idx < s.clients.size(); idx++)
5139 if (s.clients[idx]->status != CM_SUCCESS) {
5140 status = s.clients[idx]->status;
5141 if (errstr)
5142 mstrlcpy(errstr, s.clients[idx]->errorstr.c_str(), errstr_size);
5143 s.errorstr = msprintf("Aborted by client \"%s\"", s.clients[idx]->client_name.c_str());
5144 break;
5145 }
5146
5147 if (transition != TR_STOP && status != CM_SUCCESS) {
5148 /* indicate abort */
5149 i = 1;
5150 db_set_value(hDB, 0, "/Runinfo/Start abort", &i, sizeof(INT), 1, TID_INT32);
5151 i = 0;
5152 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5153
5154 return tr_finish(hDB, &s, transition, status, errstr);
5155 }
5156
5157 if (debug_flag == 1)
5158 printf("\n---- Transition %s finished ----\n", trname.c_str());
5159 if (debug_flag == 2)
5160 cm_msg(MINFO, "cm_transition", "cm_transition: ---- Transition %s finished ----", trname.c_str());
5161
5162 /* set new run state in database */
5165
5166 if (transition == TR_PAUSE)
5168
5169 if (transition == TR_STOP)
5171
5174
5175 size = sizeof(state);
5176 status = db_set_value(hDB, 0, "Runinfo/State", &state, size, 1, TID_INT32);
5177 if (status != DB_SUCCESS)
5178 cm_msg(MERROR, "cm_transition", "cannot set Runinfo/State in database, db_set_value() status %d", status);
5179
5180 /* send notification message */
5181 if (transition == TR_START)
5182 cm_msg(MINFO, "cm_transition", "Run #%d started", run_number);
5183 if (transition == TR_STOP)
5184 cm_msg(MINFO, "cm_transition", "Run #%d stopped", run_number);
5185 if (transition == TR_PAUSE)
5186 cm_msg(MINFO, "cm_transition", "Run #%d paused", run_number);
5187 if (transition == TR_RESUME)
5188 cm_msg(MINFO, "cm_transition", "Run #%d resumed", run_number);
5190 cm_msg(MINFO, "cm_transition", "Run #%d start aborted", run_number);
5191
5192 /* lock/unlock ODB values if present */
5193 db_find_key(hDB, 0, "/Experiment/Lock when running", &hKey);
5194 if (hKey) {
5195 if (state == STATE_STOPPED)
5197 else
5199 }
5200
5201 /* flush online database */
5202 if (transition == TR_STOP)
5204
5205 /* execute/stop programs on stop */
5206 if (transition == TR_STOP) {
5207 std::string cmd;
5208 db_get_value_string(hDB, 0, "/Programs/Execute on stop run", 0, &cmd, TRUE, 256);
5209 if (!cmd.empty())
5210 ss_system(cmd.c_str());
5211
5212 db_find_key(hDB, 0, "/Programs", &hRootKey);
5213 if (hRootKey) {
5214 for (i = 0;; i++) {
5215 BOOL program_info_auto_stop = FALSE;
5216 status = db_enum_key(hDB, hRootKey, i, &hKey);
5218 break;
5219
5220 db_get_key(hDB, hKey, &key);
5221
5222 /* don't check "execute on xxx" */
5223 if (key.type != TID_KEY)
5224 continue;
5225
5226 size = sizeof(program_info_auto_stop);
5227 status = db_get_value(hDB, hKey, "Auto stop", &program_info_auto_stop, &size, TID_BOOL, TRUE);
5228 if (status != DB_SUCCESS) {
5229 cm_msg(MERROR, "cm_transition", "Cannot get program info auto stop, status %d", status);
5230 continue;
5231 }
5232
5233 if (program_info_auto_stop) {
5234 cm_msg(MINFO, "cm_transition", "Auto Stopping program \"%s\"", key.name);
5236 }
5237 }
5238 }
5239 }
5240
5241
5242 /* indicate success */
5243 i = 0;
5244 db_set_value(hDB, 0, "/Runinfo/Transition in progress", &i, sizeof(INT), 1, TID_INT32);
5245
5246 if (errstr != NULL)
5247 mstrlcpy(errstr, "Success", errstr_size);
5248
5249 return tr_finish(hDB, &s, transition, CM_SUCCESS, "Success");
5250}
5251
5252/*------------------------------------------------------------------*/
5253
5254/* wrapper around cm_transition2() to send a TR_STARTABORT in case of failure */
5255static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5256 int status;
5257
5258 status = cm_transition2(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5259
5260 if (transition == TR_START && status != CM_SUCCESS) {
5261 cm_msg(MERROR, "cm_transition", "Could not start a run: cm_transition() status %d, message \'%s\'", status,
5262 errstr);
5263 cm_transition2(TR_STARTABORT, run_number, NULL, 0, async_flag, debug_flag);
5264 }
5265
5266 return status;
5267}
5268
5269/*------------------------------------------------------------------*/
5270
5271static INT tr_main_thread(void *param) {
5272 INT status;
5273 TR_PARAM *trp;
5274
5275 trp = (TR_PARAM *) param;
5276 status = cm_transition1(trp->transition, trp->run_number, trp->errstr, trp->errstr_size, trp->async_flag, trp->debug_flag);
5277
5278 trp->status = status;
5279 trp->finished = TRUE;
5280
5281 return 0;
5282}
5283
5285{
5286 if (_trp.thread && !_trp.finished) {
5287 //printf("main transition thread did not finish yet!\n");
5289 }
5290
5291 std::thread* t = _trp.thread.exchange(NULL);
5292
5293 if (t) {
5294 t->join();
5295 delete t;
5296 t = NULL;
5297 }
5298
5299 return CM_SUCCESS;
5300}
5301
5302/* wrapper around cm_transition1() for detached multi-threaded transitions */
5303INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag) {
5304 int mflag = async_flag & TR_MTHREAD;
5305 int sflag = async_flag & TR_SYNC;
5306
5308
5309 if (status != CM_SUCCESS) {
5310 cm_msg(MERROR, "cm_transition", "previous transition did not finish yet");
5312 }
5313
5314 /* get key of local client */
5315 HNDLE hDB;
5317
5318 bool deferred = (transition & TR_DEFERRED) > 0;
5319 INT trans_raw = (transition & ~TR_DEFERRED);
5320
5321 /* check for valid transition */
5322 if (trans_raw != TR_START && trans_raw != TR_STOP && trans_raw != TR_PAUSE && trans_raw != TR_RESUME && trans_raw != TR_STARTABORT) {
5323 cm_msg(MERROR, "cm_transition", "Invalid transition request \"%d\"", transition);
5324 if (errstr) {
5325 mstrlcpy(errstr, "Invalid transition request", errstr_size);
5326 }
5327 return CM_INVALID_TRANSITION;
5328 }
5329
5330 /* check if transition in progress */
5331 if (!deferred) {
5332 int i = 0;
5333 int size = sizeof(i);
5334 db_get_value(hDB, 0, "/Runinfo/Transition in progress", &i, &size, TID_INT32, TRUE);
5335 if (i == 1) {
5336 if (errstr) {
5337 sprintf(errstr, "Start/Stop transition %d already in progress, please try again later\n", i);
5338 mstrlcat(errstr, "or set \"/Runinfo/Transition in progress\" manually to zero.\n", errstr_size);
5339 }
5340 cm_msg(MERROR, "cm_transition", "another transition is already in progress");
5342 }
5343 }
5344
5345 if (mflag) {
5348 if (sflag) {
5349 /* in MTHREAD|SYNC mode, we wait until the main thread finishes and it is safe for it to write into errstr */
5350 _trp.errstr = errstr;
5351 _trp.errstr_size = errstr_size;
5352 } else {
5353 /* in normal MTHREAD mode, we return right away and
5354 * if errstr is a local variable in the caller and they return too,
5355 * errstr becomes a stale reference and writing into it will corrupt the stack
5356 * in the mlogger, errstr is a local variable in "start_the_run", "stop_the_run"
5357 * and we definitely corrupt mlogger memory with out this: */
5358 _trp.errstr = NULL;
5359 _trp.errstr_size = 0;
5360 }
5361 _trp.async_flag = async_flag;
5362 _trp.debug_flag = debug_flag;
5363 _trp.status = 0;
5365
5366 if (errstr)
5367 *errstr = 0; // null error string
5368
5369 //ss_thread_create(tr_main_thread, &_trp);
5370
5371 std::thread* t = _trp.thread.exchange(new std::thread(tr_main_thread, &_trp));
5372
5373 assert(t==NULL); // previous thread should have been reaped by cm_transition_cleanup()
5374
5375 if (sflag) {
5376
5377 /* wait until main thread has finished */
5378 do {
5379 ss_sleep(10);
5380 } while (!_trp.finished);
5381
5382 std::thread* t = _trp.thread.exchange(NULL);
5383
5384 if (t) {
5385 t->join();
5386 delete t;
5387 t = NULL;
5388 }
5389
5390 return _trp.status;
5391 }
5392 } else
5393 return cm_transition1(transition, run_number, errstr, errstr_size, async_flag, debug_flag);
5394
5395 return CM_SUCCESS;
5396}
5397
5399#ifndef DOXYGEN_SHOULD_SKIP_THIS
5400
5401/********************************************************************/
5402INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
5403/********************************************************************\
5404
5405 Routine: cm_dispatch_ipc
5406
5407 Purpose: Called from ss_suspend if an IPC message arrives
5408
5409 Input:
5410 INT msg IPC message we got, MSG_ODB/MSG_BM
5411 INT p1, p2 Optional parameters
5412 int s Optional server socket
5413
5414 Output:
5415 none
5416
5417 Function value:
5418 CM_SUCCESS Successful completion
5419
5420\********************************************************************/
5421{
5422 if (message[0] == 'O') {
5423 HNDLE hDB, hKey, hKeyRoot;
5424 INT index;
5425 index = 0;
5426 sscanf(message + 2, "%d %d %d %d", &hDB, &hKeyRoot, &hKey, &index);
5427 if (client_socket) {
5428 return db_update_record_mserver(hDB, hKeyRoot, hKey, index, client_socket);
5429 } else {
5430 return db_update_record_local(hDB, hKeyRoot, hKey, index);
5431 }
5432 }
5433
5434 /* message == "B" means "resume event sender" */
5435 if (message[0] == 'B' && message[2] != ' ') {
5436 char str[NAME_LENGTH];
5437
5438 //printf("cm_dispatch_ipc: message [%s], s=%d\n", message, s);
5439
5440 mstrlcpy(str, message + 2, sizeof(str));
5441 if (strchr(str, ' '))
5442 *strchr(str, ' ') = 0;
5443
5444 if (client_socket)
5445 return bm_notify_client(str, client_socket);
5446 else
5447 return bm_push_event(str);
5448 }
5449
5450 //printf("cm_dispatch_ipc: message [%s] ignored\n", message);
5451
5452 return CM_SUCCESS;
5453}
5454
5455/********************************************************************/
5457
5458void cm_ctrlc_handler(int sig) {
5459 if (_ctrlc_pressed) {
5460 printf("Received 2nd Ctrl-C, hard abort\n");
5461 exit(0);
5462 }
5463 printf("Received Ctrl-C, aborting...\n");
5465
5467}
5468
5472
5476
5477/********************************************************************/
5478int cm_exec_script(const char *odb_path_to_script)
5479/********************************************************************\
5480
5481 Routine: cm_exec_script
5482
5483 Purpose: Execute script from /Script tree
5484
5485 exec_script is enabled by the tree /Script
5486 The /Script struct is composed of list of keys
5487 from which the name of the key is the button name
5488 and the sub-structure is a record as follow:
5489
5490 /Script/<button_name> = <script command> (TID_STRING)
5491
5492 The "Script command", containing possible arguements,
5493 is directly executed.
5494
5495 /Script/<button_name>/<script command>
5496 <soft link1>|<arg1>
5497 <soft link2>|<arg2>
5498 ...
5499
5500 The arguments for the script are derived from the
5501 subtree below <button_name>, where <button_name> must be
5502 TID_KEY. The subtree may then contain arguments or links
5503 to other values in the ODB, like run number etc.
5504
5505\********************************************************************/
5506{
5507 HNDLE hDB, hkey;
5508 KEY key;
5509 int status;
5510
5512 if (status != DB_SUCCESS)
5513 return status;
5514
5515 status = db_find_key(hDB, 0, odb_path_to_script, &hkey);
5516 if (status != DB_SUCCESS)
5517 return status;
5518
5519 status = db_get_key(hDB, hkey, &key);
5520 if (status != DB_SUCCESS)
5521 return status;
5522
5523 std::string command;
5524
5525 if (key.type == TID_STRING) {
5526 int status = db_get_value_string(hDB, 0, odb_path_to_script, 0, &command, FALSE);
5527 if (status != DB_SUCCESS) {
5528 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" of type TID_STRING, db_get_value_string() error %d",
5529 odb_path_to_script, status);
5530 return status;
5531 }
5532 } else if (key.type == TID_KEY) {
5533 for (int i = 0;; i++) {
5534 HNDLE hsubkey;
5535 KEY subkey;
5536 db_enum_key(hDB, hkey, i, &hsubkey);
5537 if (!hsubkey)
5538 break;
5539 db_get_key(hDB, hsubkey, &subkey);
5540
5541 if (i > 0)
5542 command += " ";
5543
5544 if (subkey.type == TID_KEY) {
5545 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" should not be TID_KEY", odb_path_to_script,
5546 subkey.name);
5547 return DB_TYPE_MISMATCH;
5548 } else {
5549 int size = subkey.item_size;
5550 char *buf = (char *) malloc(size);
5551 assert(buf != NULL);
5552 int status = db_get_data(hDB, hsubkey, buf, &size, subkey.type);
5553 if (status != DB_SUCCESS) {
5554 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s/%s\" of type %d, db_get_data() error %d",
5555 odb_path_to_script, subkey.name, subkey.type, status);
5556 free(buf);
5557 return status;
5558 }
5559 if (subkey.type == TID_STRING) {
5560 command += buf;
5561 } else {
5562 command += db_sprintf(buf, subkey.item_size, 0, subkey.type);
5563 }
5564 free(buf);
5565 }
5566 }
5567 } else {
5568 cm_msg(MERROR, "cm_exec_script", "Script ODB \"%s\" has invalid type %d, should be TID_STRING or TID_KEY",
5569 odb_path_to_script, key.type);
5570 return DB_TYPE_MISMATCH;
5571 }
5572
5573 // printf("exec_script: %s\n", command.c_str());
5574
5575 if (command.length() > 0) {
5576 cm_msg(MINFO, "cm_exec_script", "Executing script \"%s\" from ODB \"%s\"", command.c_str(), odb_path_to_script);
5577 ss_system(command.c_str());
5578 }
5579
5580 return SUCCESS;
5581}
5582
5584#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5585
5586static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval);
5587
5588/********************************************************************/
5597 static DWORD alarm_last_checked_sec = 0;
5598 DWORD now_sec = ss_time();
5599
5600 DWORD now_millitime = ss_millitime();
5601 static DWORD last_millitime = 0;
5602 DWORD tdiff_millitime = now_millitime - last_millitime;
5603 const DWORD kPeriod = 1000;
5604 if (last_millitime == 0) {
5605 last_millitime = now_millitime;
5606 tdiff_millitime = kPeriod; // make sure first time we come here we do something.
5607 }
5608
5609 //printf("cm_periodic_tasks! tdiff_millitime %d\n", (int)tdiff_millitime);
5610
5611 //if (now_millitime < last_millitime) {
5612 // printf("millitime wraparound 0x%08x -> 0x%08x\n", last_millitime, now_millitime);
5613 //}
5614
5615 /* check alarms once every 10 seconds */
5616 if (now_sec - alarm_last_checked_sec > 10) {
5617 al_check();
5618 alarm_last_checked_sec = now_sec;
5619 }
5620
5621 /* run periodic checks previously done by cm_watchdog */
5622
5623 if (tdiff_millitime >= kPeriod) {
5624 BOOL wrong_interval = FALSE;
5625 if (tdiff_millitime > 60000)
5626 wrong_interval = TRUE;
5627
5628 //printf("millitime %u, diff %u, wrong_interval %d\n", now_millitime, tdiff_millitime, wrong_interval);
5629
5630 bm_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5631 db_cleanup("cm_periodic_tasks", now_millitime, wrong_interval);
5632
5634
5635 last_millitime = now_millitime;
5636 }
5637
5638 /* reap transition thread */
5639
5641
5642 return CM_SUCCESS;
5643}
5644
5645/********************************************************************/
5659INT cm_yield(INT millisec) {
5660 INT status;
5661 INT bMore;
5662 //static DWORD last_yield = 0;
5663 //static DWORD last_yield_time = 0;
5664 //DWORD start_yield = ss_millitime();
5665
5666 /* check for ctrl-c */
5667 if (_ctrlc_pressed)
5668 return RPC_SHUTDOWN;
5669
5670 /* flush the cm_msg buffer */
5672
5673 if (!rpc_is_remote()) {
5674 /* flush the ODB to its binary file */
5675 /* for remote clients, ODB is flushed by the mserver */
5676 HNDLE hDB;
5679 }
5680
5681 /* check for available events */
5682 if (rpc_is_remote()) {
5683 //printf("cm_yield() calling bm_poll_event()\n");
5685
5686 if (status == SS_ABORT) {
5687 return status;
5688 }
5689
5690 if (status == BM_SUCCESS) {
5691 /* one or more events received by bm_poll_event() */
5692 status = ss_suspend(0, 0);
5693 } else {
5694 status = ss_suspend(millisec, 0);
5695 }
5696
5697 return status;
5698 }
5699
5701
5702 if (status != CM_SUCCESS)
5703 return status;
5704
5705 //DWORD start_check = ss_millitime();
5706
5707 bMore = bm_check_buffers();
5708
5709 //DWORD end_check = ss_millitime();
5710 //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);
5711 //fflush(stdout);
5712
5713 if (bMore == BM_CORRUPTED) {
5714 status = SS_ABORT;
5715 } else if (bMore) {
5716 /* if events available, quickly check other IPC channels */
5717 status = ss_suspend(0, 0);
5718 } else {
5719 status = ss_suspend(millisec, 0);
5720 }
5721
5722 /* flush the cm_msg buffer */
5724
5725 //DWORD end_yield = ss_millitime();
5726 //last_yield_time = end_yield - start_yield;
5727 //last_yield = start_yield;
5728
5729 return status;
5730}
5731
5732/********************************************************************/
5740INT cm_execute(const char *command, char *result, INT bufsize) {
5741 char str[256];
5742 INT n;
5743 int fh;
5744 int status = 0;
5745 static int check_cm_execute = 1;
5746 static int enable_cm_execute = 0;
5747
5748 if (rpc_is_remote())
5749 return rpc_call(RPC_CM_EXECUTE, command, result, bufsize);
5750
5751 if (check_cm_execute) {
5752 int status;
5753 int size;
5754 HNDLE hDB;
5755 check_cm_execute = 0;
5756
5758 assert(status == DB_SUCCESS);
5759
5760 size = sizeof(enable_cm_execute);
5761 status = db_get_value(hDB, 0, "/Experiment/Enable cm_execute", &enable_cm_execute, &size, TID_BOOL, TRUE);
5762 assert(status == DB_SUCCESS);
5763
5764 //printf("enable_cm_execute %d\n", enable_cm_execute);
5765 }
5766
5767 if (!enable_cm_execute) {
5768 char buf[32];
5769 mstrlcpy(buf, command, sizeof(buf));
5770 cm_msg(MERROR, "cm_execute", "cm_execute(%s...) is disabled by ODB \"/Experiment/Enable cm_execute\"", buf);
5771 return CM_WRONG_PASSWORD;
5772 }
5773
5774 if (bufsize > 0) {
5775 strcpy(str, command);
5776 sprintf(str, "%s > %d.tmp", command, ss_getpid());
5777
5778 status = system(str);
5779
5780 sprintf(str, "%d.tmp", ss_getpid());
5781 fh = open(str, O_RDONLY, 0644);
5782 result[0] = 0;
5783 if (fh) {
5784 n = read(fh, result, bufsize - 1);
5785 result[MAX(0, n)] = 0;
5786 close(fh);
5787 }
5788 remove(str);
5789 } else {
5790 status = system(command);
5791 }
5792
5793 if (status < 0) {
5794 cm_msg(MERROR, "cm_execute", "cm_execute(%s) error %d", command, status);
5795 return CM_SET_ERROR;
5796 }
5797
5798 return CM_SUCCESS;
5799}
5800
5801
5802
5804#ifndef DOXYGEN_SHOULD_SKIP_THIS
5805
5806/********************************************************************/
5807INT cm_register_function(INT id, INT(*func)(INT, void **))
5808/********************************************************************\
5809
5810 Routine: cm_register_function
5811
5812 Purpose: Call rpc_register_function and publish the registered
5813 function under system/clients/<pid>/RPC
5814
5815 Input:
5816 INT id RPC ID
5817 INT *func New dispatch function
5818
5819 Output:
5820 <implicit: func gets copied to rpc_list>
5821
5822 Function value:
5823 CM_SUCCESS Successful completion
5824 RPC_INVALID_ID RPC ID not found
5825
5826\********************************************************************/
5827{
5828 HNDLE hDB, hKey;
5829 INT status;
5830 char str[80];
5831
5832 status = rpc_register_function(id, func);
5833 if (status != RPC_SUCCESS)
5834 return status;
5835
5837
5838 /* create new key for this id */
5839 status = 1;
5840 sprintf(str, "RPC/%d", id);
5841
5843 status = db_set_value(hDB, hKey, str, &status, sizeof(BOOL), 1, TID_BOOL);
5845
5846 if (status != DB_SUCCESS)
5847 return status;
5848
5849 return CM_SUCCESS;
5850}
5851
5852
5854#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5855
5856//
5857// Return "/"-terminated file path for given history channel
5858//
5859
5860std::string cm_get_history_path(const char* history_channel)
5861{
5862 int status;
5863 HNDLE hDB;
5864 std::string path;
5865
5867
5868 if (history_channel && (strlen(history_channel) > 0)) {
5869 std::string p;
5870 p += "/Logger/History/";
5871 p += history_channel;
5872 p += "/History dir";
5873
5874 // NB: be careful to avoid creating odb entries under /logger
5875 // for whatever values of "history_channel" we get called with!
5876 status = db_get_value_string(hDB, 0, p.c_str(), 0, &path, FALSE);
5877 if (status == DB_SUCCESS && path.length() > 0) {
5878 // if not absolute path, prepend with experiment directory
5879 if (path[0] != DIR_SEPARATOR)
5880 path = cm_get_path() + path;
5881 // append directory separator
5882 if (path.back() != DIR_SEPARATOR)
5883 path += DIR_SEPARATOR_STR;
5884 //printf("for [%s] returning [%s] from [%s]\n", history_channel, path.c_str(), p.c_str());
5885 return path;
5886 }
5887 }
5888
5889 status = db_get_value_string(hDB, 0, "/Logger/History dir", 0, &path, TRUE);
5890 if (status == DB_SUCCESS && path.length() > 0) {
5891 // if not absolute path, prepend with experiment directory
5892 if (path[0] != DIR_SEPARATOR)
5893 path = cm_get_path() + path;
5894 // append directory separator
5895 if (path.back() != DIR_SEPARATOR)
5896 path += DIR_SEPARATOR_STR;
5897 //printf("for [%s] returning /Logger/History dir [%s]\n", history_channel, path.c_str());
5898 return path;
5899 }
5900
5901 status = db_get_value_string(hDB, 0, "/Logger/Data dir", 0, &path, FALSE);
5902 if (status == DB_SUCCESS && path.length() > 0) {
5903 // if not absolute path, prepend with experiment directory
5904 if (path[0] != DIR_SEPARATOR)
5905 path = cm_get_path() + path;
5906 // append directory separator
5907 if (path.back() != DIR_SEPARATOR)
5908 path += DIR_SEPARATOR_STR;
5909 //printf("for [%s] returning /Logger/Data dir [%s]\n", history_channel, path.c_str());
5910 return path;
5911 }
5912
5913 //printf("for [%s] returning experiment dir [%s]\n", history_channel, cm_get_path().c_str());
5914 return cm_get_path();
5915}
5916
/* end of cmfunctionc */
5919
5925/********************************************************************\
5926* *
5927* bm_xxx - Buffer Manager Functions *
5928* *
5929\********************************************************************/
5930
5932
5933#ifdef LOCAL_ROUTINES
5934
5935// see locking code in xbm_lock_buffer()
5936static int _bm_lock_timeout = 5 * 60 * 1000;
5937static double _bm_mutex_timeout_sec = _bm_lock_timeout/1000 + 15.000;
5938
5940{
5941 const BUFFER *pbuf = pbuf_guard.get_pbuf();
5942
5943 bool badindex = false;
5944 bool badclient = false;
5945
5946 int idx = pbuf->client_index;
5947
5948 if (idx < 0) {
5949 badindex = true;
5950 } else if (idx > pbuf->buffer_header->max_client_index) {
5951 badindex = true;
5952 } else {
5953 BUFFER_CLIENT *pclient = &pbuf->buffer_header->client[idx];
5954 if (pclient->name[0] == 0)
5955 badclient = true;
5956 else if (pclient->pid != ss_getpid())
5957 badclient = true;
5958
5959 //if (strcmp(pclient->name,"mdump")==0) {
5960 // for (int i=0; i<15; i++) {
5961 // printf("sleep %d\n", i);
5962 // ::sleep(1);
5963 // }
5964 //}
5965 }
5966
5967#if 0
5968 if (badindex) {
5969 printf("bm_validate_client_index: pbuf=%p, buf_name \"%s\", client_index=%d, max_client_index=%d, badindex %d, pid=%d\n",
5970 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5971 badindex, ss_getpid());
5972 } else if (badclient) {
5973 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",
5974 pbuf, pbuf->buffer_header->name, pbuf->client_index, pbuf->buffer_header->max_client_index,
5975 pbuf->buffer_header->client[idx].name, pbuf->buffer_header->client[idx].pid,
5976 ss_getpid(), badclient);
5977 } else {
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, goodclient\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());
5982 }
5983#endif
5984
5985 if (badindex || badclient) {
5986 static int prevent_recursion = 1;
5987
5988 if (prevent_recursion) {
5989 prevent_recursion = 0;
5990
5991 if (badindex) {
5992 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());
5993 } else {
5994 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());
5995 }
5996
5997 cm_msg(MERROR, "bm_validate_client_index", "Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...");
5998 }
5999
6000 if (badindex) {
6001 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());
6002 } else {
6003 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());
6004 }
6005
6006 fprintf(stderr, "bm_validate_client_index: Maybe this client was removed by a timeout. See midas.log. Cannot continue, aborting...\n");
6007
6008 pbuf_guard.unlock();
6009
6010 abort();
6011 }
6012
6013 return idx;
6014}
6015
6017 int my_client_index = bm_validate_client_index_locked(pbuf_guard);
6018 return pbuf_guard.get_pbuf()->buffer_header->client + my_client_index;
6019}
6020
6021#endif // LOCAL_ROUTINES
6022
6023/********************************************************************/
6032INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent) {
6033 // NB: cast everything to unsigned 16 bit to avoid bitwise comparison failure
6034 // because of mismatch in sign-extension between signed 16-bit event_id and
6035 // unsigned 16-bit constants. K.O.
6036
6037 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)))
6038 /* fragmented event */
6039 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == (uint16_t(pevent->event_id) & uint16_t(0x0FFF))))
6040 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6041
6042 return (((uint16_t(event_id) == uint16_t(EVENTID_ALL)) || (uint16_t(event_id) == uint16_t(pevent->event_id)))
6043 && ((uint16_t(trigger_mask) == uint16_t(TRIGGER_ALL)) || ((uint16_t(trigger_mask) & uint16_t(pevent->trigger_mask)))));
6044}
6045
6046#ifdef LOCAL_ROUTINES
6047
6048/********************************************************************/
6053 int k, nc;
6054 BUFFER_CLIENT *pbctmp;
6055
6056 /* clear entry from client structure in buffer header */
6057 memset(&(pheader->client[j]), 0, sizeof(BUFFER_CLIENT));
6058
6059 /* calculate new max_client_index entry */
6060 for (k = MAX_CLIENTS - 1; k >= 0; k--)
6061 if (pheader->client[k].pid != 0)
6062 break;
6063 pheader->max_client_index = k + 1;
6064
6065 /* count new number of clients */
6066 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
6067 if (pheader->client[k].pid != 0)
6068 nc++;
6069 pheader->num_clients = nc;
6070
6071 /* check if anyone is waiting and wake him up */
6072 pbctmp = pheader->client;
6073
6074 for (k = 0; k < pheader->max_client_index; k++, pbctmp++)
6075 if (pbctmp->pid && (pbctmp->write_wait || pbctmp->read_wait))
6076 ss_resume(pbctmp->port, "B ");
6077}
6078
6079/********************************************************************/
6083static void bm_cleanup_buffer_locked(BUFFER* pbuf, const char *who, DWORD actual_time) {
6084 BUFFER_HEADER *pheader;
6085 BUFFER_CLIENT *pbclient;
6086 int j;
6087
6088 pheader = pbuf->buffer_header;
6089 pbclient = pheader->client;
6090
6091 /* now check other clients */
6092 for (j = 0; j < pheader->max_client_index; j++, pbclient++) {
6093 if (pbclient->pid) {
6094 if (!ss_pid_exists(pbclient->pid)) {
6095 cm_msg(MINFO, "bm_cleanup",
6096 "Client \'%s\' on buffer \'%s\' removed by %s because process pid %d does not exist", pbclient->name,
6097 pheader->name, who, pbclient->pid);
6098
6099 bm_remove_client_locked(pheader, j);
6100 continue;
6101 }
6102 }
6103
6104 /* If client process has no activity, clear its buffer entry. */
6105 if (pbclient->pid && pbclient->watchdog_timeout > 0) {
6106 DWORD tdiff = actual_time - pbclient->last_activity;
6107#if 0
6108 printf("buffer [%s] client [%-32s] times 0x%08x 0x%08x, diff 0x%08x %5d, timeout %d\n",
6109 pheader->name,
6110 pbclient->name,
6111 pbclient->last_activity,
6113 tdiff,
6114 tdiff,
6115 pbclient->watchdog_timeout);
6116#endif
6117 if (actual_time > pbclient->last_activity &&
6118 tdiff > pbclient->watchdog_timeout) {
6119
6120 cm_msg(MINFO, "bm_cleanup", "Client \'%s\' on buffer \'%s\' removed by %s (idle %1.1lfs, timeout %1.0lfs)",
6121 pbclient->name, pheader->name, who,
6122 tdiff / 1000.0,
6123 pbclient->watchdog_timeout / 1000.0);
6124
6125 bm_remove_client_locked(pheader, j);
6126 }
6127 }
6128 }
6129}
6130
6134static void bm_update_last_activity(DWORD millitime) {
6135 int pid = ss_getpid();
6136
6137 std::vector<BUFFER*> mybuffers;
6138
6139 gBuffersMutex.lock();
6140 mybuffers = gBuffers;
6141 gBuffersMutex.unlock();
6142
6143 for (BUFFER* pbuf : mybuffers) {
6144 if (!pbuf)
6145 continue;
6146 if (pbuf->attached) {
6147
6148 bm_lock_buffer_guard pbuf_guard(pbuf);
6149
6150 if (!pbuf_guard.is_locked())
6151 continue;
6152
6153 BUFFER_HEADER *pheader = pbuf->buffer_header;
6154 for (int j = 0; j < pheader->max_client_index; j++) {
6155 BUFFER_CLIENT *pclient = pheader->client + j;
6156 if (pclient->pid == pid) {
6157 pclient->last_activity = millitime;
6158 }
6159 }
6160 }
6161 }
6162}
6163
6164#endif // LOCAL_ROUTINES
6165
6169static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
6170{
6171#ifdef LOCAL_ROUTINES
6172
6173 //printf("bm_cleanup: called by %s, actual_time %d, wrong_interval %d\n", who, actual_time, wrong_interval);
6174
6175 std::vector<BUFFER*> mybuffers;
6176
6177 gBuffersMutex.lock();
6178 mybuffers = gBuffers;
6179 gBuffersMutex.unlock();
6180
6181 /* check buffers */
6182 for (BUFFER* pbuf : mybuffers) {
6183 if (!pbuf)
6184 continue;
6185 if (pbuf->attached) {
6186 /* update the last_activity entry to show that we are alive */
6187
6188 bm_lock_buffer_guard pbuf_guard(pbuf);
6189
6190 if (!pbuf_guard.is_locked())
6191 continue;
6192
6193 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
6194 pclient->last_activity = actual_time;
6195
6196 /* don't check other clients if interval is strange */
6197 if (!wrong_interval)
6199 }
6200 }
6201#endif // LOCAL_ROUTINES
6202}
6203
6204#ifdef LOCAL_ROUTINES
6205
6206static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp) {
6207 if (rp < 0 || rp > pheader->size) {
6208 cm_msg(MERROR, "bm_validate_rp",
6209 "error: buffer \"%s\" is corrupted: rp %d is invalid. buffer read_pointer %d, write_pointer %d, size %d, called from %s",
6210 pheader->name,
6211 rp,
6212 pheader->read_pointer,
6213 pheader->write_pointer,
6214 pheader->size,
6215 who);
6216 return FALSE;
6217 }
6218
6219 if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6220 // note ">" here, has to match bm_incr_rp() and bm_write_to_buffer()
6221 cm_msg(MERROR, "bm_validate_rp",
6222 "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",
6223 pheader->name,
6224 rp,
6225 (int) (rp + sizeof(EVENT_HEADER) - pheader->size),
6226 pheader->read_pointer,
6227 pheader->write_pointer,
6228 pheader->size,
6229 who);
6230 return FALSE;
6231 }
6232
6233 return TRUE;
6234}
6235
6236#if 0
6237static FILE* gRpLog = NULL;
6238#endif
6239
6240static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
6241{
6242#if 0
6243 if (gRpLog == NULL) {
6244 gRpLog = fopen("rp.log", "a");
6245 }
6246 if (gRpLog && (total_size < 16)) {
6247 const char *pdata = (const char *) (pheader + 1);
6248 const DWORD *pevent = (const DWORD*) (pdata + rp);
6249 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,
6250 pevent[0], pevent[1], pevent[2], pevent[3], pevent[4], pevent[5]);
6251 }
6252#endif
6253
6254 // these checks are already done before we come here.
6255 // but we check again as last-ressort protection. K.O.
6256 assert(total_size > 0);
6257 assert(total_size >= (int)sizeof(EVENT_HEADER));
6258
6259 rp += total_size;
6260 if (rp >= pheader->size) {
6261 rp -= pheader->size;
6262 } else if ((rp + (int) sizeof(EVENT_HEADER)) > pheader->size) {
6263 // note: ">" here to match bm_write_to_buffer_locked() and bm_validate_rp().
6264 // if at the end of the buffer, the remaining free space is exactly
6265 // equal to the size of an event header, the event header
6266 // is written there, the pointer is wrapped and the event data
6267 // is written to the beginning of the buffer.
6268 rp = 0;
6269 }
6270 return rp;
6271}
6272
6273static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp) {
6274 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + rp);
6275 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
6276 int total_size = ALIGN8(event_size);
6277
6278 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
6279 cm_msg(MERROR, "bm_next_rp",
6280 "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",
6281 pheader->name,
6282 rp,
6283 pevent->data_size,
6284 event_size,
6285 total_size,
6286 pheader->read_pointer,
6287 pheader->write_pointer,
6288 pheader->size,
6289 who);
6290 return -1;
6291 }
6292
6293 int remaining = 0;
6294 if (rp < pheader->write_pointer) {
6295 remaining = pheader->write_pointer - rp;
6296 } else {
6297 remaining = pheader->size - rp;
6298 remaining += pheader->write_pointer;
6299 }
6300
6301 //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);
6302
6303 if (total_size > remaining) {
6304 cm_msg(MERROR, "bm_next_rp",
6305 "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",
6306 pheader->name,
6307 rp,
6308 pevent->data_size,
6309 event_size,
6310 total_size,
6311 pheader->read_pointer,
6312 pheader->write_pointer,
6313 pheader->size,
6314 remaining,
6315 who);
6316 return -1;
6317 }
6318
6319 rp = bm_incr_rp_no_check(pheader, rp, total_size);
6320
6321 return rp;
6322}
6323
6324static int bm_validate_buffer_locked(const BUFFER *pbuf) {
6325 const BUFFER_HEADER *pheader = pbuf->buffer_header;
6326 const char *pdata = (const char *) (pheader + 1);
6327
6328 //printf("bm_validate_buffer: buffer \"%s\"\n", pheader->name);
6329
6330 //printf("size: %d, rp: %d, wp: %d\n", pheader->size, pheader->read_pointer, pheader->write_pointer);
6331
6332 //printf("clients: max: %d, num: %d, MAX_CLIENTS: %d\n", pheader->max_client_index, pheader->num_clients, MAX_CLIENTS);
6333
6334 if (pheader->read_pointer < 0 || pheader->read_pointer >= pheader->size) {
6335 cm_msg(MERROR, "bm_validate_buffer",
6336 "buffer \"%s\" is corrupted: invalid read pointer %d. Size %d, write pointer %d", pheader->name,
6337 pheader->read_pointer, pheader->size, pheader->write_pointer);
6338 return BM_CORRUPTED;
6339 }
6340
6341 if (pheader->write_pointer < 0 || pheader->write_pointer >= pheader->size) {
6342 cm_msg(MERROR, "bm_validate_buffer",
6343 "buffer \"%s\" is corrupted: invalid write pointer %d. Size %d, read pointer %d", pheader->name,
6344 pheader->write_pointer, pheader->size, pheader->read_pointer);
6345 return BM_CORRUPTED;
6346 }
6347
6348 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, pheader->read_pointer)) {
6349 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: read pointer %d is invalid", pheader->name,
6350 pheader->read_pointer);
6351 return BM_CORRUPTED;
6352 }
6353
6354 int rp = pheader->read_pointer;
6355 int rp0 = -1;
6356 while (rp != pheader->write_pointer) {
6357 if (!bm_validate_rp("bm_validate_buffer_locked", pheader, rp)) {
6358 cm_msg(MERROR, "bm_validate_buffer", "buffer \"%s\" is corrupted: invalid rp %d, last good event at rp %d",
6359 pheader->name, rp, rp0);
6360 return BM_CORRUPTED;
6361 }
6362 //bm_print_event(pdata, rp);
6363 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6364 if (rp1 < 0) {
6365 cm_msg(MERROR, "bm_validate_buffer",
6366 "buffer \"%s\" is corrupted: invalid event at rp %d, last good event at rp %d", pheader->name, rp, rp0);
6367 return BM_CORRUPTED;
6368 }
6369 rp0 = rp;
6370 rp = rp1;
6371 }
6372
6373 int i;
6374 for (i = 0; i < MAX_CLIENTS; i++) {
6375 const BUFFER_CLIENT *c = &pheader->client[i];
6376 if (c->pid == 0)
6377 continue;
6378 BOOL get_all = FALSE;
6379 int j;
6380 for (j = 0; j < MAX_EVENT_REQUESTS; j++) {
6381 const EVENT_REQUEST *r = &c->event_request[j];
6382 if (!r->valid)
6383 continue;
6384 BOOL xget_all = r->sampling_type == GET_ALL;
6385 get_all = (get_all || xget_all);
6386 //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);
6387 }
6388
6389 int rp = c->read_pointer;
6390 int rp0 = -1;
6391 while (rp != pheader->write_pointer) {
6392 //bm_print_event(pdata, rp);
6393 int rp1 = bm_next_rp("bm_validate_buffer_locked", pheader, pdata, rp);
6394 if (rp1 < 0) {
6395 cm_msg(MERROR, "bm_validate_buffer",
6396 "buffer \"%s\" is corrupted for client \"%s\" rp %d: invalid event at rp %d, last good event at rp %d",
6397 pheader->name, c->name, c->read_pointer, rp, rp0);
6398 return BM_CORRUPTED;
6399 }
6400 rp0 = rp;
6401 rp = rp1;
6402 }
6403 }
6404
6405 return BM_SUCCESS;
6406}
6407
6408static void bm_reset_buffer_locked(BUFFER *pbuf) {
6409 BUFFER_HEADER *pheader = pbuf->buffer_header;
6410
6411 //printf("bm_reset_buffer: buffer \"%s\"\n", pheader->name);
6412
6413 pheader->read_pointer = 0;
6414 pheader->write_pointer = 0;
6415
6416 int i;
6417 for (i = 0; i < pheader->max_client_index; i++) {
6418 BUFFER_CLIENT *pc = pheader->client + i;
6419 if (pc->pid) {
6420 pc->read_pointer = 0;
6421 }
6422 }
6423}
6424
6426 std::string str = msprintf("/System/buffers/%s/Clients/%s/writes_blocked_by", pbuf->buffer_name, pbuf->client_name);
6427 //printf("delete [%s]\n", str);
6428 db_delete(hDB, 0, str.c_str());
6429}
6430
6473
6474static 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)
6475{
6476 int status;
6477
6478 DWORD now = ss_millitime();
6479
6480 HNDLE hKey;
6481 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6482 if (status != DB_SUCCESS) {
6483 db_create_key(hDB, 0, "/System/Buffers", TID_KEY);
6484 status = db_find_key(hDB, 0, "/System/Buffers", &hKey);
6485 if (status != DB_SUCCESS)
6486 return;
6487 }
6488
6489 HNDLE hKeyBuffer;
6490 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6491 if (status != DB_SUCCESS) {
6493 status = db_find_key(hDB, hKey, buffer_name, &hKeyBuffer);
6494 if (status != DB_SUCCESS)
6495 return;
6496 }
6497
6498 double buf_size = pheader->size;
6499 double buf_rptr = pheader->read_pointer;
6500 double buf_wptr = pheader->write_pointer;
6501
6502 double buf_fill = 0;
6503 double buf_cptr = 0;
6504 double buf_cused = 0;
6505 double buf_cused_pct = 0;
6506
6507 if (client_index >= 0 && client_index <= pheader->max_client_index) {
6508 buf_cptr = pheader->client[client_index].read_pointer;
6509
6510 if (buf_wptr == buf_cptr) {
6511 buf_cused = 0;
6512 } else if (buf_wptr > buf_cptr) {
6513 buf_cused = buf_wptr - buf_cptr;
6514 } else {
6515 buf_cused = (buf_size - buf_cptr) + buf_wptr;
6516 }
6517
6518 buf_cused_pct = buf_cused / buf_size * 100.0;
6519
6520 // we cannot write buf_cused and buf_cused_pct into the buffer statistics
6521 // because some other GET_ALL client may have different buf_cused & etc,
6522 // so they must be written into the per-client statistics
6523 // and the web page should look at all the GET_ALL clients and used
6524 // the biggest buf_cused as the whole-buffer "bytes used" value.
6525 }
6526
6527 if (buf_wptr == buf_rptr) {
6528 buf_fill = 0;
6529 } else if (buf_wptr > buf_rptr) {
6530 buf_fill = buf_wptr - buf_rptr;
6531 } else {
6532 buf_fill = (buf_size - buf_rptr) + buf_wptr;
6533 }
6534
6535 double buf_fill_pct = buf_fill / buf_size * 100.0;
6536
6537 db_set_value(hDB, hKeyBuffer, "Size", &buf_size, sizeof(double), 1, TID_DOUBLE);
6538 db_set_value(hDB, hKeyBuffer, "Write pointer", &buf_wptr, sizeof(double), 1, TID_DOUBLE);
6539 db_set_value(hDB, hKeyBuffer, "Read pointer", &buf_rptr, sizeof(double), 1, TID_DOUBLE);
6540 db_set_value(hDB, hKeyBuffer, "Filled", &buf_fill, sizeof(double), 1, TID_DOUBLE);
6541 db_set_value(hDB, hKeyBuffer, "Filled pct", &buf_fill_pct, sizeof(double), 1, TID_DOUBLE);
6542
6543 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6544 if (status != DB_SUCCESS) {
6545 db_create_key(hDB, hKeyBuffer, "Clients", TID_KEY);
6546 status = db_find_key(hDB, hKeyBuffer, "Clients", &hKey);
6547 if (status != DB_SUCCESS)
6548 return;
6549 }
6550
6551 HNDLE hKeyClient;
6552 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6553 if (status != DB_SUCCESS) {
6554 db_create_key(hDB, hKey, client_name, TID_KEY);
6555 status = db_find_key(hDB, hKey, client_name, &hKeyClient);
6556 if (status != DB_SUCCESS)
6557 return;
6558 }
6559
6560 db_set_value(hDB, hKeyClient, "count_lock", &pbuf->count_lock, sizeof(int), 1, TID_INT32);
6561 db_set_value(hDB, hKeyClient, "count_sent", &pbuf->count_sent, sizeof(int), 1, TID_INT32);
6562 db_set_value(hDB, hKeyClient, "bytes_sent", &pbuf->bytes_sent, sizeof(double), 1, TID_DOUBLE);
6563 db_set_value(hDB, hKeyClient, "count_write_wait", &pbuf->count_write_wait, sizeof(int), 1, TID_INT32);
6564 db_set_value(hDB, hKeyClient, "time_write_wait", &pbuf->time_write_wait, sizeof(DWORD), 1, TID_UINT32);
6565 db_set_value(hDB, hKeyClient, "max_bytes_write_wait", &pbuf->max_requested_space, sizeof(INT), 1, TID_INT32);
6566 db_set_value(hDB, hKeyClient, "count_read", &pbuf->count_read, sizeof(int), 1, TID_INT32);
6567 db_set_value(hDB, hKeyClient, "bytes_read", &pbuf->bytes_read, sizeof(double), 1, TID_DOUBLE);
6568 db_set_value(hDB, hKeyClient, "get_all_flag", &pbuf->get_all_flag, sizeof(BOOL), 1, TID_BOOL);
6569 db_set_value(hDB, hKeyClient, "read_pointer", &buf_cptr, sizeof(double), 1, TID_DOUBLE);
6570 db_set_value(hDB, hKeyClient, "bytes_used", &buf_cused, sizeof(double), 1, TID_DOUBLE);
6571 db_set_value(hDB, hKeyClient, "pct_used", &buf_cused_pct, sizeof(double), 1, TID_DOUBLE);
6572
6573 for (int i = 0; i < MAX_CLIENTS; i++) {
6574 if (!pbuf->client_count_write_wait[i])
6575 continue;
6576
6577 if (pheader->client[i].pid == 0)
6578 continue;
6579
6580 if (pheader->client[i].name[0] == 0)
6581 continue;
6582
6583 char str[100 + NAME_LENGTH];
6584
6585 sprintf(str, "writes_blocked_by/%s/count_write_wait", pheader->client[i].name);
6586 db_set_value(hDB, hKeyClient, str, &pbuf->client_count_write_wait[i], sizeof(int), 1, TID_INT32);
6587
6588 sprintf(str, "writes_blocked_by/%s/time_write_wait", pheader->client[i].name);
6589 db_set_value(hDB, hKeyClient, str, &pbuf->client_time_write_wait[i], sizeof(DWORD), 1, TID_UINT32);
6590 }
6591
6592 db_set_value(hDB, hKeyBuffer, "Last updated", &now, sizeof(DWORD), 1, TID_UINT32);
6593 db_set_value(hDB, hKeyClient, "last_updated", &now, sizeof(DWORD), 1, TID_UINT32);
6594}
6595
6597{
6598 //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);
6599
6600 bm_lock_buffer_guard pbuf_guard(pbuf);
6601
6602 if (!pbuf_guard.is_locked())
6603 return;
6604
6605 if (!force) {
6606 if (pbuf->count_lock == pbuf->last_count_lock) {
6607 return;
6608 }
6609 }
6610
6611 std::string buffer_name = pbuf->buffer_name;
6612 std::string client_name = pbuf->client_name;
6613
6614 if ((strlen(buffer_name.c_str()) < 1) || (strlen(client_name.c_str()) < 1)) {
6615 // do not call cm_msg() while holding buffer lock, if we are SYSMSG, we will deadlock. K.O.
6616 pbuf_guard.unlock(); // unlock before cm_msg()
6617 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());
6618 return;
6619 }
6620
6621 pbuf->last_count_lock = pbuf->count_lock;
6622
6623 BUFFER_INFO xbuf(pbuf);
6624 BUFFER_HEADER xheader = *pbuf->buffer_header;
6625 int client_index = pbuf->client_index;
6626
6627 pbuf_guard.unlock();
6628
6629 bm_write_buffer_statistics_to_odb_copy(hDB, buffer_name.c_str(), client_name.c_str(), client_index, &xbuf, &xheader);
6630}
6631
6632static BUFFER* bm_get_buffer(const char* who, int buffer_handle, int* pstatus)
6633{
6634 size_t sbuffer_handle = buffer_handle;
6635
6636 size_t nbuf = 0;
6637 BUFFER* pbuf = NULL;
6638
6639 gBuffersMutex.lock();
6640
6641 nbuf = gBuffers.size();
6642 if (buffer_handle >=1 && sbuffer_handle <= nbuf) {
6643 pbuf = gBuffers[buffer_handle-1];
6644 }
6645
6646 gBuffersMutex.unlock();
6647
6648 if (sbuffer_handle > nbuf || buffer_handle <= 0) {
6649 if (who)
6650 cm_msg(MERROR, who, "invalid buffer handle %d: out of range [1..%d]", buffer_handle, (int)nbuf);
6651 if (pstatus)
6652 *pstatus = BM_INVALID_HANDLE;
6653 return NULL;
6654 }
6655
6656 if (!pbuf) {
6657 if (who)
6658 cm_msg(MERROR, who, "invalid buffer handle %d: empty slot", buffer_handle);
6659 if (pstatus)
6660 *pstatus = BM_INVALID_HANDLE;
6661 return NULL;
6662 }
6663
6664 if (!pbuf->attached) {
6665 if (who)
6666 cm_msg(MERROR, who, "invalid buffer handle %d: not attached", buffer_handle);
6667 if (pstatus)
6668 *pstatus = BM_INVALID_HANDLE;
6669 return NULL;
6670 }
6671
6672 if (pstatus)
6673 *pstatus = BM_SUCCESS;
6674
6675 return pbuf;
6676}
6677
6678#endif // LOCAL_ROUTINES
6679
6680/********************************************************************/
6727INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle) {
6728 INT status;
6729
6730 if (rpc_is_remote()) {
6731 status = rpc_call(RPC_BM_OPEN_BUFFER, buffer_name, buffer_size, buffer_handle);
6732
6733 HNDLE hDB;
6735 if (status != SUCCESS || hDB == 0) {
6736 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6737 return BM_NO_SHM;
6738 }
6739
6741
6742 int size = sizeof(INT);
6743 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6744
6745 if (status != DB_SUCCESS) {
6746 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6747 status);
6748 return status;
6749 }
6750
6751 return status;
6752 }
6753#ifdef LOCAL_ROUTINES
6754 {
6755 HNDLE shm_handle;
6756 size_t shm_size;
6757 HNDLE hDB;
6758 const int max_buffer_size = 2 * 1000 * 1024 * 1024; // limited by 32-bit integers in the buffer header
6759
6760 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
6761
6762 if (!buffer_name || !buffer_name[0]) {
6763 cm_msg(MERROR, "bm_open_buffer", "cannot open buffer with zero name");
6764 return BM_INVALID_PARAM;
6765 }
6766
6767 if (strlen(buffer_name) >= NAME_LENGTH) {
6768 cm_msg(MERROR, "bm_open_buffer", "buffer name \"%s\" is longer than %d bytes", buffer_name, NAME_LENGTH);
6769 return BM_INVALID_PARAM;
6770 }
6771
6773
6774 if (status != SUCCESS || hDB == 0) {
6775 //cm_msg(MERROR, "bm_open_buffer", "cannot open buffer \'%s\' - not connected to ODB", buffer_name);
6776 return BM_NO_SHM;
6777 }
6778
6779 /* get buffer size from ODB, user parameter as default if not present in ODB */
6780 std::string odb_path;
6781 odb_path += "/Experiment/Buffer sizes/";
6782 odb_path += buffer_name;
6783
6784 int size = sizeof(INT);
6785 status = db_get_value(hDB, 0, odb_path.c_str(), &buffer_size, &size, TID_UINT32, TRUE);
6786
6787 if (buffer_size <= 0 || buffer_size > max_buffer_size) {
6788 cm_msg(MERROR, "bm_open_buffer",
6789 "Cannot open buffer \"%s\", invalid buffer size %d in ODB \"%s\", maximum buffer size is %d",
6790 buffer_name, buffer_size, odb_path.c_str(), max_buffer_size);
6791 return BM_INVALID_PARAM;
6792 }
6793
6795
6796 size = sizeof(INT);
6797 status = db_get_value(hDB, 0, "/Experiment/MAX_EVENT_SIZE", &_bm_max_event_size, &size, TID_UINT32, TRUE);
6798
6799 if (status != DB_SUCCESS) {
6800 cm_msg(MERROR, "bm_open_buffer", "Cannot get ODB /Experiment/MAX_EVENT_SIZE, db_get_value() status %d",
6801 status);
6802 return status;
6803 }
6804
6805 /* check if buffer already is open */
6806 gBuffersMutex.lock();
6807 for (size_t i = 0; i < gBuffers.size(); i++) {
6808 BUFFER* pbuf = gBuffers[i];
6809 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6810 *buffer_handle = i + 1;
6811 gBuffersMutex.unlock();
6812 return BM_SUCCESS;
6813 }
6814 }
6815 gBuffersMutex.unlock();
6816
6817 // only one thread at a time should create new buffers
6818
6819 static std::mutex gNewBufferMutex;
6820 std::lock_guard<std::mutex> guard(gNewBufferMutex);
6821
6822 // if we had a race against another thread
6823 // and while we were waiting for gNewBufferMutex
6824 // the other thread created this buffer, we return it.
6825
6826 gBuffersMutex.lock();
6827 for (size_t i = 0; i < gBuffers.size(); i++) {
6828 BUFFER* pbuf = gBuffers[i];
6829 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
6830 *buffer_handle = i + 1;
6831 gBuffersMutex.unlock();
6832 return BM_SUCCESS;
6833 }
6834 }
6835 gBuffersMutex.unlock();
6836
6837 /* allocate new BUFFER object */
6838
6839 BUFFER* pbuf = new BUFFER;
6840
6841 /* there is no constructor for BUFFER object, we have to zero the arrays manually */
6842
6843 for (int i=0; i<MAX_CLIENTS; i++) {
6844 pbuf->client_count_write_wait[i] = 0;
6845 pbuf->client_time_write_wait[i] = 0;
6846 }
6847
6848 /* create buffer semaphore */
6849
6851
6852 if (status != SS_CREATED && status != SS_SUCCESS) {
6853 *buffer_handle = 0;
6854 delete pbuf;
6855 return BM_NO_SEMAPHORE;
6856 }
6857
6858 std::string client_name = cm_get_client_name();
6859
6860 /* store client name */
6861 mstrlcpy(pbuf->client_name, client_name.c_str(), sizeof(pbuf->client_name));
6862
6863 /* store buffer name */
6864 mstrlcpy(pbuf->buffer_name, buffer_name, sizeof(pbuf->buffer_name));
6865
6866 /* lock buffer semaphore to avoid race with bm_open_buffer() in a different program */
6867
6868 pbuf->attached = true; // required by bm_lock_buffer()
6869
6870 bm_lock_buffer_guard pbuf_guard(pbuf);
6871
6872 if (!pbuf_guard.is_locked()) {
6873 // cannot happen, no other thread can see this pbuf
6874 abort();
6875 return BM_NO_SEMAPHORE;
6876 }
6877
6878 /* open shared memory */
6879
6880 void *p = NULL;
6881 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6882
6883 if (status != SS_SUCCESS && status != SS_CREATED) {
6884 *buffer_handle = 0;
6885 pbuf_guard.unlock();
6886 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6887 delete pbuf;
6888 return BM_NO_SHM;
6889 }
6890
6891 pbuf->buffer_header = (BUFFER_HEADER *) p;
6892
6893 BUFFER_HEADER *pheader = pbuf->buffer_header;
6894
6895 bool shm_created = (status == SS_CREATED);
6896
6897 if (shm_created) {
6898 /* initialize newly created shared memory */
6899
6900 memset(pheader, 0, sizeof(BUFFER_HEADER) + buffer_size);
6901
6902 mstrlcpy(pheader->name, buffer_name, sizeof(pheader->name));
6903 pheader->size = buffer_size;
6904
6905 } else {
6906 /* validate existing shared memory */
6907
6908 if (!equal_ustring(pheader->name, buffer_name)) {
6909 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6910 pbuf_guard.unlock();
6911 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6912 cm_msg(MERROR, "bm_open_buffer",
6913 "Buffer \"%s\" is corrupted, mismatch of buffer name in shared memory \"%s\"", buffer_name,
6914 pheader->name);
6915 *buffer_handle = 0;
6916 delete pbuf;
6917 return BM_CORRUPTED;
6918 }
6919
6920 if ((pheader->num_clients < 0) || (pheader->num_clients > MAX_CLIENTS)) {
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", "Buffer \"%s\" is corrupted, num_clients %d exceeds MAX_CLIENTS %d",
6926 *buffer_handle = 0;
6927 delete pbuf;
6928 return BM_CORRUPTED;
6929 }
6930
6931 if ((pheader->max_client_index < 0) || (pheader->max_client_index > MAX_CLIENTS)) {
6932 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6933 pbuf_guard.unlock();
6934 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6935 cm_msg(MERROR, "bm_open_buffer", "Buffer \"%s\" is corrupted, max_client_index %d exceeds MAX_CLIENTS %d",
6937 *buffer_handle = 0;
6938 delete pbuf;
6939 return BM_CORRUPTED;
6940 }
6941
6942 /* check if buffer size is identical */
6943 if (pheader->size != buffer_size) {
6944 cm_msg(MINFO, "bm_open_buffer", "Buffer \"%s\" requested size %d differs from existing size %d",
6945 buffer_name, buffer_size, pheader->size);
6946
6947 buffer_size = pheader->size;
6948
6949 ss_shm_close(buffer_name, p, shm_size, shm_handle, FALSE);
6950
6951 status = ss_shm_open(buffer_name, sizeof(BUFFER_HEADER) + buffer_size, &p, &shm_size, &shm_handle, FALSE);
6952
6953 if (status != SS_SUCCESS) {
6954 *buffer_handle = 0;
6955 pbuf_guard.unlock();
6956 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6957 delete pbuf;
6958 return BM_NO_SHM;
6959 }
6960
6961 pbuf->buffer_header = (BUFFER_HEADER *) p;
6962 pheader = pbuf->buffer_header;
6963 }
6964 }
6965
6966 /* shared memory is good from here down */
6967
6968 pbuf->attached = true;
6969
6970 pbuf->shm_handle = shm_handle;
6971 pbuf->shm_size = shm_size;
6972 pbuf->callback = FALSE;
6973
6974 bm_cleanup_buffer_locked(pbuf, "bm_open_buffer", ss_millitime());
6975
6977 if (status != BM_SUCCESS) {
6978 cm_msg(MERROR, "bm_open_buffer",
6979 "buffer \'%s\' is corrupted, bm_validate_buffer() status %d, calling bm_reset_buffer()...", buffer_name,
6980 status);
6982 cm_msg(MINFO, "bm_open_buffer", "buffer \'%s\' was reset, all buffered events were lost", buffer_name);
6983 }
6984
6985 /* add our client BUFFER_HEADER */
6986
6987 int iclient = 0;
6988 for (; iclient < MAX_CLIENTS; iclient++)
6989 if (pheader->client[iclient].pid == 0)
6990 break;
6991
6992 if (iclient == MAX_CLIENTS) {
6993 *buffer_handle = 0;
6994 // unlock before calling cm_msg(). if we are SYSMSG, we wil ldeadlock. K.O.
6995 pbuf_guard.unlock();
6996 pbuf_guard.invalidate(); // destructor will see a deleted pbuf
6997 delete pbuf;
6998 cm_msg(MERROR, "bm_open_buffer", "buffer \'%s\' maximum number of clients %d exceeded", buffer_name, MAX_CLIENTS);
6999 return BM_NO_SLOT;
7000 }
7001
7002 /* store slot index in _buffer structure */
7003 pbuf->client_index = iclient;
7004
7005 /*
7006 Save the index of the last client of that buffer so that later only
7007 the clients 0..max_client_index-1 have to be searched through.
7008 */
7009 pheader->num_clients++;
7010 if (iclient + 1 > pheader->max_client_index)
7011 pheader->max_client_index = iclient + 1;
7012
7013 /* setup buffer header and client structure */
7014 BUFFER_CLIENT *pclient = &pheader->client[iclient];
7015
7016 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7017
7018 mstrlcpy(pclient->name, client_name.c_str(), sizeof(pclient->name));
7019
7020 pclient->pid = ss_getpid();
7021
7023
7024 pclient->read_pointer = pheader->write_pointer;
7025 pclient->last_activity = ss_millitime();
7026
7028
7029 pbuf_guard.unlock();
7030
7031 /* shared memory is not locked from here down, do not touch pheader and pbuf->buffer_header! */
7032
7033 pheader = NULL;
7034
7035 /* we are not holding any locks from here down, but other threads cannot see this pbuf yet */
7036
7039
7040 /* add pbuf to buffer list */
7041
7042 gBuffersMutex.lock();
7043
7044 bool added = false;
7045 for (size_t i=0; i<gBuffers.size(); i++) {
7046 if (gBuffers[i] == NULL) {
7047 gBuffers[i] = pbuf;
7048 added = true;
7049 *buffer_handle = i+1;
7050 break;
7051 }
7052 }
7053 if (!added) {
7054 *buffer_handle = gBuffers.size() + 1;
7055 gBuffers.push_back(pbuf);
7056 }
7057
7058 /* from here down we should not touch pbuf without locking it */
7059
7060 pbuf = NULL;
7061
7062 gBuffersMutex.unlock();
7063
7064 /* new buffer is now ready for use */
7065
7066 /* initialize buffer counters */
7067 bm_init_buffer_counters(*buffer_handle);
7068
7069 bm_cleanup("bm_open_buffer", ss_millitime(), FALSE);
7070
7071 if (shm_created)
7072 return BM_CREATED;
7073 }
7074#endif /* LOCAL_ROUTINES */
7075
7076 return BM_SUCCESS;
7077}
7078
7079/********************************************************************/
7085INT bm_get_buffer_handle(const char* buffer_name, INT *buffer_handle)
7086{
7087 gBuffersMutex.lock();
7088 for (size_t i = 0; i < gBuffers.size(); i++) {
7089 BUFFER* pbuf = gBuffers[i];
7090 if (pbuf && pbuf->attached && equal_ustring(pbuf->buffer_name, buffer_name)) {
7091 *buffer_handle = i + 1;
7092 gBuffersMutex.unlock();
7093 return BM_SUCCESS;
7094 }
7095 }
7096 gBuffersMutex.unlock();
7097 return BM_NOT_FOUND;
7098}
7099
7100/********************************************************************/
7106INT bm_close_buffer(INT buffer_handle) {
7107 //printf("bm_close_buffer: handle %d\n", buffer_handle);
7108
7109 if (rpc_is_remote())
7110 return rpc_call(RPC_BM_CLOSE_BUFFER, buffer_handle);
7111
7112#ifdef LOCAL_ROUTINES
7113 {
7114 int status = 0;
7115
7116 BUFFER *pbuf = bm_get_buffer(NULL, buffer_handle, &status);
7117
7118 if (!pbuf)
7119 return status;
7120
7121 //printf("bm_close_buffer: handle %d, name [%s]\n", buffer_handle, pheader->name);
7122
7123 int i;
7124
7125 { /* delete all requests for this buffer */
7126 _request_list_mutex.lock();
7127 std::vector<EventRequest> request_list_copy = _request_list;
7128 _request_list_mutex.unlock();
7129 for (size_t i = 0; i < request_list_copy.size(); i++) {
7130 if (request_list_copy[i].buffer_handle == buffer_handle) {
7132 }
7133 }
7134 }
7135
7136 HNDLE hDB;
7138
7139 if (hDB) {
7140 /* write statistics to odb */
7142 }
7143
7144 /* lock buffer in correct order */
7145
7147
7148 if (status != BM_SUCCESS) {
7149 return status;
7150 }
7151
7153
7154 if (status != BM_SUCCESS) {
7155 pbuf->read_cache_mutex.unlock();
7156 return status;
7157 }
7158
7159 bm_lock_buffer_guard pbuf_guard(pbuf);
7160
7161 if (!pbuf_guard.is_locked()) {
7162 pbuf->write_cache_mutex.unlock();
7163 pbuf->read_cache_mutex.unlock();
7164 return pbuf_guard.get_status();
7165 }
7166
7167 BUFFER_HEADER *pheader = pbuf->buffer_header;
7168
7169 /* mark entry in _buffer as empty */
7170 pbuf->attached = false;
7171
7172 BUFFER_CLIENT* pclient = bm_get_my_client_locked(pbuf_guard);
7173
7174 if (pclient) {
7175 /* clear entry from client structure in buffer header */
7176 memset(pclient, 0, sizeof(BUFFER_CLIENT));
7177 }
7178
7179 /* calculate new max_client_index entry */
7180 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7181 if (pheader->client[i].pid != 0)
7182 break;
7183 pheader->max_client_index = i + 1;
7184
7185 /* count new number of clients */
7186 int j = 0;
7187 for (i = MAX_CLIENTS - 1; i >= 0; i--)
7188 if (pheader->client[i].pid != 0)
7189 j++;
7190 pheader->num_clients = j;
7191
7192 int destroy_flag = (pheader->num_clients == 0);
7193
7194 // we hold the locks on the read cache and the write cache.
7195
7196 /* free cache */
7197 if (pbuf->read_cache_size > 0) {
7198 free(pbuf->read_cache);
7199 pbuf->read_cache = NULL;
7200 pbuf->read_cache_size = 0;
7201 pbuf->read_cache_rp = 0;
7202 pbuf->read_cache_wp = 0;
7203 }
7204
7205 if (pbuf->write_cache_size > 0) {
7206 free(pbuf->write_cache);
7207 pbuf->write_cache = NULL;
7208 pbuf->write_cache_size = 0;
7209 pbuf->write_cache_rp = 0;
7210 pbuf->write_cache_wp = 0;
7211 }
7212
7213 /* check if anyone is waiting and wake him up */
7214
7215 for (int i = 0; i < pheader->max_client_index; i++) {
7216 BUFFER_CLIENT *pclient = pheader->client + i;
7217 if (pclient->pid && (pclient->write_wait || pclient->read_wait))
7218 ss_resume(pclient->port, "B ");
7219 }
7220
7221 /* unmap shared memory, delete it if we are the last */
7222
7223 ss_shm_close(pbuf->buffer_name, pbuf->buffer_header, pbuf->shm_size, pbuf->shm_handle, destroy_flag);
7224
7225 /* after ss_shm_close() these are invalid: */
7226
7227 pheader = NULL;
7228 pbuf->buffer_header = NULL;
7229 pbuf->shm_size = 0;
7230 pbuf->shm_handle = 0;
7231
7232 /* unlock buffer in correct order */
7233
7234 pbuf_guard.unlock();
7235
7236 pbuf->write_cache_mutex.unlock();
7237 pbuf->read_cache_mutex.unlock();
7238
7239 /* delete semaphore */
7240
7241 ss_semaphore_delete(pbuf->semaphore, destroy_flag);
7242 }
7243#endif /* LOCAL_ROUTINES */
7244
7245 return BM_SUCCESS;
7246}
7247
7248/********************************************************************/
7254 if (rpc_is_remote())
7256
7257#ifdef LOCAL_ROUTINES
7258 {
7260
7261 gBuffersMutex.lock();
7262 size_t nbuf = gBuffers.size();
7263 gBuffersMutex.unlock();
7264
7265 for (size_t i = nbuf; i > 0; i--) {
7267 }
7268
7269 gBuffersMutex.lock();
7270 for (size_t i=0; i< gBuffers.size(); i++) {
7271 BUFFER* pbuf = gBuffers[i];
7272 if (!pbuf)
7273 continue;
7274 delete pbuf;
7275 pbuf = NULL;
7276 gBuffers[i] = NULL;
7277 }
7278 gBuffersMutex.unlock();
7279 }
7280#endif /* LOCAL_ROUTINES */
7281
7282 return BM_SUCCESS;
7283}
7284
7285/********************************************************************/
7291#ifdef LOCAL_ROUTINES
7292 {
7293 int status;
7294 HNDLE hDB;
7295
7297
7298 if (status != CM_SUCCESS) {
7299 //printf("bm_write_statistics_to_odb: cannot get ODB handle!\n");
7300 return BM_SUCCESS;
7301 }
7302
7303 std::vector<BUFFER*> mybuffers;
7304
7305 gBuffersMutex.lock();
7306 mybuffers = gBuffers;
7307 gBuffersMutex.unlock();
7308
7309 for (BUFFER* pbuf : mybuffers) {
7310 if (!pbuf || !pbuf->attached)
7311 continue;
7313 }
7314 }
7315#endif /* LOCAL_ROUTINES */
7316
7317 return BM_SUCCESS;
7318}
7319
/* end of bmfunctionc */
7322
7328/*-- Watchdog routines ---------------------------------------------*/
7329#ifdef LOCAL_ROUTINES
7330
7331static std::atomic<bool> _watchdog_thread_run{false}; // set by main thread
7332static std::atomic<bool> _watchdog_thread_is_running{false}; // set by watchdog thread
7333static std::atomic<std::thread*> _watchdog_thread{NULL};
7334
7335/********************************************************************/
7341 //printf("cm_watchdog_thread started!\n");
7342 while (_watchdog_thread_run) {
7343 //printf("cm_watchdog_thread runs!\n");
7344 DWORD now = ss_millitime();
7347 int i;
7348 for (i = 0; i < 20; i++) {
7349 ss_sleep(100);
7351 break;
7352 }
7353 }
7354 //printf("cm_watchdog_thread stopped!\n");
7356 return 0;
7357}
7358
7359static void xcm_watchdog_thread() {
7360 cm_watchdog_thread(NULL);
7361}
7362
7363#endif
7364
7366 /* watchdog does not run inside remote clients.
7367 * watchdog timeout timers are maintained by the mserver */
7368 if (rpc_is_remote())
7369 return CM_SUCCESS;
7370#ifdef LOCAL_ROUTINES
7371 /* only start once */
7372 if (_watchdog_thread)
7373 return CM_SUCCESS;
7374 _watchdog_thread_run = true;
7375 _watchdog_thread.store(new std::thread(xcm_watchdog_thread));
7376#endif
7377 return CM_SUCCESS;
7378}
7379
7381 /* watchdog does not run inside remote clients.
7382 * watchdog timeout timers are maintained by the mserver */
7383 if (rpc_is_remote())
7384 return CM_SUCCESS;
7385#ifdef LOCAL_ROUTINES
7386 _watchdog_thread_run = false;
7388 //printf("waiting for watchdog thread to shut down\n");
7389 ss_sleep(10);
7390 }
7391 if (_watchdog_thread != NULL) {
7392 _watchdog_thread.load()->join();
7393 delete static_cast<std::thread *>(_watchdog_thread);
7394 _watchdog_thread = NULL;
7395 }
7396#endif
7397 return CM_SUCCESS;
7398}
7399
7400/********************************************************************/
7410INT cm_shutdown(const char *name, BOOL bUnique) {
7411 INT status, return_status, i, size;
7412 HNDLE hDB, hKeyClient, hKey, hSubkey, hKeyTmp, hConn;
7413 KEY key;
7414 char client_name[NAME_LENGTH], remote_host[HOST_NAME_LENGTH];
7415 INT port;
7416 DWORD start_time;
7417 DWORD timeout;
7418 DWORD last;
7419
7420 cm_get_experiment_database(&hDB, &hKeyClient);
7421
7422 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7423 if (status != DB_SUCCESS)
7424 return DB_NO_KEY;
7425
7426 return_status = CM_NO_CLIENT;
7427
7428 /* loop over all clients */
7429 for (i = 0;; i++) {
7432 break;
7433
7434 /* don't shutdown ourselves */
7435 if (hSubkey == hKeyClient)
7436 continue;
7437
7438 if (status == DB_SUCCESS) {
7440
7441 /* contact client */
7442 size = sizeof(client_name);
7443 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7444 if (status != DB_SUCCESS)
7445 continue;
7446
7447 if (!bUnique)
7448 client_name[strlen(name)] = 0; /* strip number */
7449
7450 /* check if individual client */
7451 if (!equal_ustring("all", name) && !equal_ustring(client_name, name))
7452 continue;
7453
7454 size = sizeof(port);
7455 db_get_value(hDB, hSubkey, "Server Port", &port, &size, TID_INT32, TRUE);
7456
7457 size = sizeof(remote_host);
7458 db_get_value(hDB, hSubkey, "Host", remote_host, &size, TID_STRING, TRUE);
7459
7460 cm_get_watchdog_info(hDB, name, &timeout, &last);
7461 if (timeout == 0)
7462 timeout = 5000;
7463
7464 /* client found -> connect to its server port */
7465 status = rpc_client_connect(remote_host, port, client_name, &hConn);
7466 if (status != RPC_SUCCESS) {
7467 int client_pid = atoi(key.name);
7468 return_status = CM_NO_CLIENT;
7469 cm_msg(MERROR, "cm_shutdown", "Cannot connect to client \'%s\' on host \'%s\', port %d",
7470 client_name, remote_host, port);
7471#ifdef SIGKILL
7472 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7473 client_pid);
7474 kill(client_pid, SIGKILL);
7475 return_status = CM_SUCCESS;
7476 status = cm_delete_client_info(hDB, client_pid);
7477 if (status != CM_SUCCESS)
7478 cm_msg(MERROR, "cm_shutdown", "Cannot delete client info for client \'%s\', pid %d, status %d",
7479 name, client_pid, status);
7480#endif
7481 } else {
7482 /* call disconnect with shutdown=TRUE */
7484
7485 /* wait until client has shut down */
7486 start_time = ss_millitime();
7487 do {
7488 ss_sleep(100);
7489 status = db_find_key(hDB, hKey, key.name, &hKeyTmp);
7490 } while (status == DB_SUCCESS && (ss_millitime() - start_time < timeout));
7491
7492 if (status == DB_SUCCESS) {
7493 int client_pid = atoi(key.name);
7494 return_status = CM_NO_CLIENT;
7495 cm_msg(MERROR, "cm_shutdown", "Client \'%s\' not responding to shutdown command", client_name);
7496#ifdef SIGKILL
7497 cm_msg(MERROR, "cm_shutdown", "Killing and Deleting client \'%s\' pid %d", client_name,
7498 client_pid);
7499 kill(client_pid, SIGKILL);
7500 status = cm_delete_client_info(hDB, client_pid);
7501 if (status != CM_SUCCESS)
7502 cm_msg(MERROR, "cm_shutdown",
7503 "Cannot delete client info for client \'%s\', pid %d, status %d", name, client_pid,
7504 status);
7505#endif
7506 return_status = CM_NO_CLIENT;
7507 } else {
7508 return_status = CM_SUCCESS;
7509 i--;
7510 }
7511 }
7512 }
7513
7514 /* display any message created during each shutdown */
7516 }
7517
7518 return return_status;
7519}
7520
7521/********************************************************************/
7530INT cm_exist(const char *name, BOOL bUnique) {
7531 INT status, i, size;
7532 HNDLE hDB, hKeyClient, hKey, hSubkey;
7533 char client_name[NAME_LENGTH];
7534
7535 if (rpc_is_remote())
7536 return rpc_call(RPC_CM_EXIST, name, bUnique);
7537
7538 cm_get_experiment_database(&hDB, &hKeyClient);
7539
7540 status = db_find_key(hDB, 0, "System/Clients", &hKey);
7541 if (status != DB_SUCCESS)
7542 return DB_NO_KEY;
7543
7545
7546 /* loop over all clients */
7547 for (i = 0;; i++) {
7550 break;
7551
7552 if (hSubkey == hKeyClient)
7553 continue;
7554
7555 if (status == DB_SUCCESS) {
7556 /* get client name */
7557 size = sizeof(client_name);
7558 status = db_get_value(hDB, hSubkey, "Name", client_name, &size, TID_STRING, FALSE);
7559
7560 if (status != DB_SUCCESS) {
7561 //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);
7562 continue;
7563 }
7564
7565 if (equal_ustring(client_name, name)) {
7567 return CM_SUCCESS;
7568 }
7569
7570 if (!bUnique) {
7571 client_name[strlen(name)] = 0; /* strip number */
7572 if (equal_ustring(client_name, name)) {
7574 return CM_SUCCESS;
7575 }
7576 }
7577 }
7578 }
7579
7581
7582 return CM_NO_CLIENT;
7583}
7584
7585/********************************************************************/
7620INT cm_cleanup(const char *client_name, BOOL ignore_timeout) {
7621 if (rpc_is_remote())
7622 return rpc_call(RPC_CM_CLEANUP, client_name);
7623
7624#ifdef LOCAL_ROUTINES
7625 {
7626 DWORD interval;
7627 DWORD now = ss_millitime();
7628
7629 std::vector<BUFFER*> mybuffers;
7630
7631 gBuffersMutex.lock();
7632 mybuffers = gBuffers;
7633 gBuffersMutex.unlock();
7634
7635 /* check buffers */
7636 for (BUFFER* pbuf : mybuffers) {
7637 if (!pbuf)
7638 continue;
7639 if (pbuf->attached) {
7640 std::string msg;
7641
7642 bm_lock_buffer_guard pbuf_guard(pbuf);
7643
7644 if (!pbuf_guard.is_locked())
7645 continue;
7646
7647 /* update the last_activity entry to show that we are alive */
7648 BUFFER_HEADER *pheader = pbuf->buffer_header;
7649 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7650 pclient->last_activity = ss_millitime();
7651
7652 /* now check other clients */
7653 for (int j = 0; j < pheader->max_client_index; j++) {
7654 BUFFER_CLIENT *pbclient = &pheader->client[j];
7655 if (j != pbuf->client_index && pbclient->pid &&
7656 (client_name == NULL || client_name[0] == 0
7657 || strncmp(pbclient->name, client_name, strlen(client_name)) == 0)) {
7658 if (ignore_timeout)
7659 interval = 2 * WATCHDOG_INTERVAL;
7660 else
7661 interval = pbclient->watchdog_timeout;
7662
7663 /* If client process has no activity, clear its buffer entry. */
7664 if (interval > 0
7665 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7666
7667 /* now make again the check with the buffer locked */
7668 if (interval > 0
7669 && now > pbclient->last_activity && now - pbclient->last_activity > interval) {
7670 msg = msprintf(
7671 "Client \'%s\' on \'%s\' removed by cm_cleanup (idle %1.1lfs, timeout %1.0lfs)",
7672 pbclient->name, pheader->name,
7673 (ss_millitime() - pbclient->last_activity) / 1000.0,
7674 interval / 1000.0);
7675
7676 bm_remove_client_locked(pheader, j);
7677 }
7678
7679 /* go again through whole list */
7680 j = 0;
7681 }
7682 }
7683 }
7684
7685 // unlock buffer before calling cm_msg(), if we are SYSMSG, we will deadlock.
7686 pbuf_guard.unlock();
7687
7688 /* display info message after unlocking buffer */
7689 if (!msg.empty())
7690 cm_msg(MINFO, "cm_cleanup", "%s", msg.c_str());
7691 }
7692 }
7693
7694 db_cleanup2(client_name, ignore_timeout, now, "cm_cleanup");
7695 }
7696#endif /* LOCAL_ROUTINES */
7697
7698 return CM_SUCCESS;
7699}
7700
7701/********************************************************************/
7720std::string cm_expand_env(const char *str) {
7721 const char *s = str;
7722 std::string r;
7723 for (; *s;) {
7724 if (*s == '$') {
7725 s++;
7726 std::string envname;
7727 for (; *s;) {
7728 if (*s == DIR_SEPARATOR)
7729 break;
7730 envname += *s;
7731 s++;
7732 }
7733 const char *e = getenv(envname.c_str());
7734 //printf("expanding [%s] at [%s] envname [%s] value [%s]\n", filename, s, envname.c_str(), e);
7735 if (!e) {
7736 //cm_msg(MERROR, "expand_env", "Env.variable \"%s\" cannot be expanded in \"%s\"", envname.c_str(), filename);
7737 r += '$';
7738 r += envname;
7739 } else {
7740 r += e;
7741 //if (r[r.length()-1] != DIR_SEPARATOR)
7742 //r += DIR_SEPARATOR_STR;
7743 }
7744 } else {
7745 r += *s;
7746 s++;
7747 }
7748 }
7749 return r;
7750}
7751
7752static bool test_cm_expand_env1(const char *str, const char *expected) {
7753 std::string s = cm_expand_env(str);
7754 printf("test_expand_env: [%s] -> [%s] expected [%s]",
7755 str,
7756 s.c_str(),
7757 expected);
7758 if (s != expected) {
7759 printf(", MISMATCH!\n");
7760 return false;
7761 }
7762
7763 printf("\n");
7764 return true;
7765}
7766
7768 printf("Test expand_end()\n");
7769 setenv("FOO", "foo", 1);
7770 setenv("BAR", "bar", 1);
7771 setenv("EMPTY", "", 1);
7772 unsetenv("UNDEF");
7773
7774 bool ok = true;
7775
7776 ok &= test_cm_expand_env1("aaa", "aaa");
7777 ok &= test_cm_expand_env1("$FOO", "foo");
7778 ok &= test_cm_expand_env1("/$FOO", "/foo");
7779 ok &= test_cm_expand_env1("/$FOO/", "/foo/");
7780 ok &= test_cm_expand_env1("$FOO/$BAR", "foo/bar");
7781 ok &= test_cm_expand_env1("$FOO1", "$FOO1");
7782 ok &= test_cm_expand_env1("1$FOO", "1foo");
7783 ok &= test_cm_expand_env1("$UNDEF", "$UNDEF");
7784 ok &= test_cm_expand_env1("/$UNDEF/", "/$UNDEF/");
7785
7786 if (ok) {
7787 printf("test_expand_env: all tests passed!\n");
7788 } else {
7789 printf("test_expand_env: test FAILED!\n");
7790 }
7791}
7792
/* end of cmfunctionc */
7796
7798#ifndef DOXYGEN_SHOULD_SKIP_THIS
7799
7800/********************************************************************/
7801INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
7802/********************************************************************\
7803
7804 Routine: bm_buffer_info
7805
7806 Purpose: Copies the current buffer header referenced by buffer_handle
7807 into the *buffer_header structure which must be supplied
7808 by the calling routine.
7809
7810 Input:
7811 INT buffer_handle Handle of the buffer to get the header from
7812
7813 Output:
7814 BUFFER_HEADER *buffer_header Destination address which gets a copy
7815 of the buffer header structure.
7816
7817 Function value:
7818 BM_SUCCESS Successful completion
7819 BM_INVALID_HANDLE Buffer handle is invalid
7820 RPC_NET_ERROR Network error
7821
7822\********************************************************************/
7823{
7824 if (rpc_is_remote())
7825 return rpc_call(RPC_BM_GET_BUFFER_INFO, buffer_handle, buffer_header);
7826
7827#ifdef LOCAL_ROUTINES
7828
7829 int status = 0;
7830 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_info", buffer_handle, &status);
7831
7832 if (!pbuf)
7833 return status;
7834
7835 bm_lock_buffer_guard pbuf_guard(pbuf);
7836
7837 if (!pbuf_guard.is_locked())
7838 return pbuf_guard.get_status();
7839
7840 memcpy(buffer_header, pbuf->buffer_header, sizeof(BUFFER_HEADER));
7841
7842#endif /* LOCAL_ROUTINES */
7843
7844 return BM_SUCCESS;
7845}
7846
7847/********************************************************************/
7848INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
7849/********************************************************************\
7850
7851 Routine: bm_get_buffer_level
7852
7853 Purpose: Return number of bytes in buffer or in cache
7854
7855 Input:
7856 INT buffer_handle Handle of the buffer to get the info
7857
7858 Output:
7859 INT *n_bytes Number of bytes in buffer
7860
7861 Function value:
7862 BM_SUCCESS Successful completion
7863 BM_INVALID_HANDLE Buffer handle is invalid
7864 RPC_NET_ERROR Network error
7865
7866\********************************************************************/
7867{
7868 if (rpc_is_remote())
7869 return rpc_call(RPC_BM_GET_BUFFER_LEVEL, buffer_handle, n_bytes);
7870
7871#ifdef LOCAL_ROUTINES
7872 {
7873 int status = 0;
7874
7875 BUFFER *pbuf = bm_get_buffer("bm_get_buffer_level", buffer_handle, &status);
7876
7877 if (!pbuf)
7878 return status;
7879
7880 bm_lock_buffer_guard pbuf_guard(pbuf);
7881
7882 if (!pbuf_guard.is_locked())
7883 return pbuf_guard.get_status();
7884
7885 BUFFER_HEADER *pheader = pbuf->buffer_header;
7886
7887 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
7888
7889 *n_bytes = pheader->write_pointer - pclient->read_pointer;
7890 if (*n_bytes < 0)
7891 *n_bytes += pheader->size;
7892
7893 pbuf_guard.unlock();
7894
7895 if (pbuf->read_cache_size) {
7897 if (status == BM_SUCCESS) {
7898 /* add bytes in cache */
7899 if (pbuf->read_cache_wp > pbuf->read_cache_rp)
7900 *n_bytes += pbuf->read_cache_wp - pbuf->read_cache_rp;
7901 pbuf->read_cache_mutex.unlock();
7902 }
7903 }
7904 }
7905#endif /* LOCAL_ROUTINES */
7906
7907 return BM_SUCCESS;
7908}
7909
7910
7911#ifdef LOCAL_ROUTINES
7912
7913/********************************************************************/
7915{
7916 bool locked = ss_timed_mutex_wait_for_sec(pbuf->read_cache_mutex, "buffer read cache", _bm_mutex_timeout_sec);
7917
7918 if (!locked) {
7919 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);
7920 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);
7921 abort();
7922 /* DOES NOT RETURN */
7923 }
7924
7925 if (!pbuf->attached) {
7926 pbuf->read_cache_mutex.unlock();
7927 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);
7928 return BM_INVALID_HANDLE;
7929 }
7930
7931 return BM_SUCCESS;
7932}
7933
7934/********************************************************************/
7936{
7937 bool locked = ss_timed_mutex_wait_for_sec(pbuf->write_cache_mutex, "buffer write cache", _bm_mutex_timeout_sec);
7938
7939 if (!locked) {
7940 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);
7941 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);
7942 abort();
7943 /* DOES NOT RETURN */
7944 }
7945
7946 if (!pbuf->attached) {
7947 pbuf->write_cache_mutex.unlock();
7948 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);
7949 return BM_INVALID_HANDLE;
7950 }
7951
7952 return BM_SUCCESS;
7953}
7954
7955/********************************************************************/
7957{
7958 //printf("bm_lock_buffer_mutex %s!\n", pbuf->buffer_name);
7959
7960 bool locked = ss_timed_mutex_wait_for_sec(pbuf->buffer_mutex, "buffer mutex", _bm_mutex_timeout_sec);
7961
7962 if (!locked) {
7963 fprintf(stderr, "bm_lock_buffer_mutex: Error: Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...\n", pbuf->buffer_name);
7964 cm_msg(MERROR, "bm_lock_buffer_mutex", "Cannot lock buffer \"%s\", ss_timed_mutex_wait_for_sec() timeout, aborting...", pbuf->buffer_name);
7965 abort();
7966 /* DOES NOT RETURN */
7967 }
7968
7969 if (!pbuf->attached) {
7970 pbuf->buffer_mutex.unlock();
7971 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);
7972 return BM_INVALID_HANDLE;
7973 }
7974
7975 //static int counter = 0;
7976 //counter++;
7977 //printf("locked %d!\n", counter);
7978 //if (counter > 50)
7979 // ::sleep(3);
7980
7981 return BM_SUCCESS;
7982}
7983
7984/********************************************************************/
7985static int xbm_lock_buffer(BUFFER *pbuf)
7986{
7987 int status;
7988
7989 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
7990
7991 //if (pbuf->locked) {
7992 // fprintf(stderr, "double lock, abort!\n");
7993 // abort();
7994 //}
7995
7997
7998 if (status != BM_SUCCESS)
7999 return status;
8000
8001 status = ss_semaphore_wait_for(pbuf->semaphore, 1000);
8002
8003 if (status != SS_SUCCESS) {
8004 fprintf(stderr, "bm_lock_buffer: Lock buffer \"%s\" is taking longer than 1 second!\n", pbuf->buffer_name);
8005
8006 status = ss_semaphore_wait_for(pbuf->semaphore, 10000);
8007
8008 if (status != SS_SUCCESS) {
8009 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);
8010
8011 if (pbuf->buffer_header) {
8012 for (int i=0; i<MAX_CLIENTS; i++) {
8013 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);
8014 }
8015 }
8016
8018
8019 if (status != SS_SUCCESS) {
8020 fprintf(stderr, "bm_lock_buffer: Error: Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...\n", pbuf->buffer_name, status);
8021 cm_msg(MERROR, "bm_lock_buffer", "Cannot lock buffer \"%s\", ss_semaphore_wait_for() status %d, aborting...", pbuf->buffer_name, status);
8022 abort();
8023 /* DOES NOT RETURN */
8024 }
8025 }
8026 }
8027
8028 // protect against double lock
8029 assert(!pbuf->locked);
8030 pbuf->locked = TRUE;
8031
8032#if 0
8033 int x = MAX_CLIENTS - 1;
8034 if (pbuf->buffer_header->client[x].unused1 != 0) {
8035 printf("lllock [%s] unused1 %d pid %d\n", pbuf->buffer_name, pbuf->buffer_header->client[x].unused1, getpid());
8036 }
8037 //assert(pbuf->buffer_header->client[x].unused1 == 0);
8038 pbuf->buffer_header->client[x].unused1 = getpid();
8039#endif
8040
8041 pbuf->count_lock++;
8042
8043 return BM_SUCCESS;
8044}
8045
8046/********************************************************************/
8047static void xbm_unlock_buffer(BUFFER *pbuf) {
8048 // NB: locking order: 1st buffer mutex, 2nd buffer semaphore. Unlock in reverse order.
8049
8050#if 0
8051 int x = MAX_CLIENTS-1;
8052 if (pbuf->attached) {
8053 if (pbuf->buffer_header->client[x].unused1 != getpid()) {
8054 printf("unlock [%s] unused1 %d pid %d\n", pbuf->buffer_header->name, pbuf->buffer_header->client[x].unused1, getpid());
8055 }
8056 pbuf->buffer_header->client[x].unused1 = 0;
8057 } else {
8058 printf("unlock [??????] unused1 ????? pid %d\n", getpid());
8059 }
8060#endif
8061
8062 // protect against double unlock
8063 assert(pbuf->locked);
8064 pbuf->locked = FALSE;
8065
8067 pbuf->buffer_mutex.unlock();
8068}
8069
8070#endif /* LOCAL_ROUTINES */
8071
8072/********************************************************************/
8074/********************************************************************\
8075
8076 Routine: bm_init_event_counters
8077
8078 Purpose: Initialize counters for a specific buffer. This routine
8079 should be called at the beginning of a run.
8080
8081 Input:
8082 INT buffer_handle Handle to the buffer to be
8083 initialized.
8084 Output:
8085 none
8086
8087 Function value:
8088 BM_SUCCESS Successful completion
8089 BM_INVALID_HANDLE Buffer handle is invalid
8090
8091\********************************************************************/
8092{
8093 if (rpc_is_remote())
8094 return rpc_call(RPC_BM_INIT_BUFFER_COUNTERS, buffer_handle);
8095
8096#ifdef LOCAL_ROUTINES
8097
8098 int status = 0;
8099
8100 BUFFER* pbuf = bm_get_buffer("bm_init_buffer_counters", buffer_handle, &status);
8101
8102 if (!pbuf)
8103 return status;
8104
8105 bm_lock_buffer_guard pbuf_guard(pbuf);
8106
8107 if (!pbuf_guard.is_locked())
8108 return pbuf_guard.get_status();
8109
8110 pbuf->buffer_header->num_in_events = 0;
8111 pbuf->buffer_header->num_out_events = 0;
8112
8113#endif /* LOCAL_ROUTINES */
8114
8115 return BM_SUCCESS;
8116}
8117
8119#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8120
8126/********************************************************************/
8150INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
8151/*------------------------------------------------------------------*/
8152{
8153 if (rpc_is_remote())
8154 return rpc_call(RPC_BM_SET_CACHE_SIZE, buffer_handle, read_size, write_size);
8155
8156#ifdef LOCAL_ROUTINES
8157 {
8158 int status = 0;
8159
8160 BUFFER *pbuf = bm_get_buffer("bm_set_cache_size", buffer_handle, &status);
8161
8162 if (!pbuf)
8163 return status;
8164
8165 /* lock pbuf for local access. we do not lock buffer semaphore because we do not touch the shared memory */
8166
8168
8169 if (status != BM_SUCCESS)
8170 return status;
8171
8172 if (write_size < 0)
8173 write_size = 0;
8174
8175 if (write_size > 0) {
8176 if (write_size < MIN_WRITE_CACHE_SIZE) {
8177 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);
8178 write_size = MIN_WRITE_CACHE_SIZE;
8179 }
8180 }
8181
8182 size_t max_write_size = pbuf->buffer_header->size/MAX_WRITE_CACHE_SIZE_DIV;
8183
8184 if (write_size > max_write_size) {
8185 size_t new_write_size = max_write_size;
8186 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);
8187 write_size = new_write_size;
8188 }
8189
8190 pbuf->buffer_mutex.unlock();
8191
8192 /* resize read cache */
8193
8195
8196 if (status != BM_SUCCESS) {
8197 return status;
8198 }
8199
8200 if (pbuf->read_cache_size > 0) {
8201 free(pbuf->read_cache);
8202 pbuf->read_cache = NULL;
8203 }
8204
8205 if (read_size > 0) {
8206 pbuf->read_cache = (char *) malloc(read_size);
8207 if (pbuf->read_cache == NULL) {
8208 pbuf->read_cache_size = 0;
8209 pbuf->read_cache_rp = 0;
8210 pbuf->read_cache_wp = 0;
8211 pbuf->read_cache_mutex.unlock();
8212 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);
8213 return BM_NO_MEMORY;
8214 }
8215 }
8216
8217 pbuf->read_cache_size = read_size;
8218 pbuf->read_cache_rp = 0;
8219 pbuf->read_cache_wp = 0;
8220
8221 pbuf->read_cache_mutex.unlock();
8222
8223 /* resize the write cache */
8224
8226
8227 if (status != BM_SUCCESS)
8228 return status;
8229
8230 // FIXME: should flush the write cache!
8231 if (pbuf->write_cache_size && pbuf->write_cache_wp > 0) {
8232 cm_msg(MERROR, "bm_set_cache_size", "buffer \"%s\" lost %zu bytes from the write cache", pbuf->buffer_name, pbuf->write_cache_wp);
8233 }
8234
8235 /* manage write cache */
8236 if (pbuf->write_cache_size > 0) {
8237 free(pbuf->write_cache);
8238 pbuf->write_cache = NULL;
8239 }
8240
8241 if (write_size > 0) {
8242 pbuf->write_cache = (char *) M_MALLOC(write_size);
8243 if (pbuf->write_cache == NULL) {
8244 pbuf->write_cache_size = 0;
8245 pbuf->write_cache_rp = 0;
8246 pbuf->write_cache_wp = 0;
8247 pbuf->write_cache_mutex.unlock();
8248 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);
8249 return BM_NO_MEMORY;
8250 }
8251 }
8252
8253 pbuf->write_cache_size = write_size;
8254 pbuf->write_cache_rp = 0;
8255 pbuf->write_cache_wp = 0;
8256
8257 pbuf->write_cache_mutex.unlock();
8258 }
8259#endif /* LOCAL_ROUTINES */
8260
8261 return BM_SUCCESS;
8262}
8263
8264/********************************************************************/
8291INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
8292{
8293 event_header->event_id = event_id;
8294 event_header->trigger_mask = trigger_mask;
8295 event_header->data_size = data_size;
8296 event_header->time_stamp = ss_time();
8297 event_header->serial_number = serial;
8298
8299 return BM_SUCCESS;
8300}
8301
8302INT bm_compose_event_threadsafe(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD *serial)
8303{
8304 static std::mutex mutex;
8305
8306 event_header->event_id = event_id;
8307 event_header->trigger_mask = trigger_mask;
8308 event_header->data_size = data_size;
8309 event_header->time_stamp = ss_time();
8310 {
8311 std::lock_guard<std::mutex> lock(mutex);
8312 event_header->serial_number = *serial;
8313 *serial = *serial + 1;
8314 // implicit unlock
8315 }
8316
8317 return BM_SUCCESS;
8318}
8319
8321#ifndef DOXYGEN_SHOULD_SKIP_THIS
8322
8323/********************************************************************/
8324INT bm_add_event_request(INT buffer_handle, short int event_id,
8325 short int trigger_mask,
8326 INT sampling_type,
8327 EVENT_HANDLER *func,
8328 INT request_id)
8329/********************************************************************\
8330
8331 Routine: bm_add_event_request
8332
8333 Purpose: Place a request for a specific event type in the client
8334 structure of the buffer refereced by buffer_handle.
8335
8336 Input:
8337 INT buffer_handle Handle to the buffer where the re-
8338 quest should be placed in
8339
8340 short int event_id Event ID \
8341 short int trigger_mask Trigger mask / Event specification
8342
8343 INT sampling_type One of GET_ALL, GET_NONBLOCKING or GET_RECENT
8344
8345
8346 Note: to request all types of events, use
8347 event_id = 0 (all others should be !=0 !)
8348 trigger_mask = TRIGGER_ALL
8349 sampling_typ = GET_ALL
8350
8351
8352 void *func Callback function
8353 INT request_id Request id (unique number assigned
8354 by bm_request_event)
8355
8356 Output:
8357 none
8358
8359 Function value:
8360 BM_SUCCESS Successful completion
8361 BM_NO_MEMORY Too much request. MAX_EVENT_REQUESTS in
8362 MIDAS.H should be increased.
8363 BM_INVALID_HANDLE Buffer handle is invalid
8364 BM_INVALID_PARAM GET_RECENT is used with non-zero cache size
8365 RPC_NET_ERROR Network error
8366
8367\********************************************************************/
8368{
8369 if (rpc_is_remote())
8370 return rpc_call(RPC_BM_ADD_EVENT_REQUEST, buffer_handle, event_id,
8371 trigger_mask, sampling_type, (INT) (POINTER_T) func, request_id);
8372
8373#ifdef LOCAL_ROUTINES
8374 {
8375 int status = 0;
8376
8377 BUFFER *pbuf = bm_get_buffer("bm_add_event_request", buffer_handle, &status);
8378
8379 if (!pbuf)
8380 return status;
8381
8382 /* lock buffer */
8383 bm_lock_buffer_guard pbuf_guard(pbuf);
8384
8385 if (!pbuf_guard.is_locked())
8386 return pbuf_guard.get_status();
8387
8388 /* avoid callback/non callback requests */
8389 if (func == NULL && pbuf->callback) {
8390 pbuf_guard.unlock(); // unlock before cm_msg()
8391 cm_msg(MERROR, "bm_add_event_request", "mixing callback/non callback requests not possible");
8392 return BM_INVALID_MIXING;
8393 }
8394
8395 /* do not allow GET_RECENT with nonzero cache size */
8396 if (sampling_type == GET_RECENT && pbuf->read_cache_size > 0) {
8397 pbuf_guard.unlock(); // unlock before cm_msg()
8398 cm_msg(MERROR, "bm_add_event_request", "GET_RECENT request not possible if read cache is enabled");
8399 return BM_INVALID_PARAM;
8400 }
8401
8402 /* get a pointer to the proper client structure */
8403 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8404
8405 /* look for a empty request entry */
8406 int i;
8407 for (i = 0; i < MAX_EVENT_REQUESTS; i++)
8408 if (!pclient->event_request[i].valid)
8409 break;
8410
8411 if (i == MAX_EVENT_REQUESTS) {
8412 // implicit unlock
8413 return BM_NO_MEMORY;
8414 }
8415
8416 /* setup event_request structure */
8417 pclient->event_request[i].id = request_id;
8418 pclient->event_request[i].valid = TRUE;
8419 pclient->event_request[i].event_id = event_id;
8421 pclient->event_request[i].sampling_type = sampling_type;
8422
8423 pclient->all_flag = pclient->all_flag || (sampling_type & GET_ALL);
8424
8425 pbuf->get_all_flag = pclient->all_flag;
8426
8427 /* set callback flag in buffer structure */
8428 if (func != NULL)
8429 pbuf->callback = TRUE;
8430
8431 /*
8432 Save the index of the last request in the list so that later only the
8433 requests 0..max_request_index-1 have to be searched through.
8434 */
8435
8436 if (i + 1 > pclient->max_request_index)
8437 pclient->max_request_index = i + 1;
8438 }
8439#endif /* LOCAL_ROUTINES */
8440
8441 return BM_SUCCESS;
8442}
8443
8445#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8446
8447/********************************************************************/
8475INT bm_request_event(HNDLE buffer_handle, short int event_id,
8476 short int trigger_mask,
8477 INT sampling_type, HNDLE *request_id,
8478 EVENT_HANDLER *func)
8479{
8480 assert(request_id != NULL);
8481
8482 EventRequest r;
8483 r.buffer_handle = buffer_handle;
8484 r.event_id = event_id;
8486 r.dispatcher = func;
8487
8488 {
8489 std::lock_guard<std::mutex> guard(_request_list_mutex);
8490
8491 bool found = false;
8492
8493 // find deleted entry
8494 for (size_t i = 0; i < _request_list.size(); i++) {
8495 if (_request_list[i].buffer_handle == 0) {
8496 _request_list[i] = r;
8497 *request_id = i;
8498 found = true;
8499 break;
8500 }
8501 }
8502
8503 if (!found) { // not found
8504 *request_id = _request_list.size();
8505 _request_list.push_back(r);
8506 }
8507
8508 // implicit unlock()
8509 }
8510
8511 /* add request in buffer structure */
8512 int status = bm_add_event_request(buffer_handle, event_id, trigger_mask, sampling_type, func, *request_id);
8513 if (status != BM_SUCCESS)
8514 return status;
8515
8516 return BM_SUCCESS;
8517}
8518
8519/********************************************************************/
8528INT bm_remove_event_request(INT buffer_handle, INT request_id) {
8529 if (rpc_is_remote())
8530 return rpc_call(RPC_BM_REMOVE_EVENT_REQUEST, buffer_handle, request_id);
8531
8532#ifdef LOCAL_ROUTINES
8533 {
8534 int status = 0;
8535
8536 BUFFER *pbuf = bm_get_buffer("bm_remove_event_request", buffer_handle, &status);
8537
8538 if (!pbuf)
8539 return status;
8540
8541 /* lock buffer */
8542 bm_lock_buffer_guard pbuf_guard(pbuf);
8543
8544 if (!pbuf_guard.is_locked())
8545 return pbuf_guard.get_status();
8546
8547 INT i, deleted;
8548
8549 /* get a pointer to the proper client structure */
8550 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
8551
8552 /* check all requests and set to zero if matching */
8553 for (i = 0, deleted = 0; i < pclient->max_request_index; i++)
8554 if (pclient->event_request[i].valid && pclient->event_request[i].id == request_id) {
8555 memset(&pclient->event_request[i], 0, sizeof(EVENT_REQUEST));
8556 deleted++;
8557 }
8558
8559 /* calculate new max_request_index entry */
8560 for (i = MAX_EVENT_REQUESTS - 1; i >= 0; i--)
8561 if (pclient->event_request[i].valid)
8562 break;
8563
8564 pclient->max_request_index = i + 1;
8565
8566 /* calculate new all_flag */
8567 pclient->all_flag = FALSE;
8568
8569 for (i = 0; i < pclient->max_request_index; i++)
8570 if (pclient->event_request[i].valid && (pclient->event_request[i].sampling_type & GET_ALL)) {
8571 pclient->all_flag = TRUE;
8572 break;
8573 }
8574
8575 pbuf->get_all_flag = pclient->all_flag;
8576
8577 if (!deleted)
8578 return BM_NOT_FOUND;
8579 }
8580#endif /* LOCAL_ROUTINES */
8581
8582 return BM_SUCCESS;
8583}
8584
8585/********************************************************************/
8595{
8596 _request_list_mutex.lock();
8597
8598 if (request_id < 0 || size_t(request_id) >= _request_list.size()) {
8599 _request_list_mutex.unlock();
8600 return BM_INVALID_HANDLE;
8601 }
8602
8603 int buffer_handle = _request_list[request_id].buffer_handle;
8604
8605 _request_list[request_id].clear();
8606
8607 _request_list_mutex.unlock();
8608
8609 /* remove request entry from buffer */
8610 return bm_remove_event_request(buffer_handle, request_id);
8611}
8612
8613#if 0 // currently not used
8614static void bm_show_pointers(const BUFFER_HEADER * pheader)
8615{
8616 int i;
8617 const BUFFER_CLIENT *pclient;
8618
8619 pclient = pheader->client;
8620
8621 printf("buffer \'%s\', rptr: %d, wptr: %d, size: %d\n", pheader->name, pheader->read_pointer,
8622 pheader->write_pointer, pheader->size);
8623 for (i = 0; i < pheader->max_client_index; i++)
8624 if (pclient[i].pid) {
8625 printf("pointers: client %d \'%s\', rptr %d\n", i, pclient[i].name, pclient[i].read_pointer);
8626 }
8627
8628 printf("done\n");
8629}
8630#endif
8631
8633 assert(pheader->read_pointer >= 0 && pheader->read_pointer <= pheader->size);
8634 assert(pclient->read_pointer >= 0 && pclient->read_pointer <= pheader->size);
8635
8636 if (pheader->read_pointer <= pheader->write_pointer) {
8637
8638 if (pclient->read_pointer < pheader->read_pointer) {
8639 cm_msg(MINFO, "bm_validate_client_pointers",
8640 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8641 pclient->name,
8642 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8643
8644 pclient->read_pointer = pheader->read_pointer;
8645 }
8646
8647 if (pclient->read_pointer > pheader->write_pointer) {
8648 cm_msg(MINFO, "bm_validate_client_pointers",
8649 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, read pointer %d, size %d",
8650 pclient->name,
8651 pheader->name, pclient->read_pointer, pheader->write_pointer, pheader->read_pointer, pheader->size);
8652
8653 pclient->read_pointer = pheader->write_pointer;
8654 }
8655
8656 } else {
8657
8658 if (pclient->read_pointer < 0) {
8659 cm_msg(MINFO, "bm_validate_client_pointers",
8660 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8661 pclient->name,
8662 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8663
8664 pclient->read_pointer = pheader->read_pointer;
8665 }
8666
8667 if (pclient->read_pointer >= pheader->size) {
8668 cm_msg(MINFO, "bm_validate_client_pointers",
8669 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8670 pclient->name,
8671 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8672
8673 pclient->read_pointer = pheader->read_pointer;
8674 }
8675
8676 if (pclient->read_pointer > pheader->write_pointer && pclient->read_pointer < pheader->read_pointer) {
8677 cm_msg(MINFO, "bm_validate_client_pointers",
8678 "Corrected read pointer for client \'%s\' on buffer \'%s\' from %d to %d, write pointer %d, size %d",
8679 pclient->name,
8680 pheader->name, pclient->read_pointer, pheader->read_pointer, pheader->write_pointer, pheader->size);
8681
8682 pclient->read_pointer = pheader->read_pointer;
8683 }
8684 }
8685}
8686
8687#if 0 // currently not used
8688static void bm_validate_pointers(BUFFER_HEADER * pheader)
8689{
8690 BUFFER_CLIENT *pclient = pheader->client;
8691 int i;
8692
8693 for (i = 0; i < pheader->max_client_index; i++)
8694 if (pclient[i].pid) {
8695 bm_validate_client_pointers(pheader, &pclient[i]);
8696 }
8697}
8698#endif
8699
8700//
8701// Buffer pointers
8702//
8703// normal:
8704//
8705// zero -->
8706// ... free space
8707// read_pointer -->
8708// client1 rp -->
8709// client2 rp -->
8710// ... buffered data
8711// write_pointer -->
8712// ... free space
8713// pheader->size -->
8714//
8715// inverted:
8716//
8717// zero -->
8718// client3 rp -->
8719// ... buffered data
8720// client4 rp -->
8721// write_pointer -->
8722// ... free space
8723// read_pointer -->
8724// client1 rp -->
8725// client2 rp -->
8726// ... buffered data
8727// pheader->size -->
8728//
8729
8730static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader) {
8731 assert(caller_name);
8732
8733 /* calculate global read pointer as "minimum" of client read pointers */
8734 int min_rp = pheader->write_pointer;
8735
8736 int i;
8737 for (i = 0; i < pheader->max_client_index; i++) {
8738 BUFFER_CLIENT *pc = pheader->client + i;
8739 if (pc->pid) {
8741
8742#if 0
8743 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, min_rp %d, client [%s] rp %d\n",
8744 pheader->name,
8745 pheader->read_pointer,
8746 pheader->write_pointer,
8747 pheader->size,
8748 min_rp,
8749 pc->name,
8750 pc->read_pointer);
8751#endif
8752
8753 if (pheader->read_pointer <= pheader->write_pointer) {
8754 // normal pointers
8755 if (pc->read_pointer < min_rp)
8756 min_rp = pc->read_pointer;
8757 } else {
8758 // inverted pointers
8759 if (pc->read_pointer <= pheader->write_pointer) {
8760 // clients 3 and 4
8761 if (pc->read_pointer < min_rp)
8762 min_rp = pc->read_pointer;
8763 } else {
8764 // clients 1 and 2
8765 int xptr = pc->read_pointer - pheader->size;
8766 if (xptr < min_rp)
8767 min_rp = xptr;
8768 }
8769 }
8770 }
8771 }
8772
8773 if (min_rp < 0)
8774 min_rp += pheader->size;
8775
8776 assert(min_rp >= 0);
8777 assert(min_rp < pheader->size);
8778
8779 if (min_rp == pheader->read_pointer) {
8780 return FALSE;
8781 }
8782
8783#if 0
8784 printf("bm_update_read_pointer: [%s] rp %d, wp %d, size %d, new_rp %d, moved\n",
8785 pheader->name,
8786 pheader->read_pointer,
8787 pheader->write_pointer,
8788 pheader->size,
8789 min_rp);
8790#endif
8791
8792 pheader->read_pointer = min_rp;
8793
8794 return TRUE;
8795}
8796
8797static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc) {
8798 int i;
8799 int have_get_all_requests = 0;
8800
8801 for (i = 0; i < pc->max_request_index; i++)
8802 if (pc->event_request[i].valid)
8803 have_get_all_requests |= (pc->event_request[i].sampling_type == GET_ALL);
8804
8805 /* only GET_ALL requests actually free space in the event buffer */
8806 if (!have_get_all_requests)
8807 return;
8808
8809 /*
8810 If read pointer has been changed, it may have freed up some space
8811 for waiting producers. So check if free space is now more than 50%
8812 of the buffer size and wake waiting producers.
8813 */
8814
8815 int free_space = pc->read_pointer - pheader->write_pointer;
8816 if (free_space <= 0)
8817 free_space += pheader->size;
8818
8819 if (free_space >= pheader->size * 0.5) {
8820 for (i = 0; i < pheader->max_client_index; i++) {
8821 const BUFFER_CLIENT *pc = pheader->client + i;
8822 if (pc->pid && pc->write_wait) {
8823 BOOL send_wakeup = (pc->write_wait < free_space);
8824 //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);
8825 if (send_wakeup) {
8826 ss_resume(pc->port, "B ");
8827 }
8828 }
8829 }
8830 }
8831}
8832
8833static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
8834{
8835 _request_list_mutex.lock();
8836 bool locked = true;
8837 size_t n = _request_list.size();
8838 /* call dispatcher */
8839 for (size_t i = 0; i < n; i++) {
8840 if (!locked) {
8841 _request_list_mutex.lock();
8842 locked = true;
8843 }
8845 if (r.buffer_handle != buffer_handle)
8846 continue;
8847 if (!bm_match_event(r.event_id, r.trigger_mask, pevent))
8848 continue;
8849 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
8850 _request_list_mutex.unlock();
8851 locked = false;
8852 /* if event is fragmented, call defragmenter */
8853 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))) {
8854 bm_defragment_event(buffer_handle, i, pevent, (void *) (pevent + 1), r.dispatcher);
8855 } else {
8856 r.dispatcher(buffer_handle, i, pevent, (void *) (pevent + 1));
8857 }
8858 }
8859 if (locked)
8860 _request_list_mutex.unlock();
8861}
8862
8863#ifdef LOCAL_ROUTINES
8864
8865static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size) {
8866 /* increment read cache read pointer */
8867 pbuf->read_cache_rp += total_size;
8868
8869 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
8870 pbuf->read_cache_rp = 0;
8871 pbuf->read_cache_wp = 0;
8872 }
8873}
8874
8875static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8876{
8877 if (pbuf->read_cache_rp == pbuf->read_cache_wp)
8878 return FALSE;
8879
8880 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbuf->read_cache + pbuf->read_cache_rp);
8881 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8882 int total_size = ALIGN8(event_size);
8883
8884 if (ppevent)
8885 *ppevent = pevent;
8886 if (pevent_size)
8887 *pevent_size = event_size;
8888 if (ptotal_size)
8889 *ptotal_size = total_size;
8890
8891 return TRUE;
8892}
8893
8894//
8895// return values:
8896// BM_SUCCESS - have an event, fill ppevent, ppevent_size & co
8897// BM_ASYNC_RETURN - buffer is empty
8898// BM_CORRUPTED - buffer is corrupted
8899//
8900
8901static int bm_peek_buffer_locked(BUFFER *pbuf, BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
8902{
8903 if (pc->read_pointer == pheader->write_pointer) {
8904 /* no more events buffered for this client */
8905 if (!pc->read_wait) {
8906 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], set read_wait!\n", pheader->name, pc->name);
8907 pc->read_wait = TRUE;
8908 }
8909 return BM_ASYNC_RETURN;
8910 }
8911
8912 if (pc->read_wait) {
8913 //printf("bm_peek_buffer_locked: buffer [%s] client [%s], clear read_wait!\n", pheader->name, pc->name);
8914 pc->read_wait = FALSE;
8915 }
8916
8917 if ((pc->read_pointer < 0) || (pc->read_pointer >= pheader->size)) {
8918 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);
8919 return BM_CORRUPTED;
8920 }
8921
8922 char *pdata = (char *) (pheader + 1);
8923
8924 EVENT_HEADER *pevent = (EVENT_HEADER *) (pdata + pc->read_pointer);
8925 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
8926 int total_size = ALIGN8(event_size);
8927
8928 if ((total_size <= 0) || (total_size > pheader->size)) {
8929 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);
8930 return BM_CORRUPTED;
8931 }
8932
8933 assert(total_size > 0);
8934 assert(total_size <= pheader->size);
8935
8936 if (ppevent)
8937 *ppevent = pevent;
8938 if (pevent_size)
8939 *pevent_size = event_size;
8940 if (ptotal_size)
8941 *ptotal_size = total_size;
8942
8943 return BM_SUCCESS;
8944}
8945
8946static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
8947{
8948 const char *pdata = (const char *) (pheader + 1);
8949
8950 if (rp + event_size <= pheader->size) {
8951 /* copy event to cache */
8952 memcpy(buf, pdata + rp, event_size);
8953 } else {
8954 /* event is splitted */
8955 int size = pheader->size - rp;
8956 memcpy(buf, pdata + rp, size);
8957 memcpy(buf + size, pdata, event_size - size);
8958 }
8959}
8960
8961static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, std::vector<char> *vecptr, int event_size)
8962{
8963 const char *pdata = (const char *) (pheader + 1);
8964
8965 if (rp + event_size <= pheader->size) {
8966 /* copy event to cache */
8967 vecptr->assign(pdata + rp, pdata + rp + event_size);
8968 } else {
8969 /* event is splitted */
8970 int size = pheader->size - rp;
8971 vecptr->assign(pdata + rp, pdata + rp + size);
8972 vecptr->insert(vecptr->end(), pdata, pdata + event_size - size);
8973 }
8974}
8975
8976static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent) {
8977
8978 BOOL is_requested = FALSE;
8979 int i;
8980 for (i = 0; i < pc->max_request_index; i++) {
8981 const EVENT_REQUEST *prequest = pc->event_request + i;
8982 if (prequest->valid) {
8983 if (bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
8984 /* check if this is a recent event */
8985 if (prequest->sampling_type == GET_RECENT) {
8986 if (ss_time() - pevent->time_stamp > 1) {
8987 /* skip that event */
8988 continue;
8989 }
8990 }
8991
8992 is_requested = TRUE;
8993 break;
8994 }
8995 }
8996 }
8997 return is_requested;
8998}
8999
9000static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache);
9001
9002static int bm_fill_read_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
9003{
9004 BUFFER* pbuf = pbuf_guard.get_pbuf();
9005 BUFFER_HEADER* pheader = pbuf->buffer_header;
9006 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9007 BOOL need_wakeup = FALSE;
9008
9009 //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);
9010
9011 /* loop over all events in the buffer */
9012
9013 while (1) {
9014 EVENT_HEADER *pevent = NULL;
9015 int event_size = 3; // poison value
9016 int total_size = 3; // poison value
9017
9018 int status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
9019 if (status == BM_CORRUPTED) {
9020 return status;
9021 } else if (status != BM_SUCCESS) {
9022 /* event buffer is empty */
9023 if (timeout_msec == BM_NO_WAIT) {
9024 if (need_wakeup)
9025 bm_wakeup_producers_locked(pheader, pc);
9026 if (pbuf->read_cache_rp == pbuf->read_cache_wp) {
9027 // read cache is empty
9028 return BM_ASYNC_RETURN;
9029 }
9030 return BM_SUCCESS;
9031 }
9032
9033 int status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, TRUE);
9034
9035 if (status != BM_SUCCESS) {
9036 // we only come here with SS_ABORT & co
9037 return status;
9038 }
9039
9040 // make sure we wait for new event only once
9041 timeout_msec = BM_NO_WAIT;
9042 // go back to bm_peek_buffer_locked
9043 continue;
9044 }
9045
9046 /* loop over all requests: if this event matches a request,
9047 * copy it to the read cache */
9048
9049 BOOL is_requested = bm_check_requests(pc, pevent);
9050
9051 if (is_requested) {
9052 if (pbuf->read_cache_wp + total_size > pbuf->read_cache_size) {
9053 /* read cache is full */
9054 if (need_wakeup)
9055 bm_wakeup_producers_locked(pheader, pc);
9056 return BM_SUCCESS;
9057 }
9058
9060
9061 pbuf->read_cache_wp += total_size;
9062
9063 /* update statistics */
9064 pheader->num_out_events++;
9065 pbuf->count_read++;
9066 pbuf->bytes_read += event_size;
9067 }
9068
9069 /* shift read pointer */
9070
9071 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9072 pc->read_pointer = new_read_pointer;
9073
9074 need_wakeup = TRUE;
9075 }
9076 /* NOT REACHED */
9077}
9078
9079static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags) {
9080 /* now convert event header */
9081 if (convert_flags) {
9082 rpc_convert_single(&pevent->event_id, TID_INT16, RPC_OUTGOING, convert_flags);
9083 rpc_convert_single(&pevent->trigger_mask, TID_INT16, RPC_OUTGOING, convert_flags);
9084 rpc_convert_single(&pevent->serial_number, TID_UINT32, RPC_OUTGOING, convert_flags);
9085 rpc_convert_single(&pevent->time_stamp, TID_UINT32, RPC_OUTGOING, convert_flags);
9086 rpc_convert_single(&pevent->data_size, TID_UINT32, RPC_OUTGOING, convert_flags);
9087 }
9088}
9089
9090static int bm_wait_for_free_space_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec, int requested_space, bool unlock_write_cache)
9091{
9092 // return values:
9093 // BM_SUCCESS - have "requested_space" bytes free in the buffer
9094 // BM_CORRUPTED - shared memory is corrupted
9095 // BM_NO_MEMORY - asked for more than buffer size
9096 // BM_ASYNC_RETURN - timeout waiting for free space
9097 // BM_INVALID_HANDLE - buffer was closed (locks released) (via bm_clock_xxx())
9098 // SS_ABORT - we are told to shutdown (locks releases)
9099
9100 int status;
9101 BUFFER* pbuf = pbuf_guard.get_pbuf();
9102 BUFFER_HEADER *pheader = pbuf->buffer_header;
9103 char *pdata = (char *) (pheader + 1);
9104
9105 /* make sure the buffer never completely full:
9106 * read pointer and write pointer would coincide
9107 * and the code cannot tell if it means the
9108 * buffer is 100% full or 100% empty. It will explode
9109 * or lose events */
9110 requested_space += 100;
9111
9112 if (requested_space >= pheader->size)
9113 return BM_NO_MEMORY;
9114
9115 DWORD time_start = ss_millitime();
9116 DWORD time_end = time_start + timeout_msec;
9117
9118 //DWORD blocking_time = 0;
9119 //int blocking_loops = 0;
9120 int blocking_client_index = -1;
9121 char blocking_client_name[NAME_LENGTH];
9122 blocking_client_name[0] = 0;
9123
9124 while (1) {
9125 while (1) {
9126 /* check if enough space in buffer */
9127
9128 int free = pheader->read_pointer - pheader->write_pointer;
9129 if (free <= 0)
9130 free += pheader->size;
9131
9132 //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);
9133
9134 if (requested_space < free) { /* note the '<' to avoid 100% filling */
9135 //if (blocking_loops) {
9136 // DWORD wait_time = ss_millitime() - blocking_time;
9137 // printf("blocking client \"%s\", time %d ms, loops %d\n", blocking_client_name, wait_time, blocking_loops);
9138 //}
9139
9140 if (pbuf->wait_start_time != 0) {
9141 DWORD now = ss_millitime();
9142 DWORD wait_time = now - pbuf->wait_start_time;
9143 pbuf->time_write_wait += wait_time;
9144 pbuf->wait_start_time = 0;
9145 int iclient = pbuf->wait_client_index;
9146 //printf("bm_wait_for_free_space: wait ended: wait time %d ms, blocking client index %d\n", wait_time, iclient);
9147 if (iclient >= 0 && iclient < MAX_CLIENTS) {
9148 pbuf->client_count_write_wait[iclient] += 1;
9149 pbuf->client_time_write_wait[iclient] += wait_time;
9150 }
9151 }
9152
9153 //if (blocking_loops > 0) {
9154 // 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);
9155 //}
9156
9157 return BM_SUCCESS;
9158 }
9159
9160 if (!bm_validate_rp("bm_wait_for_free_space_locked", pheader, pheader->read_pointer)) {
9161 cm_msg(MERROR, "bm_wait_for_free_space",
9162 "error: buffer \"%s\" is corrupted: read_pointer %d, write_pointer %d, size %d, free %d, waiting for %d bytes: read pointer is invalid",
9163 pheader->name,
9164 pheader->read_pointer,
9165 pheader->write_pointer,
9166 pheader->size,
9167 free,
9168 requested_space);
9169 return BM_CORRUPTED;
9170 }
9171
9172 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pdata + pheader->read_pointer);
9173 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
9174 int total_size = ALIGN8(event_size);
9175
9176#if 0
9177 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);
9178#endif
9179
9180 if (pevent->data_size <= 0 || total_size <= 0 || total_size > pheader->size) {
9181 cm_msg(MERROR, "bm_wait_for_free_space",
9182 "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",
9183 pheader->name,
9184 pheader->read_pointer,
9185 pheader->write_pointer,
9186 pheader->size,
9187 free,
9188 requested_space,
9189 pevent->data_size,
9190 event_size,
9191 total_size);
9192 return BM_CORRUPTED;
9193 }
9194
9195 int blocking_client = -1;
9196
9197 int i;
9198 for (i = 0; i < pheader->max_client_index; i++) {
9199 BUFFER_CLIENT *pc = pheader->client + i;
9200 if (pc->pid) {
9201 if (pc->read_pointer == pheader->read_pointer) {
9202 /*
9203 First assume that the client with the "minimum" read pointer
9204 is not really blocking due to a GET_ALL request.
9205 */
9206 BOOL blocking = FALSE;
9207 //int blocking_request_id = -1;
9208
9209 int j;
9210 for (j = 0; j < pc->max_request_index; j++) {
9211 const EVENT_REQUEST *prequest = pc->event_request + j;
9212 if (prequest->valid
9213 && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9214 if (prequest->sampling_type & GET_ALL) {
9215 blocking = TRUE;
9216 //blocking_request_id = prequest->id;
9217 break;
9218 }
9219 }
9220 }
9221
9222 //printf("client [%s] blocking %d, request %d\n", pc->name, blocking, blocking_request_id);
9223
9224 if (blocking) {
9225 blocking_client = i;
9226 break;
9227 }
9228
9229 pc->read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
9230 }
9231 }
9232 } /* client loop */
9233
9234 if (blocking_client >= 0) {
9235 blocking_client_index = blocking_client;
9236 mstrlcpy(blocking_client_name, pheader->client[blocking_client].name, sizeof(blocking_client_name));
9237 //if (!blocking_time) {
9238 // blocking_time = ss_millitime();
9239 //}
9240
9241 //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);
9242
9243 // from this "break" we go into timeout check and sleep/wait.
9244 break;
9245 }
9246
9247 /* no blocking clients. move the read pointer and again check for free space */
9248
9249 BOOL moved = bm_update_read_pointer_locked("bm_wait_for_free_space", pheader);
9250
9251 if (!moved) {
9252 cm_msg(MERROR, "bm_wait_for_free_space",
9253 "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",
9254 pheader->name,
9255 pheader->read_pointer,
9256 pheader->write_pointer,
9257 pheader->size,
9258 free,
9259 requested_space);
9260 return BM_CORRUPTED;
9261 }
9262
9263 /* we freed one event, loop back to the check for free space */
9264 }
9265
9266 //blocking_loops++;
9267
9268 /* at least one client is blocking */
9269
9270 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
9271 pc->write_wait = requested_space;
9272
9273 if (pbuf->wait_start_time == 0) {
9274 pbuf->wait_start_time = ss_millitime();
9275 pbuf->count_write_wait++;
9276 if (requested_space > pbuf->max_requested_space)
9277 pbuf->max_requested_space = requested_space;
9278 pbuf->wait_client_index = blocking_client_index;
9279 }
9280
9281 DWORD now = ss_millitime();
9282
9283 //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);
9284
9285 int sleep_time_msec = 1000;
9286
9287 if (timeout_msec == BM_WAIT) {
9288 // wait forever
9289 } else if (timeout_msec == BM_NO_WAIT) {
9290 // no wait
9291 return BM_ASYNC_RETURN;
9292 } else {
9293 // check timeout
9294 if (now >= time_end) {
9295 // timeout!
9296 return BM_ASYNC_RETURN;
9297 }
9298
9299 sleep_time_msec = time_end - now;
9300
9301 if (sleep_time_msec <= 0) {
9302 sleep_time_msec = 10;
9303 } else if (sleep_time_msec > 1000) {
9304 sleep_time_msec = 1000;
9305 }
9306 }
9307
9309
9310 /* before waiting, unlock everything in the correct order */
9311
9312 pbuf_guard.unlock();
9313
9314 if (unlock_write_cache)
9315 pbuf->write_cache_mutex.unlock();
9316
9317 //printf("bm_wait_for_free_space: blocking client \"%s\"\n", blocking_client_name);
9318
9319#ifdef DEBUG_MSG
9320 cm_msg(MDEBUG, "Send sleep: rp=%d, wp=%d, level=%1.1lf", pheader->read_pointer, pheader->write_pointer, 100 - 100.0 * size / pheader->size);
9321#endif
9322
9324 //int idx = bm_validate_client_index_locked(pbuf, FALSE);
9325 //if (idx >= 0)
9326 // pheader->client[idx].write_wait = requested_space;
9327
9328 //bm_cleanup("bm_wait_for_free_space", ss_millitime(), FALSE);
9329
9330 status = ss_suspend(sleep_time_msec, MSG_BM);
9331
9332 /* we are told to shutdown */
9333 if (status == SS_ABORT) {
9334 // NB: buffer is locked!
9335 return SS_ABORT;
9336 }
9337
9338 /* make sure we do sleep in this loop:
9339 * if we are the mserver receiving data on the event
9340 * socket and the data buffer is full, ss_suspend() will
9341 * never sleep: it will detect data on the event channel,
9342 * call rpc_server_receive() (recursively, we already *are* in
9343 * rpc_server_receive()) and return without sleeping. Result
9344 * is a busy loop waiting for free space in data buffer */
9345
9346 /* update May 2021: ss_suspend(MSG_BM) no longer looks at
9347 * the event socket, and should sleep now, so this sleep below
9348 * maybe is not needed now. but for safety, I keep it. K.O. */
9349
9350 if (status != SS_TIMEOUT) {
9351 //printf("ss_suspend: status %d\n", status);
9352 ss_sleep(1);
9353 }
9354
9355 /* we may be stuck in this loop for an arbitrary long time,
9356 * depending on how other buffer clients read the accumulated data
9357 * so we should update all the timeouts & etc. K.O. */
9358
9360
9361 /* lock things again in the correct order */
9362
9363 if (unlock_write_cache) {
9365
9366 if (status != BM_SUCCESS) {
9367 // bail out with all locks released
9368 return status;
9369 }
9370 }
9371
9372 if (!pbuf_guard.relock()) {
9373 if (unlock_write_cache) {
9374 pbuf->write_cache_mutex.unlock();
9375 }
9376
9377 // bail out with all locks released
9378 return pbuf_guard.get_status();
9379 }
9380
9381 /* revalidate the client index: we could have been removed from the buffer while sleeping */
9382 pc = bm_get_my_client_locked(pbuf_guard);
9383
9384 pc->write_wait = 0;
9385
9387 //idx = bm_validate_client_index_locked(pbuf, FALSE);
9388 //if (idx >= 0)
9389 // pheader->client[idx].write_wait = 0;
9390 //else {
9391 // cm_msg(MERROR, "bm_wait_for_free_space", "our client index is no longer valid, exiting...");
9392 // status = SS_ABORT;
9393 //}
9394
9395#ifdef DEBUG_MSG
9396 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);
9397#endif
9398
9399 }
9400}
9401
9402static int bm_wait_for_more_events_locked(bm_lock_buffer_guard& pbuf_guard, BUFFER_CLIENT *pc, int timeout_msec, BOOL unlock_read_cache)
9403{
9404 BUFFER* pbuf = pbuf_guard.get_pbuf();
9405 BUFFER_HEADER* pheader = pbuf->buffer_header;
9406
9407 //printf("bm_wait_for_more_events_locked: [%s] timeout %d\n", pheader->name, timeout_msec);
9408
9409 if (pc->read_pointer != pheader->write_pointer) {
9410 // buffer has data
9411 return BM_SUCCESS;
9412 }
9413
9414 if (timeout_msec == BM_NO_WAIT) {
9415 /* event buffer is empty and we are told to not wait */
9416 if (!pc->read_wait) {
9417 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait in BM_NO_WAIT!\n", pheader->name, pc->name);
9418 pc->read_wait = TRUE;
9419 }
9420 return BM_ASYNC_RETURN;
9421 }
9422
9423 DWORD time_start = ss_millitime();
9424 DWORD time_wait = time_start + timeout_msec;
9425 DWORD sleep_time = 1000;
9426 if (timeout_msec == BM_NO_WAIT) {
9427 // default sleep time
9428 } else if (timeout_msec == BM_WAIT) {
9429 // default sleep time
9430 } else {
9431 if (sleep_time > (DWORD)timeout_msec)
9432 sleep_time = timeout_msec;
9433 }
9434
9435 //printf("time start 0x%08x, end 0x%08x, sleep %d\n", time_start, time_wait, sleep_time);
9436
9437 while (pc->read_pointer == pheader->write_pointer) {
9438 /* wait until there is data in the buffer (write pointer moves) */
9439
9440 if (!pc->read_wait) {
9441 //printf("bm_wait_for_more_events: buffer [%s] client [%s] set read_wait!\n", pheader->name, pc->name);
9442 pc->read_wait = TRUE;
9443 }
9444
9446
9448
9449 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9450
9451 pbuf_guard.unlock();
9452
9453 if (unlock_read_cache)
9454 pbuf->read_cache_mutex.unlock();
9455
9456 int status = ss_suspend(sleep_time, MSG_BM);
9457
9458 if (timeout_msec == BM_NO_WAIT) {
9459 // return immediately
9460 } else if (timeout_msec == BM_WAIT) {
9461 // wait forever
9462 } else {
9463 DWORD now = ss_millitime();
9464 //printf("check timeout: now 0x%08x, end 0x%08x, diff %d\n", now, time_wait, time_wait - now);
9465 if (now >= time_wait) {
9466 timeout_msec = BM_NO_WAIT; // cause immediate return
9467 } else {
9468 sleep_time = time_wait - now;
9469 if (sleep_time > 1000)
9470 sleep_time = 1000;
9471 //printf("time start 0x%08x, now 0x%08x, end 0x%08x, sleep %d\n", time_start, now, time_wait, sleep_time);
9472 }
9473 }
9474
9475 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
9476
9477 if (unlock_read_cache) {
9479 if (status != BM_SUCCESS) {
9480 // bail out with all locks released
9481 return status;
9482 }
9483 }
9484
9485 if (!pbuf_guard.relock()) {
9486 if (unlock_read_cache) {
9487 pbuf->read_cache_mutex.unlock();
9488 }
9489 // bail out with all locks released
9490 return pbuf_guard.get_status();
9491 }
9492
9493 /* need to revalidate our BUFFER_CLIENT after releasing the buffer lock
9494 * because we may have been removed from the buffer by bm_cleanup() & co
9495 * due to a timeout or whatever. */
9496 pc = bm_get_my_client_locked(pbuf_guard);
9497
9498 /* return if TCP connection broken */
9499 if (status == SS_ABORT)
9500 return SS_ABORT;
9501
9502 if (timeout_msec == BM_NO_WAIT)
9503 return BM_ASYNC_RETURN;
9504 }
9505
9506 if (pc->read_wait) {
9507 //printf("bm_wait_for_more_events: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9508 pc->read_wait = FALSE;
9509 }
9510
9511 return BM_SUCCESS;
9512}
9513
9514static 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)
9515{
9516 char *pdata = (char *) (pheader + 1);
9517
9518 //int old_write_pointer = pheader->write_pointer;
9519
9520 /* new event fits into the remaining space? */
9521 if ((size_t)pheader->write_pointer + total_size <= (size_t)pheader->size) {
9522 //memcpy(pdata + pheader->write_pointer, pevent, event_size);
9523 char* wptr = pdata + pheader->write_pointer;
9524 for (int i=0; i<sg_n; i++) {
9525 //printf("memcpy %p+%d\n", sg_ptr[i], (int)sg_len[i]);
9526 memcpy(wptr, sg_ptr[i], sg_len[i]);
9527 wptr += sg_len[i];
9528 }
9529 pheader->write_pointer = pheader->write_pointer + total_size;
9530 assert(pheader->write_pointer <= pheader->size);
9531 /* remaining space is smaller than size of an event header? */
9532 if ((pheader->write_pointer + (int) sizeof(EVENT_HEADER)) > pheader->size) {
9533 // note: ">" here to match "bm_incr_rp". If remaining space is exactly
9534 // equal to the event header size, we will write the next event header here,
9535 // then wrap the pointer and write the event data at the beginning of the buffer.
9536 //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);
9537 pheader->write_pointer = 0;
9538 }
9539 } else {
9540 /* split event */
9541 size_t size = pheader->size - pheader->write_pointer;
9542
9543 //printf("split: wp %d, size %d, avail %d\n", pheader->write_pointer, pheader->size, size);
9544
9545 //memcpy(pdata + pheader->write_pointer, pevent, size);
9546 //memcpy(pdata, ((const char *) pevent) + size, event_size - size);
9547
9548 char* wptr = pdata + pheader->write_pointer;
9549 size_t count = 0;
9550
9551 // copy first part
9552
9553 int i = 0;
9554 for (; i<sg_n; i++) {
9555 if (count + sg_len[i] > size)
9556 break;
9557 memcpy(wptr, sg_ptr[i], sg_len[i]);
9558 wptr += sg_len[i];
9559 count += sg_len[i];
9560 }
9561
9562 //printf("wptr %d, count %d\n", wptr-pdata, count);
9563
9564 // split segment
9565
9566 size_t first = size - count;
9567 size_t second = sg_len[i] - first;
9568 assert(first + second == sg_len[i]);
9569 assert(count + first == size);
9570
9571 //printf("first %d, second %d\n", first, second);
9572
9573 memcpy(wptr, sg_ptr[i], first);
9574 wptr = pdata + 0;
9575 count += first;
9576 memcpy(wptr, sg_ptr[i] + first, second);
9577 wptr += second;
9578 count += second;
9579 i++;
9580
9581 // copy remaining
9582
9583 for (; i<sg_n; i++) {
9584 memcpy(wptr, sg_ptr[i], sg_len[i]);
9585 wptr += sg_len[i];
9586 count += sg_len[i];
9587 }
9588
9589 //printf("wptr %d, count %d\n", wptr-pdata, count);
9590
9591 //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);
9592
9593 pheader->write_pointer = total_size - size;
9594 }
9595
9596 //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);
9597}
9598
9600 if (pc->pid) {
9601 int j;
9602 for (j = 0; j < pc->max_request_index; j++) {
9603 const EVENT_REQUEST *prequest = pc->event_request + j;
9604 if (prequest->valid && bm_match_event(prequest->event_id, prequest->trigger_mask, pevent)) {
9605 return prequest->id;
9606 }
9607 }
9608 }
9609
9610 return -1;
9611}
9612
9613static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id) {
9614 if (request_id >= 0) {
9615 /* if that client has a request and is suspended, wake it up */
9616 if (pc->read_wait) {
9617 char str[80];
9618 sprintf(str, "B %s %d", pheader->name, request_id);
9619 ss_resume(pc->port, str);
9620 //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);
9621 //printf("bm_notify_reader_locked: buffer [%s] client [%s] clear read_wait!\n", pheader->name, pc->name);
9622 pc->read_wait = FALSE;
9623 }
9624 }
9625}
9626
9627#endif // LOCAL_ROUTINES
9628
9629#if 0
9630INT bm_send_event_rpc(INT buffer_handle, const EVENT_HEADER *pevent, int event_size, int timeout_msec)
9631{
9632 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d\n", buffer_handle, event_size, timeout_msec);
9633
9634 DWORD time_start = ss_millitime();
9635 DWORD time_end = time_start + timeout_msec;
9636
9637 int xtimeout_msec = timeout_msec;
9638
9639 while (1) {
9640 if (timeout_msec == BM_WAIT) {
9641 xtimeout_msec = 1000;
9642 } else if (timeout_msec == BM_NO_WAIT) {
9643 xtimeout_msec = BM_NO_WAIT;
9644 } else {
9645 if (xtimeout_msec > 1000) {
9646 xtimeout_msec = 1000;
9647 }
9648 }
9649
9650 int status = rpc_call(RPC_BM_SEND_EVENT, buffer_handle, pevent, event_size, xtimeout_msec);
9651
9652 //printf("bm_send_event_rpc: handle %d, size %d, timeout %d, status %d\n", buffer_handle, event_size, xtimeout_msec, status);
9653
9654 if (status == BM_ASYNC_RETURN) {
9655 if (timeout_msec == BM_WAIT) {
9656 // BM_WAIT means wait forever
9657 continue;
9658 } else if (timeout_msec == BM_NO_WAIT) {
9659 // BM_NO_WAIT means do not wait
9660 return status;
9661 } else {
9662 DWORD now = ss_millitime();
9663 if (now >= time_end) {
9664 // timeout, return BM_ASYNC_RETURN
9665 return status;
9666 }
9667
9668 DWORD remain = time_end - now;
9669
9670 if (remain < xtimeout_msec) {
9671 xtimeout_msec = remain;
9672 }
9673
9674 // keep asking for event...
9675 continue;
9676 }
9677 } else if (status == BM_SUCCESS) {
9678 // success, return BM_SUCCESS
9679 return status;
9680 } else {
9681 // error
9682 return status;
9683 }
9684 }
9685}
9686#endif
9687
9688INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
9689{
9690 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9691 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9692
9693 if (data_size == 0) {
9694 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9695 return BM_INVALID_SIZE;
9696 }
9697
9698 if (data_size > MAX_DATA_SIZE) {
9699 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);
9700 return BM_INVALID_SIZE;
9701 }
9702
9703 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9704
9705 //printf("bm_send_event: pevent %p, data_size %d, event_size %d, buf_size %d\n", pevent, data_size, event_size, unused);
9706
9707 if (rpc_is_remote()) {
9708 //return bm_send_event_rpc(buffer_handle, pevent, event_size, timeout_msec);
9709 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
9710 } else {
9711 return bm_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size, timeout_msec);
9712 }
9713}
9714
9715int bm_send_event_vec(int buffer_handle, const std::vector<char>& event, int timeout_msec)
9716{
9717 const char* cptr = event.data();
9718 size_t clen = event.size();
9719 return bm_send_event_sg(buffer_handle, 1, &cptr, &clen, timeout_msec);
9720}
9721
9722int bm_send_event_vec(int buffer_handle, const std::vector<std::vector<char>>& event, int timeout_msec)
9723{
9724 int sg_n = event.size();
9725 const char* sg_ptr[sg_n];
9726 size_t sg_len[sg_n];
9727 for (int i=0; i<sg_n; i++) {
9728 sg_ptr[i] = event[i].data();
9729 sg_len[i] = event[i].size();
9730 }
9731 return bm_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len, timeout_msec);
9732}
9733
9734#ifdef LOCAL_ROUTINES
9735static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec);
9736#endif
9737
9738/********************************************************************/
9788int bm_send_event_sg(int buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[], int timeout_msec)
9789{
9790 if (rpc_is_remote())
9791 return rpc_send_event_sg(buffer_handle, sg_n, sg_ptr, sg_len);
9792
9793 if (sg_n < 1) {
9794 cm_msg(MERROR, "bm_send_event", "invalid sg_n %d", sg_n);
9795 return BM_INVALID_SIZE;
9796 }
9797
9798 if (sg_ptr[0] == NULL) {
9799 cm_msg(MERROR, "bm_send_event", "invalid sg_ptr[0] is NULL");
9800 return BM_INVALID_SIZE;
9801 }
9802
9803 if (sg_len[0] < sizeof(EVENT_HEADER)) {
9804 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));
9805 return BM_INVALID_SIZE;
9806 }
9807
9808 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
9809
9810 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
9811 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
9812
9813 if (data_size == 0) {
9814 cm_msg(MERROR, "bm_send_event", "invalid event data size zero");
9815 return BM_INVALID_SIZE;
9816 }
9817
9818 if (data_size > MAX_DATA_SIZE) {
9819 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);
9820 return BM_INVALID_SIZE;
9821 }
9822
9823 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
9824
9825 size_t count = 0;
9826 for (int i=0; i<sg_n; i++) {
9827 count += sg_len[i];
9828 }
9829
9830 if (count != event_size) {
9831 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);
9832 return BM_INVALID_SIZE;
9833 }
9834
9835 //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);
9836
9837#ifdef LOCAL_ROUTINES
9838 {
9839 int status = 0;
9840 const size_t total_size = ALIGN8(event_size);
9841
9842 BUFFER *pbuf = bm_get_buffer("bm_send_event_sg", buffer_handle, &status);
9843
9844 if (!pbuf)
9845 return status;
9846
9847 /* round up total_size to next DWORD boundary */
9848 //int total_size = ALIGN8(event_size);
9849
9850 /* check if write cache is enabled */
9851 if (pbuf->write_cache_size) {
9853
9854 if (status != BM_SUCCESS)
9855 return status;
9856
9857 /* check if write cache is enabled */
9858 if (pbuf->write_cache_size) {
9860 bool too_big = event_size > max_event_size;
9861
9862 //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);
9863
9864 /* if this event does not fit into the write cache, flush the write cache */
9865 if (pbuf->write_cache_wp > 0 && (pbuf->write_cache_wp + total_size > pbuf->write_cache_size || too_big)) {
9866 //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);
9867
9868 bm_lock_buffer_guard pbuf_guard(pbuf);
9869
9870 if (!pbuf_guard.is_locked()) {
9871 pbuf->write_cache_mutex.unlock();
9872 return pbuf_guard.get_status();
9873 }
9874
9875 int status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
9876
9877 if (pbuf_guard.is_locked()) {
9878 // check if bm_wait_for_free_space() failed to relock the buffer
9879 pbuf_guard.unlock();
9880 }
9881
9882 if (status != BM_SUCCESS) {
9883 pbuf->write_cache_mutex.unlock();
9884 // bm_flush_cache() failed: timeout in bm_wait_for_free_space() or write cache size is bigger than buffer size or buffer was closed.
9885 if (status == BM_NO_MEMORY)
9886 cm_msg(MERROR, "bm_send_event", "write cache size is bigger than buffer size");
9887 return status;
9888 }
9889
9890 // write cache must be empty here
9891 assert(pbuf->write_cache_wp == 0);
9892 }
9893
9894 /* write this event into the write cache, if it is not too big and if it fits */
9895 if (!too_big && pbuf->write_cache_wp + total_size <= pbuf->write_cache_size) {
9896 //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);
9897
9898 char* wptr = pbuf->write_cache + pbuf->write_cache_wp;
9899
9900 for (int i=0; i<sg_n; i++) {
9901 memcpy(wptr, sg_ptr[i], sg_len[i]);
9902 wptr += sg_len[i];
9903 }
9904
9905 pbuf->write_cache_wp += total_size;
9906
9907 pbuf->write_cache_mutex.unlock();
9908 return BM_SUCCESS;
9909 }
9910 }
9911
9912 /* event did not fit into the write cache, we flushed the write cache and we send it directly to shared memory */
9913 pbuf->write_cache_mutex.unlock();
9914 }
9915
9916 /* we come here only for events that are too big to fit into the cache */
9917
9918 /* lock the buffer */
9919 bm_lock_buffer_guard pbuf_guard(pbuf);
9920
9921 if (!pbuf_guard.is_locked()) {
9922 return pbuf_guard.get_status();
9923 }
9924
9925 /* calculate some shorthands */
9926 BUFFER_HEADER *pheader = pbuf->buffer_header;
9927
9928#if 0
9930 if (status != BM_SUCCESS) {
9931 printf("bm_send_event: corrupted 111!\n");
9932 abort();
9933 }
9934#endif
9935
9936 /* check if buffer is large enough */
9937 if (total_size >= (size_t)pheader->size) {
9938 pbuf_guard.unlock(); // unlock before cm_msg()
9939 cm_msg(MERROR, "bm_send_event", "total event size (%d) larger than size (%d) of buffer \'%s\'", (int)total_size, pheader->size, pheader->name);
9940 return BM_NO_MEMORY;
9941 }
9942
9943 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, total_size, false);
9944
9945 if (status != BM_SUCCESS) {
9946 // implicit unlock
9947 return status;
9948 }
9949
9950#if 0
9952 if (status != BM_SUCCESS) {
9953 printf("bm_send_event: corrupted 222!\n");
9954 abort();
9955 }
9956#endif
9957
9958 int old_write_pointer = pheader->write_pointer;
9959
9960 bm_write_to_buffer_locked(pheader, sg_n, sg_ptr, sg_len, total_size);
9961
9962 /* write pointer was incremented, but there should
9963 * always be some free space in the buffer and the
9964 * write pointer should never cacth up to the read pointer:
9965 * the rest of the code gets confused this happens (buffer 100% full)
9966 * as it is write_pointer == read_pointer can be either
9967 * 100% full or 100% empty. My solution: never fill
9968 * the buffer to 100% */
9969 assert(pheader->write_pointer != pheader->read_pointer);
9970
9971 /* send wake up messages to all clients that want this event */
9972 int i;
9973 for (i = 0; i < pheader->max_client_index; i++) {
9974 BUFFER_CLIENT *pc = pheader->client + i;
9975 int request_id = bm_find_first_request_locked(pc, pevent);
9976 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id);
9977 }
9978
9979#if 0
9981 if (status != BM_SUCCESS) {
9982 printf("bm_send_event: corrupted 333!\n");
9983 abort();
9984 }
9985#endif
9986
9987 /* update statistics */
9988 pheader->num_in_events++;
9989 pbuf->count_sent += 1;
9990 pbuf->bytes_sent += total_size;
9991 }
9992#endif /* LOCAL_ROUTINES */
9993
9994 return BM_SUCCESS;
9995}
9996
9997static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
9998{
9999 //printf("bm_flush_cache_rpc: handle %d, timeout %d\n", buffer_handle, timeout_msec);
10000
10001 DWORD time_start = ss_millitime();
10002 DWORD time_end = time_start + timeout_msec;
10003 DWORD time_bombout = time_end;
10004
10005 if (timeout_msec < 10000)
10006 time_bombout = time_start + 10000; // 10 seconds
10007
10008 int xtimeout_msec = timeout_msec;
10009
10010 while (1) {
10011 if (timeout_msec == BM_WAIT) {
10012 xtimeout_msec = 1000;
10013 } else if (timeout_msec == BM_NO_WAIT) {
10014 xtimeout_msec = BM_NO_WAIT;
10015 } else {
10016 if (xtimeout_msec > 1000) {
10017 xtimeout_msec = 1000;
10018 }
10019 }
10020
10021 int status = rpc_call(RPC_BM_FLUSH_CACHE, buffer_handle, xtimeout_msec);
10022
10023 //printf("bm_flush_cache_rpc: handle %d, timeout %d, status %d\n", buffer_handle, xtimeout_msec, status);
10024
10025 if (status == BM_ASYNC_RETURN) {
10026 if (timeout_msec == BM_WAIT) {
10027 DWORD now = ss_millitime();
10028 if (now >= time_bombout) {
10029 // timeout
10030 return BM_TIMEOUT;
10031 }
10032
10033 // BM_WAIT means wait forever
10034 continue;
10035 } else if (timeout_msec == BM_NO_WAIT) {
10036 // BM_NO_WAIT means do not wait
10037 return status;
10038 } else {
10039 DWORD now = ss_millitime();
10040 if (now >= time_end) {
10041 // timeout, return BM_ASYNC_RETURN
10042 return status;
10043 }
10044
10045 DWORD remain = time_end - now;
10046
10047 if (remain < (DWORD)xtimeout_msec) {
10048 xtimeout_msec = remain;
10049 }
10050
10051 if (now >= time_bombout) {
10052 // timeout
10053 return BM_TIMEOUT;
10054 }
10055
10056 // keep asking for event...
10057 continue;
10058 }
10059 } else if (status == BM_SUCCESS) {
10060 // success, return BM_SUCCESS
10061 return status;
10062 } else {
10063 // error
10064 return status;
10065 }
10066 }
10067}
10068
10069/********************************************************************/
10087#ifdef LOCAL_ROUTINES
10088static INT bm_flush_cache_locked(bm_lock_buffer_guard& pbuf_guard, int timeout_msec)
10089{
10090 // NB we come here with write cache locked and buffer locked.
10091
10092 {
10093 INT status = 0;
10094
10095 //printf("bm_flush_cache_locked!\n");
10096
10097 BUFFER* pbuf = pbuf_guard.get_pbuf();
10098 BUFFER_HEADER* pheader = pbuf->buffer_header;
10099
10100 //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);
10101
10102 int old_write_pointer = pheader->write_pointer;
10103
10104 int request_id[MAX_CLIENTS];
10105 for (int i = 0; i < pheader->max_client_index; i++) {
10106 request_id[i] = -1;
10107 }
10108
10109 size_t ask_rp = pbuf->write_cache_rp;
10110 size_t ask_wp = pbuf->write_cache_wp;
10111
10112 if (ask_wp == 0) { // nothing to do
10113 return BM_SUCCESS;
10114 }
10115
10116 if (ask_rp == ask_wp) { // nothing to do
10117 return BM_SUCCESS;
10118 }
10119
10120 assert(ask_rp < ask_wp);
10121
10122 size_t ask_free = ALIGN8(ask_wp - ask_rp);
10123
10124 if (ask_free == 0) { // nothing to do
10125 return BM_SUCCESS;
10126 }
10127
10128#if 0
10130 if (status != BM_SUCCESS) {
10131 printf("bm_flush_cache: corrupted 111!\n");
10132 abort();
10133 }
10134#endif
10135
10136 status = bm_wait_for_free_space_locked(pbuf_guard, timeout_msec, ask_free, true);
10137
10138 if (status != BM_SUCCESS) {
10139 return status;
10140 }
10141
10142 // NB: ask_rp, ask_wp and ask_free are invalid after calling bm_wait_for_free_space():
10143 //
10144 // wait_for_free_space() will sleep with all locks released,
10145 // during this time, another thread may call bm_send_event() that will
10146 // add one or more events to the write cache and after wait_for_free_space()
10147 // returns, size of data in cache will be bigger than the amount
10148 // of free space we requested. so we need to keep track of how
10149 // much data we write to the buffer and ask for more data
10150 // if we run short. This is the reason for the big loop
10151 // around wait_for_free_space(). We ask for slightly too little free
10152 // space to make sure all this code is always used and does work. K.O.
10153
10154 if (pbuf->write_cache_wp == 0) {
10155 /* somebody emptied the cache while we were inside bm_wait_for_free_space */
10156 return BM_SUCCESS;
10157 }
10158
10159 //size_t written = 0;
10160 while (pbuf->write_cache_rp < pbuf->write_cache_wp) {
10161 /* loop over all events in cache */
10162
10163 const EVENT_HEADER *pevent = (const EVENT_HEADER *) (pbuf->write_cache + pbuf->write_cache_rp);
10164 size_t event_size = (pevent->data_size + sizeof(EVENT_HEADER));
10165 size_t total_size = ALIGN8(event_size);
10166
10167#if 0
10168 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",
10169 int(pbuf->write_cache_size),
10170 int(pbuf->write_cache_wp),
10171 int(pbuf->write_cache_rp),
10172 int(pevent->data_size),
10173 int(event_size),
10174 int(total_size),
10175 int(ask_free),
10176 int(written));
10177#endif
10178
10179 // check for crazy event size
10180 assert(total_size >= sizeof(EVENT_HEADER));
10181 assert(total_size <= (size_t)pheader->size);
10182
10183 bm_write_to_buffer_locked(pheader, 1, (char**)&pevent, &event_size, total_size);
10184
10185 /* update statistics */
10186 pheader->num_in_events++;
10187 pbuf->count_sent += 1;
10188 pbuf->bytes_sent += total_size;
10189
10190 /* see comment for the same code in bm_send_event().
10191 * We make sure the buffer is never 100% full */
10192 assert(pheader->write_pointer != pheader->read_pointer);
10193
10194 /* check if anybody has a request for this event */
10195 for (int i = 0; i < pheader->max_client_index; i++) {
10196 BUFFER_CLIENT *pc = pheader->client + i;
10197 int r = bm_find_first_request_locked(pc, pevent);
10198 if (r >= 0) {
10199 request_id[i] = r;
10200 }
10201 }
10202
10203 /* this loop does not loop forever because rp
10204 * is monotonously incremented here. write_cache_wp does
10205 * not change */
10206
10207 pbuf->write_cache_rp += total_size;
10208 //written += total_size;
10209
10210 assert(pbuf->write_cache_rp > 0);
10211 assert(pbuf->write_cache_rp <= pbuf->write_cache_size);
10212 assert(pbuf->write_cache_rp <= pbuf->write_cache_wp);
10213 }
10214
10215 /* the write cache is now empty */
10216 assert(pbuf->write_cache_wp == pbuf->write_cache_rp);
10217 pbuf->write_cache_wp = 0;
10218 pbuf->write_cache_rp = 0;
10219
10220 /* check which clients are waiting */
10221 for (int i = 0; i < pheader->max_client_index; i++) {
10222 BUFFER_CLIENT *pc = pheader->client + i;
10223 bm_notify_reader_locked(pheader, pc, old_write_pointer, request_id[i]);
10224 }
10225 }
10226
10227 return BM_SUCCESS;
10228}
10229
10230#endif /* LOCAL_ROUTINES */
10231
10232INT bm_flush_cache(int buffer_handle, int timeout_msec)
10233{
10234 if (rpc_is_remote()) {
10235 return bm_flush_cache_rpc(buffer_handle, timeout_msec);
10236 }
10237
10238#ifdef LOCAL_ROUTINES
10239 {
10240 INT status = 0;
10241
10242 //printf("bm_flush_cache!\n");
10243
10244 BUFFER *pbuf = bm_get_buffer("bm_flush_cache", buffer_handle, &status);
10245
10246 if (!pbuf)
10247 return status;
10248
10249 if (pbuf->write_cache_size == 0)
10250 return BM_SUCCESS;
10251
10253
10254 if (status != BM_SUCCESS)
10255 return status;
10256
10257 /* check if anything needs to be flushed */
10258 if (pbuf->write_cache_wp == 0) {
10259 pbuf->write_cache_mutex.unlock();
10260 return BM_SUCCESS;
10261 }
10262
10263 /* lock the buffer */
10264 bm_lock_buffer_guard pbuf_guard(pbuf);
10265
10266 if (!pbuf_guard.is_locked())
10267 return pbuf_guard.get_status();
10268
10269 status = bm_flush_cache_locked(pbuf_guard, timeout_msec);
10270
10271 /* unlock in correct order */
10272
10273 if (pbuf_guard.is_locked()) {
10274 // check if bm_wait_for_free_space() failed to relock the buffer
10275 pbuf_guard.unlock();
10276 }
10277
10278 pbuf->write_cache_mutex.unlock();
10279
10280 return status;
10281 }
10282#endif /* LOCAL_ROUTINES */
10283
10284 return BM_SUCCESS;
10285}
10286
10287#ifdef LOCAL_ROUTINES
10288
10289static 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) {
10291
10292 int max_size = 0;
10293 if (buf_size) {
10294 max_size = *buf_size;
10295 *buf_size = 0;
10296 }
10297
10298 //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);
10299
10300 bm_lock_buffer_guard pbuf_guard(pbuf, true); // buffer is not locked
10301
10302 // NB: locking order is: 1st read cache lock, 2nd buffer lock, unlock in reverse order
10303
10304 /* look if there is anything in the cache */
10305 if (pbuf->read_cache_size > 0) {
10306
10308
10309 if (status != BM_SUCCESS)
10310 return status;
10311
10312 if (pbuf->read_cache_wp == 0) {
10313
10314 // lock buffer for the first time
10315
10316 if (!pbuf_guard.relock()) {
10317 pbuf->read_cache_mutex.unlock();
10318 return pbuf_guard.get_status();
10319 }
10320
10321 status = bm_fill_read_cache_locked(pbuf_guard, timeout_msec);
10322 if (status != BM_SUCCESS) {
10323 // unlock in correct order
10324 if (pbuf_guard.is_locked()) {
10325 // check if bm_wait_for_more_events() failed to relock the buffer
10326 pbuf_guard.unlock();
10327 }
10328 pbuf->read_cache_mutex.unlock();
10329 return status;
10330 }
10331
10332 // buffer remains locked here
10333 }
10334 EVENT_HEADER *pevent;
10335 int event_size;
10336 int total_size;
10337 if (bm_peek_read_cache_locked(pbuf, &pevent, &event_size, &total_size)) {
10338 if (pbuf_guard.is_locked()) {
10339 // do not need to keep the event buffer locked
10340 // when reading from the read cache
10341 pbuf_guard.unlock();
10342 }
10343 //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);
10345 if (buf) {
10346 if (event_size > max_size) {
10347 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);
10348 event_size = max_size;
10350 }
10351
10352 memcpy(buf, pevent, event_size);
10353
10354 if (buf_size) {
10355 *buf_size = event_size;
10356 }
10357 if (convert_flags) {
10358 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10359 }
10360 } else if (bufptr) {
10361 *bufptr = malloc(event_size);
10362 memcpy(*bufptr, pevent, event_size);
10364 } else if (vecptr) {
10365 vecptr->resize(0);
10366 char* cptr = (char*)pevent;
10367 vecptr->assign(cptr, cptr+event_size);
10368 }
10369 bm_incr_read_cache_locked(pbuf, total_size);
10370 pbuf->read_cache_mutex.unlock();
10371 if (dispatch) {
10372 // FIXME need to protect currently dispatched event against
10373 // another thread overwriting it by refilling the read cache
10374 bm_dispatch_event(buffer_handle, pevent);
10375 return BM_MORE_EVENTS;
10376 }
10377 // buffer is unlocked here
10378 return status;
10379 }
10380 pbuf->read_cache_mutex.unlock();
10381 }
10382
10383 /* we come here if the read cache is disabled */
10384 /* we come here if the next event is too big to fit into the read cache */
10385
10386 if (!pbuf_guard.is_locked()) {
10387 if (!pbuf_guard.relock())
10388 return pbuf_guard.get_status();
10389 }
10390
10391 EVENT_HEADER *event_buffer = NULL;
10392
10393 BUFFER_HEADER *pheader = pbuf->buffer_header;
10394
10395 BUFFER_CLIENT *pc = bm_get_my_client_locked(pbuf_guard);
10396
10397 while (1) {
10398 /* loop over events in the event buffer */
10399
10400 status = bm_wait_for_more_events_locked(pbuf_guard, pc, timeout_msec, FALSE);
10401
10402 if (status != BM_SUCCESS) {
10403 // implicit unlock
10404 return status;
10405 }
10406
10407 /* check if event at current read pointer matches a request */
10408
10409 EVENT_HEADER *pevent;
10410 int event_size;
10411 int total_size;
10412
10413 status = bm_peek_buffer_locked(pbuf, pheader, pc, &pevent, &event_size, &total_size);
10414 if (status == BM_CORRUPTED) {
10415 // implicit unlock
10416 return status;
10417 } else if (status != BM_SUCCESS) {
10418 /* event buffer is empty */
10419 break;
10420 }
10421
10422 BOOL is_requested = bm_check_requests(pc, pevent);
10423
10424 if (is_requested) {
10425 //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);
10426
10428
10429 if (buf) {
10430 if (event_size > max_size) {
10431 cm_msg(MERROR, "bm_read_buffer",
10432 "buffer size %d is smaller than event size %d, event truncated. buffer \"%s\"", max_size,
10433 event_size, pheader->name);
10434 event_size = max_size;
10436 }
10437
10438 bm_read_from_buffer_locked(pheader, pc->read_pointer, (char *) buf, event_size);
10439
10440 if (buf_size) {
10441 *buf_size = event_size;
10442 }
10443
10444 if (convert_flags) {
10445 bm_convert_event_header((EVENT_HEADER *) buf, convert_flags);
10446 }
10447
10448 pbuf->count_read++;
10449 pbuf->bytes_read += event_size;
10450 } else if (dispatch || bufptr) {
10451 assert(event_buffer == NULL); // make sure we only come here once
10452 event_buffer = (EVENT_HEADER *) malloc(event_size);
10454 pbuf->count_read++;
10455 pbuf->bytes_read += event_size;
10456 } else if (vecptr) {
10457 bm_read_from_buffer_locked(pheader, pc->read_pointer, vecptr, event_size);
10458 pbuf->count_read++;
10459 pbuf->bytes_read += event_size;
10460 }
10461
10462 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10463 pc->read_pointer = new_read_pointer;
10464
10465 pheader->num_out_events++;
10466 /* exit loop over events */
10467 break;
10468 }
10469
10470 int new_read_pointer = bm_incr_rp_no_check(pheader, pc->read_pointer, total_size);
10471 pc->read_pointer = new_read_pointer;
10472 pheader->num_out_events++;
10473 }
10474
10475 /*
10476 If read pointer has been changed, it may have freed up some space
10477 for waiting producers. So check if free space is now more than 50%
10478 of the buffer size and wake waiting producers.
10479 */
10480
10481 bm_wakeup_producers_locked(pheader, pc);
10482
10483 pbuf_guard.unlock();
10484
10485 if (dispatch && event_buffer) {
10486 bm_dispatch_event(buffer_handle, event_buffer);
10487 free(event_buffer);
10488 event_buffer = NULL;
10489 return BM_MORE_EVENTS;
10490 }
10491
10492 if (bufptr && event_buffer) {
10493 *bufptr = event_buffer;
10494 event_buffer = NULL;
10496 }
10497
10498 if (event_buffer) {
10499 free(event_buffer);
10500 event_buffer = NULL;
10501 }
10502
10503 return status;
10504}
10505
10506#endif
10507
10508static INT bm_receive_event_rpc(INT buffer_handle, void *buf, int *buf_size, EVENT_HEADER** ppevent, std::vector<char>* pvec, int timeout_msec)
10509{
10510 //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);
10511
10512 assert(_bm_max_event_size > sizeof(EVENT_HEADER));
10513
10514 void *xbuf = NULL;
10515 int xbuf_size = 0;
10516
10517 if (buf) {
10518 xbuf = buf;
10519 xbuf_size = *buf_size;
10520 } else if (ppevent) {
10521 *ppevent = (EVENT_HEADER*)malloc(_bm_max_event_size);
10522 xbuf_size = _bm_max_event_size;
10523 } else if (pvec) {
10524 pvec->resize(_bm_max_event_size);
10525 xbuf = pvec->data();
10526 xbuf_size = pvec->size();
10527 } else {
10528 assert(!"incorrect call to bm_receivent_event_rpc()");
10529 }
10530
10531 int status;
10532 DWORD time_start = ss_millitime();
10533 DWORD time_end = time_start + timeout_msec;
10534
10535 int xtimeout_msec = timeout_msec;
10536
10537 int zbuf_size = xbuf_size;
10538
10539 while (1) {
10540 if (timeout_msec == BM_WAIT) {
10541 xtimeout_msec = 1000;
10542 } else if (timeout_msec == BM_NO_WAIT) {
10543 xtimeout_msec = BM_NO_WAIT;
10544 } else {
10545 if (xtimeout_msec > 1000) {
10546 xtimeout_msec = 1000;
10547 }
10548 }
10549
10550 zbuf_size = xbuf_size;
10551
10552 status = rpc_call(RPC_BM_RECEIVE_EVENT, buffer_handle, xbuf, &zbuf_size, xtimeout_msec);
10553
10554 //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);
10555
10556 if (status == BM_ASYNC_RETURN) {
10557 if (timeout_msec == BM_WAIT) {
10558 // BM_WAIT means wait forever
10559 continue;
10560 } else if (timeout_msec == BM_NO_WAIT) {
10561 // BM_NO_WAIT means do not wait
10562 break;
10563 } else {
10564 DWORD now = ss_millitime();
10565 if (now >= time_end) {
10566 // timeout, return BM_ASYNC_RETURN
10567 break;
10568 }
10569
10570 DWORD remain = time_end - now;
10571
10572 if (remain < (DWORD)xtimeout_msec) {
10573 xtimeout_msec = remain;
10574 }
10575
10576 // keep asking for event...
10577 continue;
10578 }
10579 } else if (status == BM_SUCCESS) {
10580 // success, return BM_SUCCESS
10581 break;
10582 }
10583
10584 // RPC error
10585
10586 if (buf) {
10587 *buf_size = 0;
10588 } else if (ppevent) {
10589 free(*ppevent);
10590 *ppevent = NULL;
10591 } else if (pvec) {
10592 pvec->resize(0);
10593 } else {
10594 assert(!"incorrect call to bm_receivent_event_rpc()");
10595 }
10596
10597 return status;
10598 }
10599
10600 // status is BM_SUCCESS or BM_ASYNC_RETURN
10601
10602 if (buf) {
10603 *buf_size = zbuf_size;
10604 } else if (ppevent) {
10605 // nothing to do
10606 // ppevent = realloc(ppevent, xbuf_size); // shrink memory allocation
10607 } else if (pvec) {
10608 pvec->resize(zbuf_size);
10609 } else {
10610 assert(!"incorrect call to bm_receivent_event_rpc()");
10611 }
10612
10613 return status;
10614}
10615
10616/********************************************************************/
10675INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec) {
10676 //printf("bm_receive_event: handle %d, async %d\n", buffer_handle, async_flag);
10677 if (rpc_is_remote()) {
10678 return bm_receive_event_rpc(buffer_handle, destination, buf_size, NULL, NULL, timeout_msec);
10679 }
10680#ifdef LOCAL_ROUTINES
10681 {
10683
10684 BUFFER *pbuf = bm_get_buffer("bm_receive_event", buffer_handle, &status);
10685
10686 if (!pbuf)
10687 return status;
10688
10689 int convert_flags = rpc_get_convert_flags();
10690
10691 status = bm_read_buffer(pbuf, buffer_handle, NULL, destination, buf_size, NULL, timeout_msec, convert_flags, FALSE);
10692 //printf("bm_receive_event: handle %d, async %d, status %d, size %d\n", buffer_handle, async_flag, status, *buf_size);
10693 return status;
10694 }
10695#else /* LOCAL_ROUTINES */
10696
10697 return BM_SUCCESS;
10698#endif
10699}
10700
10701/********************************************************************/
10756INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec) {
10757 if (rpc_is_remote()) {
10758 return bm_receive_event_rpc(buffer_handle, NULL, NULL, ppevent, NULL, timeout_msec);
10759 }
10760#ifdef LOCAL_ROUTINES
10761 {
10763
10764 BUFFER *pbuf = bm_get_buffer("bm_receive_event_alloc", buffer_handle, &status);
10765
10766 if (!pbuf)
10767 return status;
10768
10769 int convert_flags = rpc_get_convert_flags();
10770
10771 return bm_read_buffer(pbuf, buffer_handle, (void **) ppevent, NULL, NULL, NULL, timeout_msec, convert_flags, FALSE);
10772 }
10773#else /* LOCAL_ROUTINES */
10774
10775 return BM_SUCCESS;
10776#endif
10777}
10778
10779/********************************************************************/
10834INT bm_receive_event_vec(INT buffer_handle, std::vector<char> *pvec, int timeout_msec) {
10835 if (rpc_is_remote()) {
10836 return bm_receive_event_rpc(buffer_handle, NULL, NULL, NULL, pvec, timeout_msec);
10837 }
10838#ifdef LOCAL_ROUTINES
10839 {
10841
10842 BUFFER *pbuf = bm_get_buffer("bm_receive_event_vec", buffer_handle, &status);
10843
10844 if (!pbuf)
10845 return status;
10846
10847 int convert_flags = rpc_get_convert_flags();
10848
10849 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, pvec, timeout_msec, convert_flags, FALSE);
10850 }
10851#else /* LOCAL_ROUTINES */
10852 return BM_SUCCESS;
10853#endif
10854}
10855
10856#ifdef LOCAL_ROUTINES
10857
10858static int bm_skip_event(BUFFER* pbuf)
10859{
10860 /* clear read cache */
10861 if (pbuf->read_cache_size > 0) {
10862
10864
10865 if (status != BM_SUCCESS)
10866 return status;
10867
10868 pbuf->read_cache_rp = 0;
10869 pbuf->read_cache_wp = 0;
10870
10871 pbuf->read_cache_mutex.unlock();
10872 }
10873
10874 bm_lock_buffer_guard pbuf_guard(pbuf);
10875
10876 if (!pbuf_guard.is_locked())
10877 return pbuf_guard.get_status();
10878
10879 BUFFER_HEADER *pheader = pbuf->buffer_header;
10880
10881 /* forward read pointer to global write pointer */
10882 BUFFER_CLIENT *pclient = bm_get_my_client_locked(pbuf_guard);
10883 pclient->read_pointer = pheader->write_pointer;
10884
10885 return BM_SUCCESS;
10886}
10887
10888#endif /* LOCAL_ROUTINES */
10889
10890/********************************************************************/
10899INT bm_skip_event(INT buffer_handle) {
10900 if (rpc_is_remote())
10901 return rpc_call(RPC_BM_SKIP_EVENT, buffer_handle);
10902
10903#ifdef LOCAL_ROUTINES
10904 {
10905 int status = 0;
10906
10907 BUFFER *pbuf = bm_get_buffer("bm_skip_event", buffer_handle, &status);
10908
10909 if (!pbuf)
10910 return status;
10911
10912 return bm_skip_event(pbuf);
10913 }
10914#endif
10915
10916 return BM_SUCCESS;
10917}
10918
10919#ifdef LOCAL_ROUTINES
10920/********************************************************************/
10927static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle) {
10928 //printf("bm_push_buffer: buffer [%s], handle %d, callback %d\n", pbuf->buffer_header->name, buffer_handle, pbuf->callback);
10929
10930 /* return immediately if no callback routine is defined */
10931 if (!pbuf->callback)
10932 return BM_SUCCESS;
10933
10934 return bm_read_buffer(pbuf, buffer_handle, NULL, NULL, NULL, NULL, BM_NO_WAIT, 0, TRUE);
10935}
10936
10937/********************************************************************/
10943static INT bm_push_event(const char *buffer_name)
10944{
10945 std::vector<BUFFER*> mybuffers;
10946
10947 gBuffersMutex.lock();
10948 mybuffers = gBuffers;
10949 gBuffersMutex.unlock();
10950
10951 for (size_t i = 0; i < mybuffers.size(); i++) {
10952 BUFFER *pbuf = mybuffers[i];
10953 if (!pbuf || !pbuf->attached)
10954 continue;
10955 // FIXME: unlocked read access to pbuf->buffer_name!
10956 if (strcmp(buffer_name, pbuf->buffer_name) == 0) {
10957 return bm_push_buffer(pbuf, i + 1);
10958 }
10959 }
10960
10961 return BM_INVALID_HANDLE;
10962}
10963
10964#else
10965
10966static INT bm_push_event(const char *buffer_name)
10967{
10968 return BM_SUCCESS;
10969}
10970
10971#endif /* LOCAL_ROUTINES */
10972
10973/********************************************************************/
10980#ifdef LOCAL_ROUTINES
10981 {
10982 INT status = 0;
10983 BOOL bMore;
10984 DWORD start_time;
10985 //static DWORD last_time = 0;
10986
10987 /* if running as a server, buffer checking is done by client
10988 via ASYNC bm_receive_event */
10989 if (rpc_is_mserver()) {
10990 return FALSE;
10991 }
10992
10993 bMore = FALSE;
10994 start_time = ss_millitime();
10995
10996 std::vector<BUFFER*> mybuffers;
10997
10998 gBuffersMutex.lock();
10999 mybuffers = gBuffers;
11000 gBuffersMutex.unlock();
11001
11002 /* go through all buffers */
11003 for (size_t idx = 0; idx < mybuffers.size(); idx++) {
11004 BUFFER* pbuf = mybuffers[idx];
11005
11006 if (!pbuf || !pbuf->attached)
11007 continue;
11008
11009 //int count_loops = 0;
11010 while (1) {
11011 if (pbuf->attached) {
11012 /* one bm_push_event could cause a run stop and a buffer close, which
11013 * would crash the next call to bm_push_event(). So check for valid
11014 * buffer on each call */
11015
11016 /* this is what happens:
11017 * bm_push_buffer() may call a user callback function
11018 * user callback function may indirectly call bm_close() of this buffer,
11019 * i.e. if it stops the run,
11020 * bm_close() will set pbuf->attached to false, but will not delete pbuf or touch gBuffers
11021 * here we will see pbuf->attched is false and quit this loop
11022 */
11023
11024 status = bm_push_buffer(pbuf, idx + 1);
11025
11026 if (status == BM_CORRUPTED) {
11027 return status;
11028 }
11029
11030 //printf("bm_check_buffers: bm_push_buffer() returned %d, loop %d, time %d\n", status, count_loops, ss_millitime() - start_time);
11031
11032 if (status != BM_MORE_EVENTS) {
11033 //DWORD t = ss_millitime() - start_time;
11034 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, no more events\n", idx, start_time - last_time, t, count_loops);
11035 break;
11036 }
11037
11038 // count_loops++;
11039 }
11040
11041 // NB: this code has a logic error: if 2 buffers always have data,
11042 // this timeout will cause us to exit reading the 1st buffer
11043 // after 1000 msec, then we read the 2nd buffer exactly once,
11044 // and exit the loop because the timeout is still active -
11045 // we did not reset "start_time" when we started reading
11046 // from the 2nd buffer. Result is that we always read all
11047 // the data in a loop from the 1st buffer, but read just
11048 // one event from the 2nd buffer, resulting in severe unfairness.
11049
11050 /* stop after one second */
11051 DWORD t = ss_millitime() - start_time;
11052 if (t > 1000) {
11053 //printf("bm_check_buffers: index %d, period %d, elapsed %d, loop %d, timeout.\n", idx, start_time - last_time, t, count_loops);
11054 bMore = TRUE;
11055 break;
11056 }
11057 }
11058 }
11059
11060 //last_time = start_time;
11061
11062 return bMore;
11063
11064 }
11065#else /* LOCAL_ROUTINES */
11066
11067 return FALSE;
11068
11069#endif
11070}
11071
11072/********************************************************************/
11073static INT bm_notify_client(const char *buffer_name, int client_socket)
11074/********************************************************************\
11075
11076 Routine: bm_notify_client
11077
11078 Purpose: Called by cm_dispatch_ipc. Send an event notification to
11079 the connected client. Used by mserver to relay the BM_MSG
11080 buffer message from local UDP socket to the remote connected client.
11081
11082 Input:
11083 char *buffer_name Name of buffer
11084 int client_socket Network socket to client
11085
11086 Output:
11087 none
11088
11089 Function value:
11090 BM_SUCCESS Successful completion
11091
11092\********************************************************************/
11093{
11094 static DWORD last_time = 0;
11095 DWORD now = ss_millitime();
11096
11097 //printf("bm_notify_client: buffer [%s], socket %d, time %d\n", buffer_name, client_socket, now - last_time);
11098
11099 BUFFER* fbuf = NULL;
11100
11101 gBuffersMutex.lock();
11102
11103 for (size_t i = 0; i < gBuffers.size(); i++) {
11104 BUFFER* pbuf = gBuffers[i];
11105 if (!pbuf || !pbuf->attached)
11106 continue;
11107 if (strcmp(buffer_name, pbuf->buffer_header->name) == 0) {
11108 fbuf = pbuf;
11109 break;
11110 }
11111 }
11112
11113 gBuffersMutex.unlock();
11114
11115 if (!fbuf)
11116 return BM_INVALID_HANDLE;
11117
11118 /* don't send notification if client has no callback defined
11119 to receive events -> client calls bm_receive_event manually */
11120 if (!fbuf->callback)
11121 return DB_SUCCESS;
11122
11123 int convert_flags = rpc_get_convert_flags();
11124
11125 /* only send notification once each 500ms */
11126 if (now - last_time < 500)
11127 return DB_SUCCESS;
11128
11129 last_time = now;
11130
11131 char buffer[32];
11132 NET_COMMAND *nc = (NET_COMMAND *) buffer;
11133
11134 nc->header.routine_id = MSG_BM;
11135 nc->header.param_size = 0;
11136
11137 if (convert_flags) {
11140 }
11141
11142 //printf("bm_notify_client: Sending MSG_BM! buffer [%s]\n", buffer_name);
11143
11144 /* send the update notification to the client */
11145 send_tcp(client_socket, (char *) buffer, sizeof(NET_COMMAND_HEADER), 0);
11146
11147 return BM_SUCCESS;
11148}
11149
11150/********************************************************************/
11152/********************************************************************\
11153
11154 Routine: bm_poll_event
11155
11156 Purpose: Poll an event from a remote server. Gets called by
11157 rpc_client_dispatch() and by cm_yield()
11158
11159 Function value:
11160 BM_SUCCESS At least one event was received and dispatched
11161 BM_ASYNC_RETURN No events received
11162 SS_ABORT Network connection broken
11163
11164\********************************************************************/
11165{
11166 BOOL dispatched_something = FALSE;
11167
11168 //printf("bm_poll_event!\n");
11169
11170 DWORD start_time = ss_millitime();
11171
11172 std::vector<char> vec;
11173
11174 /* loop over all requests */
11175 _request_list_mutex.lock();
11176 bool locked = true;
11177 size_t n = _request_list.size();
11178 for (size_t i = 0; i < n; i++) {
11179 if (!locked) {
11180 _request_list_mutex.lock();
11181 locked = true;
11182 }
11183 /* continue if no dispatcher set (manual bm_receive_event) */
11184 if (_request_list[i].dispatcher == NULL)
11185 continue;
11186
11187 int buffer_handle = _request_list[i].buffer_handle;
11188
11189 /* must release the lock on the request list: user provided r.dispatcher() can add or remove event requests, and we will deadlock. K.O. */
11190 _request_list_mutex.unlock();
11191 locked = false;
11192
11193 do {
11194 /* receive event */
11195 int status = bm_receive_event_vec(buffer_handle, &vec, BM_NO_WAIT);
11196
11197 //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());
11198
11199 /* call user function if successful */
11200 if (status == BM_SUCCESS) {
11201 bm_dispatch_event(buffer_handle, (EVENT_HEADER*)vec.data());
11202 dispatched_something = TRUE;
11203 }
11204
11205 /* break if no more events */
11206 if (status == BM_ASYNC_RETURN)
11207 break;
11208
11209 /* break if corrupted event buffer */
11210 if (status == BM_TRUNCATED) {
11211 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());
11212 }
11213
11214 /* break if corrupted event buffer */
11215 if (status == BM_CORRUPTED)
11216 return SS_ABORT;
11217
11218 /* break if server died */
11219 if (status == RPC_NET_ERROR) {
11220 return SS_ABORT;
11221 }
11222
11223 /* stop after one second */
11224 if (ss_millitime() - start_time > 1000) {
11225 break;
11226 }
11227
11228 } while (TRUE);
11229 }
11230
11231 if (locked)
11232 _request_list_mutex.unlock();
11233
11234 if (dispatched_something)
11235 return BM_SUCCESS;
11236 else
11237 return BM_ASYNC_RETURN;
11238}
11239
11240/********************************************************************/
11266 if (rpc_is_remote())
11268
11269#ifdef LOCAL_ROUTINES
11270 {
11271 std::vector<BUFFER*> mybuffers;
11272
11273 gBuffersMutex.lock();
11274 mybuffers = gBuffers;
11275 gBuffersMutex.unlock();
11276
11277 /* go through all buffers */
11278 for (BUFFER* pbuf : mybuffers) {
11279 if (!pbuf)
11280 continue;
11281 if (!pbuf->attached)
11282 continue;
11283
11284 int status = bm_skip_event(pbuf);
11285 if (status != BM_SUCCESS)
11286 return status;
11287 }
11288 }
11289#endif /* LOCAL_ROUTINES */
11290
11291 return BM_SUCCESS;
11292}
11293
11295#ifndef DOXYGEN_SHOULD_SKIP_THIS
11296
11297#define MAX_DEFRAG_EVENTS 10
11298
11305
11307
11308/********************************************************************/
11309static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id,
11310 EVENT_HEADER *pevent, void *pdata,
11311 EVENT_HANDLER *dispatcher)
11312/********************************************************************\
11313
11314 Routine: bm_defragment_event
11315
11316 Purpose: Called internally from the event receiving routines
11317 bm_push_event and bm_poll_event to recombine event
11318 fragments and call the user callback routine upon
11319 completion.
11320
11321 Input:
11322 HNDLE buffer_handle Handle for the buffer containing event
11323 HNDLE request_id Handle for event request
11324 EVENT_HEADER *pevent Pointer to event header
11325 void *pata Pointer to event data
11326 dispatcher() User callback routine
11327
11328 Output:
11329 <calls dispatcher() after successfull recombination of event>
11330
11331 Function value:
11332 void
11333
11334\********************************************************************/
11335{
11336 INT i;
11337
11338 if ((uint16_t(pevent->event_id) & uint16_t(0xF000)) == uint16_t(EVENTID_FRAG1)) {
11339 /*---- start new event ----*/
11340
11341 //printf("First Frag detected : Ser#:%d ID=0x%x \n", pevent->serial_number, pevent->event_id);
11342
11343 /* check if fragments already stored */
11344 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11345 if (defrag_buffer[i].event_id == (pevent->event_id & 0x0FFF))
11346 break;
11347
11348 if (i < MAX_DEFRAG_EVENTS) {
11349 free(defrag_buffer[i].pevent);
11350 defrag_buffer[i].pevent = NULL;
11351 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11352 cm_msg(MERROR, "bm_defragement_event",
11353 "Received new event with ID %d while old fragments were not completed",
11354 (pevent->event_id & 0x0FFF));
11355 }
11356
11357 /* search new slot */
11358 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11359 if (defrag_buffer[i].event_id == 0)
11360 break;
11361
11362 if (i == MAX_DEFRAG_EVENTS) {
11363 cm_msg(MERROR, "bm_defragment_event",
11364 "Not enough defragment buffers, please increase MAX_DEFRAG_EVENTS and recompile");
11365 return;
11366 }
11367
11368 /* check event size */
11369 if (pevent->data_size != sizeof(DWORD)) {
11370 cm_msg(MERROR, "bm_defragment_event",
11371 "Received first event fragment with %d bytes instead of %d bytes, event ignored",
11372 pevent->data_size, (int) sizeof(DWORD));
11373 return;
11374 }
11375
11376 /* setup defragment buffer */
11377 defrag_buffer[i].event_id = (pevent->event_id & 0x0FFF);
11378 defrag_buffer[i].data_size = *(DWORD *) pdata;
11381
11382 if (defrag_buffer[i].pevent == NULL) {
11383 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11384 cm_msg(MERROR, "bm_defragement_event", "Not enough memory to allocate event defragment buffer");
11385 return;
11386 }
11387
11388 memcpy(defrag_buffer[i].pevent, pevent, sizeof(EVENT_HEADER));
11391
11392 // printf("First frag[%d] (ID %d) Ser#:%d sz:%d\n", i, defrag_buffer[i].event_id,
11393 // pevent->serial_number, defrag_buffer[i].data_size);
11394
11395 return;
11396 }
11397
11398 /* search buffer for that event */
11399 for (i = 0; i < MAX_DEFRAG_EVENTS; i++)
11400 if (defrag_buffer[i].event_id == (pevent->event_id & 0xFFF))
11401 break;
11402
11403 if (i == MAX_DEFRAG_EVENTS) {
11404 /* no buffer available -> no first fragment received */
11405 cm_msg(MERROR, "bm_defragement_event",
11406 "Received fragment without first fragment (ID %d) Ser#:%d",
11407 pevent->event_id & 0x0FFF, pevent->serial_number);
11408 return;
11409 }
11410
11411 /* add fragment to buffer */
11413 free(defrag_buffer[i].pevent);
11414 defrag_buffer[i].pevent = NULL;
11415 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11416 cm_msg(MERROR, "bm_defragement_event",
11417 "Received fragments with more data (%d) than event size (%d)",
11418 pevent->data_size + defrag_buffer[i].received, defrag_buffer[i].data_size);
11419 return;
11420 }
11421
11422 memcpy(((char *) defrag_buffer[i].pevent) + sizeof(EVENT_HEADER) +
11423 defrag_buffer[i].received, pdata, pevent->data_size);
11424
11425 defrag_buffer[i].received += pevent->data_size;
11426
11427 //printf("Other frag[%d][%d] (ID %d) Ser#:%d sz:%d\n", i, j++,
11428 // defrag_buffer[i].event_id, pevent->serial_number, pevent->data_size);
11429
11430 if (defrag_buffer[i].received == defrag_buffer[i].data_size) {
11431 /* event complete */
11432 dispatcher(buffer_handle, request_id, defrag_buffer[i].pevent, defrag_buffer[i].pevent + 1);
11433 free(defrag_buffer[i].pevent);
11434 defrag_buffer[i].pevent = NULL;
11435 memset(&defrag_buffer[i].event_id, 0, sizeof(EVENT_DEFRAG_BUFFER));
11436 }
11437}
11438
11440#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11441
/* end of bmfunctionc */
11444
11450/********************************************************************\
11451* *
11452* RPC functions *
11453* *
11454\********************************************************************/
11455
11457{
11458public:
11459 std::atomic_bool connected{false}; /* socket is connected */
11460 std::string host_name; /* server name */
11461 int port = 0; /* server port */
11462 std::mutex mutex; /* connection lock */
11463 int index = 0; /* index in the connection array */
11464 std::string client_name; /* name of remote client */
11465 int send_sock = 0; /* tcp socket */
11466 int remote_hw_type = 0; /* remote hardware type */
11467 int rpc_timeout = 0; /* timeout in milliseconds */
11468
11469 void print() {
11470 printf("index %d, client \"%s\", host \"%s\", port %d, socket %d, connected %d, timeout %d",
11471 index,
11472 client_name.c_str(),
11473 host_name.c_str(),
11474 port,
11475 send_sock,
11476 int(connected),
11477 rpc_timeout);
11478 }
11479
11481 if (send_sock > 0) {
11483 }
11484 connected = false;
11485 }
11486};
11487
11488/* globals */
11489
11490//
11491// locking rules for client connections:
11492//
11493// lock _client_connections_mutex, look at _client_connections vector and c->connected, unlock _client_connections_mutex
11494// 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
11495// lock individual connection, check c->connected, work on the connection, unlock connection
11496//
11497// ok to access without locking client connection:
11498//
11499// - c->connected (std::atomic, but must recheck it after taking the lock)
11500// - only inside rpc_client_connect() under protection of gHostnameMutex: c->host_name and c->port
11501//
11502// this will deadlock, wrong locking order: lock individual connection, lock of _client_connections_mutex
11503// this will deadlock, wrong unlocking order: unlock of _client_connections_mutex, unlock individual connection
11504//
11505// lifetime of client connections:
11506//
11507// - client connection slots are allocated by rpc_client_connect()
11508// - client connection slots are deleted by rpc_client_shutdown() called from cm_disconnect_experiment()
11509// - client slots marked NULL are free and will be reused by rpc_client_connect()
11510// - client slots marked c->connected == false are free and will be reused by rpc_client_connect()
11511// - rpc_client_check() will close connections that have dead tcp sockets, set c->connected = FALSE to mark the slot free for reuse
11512// - rpc_client_disconnect() will close the connection and set c->connected = FALSE to mark the slot free for reuse
11513// - rpc_client_call() can race rpc_client_disconnect() running in another thread, if disconnect happens first,
11514// client call will see an empty slot and return an error
11515// - rpc_client_call() can race a disconnect()/connect() pair, if disconnect and connect happen first,
11516// client call will be made to the wrong connection. for this reason, one should call rpc_client_disconnect()
11517// only when one is sure no other threads are running concurrent rpc client calls.
11518//
11519
11521static std::vector<RPC_CLIENT_CONNECTION*> _client_connections;
11522
11523static RPC_SERVER_CONNECTION _server_connection; // connection to the mserver
11524static bool _rpc_is_remote = false;
11525
11526//static RPC_SERVER_ACCEPTION _server_acception[MAX_RPC_CONNECTION];
11527static std::vector<RPC_SERVER_ACCEPTION*> _server_acceptions;
11528static RPC_SERVER_ACCEPTION* _mserver_acception = NULL; // mserver acception
11529
11531{
11532 assert(idx >= 0);
11533 assert(idx < (int)_server_acceptions.size());
11534 assert(_server_acceptions[idx] != NULL);
11535 return _server_acceptions[idx];
11536}
11537
11542
11544{
11545 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11546 if (_server_acceptions[idx] && (_server_acceptions[idx]->recv_sock == 0)) {
11547 //printf("rpc_new_server_acception: reuse acception in slot %d\n", idx);
11548 return _server_acceptions[idx];
11549 }
11550 }
11551
11553
11554 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
11555 if (_server_acceptions[idx] == NULL) {
11556 //printf("rpc_new_server_acception: new acception, reuse slot %d\n", idx);
11557 _server_acceptions[idx] = sa;
11558 return _server_acceptions[idx];
11559 }
11560 }
11561
11562 //printf("rpc_new_server_acception: new acception, array size %d, push_back\n", (int)_server_acceptions.size());
11563 _server_acceptions.push_back(sa);
11564
11565 return sa;
11566}
11567
11569{
11570 //printf("RPC_SERVER_ACCEPTION::close: connection from %s program %s mserver %d\n", host_name.c_str(), prog_name.c_str(), is_mserver);
11571
11572 if (is_mserver) {
11573 assert(_mserver_acception == this);
11574 _mserver_acception = NULL;
11575 is_mserver = false;
11576 }
11577
11578 /* close server connection */
11579 if (recv_sock)
11581 if (send_sock)
11583 if (event_sock)
11585
11586 /* free TCP cache */
11587 if (net_buffer) {
11588 //printf("free net_buffer %p+%d\n", net_buffer, net_buffer_size);
11589 free(net_buffer);
11590 net_buffer = NULL;
11591 net_buffer_size = 0;
11592 }
11593
11594 /* mark this entry as invalid */
11595 clear();
11596}
11597
11598static std::vector<RPC_LIST> rpc_list;
11599static std::mutex rpc_list_mutex;
11600
11602
11603
11604/********************************************************************\
11605* conversion functions *
11606\********************************************************************/
11607
11608void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags) {
11609 *convert_flags = 0;
11610
11611 /* big/little endian conversion */
11612 if (((remote_hw_type & DRI_BIG_ENDIAN) &&
11613 (hw_type & DRI_LITTLE_ENDIAN)) || ((remote_hw_type & DRI_LITTLE_ENDIAN)
11614 && (hw_type & DRI_BIG_ENDIAN)))
11615 *convert_flags |= CF_ENDIAN;
11616
11617 /* float conversion between IEEE and VAX G */
11618 if ((remote_hw_type & DRF_G_FLOAT) && (hw_type & DRF_IEEE))
11619 *convert_flags |= CF_VAX2IEEE;
11620
11621 /* float conversion between VAX G and IEEE */
11622 if ((remote_hw_type & DRF_IEEE) && (hw_type & DRF_G_FLOAT))
11623 *convert_flags |= CF_IEEE2VAX;
11624
11626 //if (remote_hw_type & DR_ASCII)
11627 // *convert_flags |= CF_ASCII;
11628}
11629
11630/********************************************************************/
11634
11635/********************************************************************/
11636void rpc_ieee2vax_float(float *var) {
11637 unsigned short int lo, hi;
11638
11639 /* swap hi and lo word */
11640 lo = *((short int *) (var) + 1);
11641 hi = *((short int *) (var));
11642
11643 /* correct exponent */
11644 if (lo != 0)
11645 lo += 0x100;
11646
11647 *((short int *) (var) + 1) = hi;
11648 *((short int *) (var)) = lo;
11649}
11650
11651void rpc_vax2ieee_float(float *var) {
11652 unsigned short int lo, hi;
11653
11654 /* swap hi and lo word */
11655 lo = *((short int *) (var) + 1);
11656 hi = *((short int *) (var));
11657
11658 /* correct exponent */
11659 if (hi != 0)
11660 hi -= 0x100;
11661
11662 *((short int *) (var) + 1) = hi;
11663 *((short int *) (var)) = lo;
11664
11665}
11666
11667void rpc_vax2ieee_double(double *var) {
11668 unsigned short int i1, i2, i3, i4;
11669
11670 /* swap words */
11671 i1 = *((short int *) (var) + 3);
11672 i2 = *((short int *) (var) + 2);
11673 i3 = *((short int *) (var) + 1);
11674 i4 = *((short int *) (var));
11675
11676 /* correct exponent */
11677 if (i4 != 0)
11678 i4 -= 0x20;
11679
11680 *((short int *) (var) + 3) = i4;
11681 *((short int *) (var) + 2) = i3;
11682 *((short int *) (var) + 1) = i2;
11683 *((short int *) (var)) = i1;
11684}
11685
11686void rpc_ieee2vax_double(double *var) {
11687 unsigned short int i1, i2, i3, i4;
11688
11689 /* swap words */
11690 i1 = *((short int *) (var) + 3);
11691 i2 = *((short int *) (var) + 2);
11692 i3 = *((short int *) (var) + 1);
11693 i4 = *((short int *) (var));
11694
11695 /* correct exponent */
11696 if (i1 != 0)
11697 i1 += 0x20;
11698
11699 *((short int *) (var) + 3) = i4;
11700 *((short int *) (var) + 2) = i3;
11701 *((short int *) (var) + 1) = i2;
11702 *((short int *) (var)) = i1;
11703}
11704
11705/********************************************************************/
11706void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags) {
11707
11708 if (convert_flags & CF_ENDIAN) {
11709 if (tid == TID_UINT16 || tid == TID_INT16) WORD_SWAP(data);
11710 if (tid == TID_UINT32 || tid == TID_INT32 || tid == TID_BOOL || tid == TID_FLOAT) DWORD_SWAP(data);
11711 if (tid == TID_DOUBLE) QWORD_SWAP(data);
11712 }
11713
11714 if (((convert_flags & CF_IEEE2VAX) && !(flags & RPC_OUTGOING)) ||
11715 ((convert_flags & CF_VAX2IEEE) && (flags & RPC_OUTGOING))) {
11716 if (tid == TID_FLOAT)
11717 rpc_ieee2vax_float((float *) data);
11718 if (tid == TID_DOUBLE)
11719 rpc_ieee2vax_double((double *) data);
11720 }
11721
11722 if (((convert_flags & CF_IEEE2VAX) && (flags & RPC_OUTGOING)) ||
11723 ((convert_flags & CF_VAX2IEEE) && !(flags & RPC_OUTGOING))) {
11724 if (tid == TID_FLOAT)
11725 rpc_vax2ieee_float((float *) data);
11726 if (tid == TID_DOUBLE)
11727 rpc_vax2ieee_double((double *) data);
11728 }
11729}
11730
11731void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
11732/********************************************************************\
11733
11734 Routine: rpc_convert_data
11735
11736 Purpose: Convert data format between differenct computers
11737
11738 Input:
11739 void *data Pointer to data
11740 INT tid Type ID of data, one of TID_xxx
11741 INT flags Combination of following flags:
11742 RPC_IN: data is input parameter
11743 RPC_OUT: data is output variable
11744 RPC_FIXARRAY, RPC_VARARRAY: data is array
11745 of "size" bytes (see next param.)
11746 RPC_OUTGOING: data is outgoing
11747 INT total_size Size of bytes of data. Used for variable
11748 length arrays.
11749 INT convert_flags Flags for data conversion
11750
11751 Output:
11752 void *data Is converted according to _convert_flag
11753 value
11754
11755 Function value:
11756 RPC_SUCCESS Successful completion
11757
11758\********************************************************************/
11759{
11760 /* convert array */
11761 if (flags & (RPC_FIXARRAY | RPC_VARARRAY)) {
11762 int single_size = rpc_tid_size(tid);
11763 /* don't convert TID_ARRAY & TID_STRUCT */
11764 if (single_size == 0)
11765 return;
11766
11767 int n = total_size / single_size;
11768
11769 for (int i = 0; i < n; i++) {
11770 char* p = (char *) data + (i * single_size);
11771 rpc_convert_single(p, tid, flags, convert_flags);
11772 }
11773 } else {
11774 rpc_convert_single(data, tid, flags, convert_flags);
11775 }
11776}
11777
11778/********************************************************************\
11779* type ID functions *
11780\********************************************************************/
11781
11783 if (id >= 0 && id < TID_LAST)
11784 return tid_size[id];
11785
11786 return 0;
11787}
11788
11789const char *rpc_tid_name(INT id) {
11790 if (id >= 0 && id < TID_LAST)
11791 return tid_name[id];
11792 else
11793 return "<unknown>";
11794}
11795
11796const char *rpc_tid_name_old(INT id) {
11797 if (id >= 0 && id < TID_LAST)
11798 return tid_name_old[id];
11799 else
11800 return "<unknown>";
11801}
11802
11803int rpc_name_tid(const char* name) // inverse of rpc_tid_name()
11804{
11805 for (int i=0; i<TID_LAST; i++) {
11806 if (strcmp(name, tid_name[i]) == 0)
11807 return i;
11808 }
11809
11810 for (int i=0; i<TID_LAST; i++) {
11811 if (strcmp(name, tid_name_old[i]) == 0)
11812 return i;
11813 }
11814
11815 return 0;
11816}
11817
11818/********************************************************************\
11819* client functions *
11820\********************************************************************/
11821
11822/********************************************************************/
11837
11838 return RPC_SUCCESS;
11839}
11840
11841/********************************************************************/
11853{
11854 for (int i = 0; new_list[i].id != 0; i++) {
11855 /* check valid ID for user functions */
11856 if (new_list != rpc_get_internal_list(0) &&
11857 new_list != rpc_get_internal_list(1) && (new_list[i].id < RPC_MIN_ID
11858 || new_list[i].id > RPC_MAX_ID)) {
11859 cm_msg(MERROR, "rpc_register_functions", "registered RPC function with invalid ID %d", new_list[i].id);
11860 }
11861 }
11862
11863 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11864
11865 /* check double defined functions */
11866 for (int i = 0; new_list[i].id != 0; i++) {
11867 for (size_t j = 0; j < rpc_list.size(); j++) {
11868 if (rpc_list[j].id == new_list[i].id) {
11869 return RPC_DOUBLE_DEFINED;
11870 }
11871 }
11872 }
11873
11874 /* append new functions */
11875 for (int i = 0; new_list[i].id != 0; i++) {
11876 RPC_LIST e = new_list[i];
11877
11878 /* set default dispatcher */
11879 if (e.dispatch == NULL) {
11880 e.dispatch = func;
11881 }
11882
11883 rpc_list.push_back(e);
11884 }
11885
11886 return RPC_SUCCESS;
11887}
11888
11889
11890
11892#ifndef DOXYGEN_SHOULD_SKIP_THIS
11893
11894/********************************************************************/
11896/********************************************************************\
11897
11898 Routine: rpc_deregister_functions
11899
11900 Purpose: Free memory of previously registered functions
11901
11902 Input:
11903 none
11904
11905 Output:
11906 none
11907
11908 Function value:
11909 RPC_SUCCESS Successful completion
11910
11911\********************************************************************/
11912{
11913 rpc_list_mutex.lock();
11914 rpc_list.clear();
11915 rpc_list_mutex.unlock();
11916
11917 return RPC_SUCCESS;
11918}
11919
11920
11921/********************************************************************/
11922INT rpc_register_function(INT id, INT(*func)(INT, void **))
11923/********************************************************************\
11924
11925 Routine: rpc_register_function
11926
11927 Purpose: Replace a dispatch function for a specific rpc routine
11928
11929 Input:
11930 INT id RPC ID
11931 INT *func New dispatch function
11932
11933 Output:
11934 <implicit: func gets copied to rpc_list>
11935
11936 Function value:
11937 RPC_SUCCESS Successful completion
11938 RPC_INVALID_ID RPC ID not found
11939
11940\********************************************************************/
11941{
11942 std::lock_guard<std::mutex> guard(rpc_list_mutex);
11943
11944 for (size_t i = 0; i < rpc_list.size(); i++) {
11945 if (rpc_list[i].id == id) {
11946 rpc_list[i].dispatch = func;
11947 return RPC_SUCCESS;
11948 }
11949 }
11950
11951 return RPC_INVALID_ID;
11952}
11953
11954/********************************************************************/
11955
11956static int handle_msg_odb(int n, const NET_COMMAND *nc) {
11957 //printf("rpc_client_dispatch: MSG_ODB: packet size %d, expected %d\n", n, (int)(sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)));
11958 if (n == sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT)) {
11959 /* update a changed record */
11960 HNDLE hDB = *((INT *) nc->param);
11961 HNDLE hKeyRoot = *((INT *) nc->param + 1);
11962 HNDLE hKey = *((INT *) nc->param + 2);
11963 int index = *((INT *) nc->param + 3);
11964 return db_update_record_local(hDB, hKeyRoot, hKey, index);
11965 }
11966 return CM_VERSION_MISMATCH;
11967}
11968
11969/********************************************************************/
11971/********************************************************************\
11972
11973 Routine: rpc_client_dispatch
11974
11975 Purpose: Receive data from the mserver: watchdog and buffer notification messages
11976
11977\********************************************************************/
11978{
11979 INT status = 0;
11980 char net_buffer[256];
11981
11982 int n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
11983 if (n <= 0)
11984 return SS_ABORT;
11985
11986 NET_COMMAND *nc = (NET_COMMAND *) net_buffer;
11987
11988 if (nc->header.routine_id == MSG_ODB) {
11989 status = handle_msg_odb(n, nc);
11990 } else if (nc->header.routine_id == MSG_WATCHDOG) {
11991 nc->header.routine_id = 1;
11992 nc->header.param_size = 0;
11993 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
11995 } else if (nc->header.routine_id == MSG_BM) {
11996 fd_set readfds;
11997 struct timeval timeout;
11998
11999 //printf("rpc_client_dispatch: received MSG_BM!\n");
12000
12001 /* receive further messages to empty TCP queue */
12002 do {
12003 FD_ZERO(&readfds);
12004 FD_SET(sock, &readfds);
12005
12006 timeout.tv_sec = 0;
12007 timeout.tv_usec = 0;
12008
12009 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12010
12011 if (FD_ISSET(sock, &readfds)) {
12012 n = recv_tcp(sock, net_buffer, sizeof(net_buffer), 0);
12013 if (n <= 0)
12014 return SS_ABORT;
12015
12016 if (nc->header.routine_id == MSG_ODB) {
12017 status = handle_msg_odb(n, nc);
12018 } else if (nc->header.routine_id == MSG_WATCHDOG) {
12019 nc->header.routine_id = 1;
12020 nc->header.param_size = 0;
12021 send_tcp(sock, net_buffer, sizeof(NET_COMMAND_HEADER), 0);
12023 }
12024 }
12025
12026 } while (FD_ISSET(sock, &readfds));
12027
12028 /* poll event from server */
12030 }
12031
12032 return status;
12033}
12034
12035
12036/********************************************************************/
12037INT rpc_client_connect(const char *host_name, INT port, const char *client_name, HNDLE *hConnection)
12038/********************************************************************\
12039
12040 Routine: rpc_client_connect
12041
12042 Purpose: Establish a network connection to a remote client
12043
12044 Input:
12045 char *host_name IP address of host to connect to.
12046 INT port TPC port to connect to.
12047 char *clinet_name Client program name
12048
12049 Output:
12050 HNDLE *hConnection Handle for new connection which can be used
12051 in future rpc_call(hConnection....) calls
12052
12053 Function value:
12054 RPC_SUCCESS Successful completion
12055 RPC_NET_ERROR Error in socket call
12056 RPC_NO_CONNECTION Maximum number of connections reached
12057 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12058
12059\********************************************************************/
12060{
12061 INT i, status;
12062 bool debug = false;
12063
12064 /* check if cm_connect_experiment was called */
12065 if (_client_name.length() == 0) {
12066 cm_msg(MERROR, "rpc_client_connect", "cm_connect_experiment/rpc_set_name not called");
12067 return RPC_NOT_REGISTERED;
12068 }
12069
12070 /* refuse connection to port 0 */
12071 if (port == 0) {
12072 cm_msg(MERROR, "rpc_client_connect", "invalid port %d", port);
12073 return RPC_NET_ERROR;
12074 }
12075
12076 RPC_CLIENT_CONNECTION* c = NULL;
12077
12078 static std::mutex gHostnameMutex;
12079
12080 {
12081 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12082
12083 if (debug) {
12084 printf("rpc_client_connect: host \"%s\", port %d, client \"%s\"\n", host_name, port, client_name);
12085 for (size_t i = 0; i < _client_connections.size(); i++) {
12086 if (_client_connections[i]) {
12087 printf("client connection %d: ", (int)i);
12088 _client_connections[i]->print();
12089 printf("\n");
12090 }
12091 }
12092 }
12093
12094 // slot with index 0 is not used, fill it with a NULL
12095
12096 if (_client_connections.empty()) {
12097 _client_connections.push_back(NULL);
12098 }
12099
12100 bool hostname_locked = false;
12101
12102 /* check if connection already exists */
12103 for (size_t i = 1; i < _client_connections.size(); i++) {
12105 if (c && c->connected) {
12106
12107 if (!hostname_locked) {
12108 gHostnameMutex.lock();
12109 hostname_locked = true;
12110 }
12111
12112 if ((c->host_name == host_name) && (c->port == port)) {
12113 // NB: we must release the hostname lock before taking
12114 // c->mutex to avoid a locking order inversion deadlock:
12115 // later on we lock the hostname mutex while holding the c->mutex
12116 gHostnameMutex.unlock();
12117 hostname_locked = false;
12118 std::lock_guard<std::mutex> cguard(c->mutex);
12119 // check if socket is still connected
12120 if (c->connected) {
12121 // found connection slot with matching hostname and port number
12122 status = ss_socket_wait(c->send_sock, 0);
12123 if (status == SS_TIMEOUT) { // yes, still connected and empty
12124 // so reuse it connection
12125 *hConnection = c->index;
12126 if (debug) {
12127 printf("already connected: ");
12128 c->print();
12129 printf("\n");
12130 }
12131 // implicit unlock of c->mutex
12132 // gHostnameLock is not locked here
12133 return RPC_SUCCESS;
12134 }
12135 //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);
12136 c->close_locked();
12137 }
12138 // implicit unlock of c->mutex
12139 }
12140 }
12141 }
12142
12143 if (hostname_locked) {
12144 gHostnameMutex.unlock();
12145 hostname_locked = false;
12146 }
12147
12148 // only start reusing connections once we have
12149 // a good number of slots allocated.
12150 if (_client_connections.size() > 10) {
12151 static int last_reused = 0;
12152
12153 int size = _client_connections.size();
12154 for (int j = 1; j < size; j++) {
12155 int i = (last_reused + j) % size;
12156 if (_client_connections[i] && !_client_connections[i]->connected) {
12158 if (debug) {
12159 printf("last reused %d, reusing slot %d: ", last_reused, (int)i);
12160 c->print();
12161 printf("\n");
12162 }
12163 last_reused = i;
12164 break;
12165 }
12166 }
12167 }
12168
12169 // no slots to reuse, allocate a new slot.
12170 if (!c) {
12172
12173 // if empty slot not found, add to end of array
12174 c->index = _client_connections.size();
12175 _client_connections.push_back(c);
12176
12177 if (debug) {
12178 printf("new connection appended to array: ");
12179 c->print();
12180 printf("\n");
12181 }
12182 }
12183
12184 c->mutex.lock();
12185 c->connected = true; // rpc_client_connect() in another thread may try to grab this slot
12186
12187 // done with the array of connections
12188 // implicit unlock of _client_connections_mutex
12189 }
12190
12191 // locked connection slot for new connection
12192 assert(c != NULL);
12193
12194 std::string errmsg;
12195
12196 /* create a new socket for connecting to remote server */
12197 status = ss_socket_connect_tcp(host_name, port, &c->send_sock, &errmsg);
12198 if (status != SS_SUCCESS) {
12199 cm_msg(MERROR, "rpc_client_connect", "cannot connect to \"%s\" port %d: %s", host_name, port, errmsg.c_str());
12200 c->mutex.unlock();
12201 return RPC_NET_ERROR;
12202 }
12203
12204 gHostnameMutex.lock();
12205
12206 c->host_name = host_name;
12207 c->port = port;
12208
12209 gHostnameMutex.unlock();
12210
12211 c->client_name = client_name;
12212 c->rpc_timeout = DEFAULT_RPC_TIMEOUT;
12213
12214 /* set TCP_NODELAY option for better performance */
12215 i = 1;
12216 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i));
12217
12218 /* send local computer info */
12219 std::string local_prog_name = rpc_get_name();
12220 std::string local_host_name = ss_gethostname();
12221
12222 int hw_type = rpc_get_hw_type();
12223
12224 std::string cstr = msprintf("%d %s %s %s", hw_type, cm_get_version(), local_prog_name.c_str(), local_host_name.c_str());
12225
12226 int size = cstr.length() + 1;
12227 i = send(c->send_sock, cstr.c_str(), size, 0);
12228 if (i < 0 || i != size) {
12229 cm_msg(MERROR, "rpc_client_connect", "cannot send %d bytes, send() returned %d, errno %d (%s)", size, i, errno, strerror(errno));
12230 c->mutex.unlock();
12231 return RPC_NET_ERROR;
12232 }
12233
12234 bool restore_watchdog_timeout = false;
12235 BOOL watchdog_call;
12236 DWORD watchdog_timeout;
12237 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
12238
12239 //printf("watchdog timeout: %d, rpc_connect_timeout: %d\n", watchdog_timeout, _rpc_connect_timeout);
12240
12241 if (_rpc_connect_timeout >= (int) watchdog_timeout) {
12242 restore_watchdog_timeout = true;
12243 cm_set_watchdog_params(watchdog_call, _rpc_connect_timeout + 1000);
12244 }
12245
12246 char str[256];
12247
12248 /* receive remote computer info */
12249 i = recv_string(c->send_sock, str, sizeof(str), _rpc_connect_timeout);
12250
12251 if (restore_watchdog_timeout) {
12252 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
12253 }
12254
12255 if (i <= 0) {
12256 cm_msg(MERROR, "rpc_client_connect", "timeout waiting for server reply");
12257 c->close_locked();
12258 c->mutex.unlock();
12259 return RPC_NET_ERROR;
12260 }
12261
12262 int remote_hw_type = 0;
12263 char remote_version[32];
12264 remote_version[0] = 0;
12265 sscanf(str, "%d %s", &remote_hw_type, remote_version);
12266
12267 c->remote_hw_type = remote_hw_type;
12268
12269 /* print warning if version patch level doesn't agree */
12270 char v1[32];
12271 mstrlcpy(v1, remote_version, sizeof(v1));
12272 if (strchr(v1, '.'))
12273 if (strchr(strchr(v1, '.') + 1, '.'))
12274 *strchr(strchr(v1, '.') + 1, '.') = 0;
12275
12276 mstrlcpy(str, cm_get_version(), sizeof(str));
12277 if (strchr(str, '.'))
12278 if (strchr(strchr(str, '.') + 1, '.'))
12279 *strchr(strchr(str, '.') + 1, '.') = 0;
12280
12281 if (strcmp(v1, str) != 0) {
12282 cm_msg(MERROR, "rpc_client_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", remote_version, cm_get_version());
12283 }
12284
12285 c->connected = true;
12286
12287 *hConnection = c->index;
12288
12289 c->mutex.unlock();
12290
12291 return RPC_SUCCESS;
12292}
12293
12294/********************************************************************/
12296/********************************************************************\
12297
12298 Routine: rpc_client_check
12299
12300 Purpose: Check all client connections if remote client closed link
12301
12302\********************************************************************/
12303{
12304#if 0
12305 for (i = 0; i < MAX_RPC_CONNECTION; i++)
12306 if (_client_connection[i].send_sock != 0)
12307 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);
12308#endif
12309
12310 std::lock_guard<std::mutex> guard(_client_connections_mutex);
12311
12312 /* check for broken connections */
12313 for (unsigned i = 0; i < _client_connections.size(); i++) {
12315 if (c && c->connected) {
12316 std::lock_guard<std::mutex> cguard(c->mutex);
12317
12318 if (!c->connected) {
12319 // implicit unlock
12320 continue;
12321 }
12322
12323 //printf("rpc_client_check: connection %d: ", i);
12324 //c->print();
12325 //printf("\n");
12326
12327 int ok = 0;
12328
12329 fd_set readfds;
12330 FD_ZERO(&readfds);
12331 FD_SET(c->send_sock, &readfds);
12332
12333 struct timeval timeout;
12334 timeout.tv_sec = 0;
12335 timeout.tv_usec = 0;
12336
12337 int status;
12338
12339#ifdef OS_WINNT
12340 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12341#else
12342 do {
12343 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12344 } while (status == -1 && errno == EINTR); /* dont return if an alarm signal was cought */
12345#endif
12346
12347 if (!FD_ISSET(c->send_sock, &readfds)) {
12348 // implicit unlock
12349 continue;
12350 }
12351
12352 char buffer[64];
12353
12354 status = recv(c->send_sock, (char *) buffer, sizeof(buffer), MSG_PEEK);
12355 //printf("recv %d status %d, errno %d (%s)\n", sock, status, errno, strerror(errno));
12356
12357 if (status < 0) {
12358#ifndef OS_WINNT
12359 if (errno == EAGAIN) { // still connected
12360 ok = 1;
12361 } else
12362#endif
12363 {
12364 // connection error
12365 cm_msg(MERROR, "rpc_client_check",
12366 "RPC client connection to \"%s\" on host \"%s\" is broken, recv() errno %d (%s)",
12367 c->client_name.c_str(),
12368 c->host_name.c_str(),
12369 errno, strerror(errno));
12370 ok = 0;
12371 }
12372 } else if (status == 0) {
12373 // connection closed by remote end without sending an EXIT message
12374 // this can happen if the remote end has crashed, so this message
12375 // is still necessary as a useful diagnostic for unexpected crashes
12376 // of midas programs. K.O.
12377 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());
12378 ok = 0;
12379 } else {
12380 // read some data
12381 ok = 1;
12382 if (equal_ustring(buffer, "EXIT")) {
12383 /* normal exit */
12384 ok = 0;
12385 }
12386 }
12387
12388 if (!ok) {
12389 //printf("rpc_client_check: closing connection %d: ", i);
12390 //c->print();
12391 //printf("\n");
12392
12393 // connection lost, close the socket
12394 c->close_locked();
12395 }
12396
12397 // implicit unlock
12398 }
12399 }
12400
12401 // implicit unlock of _client_connections_mutex
12402}
12403
12404
12405/********************************************************************/
12406INT rpc_server_connect(const char *host_name, const char *exp_name)
12407/********************************************************************\
12408
12409 Routine: rpc_server_connect
12410
12411 Purpose: Extablish a network connection to a remote MIDAS
12412 server using a callback scheme.
12413
12414 Input:
12415 char *host_name IP address of host to connect to.
12416
12417 INT port TPC port to connect to.
12418
12419 char *exp_name Name of experiment to connect to. By using
12420 this name, several experiments (e.g. online
12421 DAQ and offline analysis) can run simultan-
12422 eously on the same host.
12423
12424 Output:
12425 none
12426
12427 Function value:
12428 RPC_SUCCESS Successful completion
12429 RPC_NET_ERROR Error in socket call
12430 RPC_NOT_REGISTERED cm_connect_experiment was not called properly
12431 CM_UNDEF_EXP Undefined experiment on server
12432
12433\********************************************************************/
12434{
12435 INT i, status;
12436 INT remote_hw_type, hw_type;
12437 char str[200], version[32], v1[32];
12438 fd_set readfds;
12439 struct timeval timeout;
12440 int port = MIDAS_TCP_PORT;
12441 char *s;
12442
12443#ifdef OS_WINNT
12444 {
12445 WSADATA WSAData;
12446
12447 /* Start windows sockets */
12448 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
12449 return RPC_NET_ERROR;
12450 }
12451#endif
12452
12453 /* check if local connection */
12454 if (host_name[0] == 0)
12455 return RPC_SUCCESS;
12456
12457 /* register system functions */
12459
12460 /* check if cm_connect_experiment was called */
12461 if (_client_name.length() == 0) {
12462 cm_msg(MERROR, "rpc_server_connect", "cm_connect_experiment/rpc_set_name not called");
12463 return RPC_NOT_REGISTERED;
12464 }
12465
12466 /* check if connection already exists */
12468 return RPC_SUCCESS;
12469
12473
12474 bool listen_localhost = false;
12475
12476 if (strcmp(host_name, "localhost") == 0)
12477 listen_localhost = true;
12478
12479 int lsock1, lport1;
12480 int lsock2, lport2;
12481 int lsock3, lport3;
12482
12483 std::string errmsg;
12484
12485 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock1, &lport1, &errmsg);
12486
12487 if (status != SS_SUCCESS) {
12488 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12489 return RPC_NET_ERROR;
12490 }
12491
12492 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock2, &lport2, &errmsg);
12493
12494 if (status != SS_SUCCESS) {
12495 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12496 return RPC_NET_ERROR;
12497 }
12498
12499 status = ss_socket_listen_tcp(listen_localhost, 0, &lsock3, &lport3, &errmsg);
12500
12501 if (status != SS_SUCCESS) {
12502 cm_msg(MERROR, "rpc_server_connect", "cannot create listener socket: %s", errmsg.c_str());
12503 return RPC_NET_ERROR;
12504 }
12505
12506 /* extract port number from host_name */
12507 mstrlcpy(str, host_name, sizeof(str));
12508 s = strchr(str, ':');
12509 if (s) {
12510 *s = 0;
12511 port = strtoul(s + 1, NULL, 0);
12512 }
12513
12514 int sock;
12515
12516 status = ss_socket_connect_tcp(str, port, &sock, &errmsg);
12517
12518 if (status != SS_SUCCESS) {
12519 cm_msg(MERROR, "rpc_server_connect", "cannot connect to mserver on host \"%s\" port %d: %s", str, port, errmsg.c_str());
12520 return RPC_NET_ERROR;
12521 }
12522
12523 /* connect to experiment */
12524 if (exp_name[0] == 0)
12525 sprintf(str, "C %d %d %d %s Default", lport1, lport2, lport3, cm_get_version());
12526 else
12527 sprintf(str, "C %d %d %d %s %s", lport1, lport2, lport3, cm_get_version(), exp_name);
12528
12529 send(sock, str, strlen(str) + 1, 0);
12530 i = recv_string(sock, str, sizeof(str), _rpc_connect_timeout);
12531 ss_socket_close(&sock);
12532 if (i <= 0) {
12533 cm_msg(MERROR, "rpc_server_connect", "timeout on receive status from server");
12534 return RPC_NET_ERROR;
12535 }
12536
12537 status = version[0] = 0;
12538 sscanf(str, "%d %s", &status, version);
12539
12540 if (status == 2) {
12541/* message "undefined experiment" should be displayed by application */
12542 return CM_UNDEF_EXP;
12543 }
12544
12545 /* print warning if version patch level doesn't agree */
12546 strcpy(v1, version);
12547 if (strchr(v1, '.'))
12548 if (strchr(strchr(v1, '.') + 1, '.'))
12549 *strchr(strchr(v1, '.') + 1, '.') = 0;
12550
12551 strcpy(str, cm_get_version());
12552 if (strchr(str, '.'))
12553 if (strchr(strchr(str, '.') + 1, '.'))
12554 *strchr(strchr(str, '.') + 1, '.') = 0;
12555
12556 if (strcmp(v1, str) != 0) {
12557 cm_msg(MERROR, "rpc_server_connect", "remote MIDAS version \'%s\' differs from local version \'%s\'", version,
12558 cm_get_version());
12559 }
12560
12561 /* wait for callback on send and recv socket with timeout */
12562 FD_ZERO(&readfds);
12563 FD_SET(lsock1, &readfds);
12564 FD_SET(lsock2, &readfds);
12565 FD_SET(lsock3, &readfds);
12566
12567 timeout.tv_sec = _rpc_connect_timeout / 1000;
12568 timeout.tv_usec = 0;
12569
12570 do {
12571 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
12572
12573 /* if an alarm signal was cought, restart select with reduced timeout */
12574 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
12575 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
12576
12577 } while (status == -1); /* dont return if an alarm signal was cought */
12578
12579 if (!FD_ISSET(lsock1, &readfds)) {
12580 cm_msg(MERROR, "rpc_server_connect", "mserver subprocess could not be started (check path)");
12581 ss_socket_close(&lsock1);
12582 ss_socket_close(&lsock2);
12583 ss_socket_close(&lsock3);
12584 return RPC_NET_ERROR;
12585 }
12586
12587 _server_connection.send_sock = accept(lsock1, NULL, NULL);
12588 _server_connection.recv_sock = accept(lsock2, NULL, NULL);
12589 _server_connection.event_sock = accept(lsock3, NULL, NULL);
12590
12592 cm_msg(MERROR, "rpc_server_connect", "accept() failed");
12593 return RPC_NET_ERROR;
12594 }
12595
12596 ss_socket_close(&lsock1);
12597 ss_socket_close(&lsock2);
12598 ss_socket_close(&lsock3);
12599
12600 /* set TCP_NODELAY option for better performance */
12601 int flag = 1;
12602 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12603 setsockopt(_server_connection.event_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag));
12604
12605 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
12606 flag = 2 * 1024 * 1024;
12607 status = setsockopt(_server_connection.event_sock, SOL_SOCKET, SO_SNDBUF, (char *) &flag, sizeof(flag));
12608 if (status != 0)
12609 cm_msg(MERROR, "rpc_server_connect", "cannot setsockopt(SOL_SOCKET, SO_SNDBUF), errno %d (%s)", errno, strerror(errno));
12610
12611 /* send local computer info */
12612 std::string local_prog_name = rpc_get_name();
12613 hw_type = rpc_get_hw_type();
12614 sprintf(str, "%d %s", hw_type, local_prog_name.c_str());
12615
12616 send(_server_connection.send_sock, str, strlen(str) + 1, 0);
12617
12618 /* receive remote computer info */
12620 if (i <= 0) {
12621 cm_msg(MERROR, "rpc_server_connect", "timeout on receive remote computer info");
12622 return RPC_NET_ERROR;
12623 }
12624
12625 sscanf(str, "%d", &remote_hw_type);
12626 _server_connection.remote_hw_type = remote_hw_type;
12627
12629
12630 _rpc_is_remote = true;
12631
12632 return RPC_SUCCESS;
12633}
12634
12635/********************************************************************/
12636
12638{
12640 if (hConn >= 0 && hConn < (int)_client_connections.size()) {
12642 if (c && c->connected) {
12644 c->mutex.lock();
12645 if (!c->connected) {
12646 // disconnected while we were waiting for the lock
12647 c->mutex.unlock();
12648 return NULL;
12649 }
12650 return c;
12651 }
12652 }
12654 return NULL;
12655}
12656
12658{
12659 /* close all open connections */
12660
12662
12663 for (unsigned i = 0; i < _client_connections.size(); i++) {
12665 if (c && c->connected) {
12666 int index = c->index;
12667 // must unlock the array, otherwise we hang -
12668 // rpc_client_disconnect() will do rpc_call_client()
12669 // which needs to lock the array to convert handle
12670 // to connection pointer. Ouch! K.O. Dec 2020.
12674 }
12675 }
12676
12677 for (unsigned i = 0; i < _client_connections.size(); i++) {
12679 //printf("client connection %d %p\n", i, c);
12680 if (c) {
12681 //printf("client connection %d %p connected %d\n", i, c, c->connected);
12682 if (!c->connected) {
12683 delete c;
12684 _client_connections[i] = NULL;
12685 }
12686 }
12687 }
12688
12690
12691 /* close server connection from other clients */
12692 for (unsigned i = 0; i < _server_acceptions.size(); i++) {
12693 if (_server_acceptions[i] && _server_acceptions[i]->recv_sock) {
12694 send(_server_acceptions[i]->recv_sock, "EXIT", 5, 0);
12695 _server_acceptions[i]->close();
12696 }
12697 }
12698}
12699
12700/********************************************************************/
12702/********************************************************************\
12703
12704 Routine: rpc_client_disconnect
12705
12706 Purpose: Close a rpc connection to a MIDAS client
12707
12708 Input:
12709 HNDLE hConn Handle of connection
12710 BOOL bShutdown Shut down remote server if TRUE
12711
12712 Output:
12713 none
12714
12715 Function value:
12716 RPC_SUCCESS Successful completion
12717
12718\********************************************************************/
12719{
12720 /* notify server about exit */
12721
12722 /* call exit and shutdown with RPC_NO_REPLY because client will exit immediately without possibility of replying */
12723
12725
12726 return RPC_SUCCESS;
12727}
12728
12729/********************************************************************/
12731/********************************************************************\
12732
12733 Routine: rpc_server_disconnect
12734
12735 Purpose: Close a rpc connection to a MIDAS server and close all
12736 server connections from other clients
12737
12738 Input:
12739 none
12740
12741 Output:
12742 none
12743
12744 Function value:
12745 RPC_SUCCESS Successful completion
12746 RPC_NET_ERROR Error in socket call
12747 RPC_NO_CONNECTION Maximum number of connections reached
12748
12749\********************************************************************/
12750{
12751 static int rpc_server_disconnect_recursion_level = 0;
12752
12753 if (rpc_server_disconnect_recursion_level)
12754 return RPC_SUCCESS;
12755
12756 rpc_server_disconnect_recursion_level = 1;
12757
12758 /* flush remaining events */
12760
12761 /* notify server about exit */
12762 if (rpc_is_connected()) {
12764 }
12765
12766 /* close sockets */
12773
12775
12776 /* remove semaphore */
12777 if (_mutex_rpc)
12779 _mutex_rpc = NULL;
12780
12781 rpc_server_disconnect_recursion_level = 0;
12782 return RPC_SUCCESS;
12783}
12784
12785/********************************************************************/
12787/********************************************************************\
12788
12789 Routine: rpc_is_remote
12790
12791 Purpose: Return true if program is connected to a remote server
12792
12793 Input:
12794 none
12795
12796 Output:
12797 none
12798
12799 Function value:
12800 INT true is remote client connected to mserver, false if local connection
12801
12802\********************************************************************/
12803{
12804 return _rpc_is_remote;
12805}
12806
12807/********************************************************************/
12809/********************************************************************\
12810
12811 Routine: rpc_is_connected
12812
12813 Purpose: Return true if connection to mserver is still open
12814
12815 Input:
12816 none
12817
12818 Output:
12819 none
12820
12821 Function value:
12822 INT true if connection to mserver is still open, false if connection to mserver is already closed
12823
12824\********************************************************************/
12825{
12826 return _server_connection.send_sock != 0;
12827}
12828
12829/********************************************************************/
12831/********************************************************************\
12832
12833 Routine: rpc_get_mserver_hostname
12834
12835 Purpose: Return the hostname of the mserver connection (host:port format)
12836
12837\********************************************************************/
12838{
12840}
12841
12842/********************************************************************/
12844/********************************************************************\
12845
12846 Routine: rpc_is_mserver
12847
12848 Purpose: Return true if we are the mserver
12849
12850 Function value:
12851 INT "true" if we are the mserver
12852
12853\********************************************************************/
12854{
12855 return _mserver_acception != NULL;
12856}
12857
12858/********************************************************************/
12860/********************************************************************\
12861
12862 Routine: rpc_get_hw_type
12863
12864 Purpose: get hardware information
12865
12866 Function value:
12867 INT combination of DRI_xxx bits
12868
12869\********************************************************************/
12870{
12871 {
12872 {
12873 INT tmp_type, size;
12874 DWORD dummy;
12875 unsigned char *p;
12876 float f;
12877 double d;
12878
12879 tmp_type = 0;
12880
12881 /* test pointer size */
12882 size = sizeof(p);
12883 if (size == 2)
12884 tmp_type |= DRI_16;
12885 if (size == 4)
12886 tmp_type |= DRI_32;
12887 if (size == 8)
12888 tmp_type |= DRI_64;
12889
12890 /* test if little or big endian machine */
12891 dummy = 0x12345678;
12892 p = (unsigned char *) &dummy;
12893 if (*p == 0x78)
12894 tmp_type |= DRI_LITTLE_ENDIAN;
12895 else if (*p == 0x12)
12896 tmp_type |= DRI_BIG_ENDIAN;
12897 else
12898 cm_msg(MERROR, "rpc_get_option", "unknown byte order format");
12899
12900 /* floating point format */
12901 f = (float) 1.2345;
12902 dummy = 0;
12903 memcpy(&dummy, &f, sizeof(f));
12904 if ((dummy & 0xFF) == 0x19 &&
12905 ((dummy >> 8) & 0xFF) == 0x04 && ((dummy >> 16) & 0xFF) == 0x9E
12906 && ((dummy >> 24) & 0xFF) == 0x3F)
12907 tmp_type |= DRF_IEEE;
12908 else if ((dummy & 0xFF) == 0x9E &&
12909 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x19
12910 && ((dummy >> 24) & 0xFF) == 0x04)
12911 tmp_type |= DRF_G_FLOAT;
12912 else
12913 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12914
12915 d = (double) 1.2345;
12916 dummy = 0;
12917 memcpy(&dummy, &d, sizeof(f));
12918 if ((dummy & 0xFF) == 0x8D && /* little endian */
12919 ((dummy >> 8) & 0xFF) == 0x97 && ((dummy >> 16) & 0xFF) == 0x6E
12920 && ((dummy >> 24) & 0xFF) == 0x12)
12921 tmp_type |= DRF_IEEE;
12922 else if ((dummy & 0xFF) == 0x83 && /* big endian */
12923 ((dummy >> 8) & 0xFF) == 0xC0 && ((dummy >> 16) & 0xFF) == 0xF3
12924 && ((dummy >> 24) & 0xFF) == 0x3F)
12925 tmp_type |= DRF_IEEE;
12926 else if ((dummy & 0xFF) == 0x13 &&
12927 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x83
12928 && ((dummy >> 24) & 0xFF) == 0xC0)
12929 tmp_type |= DRF_G_FLOAT;
12930 else if ((dummy & 0xFF) == 0x9E &&
12931 ((dummy >> 8) & 0xFF) == 0x40 && ((dummy >> 16) & 0xFF) == 0x18
12932 && ((dummy >> 24) & 0xFF) == 0x04)
12933 cm_msg(MERROR, "rpc_get_option",
12934 "MIDAS cannot handle VAX D FLOAT format. Please compile with the /g_float flag");
12935 else
12936 cm_msg(MERROR, "rpc_get_option", "unknown floating point format");
12937
12938 return tmp_type;
12939 }
12940 }
12941}
12942
12944#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12945
12946/********************************************************************/
12954#if 0
12955INT rpc_set_option(HNDLE hConn, INT item, INT value) {
12956 switch (item) {
12957 case RPC_OTIMEOUT:
12958 if (hConn == -1)
12960 else if (hConn == -2)
12962 else {
12964 if (c) {
12965 c->rpc_timeout = value;
12966 c->mutex.unlock();
12967 }
12968 }
12969 break;
12970
12971 case RPC_NODELAY:
12972 if (hConn == -1) {
12973 setsockopt(_server_connection.send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12974 } else {
12976 if (c) {
12977 setsockopt(c->send_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
12978 c->mutex.unlock();
12979 }
12980 }
12981 break;
12982
12983 default:
12984 cm_msg(MERROR, "rpc_set_option", "invalid argument");
12985 break;
12986 }
12987
12988 return 0;
12989}
12990#endif
12991
12992/********************************************************************/
12999{
13000 if (hConn == RPC_HNDLE_MSERVER) {
13002 } else if (hConn == RPC_HNDLE_CONNECT) {
13003 return _rpc_connect_timeout;
13004 } else {
13006 if (c) {
13007 int timeout = c->rpc_timeout;
13008 c->mutex.unlock();
13009 return timeout;
13010 }
13011 }
13012 return 0;
13013}
13014
13015/********************************************************************/
13023INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int* old_timeout_msec)
13024{
13025 //printf("rpc_set_timeout: hConn %d, timeout_msec %d\n", hConn, timeout_msec);
13026
13027 if (hConn == RPC_HNDLE_MSERVER) {
13028 if (old_timeout_msec)
13029 *old_timeout_msec = _server_connection.rpc_timeout;
13030 _server_connection.rpc_timeout = timeout_msec;
13031 } else if (hConn == RPC_HNDLE_CONNECT) {
13032 if (old_timeout_msec)
13033 *old_timeout_msec = _rpc_connect_timeout;
13034 _rpc_connect_timeout = timeout_msec;
13035 } else {
13037 if (c) {
13038 if (old_timeout_msec)
13039 *old_timeout_msec = c->rpc_timeout;
13040 c->rpc_timeout = timeout_msec;
13041 c->mutex.unlock();
13042 } else {
13043 if (old_timeout_msec)
13044 *old_timeout_msec = 0;
13045 }
13046 }
13047 return RPC_SUCCESS;
13048}
13049
13050
13052#ifndef DOXYGEN_SHOULD_SKIP_THIS
13053
13054/********************************************************************/
13056/********************************************************************\
13057
13058 Routine: rpc_get_convert_flags
13059
13060 Purpose: Get RPC convert_flags for the mserver connection
13061
13062 Function value:
13063 INT Actual option
13064
13065\********************************************************************/
13066{
13069 else
13070 return 0;
13071}
13072
13073static std::string _mserver_path;
13074
13075/********************************************************************/
13077/********************************************************************\
13078
13079 Routine: rpc_get_mserver_path()
13080
13081 Purpose: Get path of the mserver executable
13082
13083\********************************************************************/
13084{
13085 return _mserver_path.c_str();
13086}
13087
13088/********************************************************************/
13089INT rpc_set_mserver_path(const char *path)
13090/********************************************************************\
13091
13092 Routine: rpc_set_mserver_path
13093
13094 Purpose: Remember the path of the mserver executable
13095
13096 Input:
13097 char *path Full path of the mserver executable
13098
13099 Function value:
13100 RPC_SUCCESS Successful completion
13101
13102\********************************************************************/
13103{
13104 _mserver_path = path;
13105 return RPC_SUCCESS;
13106}
13107
13108/********************************************************************/
13109std::string rpc_get_name()
13110/********************************************************************\
13111
13112 Routine: rpc_get_name
13113
13114 Purpose: Get name set by rpc_set_name
13115
13116 Input:
13117 none
13118
13119 Output:
13120 char* name The location pointed by *name receives a
13121 copy of the _prog_name
13122
13123 Function value:
13124 RPC_SUCCESS Successful completion
13125
13126\********************************************************************/
13127{
13128 return _client_name;
13129}
13130
13131
13132/********************************************************************/
13134/********************************************************************\
13135
13136 Routine: rpc_set_name
13137
13138 Purpose: Set name of actual program for further rpc connections
13139
13140 Input:
13141 char *name Program name, up to NAME_LENGTH chars,
13142 no blanks
13143
13144 Output:
13145 none
13146
13147 Function value:
13148 RPC_SUCCESS Successful completion
13149
13150\********************************************************************/
13151{
13153
13154 return RPC_SUCCESS;
13155}
13156
13157
13158/********************************************************************/
13159INT rpc_set_debug(void (*func)(const char *), INT mode)
13160/********************************************************************\
13161
13162 Routine: rpc_set_debug
13163
13164 Purpose: Set a function which is called on every RPC call to
13165 display the function name and parameters of the RPC
13166 call.
13167
13168 Input:
13169 void *func(char*) Pointer to function.
13170 INT mode Debug mode
13171
13172 Output:
13173 none
13174
13175 Function value:
13176 RPC_SUCCESS Successful completion
13177
13178\********************************************************************/
13179{
13180 _debug_print = func;
13181 _debug_mode = mode;
13182 return RPC_SUCCESS;
13183}
13184
13185/********************************************************************/
13186void rpc_debug_printf(const char *format, ...)
13187/********************************************************************\
13188
13189 Routine: rpc_debug_print
13190
13191 Purpose: Calls function set via rpc_set_debug to output a string.
13192
13193 Input:
13194 char *str Debug string
13195
13196 Output:
13197 none
13198
13199\********************************************************************/
13200{
13201 va_list argptr;
13202 char str[1000];
13203
13204 if (_debug_mode) {
13205 va_start(argptr, format);
13206 vsprintf(str, (char *) format, argptr);
13207 va_end(argptr);
13208
13209 if (_debug_print) {
13210 strcat(str, "\n");
13212 } else
13213 puts(str);
13214 }
13215}
13216
13217/********************************************************************/
13218void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg) {
13219 switch (arg_type) {
13220 /* On the stack, the minimum parameter size is sizeof(int).
13221 To avoid problems on little endian systems, treat all
13222 smaller parameters as int's */
13223 case TID_UINT8:
13224 case TID_INT8:
13225 case TID_CHAR:
13226 case TID_UINT16:
13227 case TID_INT16:
13228 *((int *) arg) = va_arg(*arg_ptr, int);
13229 break;
13230
13231 case TID_INT32:
13232 case TID_BOOL:
13233 *((INT *) arg) = va_arg(*arg_ptr, INT);
13234 break;
13235
13236 case TID_UINT32:
13237 *((DWORD *) arg) = va_arg(*arg_ptr, DWORD);
13238 break;
13239
13240 /* float variables are passed as double by the compiler */
13241 case TID_FLOAT:
13242 *((float *) arg) = (float) va_arg(*arg_ptr, double);
13243 break;
13244
13245 case TID_DOUBLE:
13246 *((double *) arg) = va_arg(*arg_ptr, double);
13247 break;
13248
13249 case TID_ARRAY:
13250 *((char **) arg) = va_arg(*arg_ptr, char *);
13251 break;
13252 }
13253}
13254
13255/********************************************************************/
13256static void rpc_call_encode(va_list& ap, const RPC_LIST& rl, NET_COMMAND** nc)
13257{
13258 bool debug = false;
13259
13260 if (debug) {
13261 printf("encode rpc_id %d \"%s\"\n", rl.id, rl.name);
13262 for (int i=0; rl.param[i].tid != 0; i++) {
13263 int tid = rl.param[i].tid;
13264 int flags = rl.param[i].flags;
13265 int n = rl.param[i].n;
13266 printf("i=%d, tid %d, flags 0x%x, n %d\n", i, tid, flags, n);
13267 }
13268 }
13269
13270 char args[MAX_RPC_PARAMS][8];
13271
13272 for (int i=0; rl.param[i].tid != 0; i++) {
13273 int tid = rl.param[i].tid;
13274 int flags = rl.param[i].flags;
13275 int arg_type = 0;
13276
13277 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13278 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13279 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13280
13281 if (bpointer)
13282 arg_type = TID_ARRAY;
13283 else
13284 arg_type = tid;
13285
13286 /* floats are passed as doubles, at least under NT */
13287 if (tid == TID_FLOAT && !bpointer)
13288 arg_type = TID_DOUBLE;
13289
13290 //printf("arg %d, tid %d, flags 0x%x, arg_type %d, bpointer %d\n", i, tid, flags, arg_type, bpointer);
13291
13292 rpc_va_arg(&ap, arg_type, args[i]);
13293 }
13294
13295 size_t buf_size = sizeof(NET_COMMAND) + 4 * 1024;
13296 char* buf = (char *)malloc(buf_size);
13297 assert(buf);
13298
13299 (*nc) = (NET_COMMAND*) buf;
13300
13301 /* find out if we are on a big endian system */
13302 bool bbig = ((rpc_get_hw_type() & DRI_BIG_ENDIAN) > 0);
13303
13304 char* param_ptr = (*nc)->param;
13305
13306 for (int i=0; rl.param[i].tid != 0; i++) {
13307 int tid = rl.param[i].tid;
13308 int flags = rl.param[i].flags;
13309 int arg_type = 0;
13310
13311 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13312 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13313 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13314
13315 if (bpointer)
13316 arg_type = TID_ARRAY;
13317 else
13318 arg_type = tid;
13319
13320 /* floats are passed as doubles, at least under NT */
13321 if (tid == TID_FLOAT && !bpointer)
13322 arg_type = TID_DOUBLE;
13323
13324 /* get pointer to argument */
13325 //char arg[8];
13326 //rpc_va_arg(&ap, arg_type, arg);
13327
13328 char* arg = args[i];
13329
13330 /* shift 1- and 2-byte parameters to the LSB on big endian systems */
13331 if (bbig) {
13332 if (tid == TID_UINT8 || tid == TID_CHAR || tid == TID_INT8) {
13333 arg[0] = arg[3];
13334 }
13335 if (tid == TID_UINT16 || tid == TID_INT16) {
13336 arg[0] = arg[2];
13337 arg[1] = arg[3];
13338 }
13339 }
13340
13341 if (flags & RPC_IN) {
13342 int arg_size = 0;
13343
13344 if (bpointer)
13345 arg_size = rpc_tid_size(tid);
13346 else
13347 arg_size = rpc_tid_size(arg_type);
13348
13349 /* for strings, the argument size depends on the string length */
13350 if (tid == TID_STRING || tid == TID_LINK) {
13351 arg_size = 1 + strlen((char *) *((char **) arg));
13352 }
13353
13354 /* for varibale length arrays, the size is given by
13355 the next parameter on the stack */
13356 if (flags & RPC_VARARRAY) {
13357 //va_list aptmp;
13359 //va_copy(aptmp, ap);
13360
13361 //char arg_tmp[8];
13362 //rpc_va_arg(&aptmp, TID_ARRAY, arg_tmp);
13363
13364 const char* arg_tmp = args[i+1];
13365
13366 /* for (RPC_IN+RPC_OUT) parameters, size argument is a pointer */
13367 if (flags & RPC_OUT)
13368 arg_size = *((INT *) *((void **) arg_tmp));
13369 else
13370 arg_size = *((INT *) arg_tmp);
13371
13372 *((INT *) param_ptr) = ALIGN8(arg_size);
13373 param_ptr += ALIGN8(sizeof(INT));
13374
13375 //va_end(aptmp);
13376 }
13377
13378 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13379 arg_size = rl.param[i].n;
13380
13381 /* always align parameter size */
13382 int param_size = ALIGN8(arg_size);
13383
13384 {
13385 size_t param_offset = (char *) param_ptr - (char *)(*nc);
13386
13387 if (param_offset + param_size + 16 > buf_size) {
13388 size_t new_size = param_offset + param_size + 1024;
13389 //printf("resize nc %zu to %zu\n", buf_size, new_size);
13390 buf = (char *) realloc(buf, new_size);
13391 assert(buf);
13392 buf_size = new_size;
13393 (*nc) = (NET_COMMAND*) buf;
13394 param_ptr = buf + param_offset;
13395 }
13396 }
13397
13398 if (bpointer) {
13399 if (debug) {
13400 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);
13401 }
13402 memcpy(param_ptr, (void *) *((void **) arg), arg_size);
13403 } else if (tid == TID_FLOAT) {
13404 if (debug) {
13405 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);
13406 }
13407 /* floats are passed as doubles on most systems */
13408 *((float *) param_ptr) = (float) *((double *) arg);
13409 } else {
13410 if (debug) {
13411 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);
13412 }
13413 memcpy(param_ptr, arg, arg_size);
13414 }
13415
13416 param_ptr += param_size;
13417 }
13418 }
13419
13420 (*nc)->header.param_size = (POINTER_T) param_ptr - (POINTER_T) (*nc)->param;
13421
13422 if (debug)
13423 printf("encode rpc_id %d \"%s\" buf_size %d, param_size %d\n", rl.id, rl.name, (int)buf_size, (*nc)->header.param_size);
13424}
13425
13426/********************************************************************/
13427static int rpc_call_decode(va_list& ap, const RPC_LIST& rl, const char* buf, size_t buf_size)
13428{
13429 bool debug = false;
13430
13431 if (debug)
13432 printf("decode reply to rpc_id %d \"%s\" has %d bytes\n", rl.id, rl.name, (int)buf_size);
13433
13434 /* extract result variables and place it to argument list */
13435
13436 const char* param_ptr = buf;
13437
13438 for (int i = 0; rl.param[i].tid != 0; i++) {
13439 int tid = rl.param[i].tid;
13440 int flags = rl.param[i].flags;
13441 int arg_type = 0;
13442
13443 bool bpointer = (flags & RPC_POINTER) || (flags & RPC_OUT) ||
13444 (flags & RPC_FIXARRAY) || (flags & RPC_VARARRAY) ||
13445 tid == TID_STRING || tid == TID_ARRAY || tid == TID_STRUCT || tid == TID_LINK;
13446
13447 if (bpointer)
13448 arg_type = TID_ARRAY;
13449 else
13450 arg_type = rl.param[i].tid;
13451
13452 if (tid == TID_FLOAT && !bpointer)
13453 arg_type = TID_DOUBLE;
13454
13455 char arg[8];
13456 rpc_va_arg(&ap, arg_type, arg);
13457
13458 if (rl.param[i].flags & RPC_OUT) {
13459
13460 if (param_ptr == NULL) {
13461 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);
13462 return RPC_NET_ERROR;
13463 }
13464
13465 tid = rl.param[i].tid;
13466 int arg_size = rpc_tid_size(tid);
13467
13468 if (tid == TID_STRING || tid == TID_LINK)
13469 arg_size = strlen((char *) (param_ptr)) + 1;
13470
13471 if (flags & RPC_VARARRAY) {
13472 arg_size = *((INT *) param_ptr);
13473 param_ptr += ALIGN8(sizeof(INT));
13474 }
13475
13476 if (tid == TID_STRUCT || (flags & RPC_FIXARRAY))
13477 arg_size = rl.param[i].n;
13478
13479 /* parameter size is always aligned */
13480 int param_size = ALIGN8(arg_size);
13481
13482 /* return parameters are always pointers */
13483 if (*((char **) arg)) {
13484 if (debug)
13485 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);
13486 memcpy((void *) *((char **) arg), param_ptr, arg_size);
13487 }
13488
13489 param_ptr += param_size;
13490 }
13491 }
13492
13493 return RPC_SUCCESS;
13494}
13495
13496/********************************************************************/
13497INT rpc_client_call(HNDLE hConn, DWORD routine_id, ...)
13498/********************************************************************\
13499
13500 Routine: rpc_client_call
13501
13502 Purpose: Call a function on a MIDAS client
13503
13504 Input:
13505 INT hConn Client connection
13506 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13507
13508 ... variable argument list
13509
13510 Output:
13511 (depends on argument list)
13512
13513 Function value:
13514 RPC_SUCCESS Successful completion
13515 RPC_NET_ERROR Error in socket call
13516 RPC_NO_CONNECTION No active connection
13517 RPC_TIMEOUT Timeout in RPC call
13518 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13519 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13520
13521\********************************************************************/
13522{
13524
13525 if (!c) {
13526 cm_msg(MERROR, "rpc_client_call", "invalid rpc connection handle %d", hConn);
13527 return RPC_NO_CONNECTION;
13528 }
13529
13530 //printf("rpc_client_call: handle %d, connection: ", hConn);
13531 //c->print();
13532 //printf("\n");
13533
13534 INT i, status;
13535
13536 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13537 routine_id &= ~RPC_NO_REPLY;
13538
13539 //if (rpc_no_reply)
13540 // printf("rpc_client_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13541
13542 // make local copy of the client name just in case _client_connection is erased by another thread
13543
13544 /* find rpc_index */
13545
13546 int rpc_index = -1;
13547 const char *rpc_name = NULL;
13548 RPC_LIST rpc_entry;
13549
13550 rpc_list_mutex.lock();
13551 for (size_t i = 0; i < rpc_list.size(); i++) {
13552 if (rpc_list[i].id == (int) routine_id) {
13553 rpc_index = i;
13554 rpc_name = rpc_list[rpc_index].name;
13555 rpc_entry = rpc_list[rpc_index];
13556 break;
13557 }
13558 }
13559 rpc_list_mutex.unlock();
13560
13561 if (rpc_index < 0) {
13562 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);
13563 c->mutex.unlock();
13564 return RPC_INVALID_ID;
13565 }
13566
13567 NET_COMMAND *nc = NULL;
13568
13569 /* examine variable argument list and convert it to parameter array */
13570 va_list ap;
13571 va_start(ap, routine_id);
13572
13573 rpc_call_encode(ap, rpc_entry, &nc);
13574
13575 va_end(ap);
13576
13577 nc->header.routine_id = routine_id;
13578
13579 if (rpc_no_reply)
13581
13582 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13583
13584 /* in FAST TCP mode, only send call and return immediately */
13585 if (rpc_no_reply) {
13586 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13587
13588 if (i != send_size) {
13589 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);
13590 free(nc);
13591 c->mutex.unlock();
13592 return RPC_NET_ERROR;
13593 }
13594
13595 free(nc);
13596
13597 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN) {
13598 //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);
13599 //c->print();
13600 //printf("\n");
13601 c->close_locked();
13602 }
13603
13604 c->mutex.unlock();
13605 return RPC_SUCCESS;
13606 }
13607
13608 /* in TCP mode, send and wait for reply on send socket */
13609 i = send_tcp(c->send_sock, (char *) nc, send_size, 0);
13610 if (i != send_size) {
13611 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);
13612 c->mutex.unlock();
13613 return RPC_NET_ERROR;
13614 }
13615
13616 free(nc);
13617 nc = NULL;
13618
13619 bool restore_watchdog_timeout = false;
13620 BOOL watchdog_call;
13621 DWORD watchdog_timeout;
13622 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13623
13624 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, c->rpc_timeout);
13625
13626 if (c->rpc_timeout >= (int) watchdog_timeout) {
13627 restore_watchdog_timeout = true;
13628 cm_set_watchdog_params(watchdog_call, c->rpc_timeout + 1000);
13629 }
13630
13631 DWORD rpc_status = 0;
13632 DWORD buf_size = 0;
13633 char* buf = NULL;
13634
13635 /* receive result on send socket */
13636 status = ss_recv_net_command(c->send_sock, &rpc_status, &buf_size, &buf, c->rpc_timeout);
13637
13638 if (restore_watchdog_timeout) {
13639 cm_set_watchdog_params(watchdog_call, watchdog_timeout);
13640 }
13641
13642 if (status == SS_TIMEOUT) {
13643 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);
13644 if (buf)
13645 free(buf);
13646 c->mutex.unlock();
13647 return RPC_TIMEOUT;
13648 }
13649
13650 if (status != SS_SUCCESS) {
13651 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);
13652 if (buf)
13653 free(buf);
13654 c->mutex.unlock();
13655 return RPC_NET_ERROR;
13656 }
13657
13658 c->mutex.unlock();
13659
13660 if (rpc_status == RPC_INVALID_ID) {
13661 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);
13662 if (buf)
13663 free(buf);
13664 return rpc_status;
13665 }
13666
13667 /* extract result variables and place it to argument list */
13668
13669 va_start(ap, routine_id);
13670
13671 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13672
13673 if (status != RPC_SUCCESS) {
13674 rpc_status = status;
13675 }
13676
13677 va_end(ap);
13678
13679 if (buf)
13680 free(buf);
13681 buf = NULL;
13682 buf_size = 0;
13683
13684 return rpc_status;
13685}
13686
13687/********************************************************************/
13688INT rpc_call(DWORD routine_id, ...)
13689/********************************************************************\
13690
13691 Routine: rpc_call
13692
13693 Purpose: Call a function on a MIDAS server
13694
13695 Input:
13696 INT routine_id routine ID as defined in RPC.H (RPC_xxx)
13697
13698 ... variable argument list
13699
13700 Output:
13701 (depends on argument list)
13702
13703 Function value:
13704 RPC_SUCCESS Successful completion
13705 RPC_NET_ERROR Error in socket call
13706 RPC_NO_CONNECTION No active connection
13707 RPC_TIMEOUT Timeout in RPC call
13708 RPC_INVALID_ID Invalid routine_id (not in rpc_list)
13709 RPC_EXCEED_BUFFER Paramters don't fit in network buffer
13710
13711\********************************************************************/
13712{
13713 va_list ap;
13714 INT i, status;
13715
13716 BOOL rpc_no_reply = routine_id & RPC_NO_REPLY;
13717 routine_id &= ~RPC_NO_REPLY;
13718
13719 //if (rpc_no_reply)
13720 // printf("rpc_call: routine_id %d, RPC_NO_REPLY\n", routine_id);
13721
13722 int send_sock = _server_connection.send_sock;
13723 int rpc_timeout = _server_connection.rpc_timeout;
13724
13725 if (!send_sock) {
13726 fprintf(stderr, "rpc_call(routine_id=%d) failed, no connection to mserver.\n", routine_id);
13727 return RPC_NET_ERROR;
13728 }
13729
13730 if (!_mutex_rpc) {
13731 /* create a local mutex for multi-threaded applications */
13733 }
13734
13735 status = ss_mutex_wait_for(_mutex_rpc, 10000 + rpc_timeout);
13736 if (status != SS_SUCCESS) {
13737 cm_msg(MERROR, "rpc_call", "Mutex timeout");
13738 return RPC_MUTEX_TIMEOUT;
13739 }
13740
13741 /* find rpc definition */
13742
13743 int idx = -1;
13744 const char* rpc_name = NULL;
13745 RPC_LIST rpc_entry;
13746
13747 rpc_list_mutex.lock();
13748
13749 for (size_t i = 0; i < rpc_list.size(); i++) {
13750 if (rpc_list[i].id == (int) routine_id) {
13751 idx = i;
13752 rpc_name = rpc_list[idx].name;
13753 rpc_entry = rpc_list[idx];
13754 break;
13755 }
13756 }
13757
13758 rpc_list_mutex.unlock();
13759
13760 if (idx < 0) {
13762 cm_msg(MERROR, "rpc_call", "invalid rpc ID (%d)", routine_id);
13763 return RPC_INVALID_ID;
13764 }
13765
13766 /* prepare output buffer */
13767
13768 NET_COMMAND* nc = NULL;
13769
13770 /* examine variable argument list and convert it to parameter array */
13771 va_start(ap, routine_id);
13772
13773 rpc_call_encode(ap, rpc_entry, &nc);
13774
13775 va_end(ap);
13776
13777 nc->header.routine_id = routine_id;
13778
13779 if (rpc_no_reply)
13781
13782 int send_size = nc->header.param_size + sizeof(NET_COMMAND_HEADER);
13783
13784 /* do not wait for reply if requested RPC_NO_REPLY */
13785 if (rpc_no_reply) {
13786 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13787
13788 if (i != send_size) {
13790 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13791 free(nc);
13792 return RPC_NET_ERROR;
13793 }
13794
13796 free(nc);
13797 return RPC_SUCCESS;
13798 }
13799
13800 /* in TCP mode, send and wait for reply on send socket */
13801 i = send_tcp(send_sock, (char *) nc, send_size, 0);
13802 if (i != send_size) {
13804 cm_msg(MERROR, "rpc_call", "rpc \"%s\" error: send_tcp() failed", rpc_name);
13805 free(nc);
13806 return RPC_NET_ERROR;
13807 }
13808
13809 free(nc);
13810 nc = NULL;
13811
13812 bool restore_watchdog_timeout = false;
13813 BOOL watchdog_call;
13814 DWORD watchdog_timeout;
13815 cm_get_watchdog_params(&watchdog_call, &watchdog_timeout);
13816
13817 //printf("watchdog timeout: %d, rpc_timeout: %d\n", watchdog_timeout, rpc_timeout);
13818
13819 if (!rpc_is_remote()) {
13820 // if RPC is remote, we are connected to an mserver,
13821 // the mserver takes care of watchdog timeouts.
13822 // otherwise we should make sure the watchdog timeout
13823 // is longer than the RPC timeout. K.O.
13824 if (rpc_timeout >= (int) watchdog_timeout) {
13825 restore_watchdog_timeout = true;
13826 cm_set_watchdog_params_local(watchdog_call, rpc_timeout + 1000);
13827 }
13828 }
13829
13830 DWORD rpc_status = 0;
13831 DWORD buf_size = 0;
13832 char* buf = NULL;
13833
13834 status = ss_recv_net_command(send_sock, &rpc_status, &buf_size, &buf, rpc_timeout);
13835
13836 if (restore_watchdog_timeout) {
13837 cm_set_watchdog_params_local(watchdog_call, watchdog_timeout);
13838 }
13839
13840 /* drop the mutex, we are done with the socket, argument unpacking is done from our own buffer */
13841
13843
13844 /* check for reply errors */
13845
13846 if (status == SS_TIMEOUT) {
13847 cm_msg(MERROR, "rpc_call", "routine \"%s\": timeout waiting for reply, program abort", rpc_name);
13848 if (buf)
13849 free(buf);
13850 abort(); // cannot continue - our mserver is not talking to us!
13851 return RPC_TIMEOUT;
13852 }
13853
13854 if (status != SS_SUCCESS) {
13855 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, ss_recv_net_command() status %d, program abort", rpc_name, status);
13856 if (buf)
13857 free(buf);
13858 abort(); // cannot continue - something is wrong with our mserver connection
13859 return RPC_NET_ERROR;
13860 }
13861
13862 if (rpc_status == RPC_INVALID_ID) {
13863 cm_msg(MERROR, "rpc_call", "routine \"%s\": error, unknown RPC, status %d", rpc_name, rpc_status);
13864 if (buf)
13865 free(buf);
13866 return rpc_status;
13867 }
13868
13869 /* extract result variables and place it to argument list */
13870
13871 va_start(ap, routine_id);
13872
13873 status = rpc_call_decode(ap, rpc_entry, buf, buf_size);
13874
13875 if (status != RPC_SUCCESS) {
13876 rpc_status = status;
13877 }
13878
13879 va_end(ap);
13880
13881 if (buf)
13882 free(buf);
13883
13884 return rpc_status;
13885}
13886
13887
13888/********************************************************************/
13890 INT old;
13891
13892 old = _opt_tcp_size;
13893 _opt_tcp_size = tcp_size;
13894 return old;
13895}
13896
13898 return _opt_tcp_size;
13899}
13900
13902#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13903
13904/********************************************************************/
13926INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
13927{
13928 if (rpc_is_remote()) {
13929 return rpc_send_event1(buffer_handle, pevent);
13930 } else {
13931 return bm_send_event(buffer_handle, pevent, unused, async_flag);
13932 }
13933}
13934
13935/********************************************************************/
13944INT rpc_send_event1(INT buffer_handle, const EVENT_HEADER *pevent)
13945{
13946 const size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
13947 return rpc_send_event_sg(buffer_handle, 1, (char**)&pevent, &event_size);
13948}
13949
13950INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char* const sg_ptr[], const size_t sg_len[])
13951{
13952 if (sg_n < 1) {
13953 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_n %d", sg_n);
13954 return BM_INVALID_SIZE;
13955 }
13956
13957 if (sg_ptr[0] == NULL) {
13958 cm_msg(MERROR, "rpc_send_event_sg", "invalid sg_ptr[0] is NULL");
13959 return BM_INVALID_SIZE;
13960 }
13961
13962 if (sg_len[0] < sizeof(EVENT_HEADER)) {
13963 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));
13964 return BM_INVALID_SIZE;
13965 }
13966
13967 const EVENT_HEADER* pevent = (const EVENT_HEADER*)sg_ptr[0];
13968
13969 const DWORD MAX_DATA_SIZE = (0x7FFFFFF0 - 16); // event size computations are not 32-bit clean, limit event size to 2GB. K.O.
13970 const DWORD data_size = pevent->data_size; // 32-bit unsigned value
13971
13972 if (data_size == 0) {
13973 cm_msg(MERROR, "rpc_send_event_sg", "invalid event data size zero");
13974 return BM_INVALID_SIZE;
13975 }
13976
13977 if (data_size > MAX_DATA_SIZE) {
13978 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);
13979 return BM_INVALID_SIZE;
13980 }
13981
13982 const size_t event_size = sizeof(EVENT_HEADER) + data_size;
13983 const size_t total_size = ALIGN8(event_size);
13984
13985 size_t count = 0;
13986 for (int i=0; i<sg_n; i++) {
13987 count += sg_len[i];
13988 }
13989
13990 if (count != event_size) {
13991 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);
13992 return BM_INVALID_SIZE;
13993 }
13994
13995 // protect non-atomic access to _server_connection.event_sock. K.O.
13996
13997 std::lock_guard<std::mutex> guard(_server_connection.event_sock_mutex);
13998
13999 //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);
14000
14001 if (_server_connection.event_sock == 0) {
14002 return RPC_NO_CONNECTION;
14003 }
14004
14005 //
14006 // event socket wire protocol: (see also rpc_server_receive_event() and recv_event_server_realloc())
14007 //
14008 // 4 bytes of buffer handle
14009 // 16 bytes of event header, includes data_size
14010 // ALIGN8(data_size) bytes of event data
14011 //
14012
14013 int status;
14014
14015 /* send buffer handle */
14016
14017 assert(sizeof(DWORD) == 4);
14018 DWORD bh_buf = buffer_handle;
14019
14020 status = ss_write_tcp(_server_connection.event_sock, (const char *) &bh_buf, sizeof(DWORD));
14021 if (status != SS_SUCCESS) {
14023 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(buffer handle) failed, event socket is now closed");
14024 return RPC_NET_ERROR;
14025 }
14026
14027 /* send data */
14028
14029 for (int i=0; i<sg_n; i++) {
14030 status = ss_write_tcp(_server_connection.event_sock, sg_ptr[i], sg_len[i]);
14031 if (status != SS_SUCCESS) {
14033 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(event data) failed, event socket is now closed");
14034 return RPC_NET_ERROR;
14035 }
14036 }
14037
14038 /* send padding */
14039
14040 if (count < total_size) {
14041 char padding[8] = { 0,0,0,0,0,0,0,0 };
14042 size_t padlen = total_size - count;
14043 assert(padlen < 8);
14045 if (status != SS_SUCCESS) {
14047 cm_msg(MERROR, "rpc_send_event_sg", "ss_write_tcp(padding) failed, event socket is now closed");
14048 return RPC_NET_ERROR;
14049 }
14050 }
14051
14052 return RPC_SUCCESS;
14053}
14054
14055/********************************************************************/
14064 return RPC_SUCCESS;
14065}
14066
14067/********************************************************************/
14068
14069struct TR_FIFO {
14070 int transition = 0;
14071 int run_number = 0;
14072 time_t trans_time = 0;
14074};
14075
14076static std::mutex _tr_fifo_mutex;
14078static int _tr_fifo_wp = 0;
14079static int _tr_fifo_rp = 0;
14080
14081static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
14082/********************************************************************\
14083
14084 Routine: rpc_transition_dispatch
14085
14086 Purpose: Gets called when a transition function was registered and
14087 a transition occured. Internal use only.
14088
14089 Input:
14090 INT idx RPC function ID
14091 void *prpc_param RPC parameters
14092
14093 Output:
14094 none
14095
14096 Function value:
14097 INT return value from called user routine
14098
14099\********************************************************************/
14100{
14101 /* erase error string */
14102 *(CSTRING(2)) = 0;
14103
14104 if (idx == RPC_RC_TRANSITION) {
14105 // find registered handler
14106 // NB: this code should match same code in cm_transition_call_direct()
14107 // NB: only use the first handler, this is how MIDAS always worked
14108 // NB: we could run all handlers, but we can return the status and error string of only one of them.
14109 _trans_table_mutex.lock();
14110 size_t n = _trans_table.size();
14111 _trans_table_mutex.unlock();
14112
14113 for (size_t i = 0; i < n; i++) {
14114 _trans_table_mutex.lock();
14116 _trans_table_mutex.unlock();
14117
14118 if (tt.transition == CINT(0) && tt.sequence_number == CINT(4)) {
14119 if (tt.func) {
14120 /* execute callback if defined */
14121 return tt.func(CINT(1), CSTRING(2));
14122 } else {
14123 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14124 /* store transition in FIFO */
14127 _tr_fifo[_tr_fifo_wp].trans_time = time(NULL);
14129 _tr_fifo_wp = (_tr_fifo_wp + 1) % 10;
14130 // implicit unlock
14131 return RPC_SUCCESS;
14132 }
14133 }
14134 }
14135 // no handler for this transition
14136 cm_msg(MERROR, "rpc_transition_dispatch", "no handler for transition %d with sequence number %d", CINT(0), CINT(4));
14137 return CM_SUCCESS;
14138 } else {
14139 cm_msg(MERROR, "rpc_transition_dispatch", "received unrecognized command %d", idx);
14140 return RPC_INVALID_ID;
14141 }
14142}
14143
14144/********************************************************************/
14145int cm_query_transition(int *transition, int *run_number, int *trans_time)
14146/********************************************************************\
14147
14148 Routine: cm_query_transition
14149
14150 Purpose: Query system if transition has occured. Normally, one
14151 registers callbacks for transitions via
14152 cm_register_transition. In some environments however,
14153 callbacks are not possible. In that case one spciefies
14154 a NULL pointer as the callback routine and can query
14155 transitions "manually" by calling this functions. A small
14156 FIFO takes care that no transition is lost if this functions
14157 did not get called between some transitions.
14158
14159 Output:
14160 INT *transition Type of transition, one of TR_xxx
14161 INT *run_nuber Run number for transition
14162 time_t *trans_time Time (in UNIX time) of transition
14163
14164 Function value:
14165 FALSE No transition occured since last call
14166 TRUE Transition occured
14167
14168\********************************************************************/
14169{
14170 std::lock_guard<std::mutex> guard(_tr_fifo_mutex);
14171
14172 if (_tr_fifo_wp == _tr_fifo_rp)
14173 return FALSE;
14174
14175 if (transition)
14177
14178 if (run_number)
14180
14181 if (trans_time)
14182 *trans_time = (int) _tr_fifo[_tr_fifo_rp].trans_time;
14183
14184 _tr_fifo_rp = (_tr_fifo_rp + 1) % 10;
14185
14186 // implicit unlock
14187 return TRUE;
14188}
14189
14190/********************************************************************\
14191* server functions *
14192\********************************************************************/
14193
14194#if 0
14195void debug_dump(unsigned char *p, int size)
14196{
14197 int i, j;
14198 unsigned char c;
14199
14200 for (i = 0; i < (size - 1) / 16 + 1; i++) {
14201 printf("%p ", p + i * 16);
14202 for (j = 0; j < 16; j++)
14203 if (i * 16 + j < size)
14204 printf("%02X ", p[i * 16 + j]);
14205 else
14206 printf(" ");
14207 printf(" ");
14208
14209 for (j = 0; j < 16; j++) {
14210 c = p[i * 16 + j];
14211 if (i * 16 + j < size)
14212 printf("%c", (c >= 32 && c < 128) ? p[i * 16 + j] : '.');
14213 }
14214 printf("\n");
14215 }
14216
14217 printf("\n");
14218}
14219#endif
14220
14221/********************************************************************/
14222static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
14223/********************************************************************\
14224
14225 Routine: recv_net_command
14226
14227 Purpose: TCP receive routine with local cache. To speed up network
14228 performance, a 64k buffer is read in at once and split into
14229 several RPC command on successive calls to recv_net_command.
14230 Therefore, the number of recv() calls is minimized.
14231
14232 This routine is ment to be called by the server process.
14233 Clients should call recv_tcp instead.
14234
14235 Input:
14236 INT idx Index of server connection
14237 DWORD buffer_size Size of the buffer in bytes.
14238 INT flags Flags passed to recv()
14239 INT convert_flags Convert flags needed for big/little
14240 endian conversion
14241
14242 Output:
14243 char *buffer Network receive buffer.
14244 INT *remaining Remaining data in cache
14245
14246 Function value:
14247 INT Same as recv()
14248
14249\********************************************************************/
14250{
14251 char *buffer = NULL; // buffer is changed to point to *pbuf when we receive the NET_COMMAND header
14252
14254
14255 int sock = sa->recv_sock;
14256
14257 if (!sa->net_buffer) {
14258 if (sa->is_mserver)
14260 else
14262
14263 sa->net_buffer = (char *) malloc(sa->net_buffer_size);
14264 //printf("sa %p idx %d, net_buffer %p+%d\n", sa, idx, sa->net_buffer, sa->net_buffer_size);
14265 sa->write_ptr = 0;
14266 sa->read_ptr = 0;
14267 sa->misalign = 0;
14268 }
14269 if (!sa->net_buffer) {
14270 cm_msg(MERROR, "recv_net_command", "Cannot allocate %d bytes for network buffer", sa->net_buffer_size);
14271 return -1;
14272 }
14273
14274 int copied = 0;
14275 int param_size = -1;
14276
14277 int write_ptr = sa->write_ptr;
14278 int read_ptr = sa->read_ptr;
14279 int misalign = sa->misalign;
14280 char *net_buffer = sa->net_buffer;
14281
14282 do {
14283 if (write_ptr - read_ptr >= (INT) sizeof(NET_COMMAND_HEADER) - copied) {
14284 if (param_size == -1) {
14285 if (copied > 0) {
14286 /* assemble split header */
14287 memcpy(buffer + copied, net_buffer + read_ptr, (INT) sizeof(NET_COMMAND_HEADER) - copied);
14288 NET_COMMAND *nc = (NET_COMMAND *) (buffer);
14289 param_size = (INT) nc->header.param_size;
14290 } else {
14291 NET_COMMAND *nc = (NET_COMMAND *) (net_buffer + read_ptr);
14292 param_size = (INT) nc->header.param_size;
14293 }
14294
14295 if (sa->convert_flags)
14296 rpc_convert_single(&param_size, TID_UINT32, 0, sa->convert_flags);
14297 }
14298
14299 //printf("recv_net_command: param_size %d, NET_COMMAND_HEADER %d, buffer_size %d\n", param_size, (int)sizeof(NET_COMMAND_HEADER), *pbufsize);
14300
14301 /* check if parameters fit in buffer */
14302 if (*pbufsize < (param_size + (int) sizeof(NET_COMMAND_HEADER))) {
14303 int new_size = param_size + sizeof(NET_COMMAND_HEADER) + 1024;
14304 char *p = (char *) realloc(*pbuf, new_size);
14305 //printf("recv_net_command: reallocate buffer %d -> %d, %p\n", *pbufsize, new_size, p);
14306 if (p == NULL) {
14307 cm_msg(MERROR, "recv_net_command", "cannot reallocate buffer from %d bytes to %d bytes", *pbufsize, new_size);
14308 sa->read_ptr = 0;
14309 sa->write_ptr = 0;
14310 return -1;
14311 }
14312 *pbuf = p;
14313 *pbufsize = new_size;
14314 }
14315
14316 buffer = *pbuf;
14317
14318 /* check if we have all parameters in buffer */
14319 if (write_ptr - read_ptr >= param_size + (INT) sizeof(NET_COMMAND_HEADER) - copied)
14320 break;
14321 }
14322
14323 /* not enough data, so copy partially and get new */
14324 int size = write_ptr - read_ptr;
14325
14326 if (size > 0) {
14327 memcpy(buffer + copied, net_buffer + read_ptr, size);
14328 copied += size;
14329 read_ptr = write_ptr;
14330 }
14331#ifdef OS_UNIX
14332 do {
14333 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14334
14335 /* don't return if an alarm signal was cought */
14336 } while (write_ptr == -1 && errno == EINTR);
14337#else
14338 write_ptr = recv(sock, net_buffer + misalign, sa->net_buffer_size - 8, 0);
14339#endif
14340
14341 /* abort if connection broken */
14342 if (write_ptr <= 0) {
14343 if (write_ptr == 0)
14344 cm_msg(MERROR, "recv_net_command", "rpc connection from \'%s\' on \'%s\' unexpectedly closed", sa->prog_name.c_str(), sa->host_name.c_str());
14345 else
14346 cm_msg(MERROR, "recv_net_command", "recv() returned %d, errno: %d (%s)", write_ptr, errno, strerror(errno));
14347
14348 if (remaining)
14349 *remaining = 0;
14350
14351 return write_ptr;
14352 }
14353
14354 read_ptr = misalign;
14355 write_ptr += misalign;
14356
14357 misalign = write_ptr % 8;
14358 } while (TRUE);
14359
14360 /* copy rest of parameters */
14361 int size = param_size + sizeof(NET_COMMAND_HEADER) - copied;
14362 memcpy(buffer + copied, net_buffer + read_ptr, size);
14363 read_ptr += size;
14364
14365 if (remaining) {
14366 /* don't keep rpc_server_receive in an infinite loop */
14367 if (write_ptr - read_ptr < param_size)
14368 *remaining = 0;
14369 else
14370 *remaining = write_ptr - read_ptr;
14371 }
14372
14373 sa->write_ptr = write_ptr;
14374 sa->read_ptr = read_ptr;
14375 sa->misalign = misalign;
14376
14377 return size + copied;
14378}
14379
14380
14381/********************************************************************/
14383/********************************************************************\
14384
14385 Routine: recv_tcp_check
14386
14387 Purpose: Check if in TCP receive buffer associated with sock is
14388 some data. Called by ss_suspend.
14389
14390 Input:
14391 INT sock TCP receive socket
14392
14393 Output:
14394 none
14395
14396 Function value:
14397 INT count Number of bytes remaining in TCP buffer
14398
14399\********************************************************************/
14400{
14401 /* figure out to which connection socket belongs */
14402 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++)
14403 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock == sock) {
14404 return _server_acceptions[idx]->write_ptr - _server_acceptions[idx]->read_ptr;
14405 }
14406
14407 return 0;
14408}
14409
14410
14411/********************************************************************/
14412static int recv_event_server_realloc(INT idx, RPC_SERVER_ACCEPTION* psa, char **pbuffer, int *pbuffer_size)
14413/********************************************************************\
14414
14415 Routine: recv_event_server_realloc
14416
14417 Purpose: receive events sent by rpc_send_event()
14418
14419 Input:
14420 INT idx Index of server connection
14421 DWORD buffer_size Size of the buffer in bytes.
14422 INT flags Flags passed to recv()
14423 INT convert_flags Convert flags needed for big/little
14424 endian conversion
14425
14426 Output:
14427 char *buffer Network receive buffer.
14428 INT *remaining Remaining data in cache
14429
14430 Function value:
14431 INT Same as recv()
14432
14433\********************************************************************/
14434{
14435 int sock = psa->event_sock;
14436
14437 //printf("recv_event_server: idx %d, buffer %p, buffer_size %d\n", idx, buffer, buffer_size);
14438
14439 const size_t header_size = (sizeof(EVENT_HEADER) + sizeof(INT));
14440
14441 char header_buf[header_size];
14442
14443 // First read the header.
14444 //
14445 // Data format is:
14446 // INT buffer handle (4 bytes)
14447 // EVENT_HEADER (16 bytes)
14448 // event data
14449 // ALIGN8() padding
14450 // ...next event
14451
14452 int hrd = recv_tcp2(sock, header_buf, header_size, 1);
14453
14454 if (hrd == 0) {
14455 // timeout waiting for data
14456 return 0;
14457 }
14458
14459 /* abort if connection broken */
14460 if (hrd < 0) {
14461 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d", hrd);
14462 return -1;
14463 }
14464
14465 if (hrd < (int) header_size) {
14466 int hrd1 = recv_tcp2(sock, header_buf + hrd, header_size - hrd, 0);
14467
14468 /* abort if connection broken */
14469 if (hrd1 <= 0) {
14470 cm_msg(MERROR, "recv_event_server", "recv_tcp2(more header) returned %d", hrd1);
14471 return -1;
14472 }
14473
14474 hrd += hrd1;
14475 }
14476
14477 /* abort if connection broken */
14478 if (hrd != (int) header_size) {
14479 cm_msg(MERROR, "recv_event_server", "recv_tcp2(header) returned %d instead of %d", hrd, (int) header_size);
14480 return -1;
14481 }
14482
14483 INT *pbh = (INT *) header_buf;
14484 EVENT_HEADER *pevent = (EVENT_HEADER *) (((INT *) header_buf) + 1);
14485
14486 /* convert header little endian/big endian */
14487 if (psa->convert_flags) {
14494 }
14495
14496 int event_size = pevent->data_size + sizeof(EVENT_HEADER);
14497 int total_size = ALIGN8(event_size);
14498
14499 //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);
14500
14501 if (pevent->data_size == 0) {
14502 for (int i=0; i<5; i++) {
14503 printf("recv_event_server: header[%d]: 0x%08x\n", i, pbh[i]);
14504 }
14505 abort();
14506 }
14507
14508 /* check for sane event size */
14509 if (event_size <= 0 || total_size <= 0) {
14510 cm_msg(MERROR, "recv_event_server",
14511 "received event header with invalid data_size %d: event_size %d, total_size %d", pevent->data_size,
14512 event_size, total_size);
14513 return -1;
14514 }
14515
14516 //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);
14517
14518
14519 int bufsize = sizeof(INT) + total_size;
14520
14521 // Second, check that output buffer is big enough
14522
14523 /* check if data part fits in buffer */
14524 if (*pbuffer_size < bufsize) {
14525 int newsize = 1024 + ALIGN8(bufsize);
14526
14527 //printf("recv_event_server: buffer realloc %d -> %d\n", *pbuffer_size, newsize);
14528
14529 char *newbuf = (char *) realloc(*pbuffer, newsize);
14530 if (newbuf == NULL) {
14531 cm_msg(MERROR, "recv_event_server", "cannot realloc() event buffer from %d to %d bytes", *pbuffer_size,
14532 newsize);
14533 return -1;
14534 }
14535 *pbuffer = newbuf;
14536 *pbuffer_size = newsize;
14537 }
14538
14539 // Third, copy header into output buffer
14540
14541 memcpy(*pbuffer, header_buf, header_size);
14542
14543 // Forth, read the event data
14544
14545 int to_read = sizeof(INT) + total_size - header_size;
14546 int rptr = header_size;
14547
14548 if (to_read > 0) {
14549 int drd = recv_tcp2(sock, (*pbuffer) + rptr, to_read, 0);
14550
14551 /* abort if connection broken */
14552 if (drd <= 0) {
14553 cm_msg(MERROR, "recv_event_server", "recv_tcp2(data) returned %d instead of %d", drd, to_read);
14554 return -1;
14555 }
14556 }
14557
14558 return bufsize;
14559}
14560
14561
14562/********************************************************************/
14563INT rpc_register_server(int port, int *plsock, int *pport)
14564/********************************************************************\
14565
14566 Routine: rpc_register_listener
14567
14568 Purpose: Register the calling process as a MIDAS RPC server. Note
14569 that cm_connnect_experiment must be called prior to any call of
14570 rpc_register_server.
14571
14572 Input:
14573 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14574
14575 Output:
14576 int *plsock Listener socket, can be NULL
14577 int *pport Port under which server is listening, can be NULL
14578
14579 Function value:
14580 RPC_SUCCESS Successful completion
14581 RPC_NET_ERROR Error in socket call
14582 RPC_NOT_REGISTERED cm_connect_experiment was not called
14583
14584\********************************************************************/
14585{
14586 int status;
14587 int lsock;
14588
14589 status = rpc_register_listener(port, NULL, &lsock, pport);
14590 if (status != RPC_SUCCESS)
14591 return status;
14592
14594 if (status != SS_SUCCESS)
14595 return status;
14596
14597 if (plsock)
14598 *plsock = lsock;
14599
14600 return RPC_SUCCESS;
14601}
14602
14603/********************************************************************/
14604INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
14605/********************************************************************\
14606
14607 Routine: rpc_register_listener
14608
14609 Purpose: Register the calling process as a MIDAS RPC server. Note
14610 that cm_connnect_experiment must be called prior to any call of
14611 rpc_register_listener.
14612
14613 Input:
14614 INT port TCP port for listen. If port==0, the OS chooses a free port and returns it in *pport
14615 INT *func Default dispatch function
14616
14617 Output:
14618 int *plsock Listener socket, should not be NULL
14619 int *pport Port under which server is listening, can be NULL
14620
14621 Function value:
14622 RPC_SUCCESS Successful completion
14623 RPC_NET_ERROR Error in socket call
14624 RPC_NOT_REGISTERED cm_connect_experiment was not called
14625
14626\********************************************************************/
14627{
14628 /* register system functions: RPC_ID_EXIT, RPC_ID_SHUTDOWN, RPC_ID_WATCHDOG */
14630
14631 /* create a socket for listening */
14632 int lsock = 0;
14633 int lport = 0;
14634 std::string errmsg;
14635
14636 int status = ss_socket_listen_tcp(!disable_bind_rpc_to_localhost, port, &lsock, &lport, &errmsg);
14637
14638 if (status != SS_SUCCESS) {
14639 cm_msg(MERROR, "rpc_register_server", "cannot listen to tcp port %d: %s", port, errmsg.c_str());
14640 return RPC_NET_ERROR;
14641 }
14642
14643 /* set close-on-exec flag to prevent child mserver processes from inheriting the listen socket */
14644#if defined(F_SETFD) && defined(FD_CLOEXEC)
14645 status = fcntl(lsock, F_SETFD, fcntl(lsock, F_GETFD) | FD_CLOEXEC);
14646 if (status < 0) {
14647 cm_msg(MERROR, "rpc_register_server", "fcntl(F_SETFD, FD_CLOEXEC) failed, errno %d (%s)", errno, strerror(errno));
14648 return RPC_NET_ERROR;
14649 }
14650#endif
14651
14652 /* return port wich OS has choosen */
14653 if (pport) {
14654 *pport = lport;
14655 }
14656
14657 if (plsock)
14658 *plsock = lsock;
14659
14660 //printf("rpc_register_server: requested port %d, actual port %d, socket %d\n", port, *pport, *plsock);
14661
14662 return RPC_SUCCESS;
14663}
14664
14670
14672static int tls_size = 0;
14673
14674/********************************************************************/
14675INT rpc_execute(INT sock, char *buffer, INT convert_flags)
14676/********************************************************************\
14677
14678 Routine: rpc_execute
14679
14680 Purpose: Execute a RPC command received over the network
14681
14682 Input:
14683 INT sock TCP socket to which the result should be
14684 send back
14685
14686 char *buffer Command buffer
14687 INT convert_flags Flags for data conversion
14688
14689 Output:
14690 none
14691
14692 Function value:
14693 RPC_SUCCESS Successful completion
14694 RPC_INVALID_ID Invalid routine_id received
14695 RPC_NET_ERROR Error in socket call
14696 RPC_EXCEED_BUFFER Not enough memory for network buffer
14697 RPC_SHUTDOWN Shutdown requested
14698 SS_ABORT TCP connection broken
14699 SS_EXIT TCP connection closed
14700
14701\********************************************************************/
14702{
14703 INT i, routine_id, status;
14704 char *in_param_ptr, *out_param_ptr, *last_param_ptr;
14705 INT tid, flags;
14706 NET_COMMAND *nc_in, *nc_out;
14707 INT param_size, max_size;
14708 void *prpc_param[20];
14709 char debug_line[1024], *return_buffer;
14710 int return_buffer_size;
14711 int return_buffer_tls;
14712#ifdef FIXED_BUFFER
14713 int initial_buffer_size = NET_BUFFER_SIZE;
14714#else
14715 int initial_buffer_size = 1024;
14716#endif
14717
14718 /* return buffer must must use thread local storage multi-thread servers */
14719 if (!tls_size) {
14720 tls_buffer = (TLS_POINTER *) malloc(sizeof(TLS_POINTER));
14722 tls_buffer[tls_size].buffer_size = initial_buffer_size;
14723 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14724 tls_size = 1;
14725 }
14726 for (i = 0; i < tls_size; i++)
14727 if (tls_buffer[i].thread_id == ss_gettid())
14728 break;
14729 if (i == tls_size) {
14730 /* new thread -> allocate new buffer */
14731 tls_buffer = (TLS_POINTER *) realloc(tls_buffer, (tls_size + 1) * sizeof(TLS_POINTER));
14733 tls_buffer[tls_size].buffer_size = initial_buffer_size;
14734 tls_buffer[tls_size].buffer = (char *) malloc(tls_buffer[tls_size].buffer_size);
14735 tls_size++;
14736 }
14737
14738 return_buffer_tls = i;
14739 return_buffer_size = tls_buffer[i].buffer_size;
14740 return_buffer = tls_buffer[i].buffer;
14741 assert(return_buffer);
14742
14743 // make valgrind happy - the RPC parameter encoder skips the alignement padding bytes
14744 // and valgrind complains that we transmit uninitialized data
14745 //memset(return_buffer, 0, return_buffer_size);
14746
14747 /* extract pointer array to parameters */
14748 nc_in = (NET_COMMAND *) buffer;
14749
14750 /* convert header format (byte swapping) */
14751 if (convert_flags) {
14752 rpc_convert_single(&nc_in->header.routine_id, TID_UINT32, 0, convert_flags);
14753 rpc_convert_single(&nc_in->header.param_size, TID_UINT32, 0, convert_flags);
14754 }
14755
14756 //if (nc_in->header.routine_id & RPC_NO_REPLY) {
14757 // printf("rpc_execute: routine_id %d, RPC_NO_REPLY\n", (int)(nc_in->header.routine_id & ~RPC_NO_REPLY));
14758 //}
14759
14760 /* no result return as requested */
14761 if (nc_in->header.routine_id & RPC_NO_REPLY)
14762 sock = 0;
14763
14764 /* find entry in rpc_list */
14765 routine_id = nc_in->header.routine_id & ~RPC_NO_REPLY;
14766
14767 int idx = -1;
14768 RPC_LIST rl;
14769
14770 rpc_list_mutex.lock();
14771
14772 for (size_t i = 0; i < rpc_list.size(); i++) {
14773 if (rpc_list[i].id == routine_id) {
14774 idx = i;
14775 rl = rpc_list[idx];
14776 break;
14777 }
14778 }
14779
14780 rpc_list_mutex.unlock();
14781
14782 if (idx < 0) {
14783 cm_msg(MERROR, "rpc_execute", "Invalid rpc ID (%d)", routine_id);
14784 return RPC_INVALID_ID;
14785 }
14786
14787 again:
14788
14789 in_param_ptr = nc_in->param;
14790
14791 nc_out = (NET_COMMAND *) return_buffer;
14792 out_param_ptr = nc_out->param;
14793
14794 sprintf(debug_line, "%s(", rl.name);
14795
14796 for (i = 0; rl.param[i].tid != 0; i++) {
14797 tid = rl.param[i].tid;
14798 flags = rl.param[i].flags;
14799
14800 if (flags & RPC_IN) {
14801 param_size = ALIGN8(rpc_tid_size(tid));
14802
14803 if (tid == TID_STRING || tid == TID_LINK)
14804 param_size = ALIGN8(1 + strlen((char *) (in_param_ptr)));
14805
14806 if (flags & RPC_VARARRAY) {
14807 /* for arrays, the size is stored as a INT in front of the array */
14808 param_size = *((INT *) in_param_ptr);
14809 if (convert_flags)
14810 rpc_convert_single(&param_size, TID_INT32, 0, convert_flags);
14811 param_size = ALIGN8(param_size);
14812
14813 in_param_ptr += ALIGN8(sizeof(INT));
14814 }
14815
14816 if (tid == TID_STRUCT)
14817 param_size = ALIGN8(rl.param[i].n);
14818
14819 prpc_param[i] = in_param_ptr;
14820
14821 /* convert data format */
14822 if (convert_flags) {
14823 if (flags & RPC_VARARRAY)
14824 rpc_convert_data(in_param_ptr, tid, flags, param_size, convert_flags);
14825 else
14826 rpc_convert_data(in_param_ptr, tid, flags, rl.param[i].n * rpc_tid_size(tid),
14827 convert_flags);
14828 }
14829
14830 std::string str = db_sprintf(in_param_ptr, param_size, 0, rl.param[i].tid);
14831 if (rl.param[i].tid == TID_STRING) {
14832 /* check for long strings (db_create_record...) */
14833 if (strlen(debug_line) + str.length() + 2 < sizeof(debug_line)) {
14834 strcat(debug_line, "\"");
14835 strcat(debug_line, str.c_str());
14836 strcat(debug_line, "\"");
14837 } else
14838 strcat(debug_line, "...");
14839 } else
14840 strcat(debug_line, str.c_str());
14841
14842 in_param_ptr += param_size;
14843 }
14844
14845 if (flags & RPC_OUT) {
14846 param_size = ALIGN8(rpc_tid_size(tid));
14847
14848 if (flags & RPC_VARARRAY || tid == TID_STRING) {
14849
14850 /* save maximum array length from the value of the next argument.
14851 * this means RPC_OUT arrays and strings should always be passed like this:
14852 * rpc_call(..., array_ptr, array_max_size, ...); */
14853
14854 max_size = *((INT *) in_param_ptr);
14855
14856 if (convert_flags)
14857 rpc_convert_single(&max_size, TID_INT32, 0, convert_flags);
14858 max_size = ALIGN8(max_size);
14859
14860 *((INT *) out_param_ptr) = max_size;
14861
14862 /* save space for return array length */
14863 out_param_ptr += ALIGN8(sizeof(INT));
14864
14865 /* use maximum array length from input */
14866 param_size = max_size;
14867 }
14868
14869 if (rl.param[i].tid == TID_STRUCT)
14870 param_size = ALIGN8(rl.param[i].n);
14871
14872 if ((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size > return_buffer_size) {
14873#ifdef FIXED_BUFFER
14874 cm_msg(MERROR, "rpc_execute",
14875 "return parameters (%d) too large for network buffer (%d)",
14876 (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size, return_buffer_size);
14877
14878 return RPC_EXCEED_BUFFER;
14879#else
14880 int itls;
14881 int new_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size + 1024;
14882
14883#if 0
14884 cm_msg(MINFO, "rpc_execute",
14885 "rpc_execute: return parameters (%d) too large for network buffer (%d), new buffer size (%d)",
14886 (int)((POINTER_T) out_param_ptr - (POINTER_T) nc_out + param_size), return_buffer_size, new_size);
14887#endif
14888
14889 itls = return_buffer_tls;
14890
14891 tls_buffer[itls].buffer_size = new_size;
14892 tls_buffer[itls].buffer = (char *) realloc(tls_buffer[itls].buffer, new_size);
14893
14894 if (!tls_buffer[itls].buffer) {
14895 cm_msg(MERROR, "rpc_execute", "Cannot allocate return buffer of size %d", new_size);
14896 return RPC_EXCEED_BUFFER;
14897 }
14898
14899 return_buffer_size = tls_buffer[itls].buffer_size;
14900 return_buffer = tls_buffer[itls].buffer;
14901 assert(return_buffer);
14902
14903 goto again;
14904#endif
14905 }
14906
14907 /* if parameter goes both directions, copy input to output */
14908 if (rl.param[i].flags & RPC_IN)
14909 memcpy(out_param_ptr, prpc_param[i], param_size);
14910
14911 if (_debug_print && !(flags & RPC_IN))
14912 strcat(debug_line, "-");
14913
14914 prpc_param[i] = out_param_ptr;
14915 out_param_ptr += param_size;
14916 }
14917
14918 if (rl.param[i + 1].tid)
14919 strcat(debug_line, ", ");
14920 }
14921
14922 //printf("predicted return size %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out);
14923
14924 strcat(debug_line, ")");
14925 rpc_debug_printf(debug_line);
14926
14927 last_param_ptr = out_param_ptr;
14928
14929 /*********************************\
14930 * call dispatch function *
14931 \*********************************/
14932 if (rl.dispatch)
14933 status = rl.dispatch(routine_id, prpc_param);
14934 else
14936
14937 if (routine_id == RPC_ID_EXIT || routine_id == RPC_ID_SHUTDOWN || routine_id == RPC_ID_WATCHDOG)
14939
14940 /* return immediately for closed down client connections */
14941 if (!sock && routine_id == RPC_ID_EXIT)
14942 return SS_EXIT;
14943
14944 if (!sock && routine_id == RPC_ID_SHUTDOWN)
14945 return RPC_SHUTDOWN;
14946
14947 /* Return if TCP connection broken */
14948 if (status == SS_ABORT)
14949 return SS_ABORT;
14950
14951 /* if sock == 0, we are in FTCP mode and may not sent results */
14952 if (!sock)
14953 return RPC_SUCCESS;
14954
14955 /* compress variable length arrays */
14956 out_param_ptr = nc_out->param;
14957 for (i = 0; rl.param[i].tid != 0; i++)
14958 if (rl.param[i].flags & RPC_OUT) {
14959 tid = rl.param[i].tid;
14960 flags = rl.param[i].flags;
14961 param_size = ALIGN8(rpc_tid_size(tid));
14962
14963 if (tid == TID_STRING) {
14964 max_size = *((INT *) out_param_ptr);
14965 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14966 // and prpc_param() is now pointing to the wrong place. here we know our string data
14967 // starts right after max_size and we do not need to use prpc_param[] to find it. K.O.
14968 //const char* param_ptr = (char *) prpc_param[i];
14969 const char* param_ptr = ((char *) out_param_ptr) + ALIGN8(sizeof(INT));
14970 //printf("string param [%s] max_size %d\n", param_ptr, max_size);
14971 param_size = strlen(param_ptr) + 1;
14972 param_size = ALIGN8(param_size);
14973
14974 /* move string ALIGN8(sizeof(INT)) left */
14975 memmove(out_param_ptr, out_param_ptr + ALIGN8(sizeof(INT)), param_size);
14976
14977 /* move remaining parameters to end of string */
14978 memmove(out_param_ptr + param_size,
14979 out_param_ptr + max_size + ALIGN8(sizeof(INT)),
14980 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size + ALIGN8(sizeof(INT))));
14981 }
14982
14983 if (flags & RPC_VARARRAY) {
14984 /* store array length at current out_param_ptr */
14985 max_size = *((INT *) out_param_ptr);
14986 // note: RPC_OUT parameters may have been shifted in the output buffer by memmove()
14987 // and prpc_param() is now pointing to the wrong place. instead, compute location
14988 // of next parameter using max_size. K.O.
14989 // note: RPC_IN parameters are in the input buffer and we must use the prpc_param[] pointer. K.O.
14990 if (rl.param[i+1].flags & RPC_OUT)
14991 param_size = *((INT *) (out_param_ptr + ALIGN8(sizeof(INT)) + ALIGN8(max_size)));
14992 else
14993 param_size = *((INT *) prpc_param[i + 1]);
14994 *((INT *) out_param_ptr) = param_size; // store new array size
14995 if (convert_flags)
14996 rpc_convert_single(out_param_ptr, TID_INT32, RPC_OUTGOING, convert_flags);
14997
14998 out_param_ptr += ALIGN8(sizeof(INT)); // step over array size
14999
15000 param_size = ALIGN8(param_size);
15001
15002 /* move remaining parameters to end of array */
15003 memmove(out_param_ptr + param_size,
15004 out_param_ptr + max_size,
15005 (POINTER_T) last_param_ptr - ((POINTER_T) out_param_ptr + max_size));
15006 }
15007
15008 if (tid == TID_STRUCT)
15009 param_size = ALIGN8(rl.param[i].n);
15010
15011 /* convert data format */
15012 if (convert_flags) {
15013 if (flags & RPC_VARARRAY)
15014 rpc_convert_data(out_param_ptr, tid,
15015 rl.param[i].flags | RPC_OUTGOING, param_size, convert_flags);
15016 else
15017 rpc_convert_data(out_param_ptr, tid,
15018 rl.param[i].flags | RPC_OUTGOING,
15019 rl.param[i].n * rpc_tid_size(tid), convert_flags);
15020 }
15021
15022 out_param_ptr += param_size;
15023 }
15024
15025 /* send return parameters */
15026 param_size = (POINTER_T) out_param_ptr - (POINTER_T) nc_out->param;
15027 nc_out->header.routine_id = status;
15028 nc_out->header.param_size = param_size;
15029
15030 //printf("actual return size %d, buffer used %d\n", (POINTER_T) out_param_ptr - (POINTER_T) nc_out, sizeof(NET_COMMAND_HEADER) + param_size);
15031
15032 /* convert header format (byte swapping) if necessary */
15033 if (convert_flags) {
15034 rpc_convert_single(&nc_out->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
15035 rpc_convert_single(&nc_out->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
15036 }
15037
15038 // valgrind complains about sending uninitialized data, if you care about this, uncomment
15039 // the memset(return_buffer,0) call above (search for "valgrind"). K.O.
15040
15041 status = send_tcp(sock, return_buffer, sizeof(NET_COMMAND_HEADER) + param_size, 0);
15042
15043 if (status < 0) {
15044 cm_msg(MERROR, "rpc_execute", "send_tcp() failed");
15045 return RPC_NET_ERROR;
15046 }
15047
15048 /* print return buffer */
15049/*
15050 printf("Return buffer, ID %d:\n", routine_id);
15051 for (i=0; i<param_size ; i++)
15052 {
15053 status = (char) nc_out->param[i];
15054 printf("%02X ", status);
15055 if (i%8 == 7)
15056 printf("\n");
15057 }
15058*/
15059 /* return SS_EXIT if RPC_EXIT is called */
15060 if (routine_id == RPC_ID_EXIT)
15061 return SS_EXIT;
15062
15063 /* return SS_SHUTDOWN if RPC_SHUTDOWN is called */
15064 if (routine_id == RPC_ID_SHUTDOWN)
15065 return RPC_SHUTDOWN;
15066
15067 return RPC_SUCCESS;
15068}
15069
15070/********************************************************************/
15072/********************************************************************\
15073 Routine: rpc_test_rpc
15074
15075 Purpose: Test RPC parameters encoding and decoding
15076
15077 Input:
15078 none
15079
15080 Output:
15081 none
15082
15083 Function value:
15084 RPC_SUCCESS Successful completion
15085
15086\********************************************************************/
15087{
15088 int status = RPC_SUCCESS;
15089
15090 printf("rpc_test_rpc!\n");
15091
15092 int int_out = 0;
15093 int int_inout = 456;
15094
15095 char string_out[32];
15096 char string2_out[48];
15097
15098 char string_inout[25];
15099 strcpy(string_inout, "string_inout");
15100
15101 KEY struct_in;
15102
15103 struct_in.type = 111;
15104 struct_in.num_values = 222;
15105 strcpy(struct_in.name, "name");
15106 struct_in.last_written = 333;
15107
15108 KEY struct_out;
15109 KEY struct_inout;
15110
15111 struct_inout.type = 111111;
15112 struct_inout.num_values = 222222;
15113 strcpy(struct_inout.name, "name_name");
15114 struct_inout.last_written = 333333;
15115
15116 uint32_t dwordarray_inout[10];
15117 size_t dwordarray_inout_size = sizeof(dwordarray_inout);
15118
15119 for (int i=0; i<10; i++) {
15120 dwordarray_inout[i] = i*10;
15121 }
15122
15123 char array_in[10];
15124
15125 for (size_t i=0; i<sizeof(array_in); i++) {
15126 array_in[i] = 'a' + i;
15127 }
15128
15129 char array_out[16];
15130 size_t array_out_size = sizeof(array_out);
15131
15133 123,
15134 &int_out,
15135 &int_inout,
15136 "test string",
15137 string_out, sizeof(string_out),
15138 string2_out, sizeof(string2_out),
15139 string_inout, sizeof(string_inout),
15140 &struct_in,
15141 &struct_out,
15142 &struct_inout,
15143 dwordarray_inout, &dwordarray_inout_size,
15144 array_in, sizeof(array_in),
15145 array_out, &array_out_size
15146 );
15147
15148 printf("rpc_call(RPC_TEST2) status %d\n", status);
15149
15150 if (int_out != 789) {
15151 printf("int_out mismatch!\n");
15152 status = 0;
15153 }
15154
15155 if (int_inout != 456*2) {
15156 printf("int_inout mismatch!\n");
15157 status = 0;
15158 }
15159
15160 if (strcmp(string_out, "string_out") != 0) {
15161 printf("string_out mismatch [%s] vs [%s]\n", string_out, "string_out");
15162 status = 0;
15163 }
15164
15165 if (strcmp(string2_out, "second string_out") != 0) {
15166 printf("string2_out mismatch [%s] vs [%s]\n", string2_out, "second string_out");
15167 status = 0;
15168 }
15169
15170 if (strcmp(string_inout, "return string_inout") != 0) {
15171 printf("string_inout mismatch [%s] vs [%s]\n", string_inout, "return string_inout");
15172 status = 0;
15173 }
15174
15175 KEY* pkey;
15176
15177 pkey = &struct_in;
15178
15179 //printf("struct_in: type %d, num_values %d, name [%s], last_written %d\n", pkey->type, pkey->num_values, pkey->name, pkey->last_written);
15180
15181 pkey = &struct_out;
15182
15183 if (pkey->type != 444 || pkey->num_values != 555 || strcmp(pkey->name, "out_name") || pkey->last_written != 666) {
15184 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);
15185 status = 0;
15186 }
15187
15188 pkey = &struct_inout;
15189
15190 if (pkey->type != 444444 || pkey->num_values != 555555 || strcmp(pkey->name, "inout_name") || pkey->last_written != 666666) {
15191 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);
15192 status = 0;
15193 }
15194
15195#if 0
15196 if (dwordarray_inout_size != 4*5) {
15197 printf("dwordarray_inout_size mismatch %d vs %d\n", (int)dwordarray_inout_size, 4*5);
15198 status = 0;
15199 }
15200
15201 for (size_t i=0; i<dwordarray_inout_size/sizeof(uint32_t); i++) {
15202 printf("dwordarray_inout[%d] is %d\n", (int)i, dwordarray_inout[i]);
15203 }
15204#endif
15205
15206#if 0
15207 {RPC_TEST_CXX, "test_cxx",
15208 {{TID_INT32, RPC_IN},
15209 {TID_INT32, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
15210 {TID_STRING, RPC_IN},
15211 {TID_STRING, RPC_IN | RPC_CXX},
15212 {TID_STRING, RPC_OUT | RPC_CXX},
15213 {TID_STRING, RPC_IN | RPC_OUT | RPC_CXX},
15214 {TID_STRUCT, RPC_IN | RPC_CXX, sizeof(KEY)},
15215 {TID_STRUCT, RPC_OUT | RPC_CXX, sizeof(KEY)},
15216 {TID_STRUCT, RPC_IN | RPC_OUT | RPC_CXX, sizeof(KEY)},
15217 {TID_ARRAY, RPC_IN | RPC_VARARRAY | RPC_CXX},
15218 {TID_ARRAY, RPC_OUT | RPC_VARARRAY | RPC_CXX},
15219 {TID_ARRAY, RPC_IN | RPC_OUT | RPC_VARARRAY | RPC_CXX},
15220 {0}}},
15221#endif
15222
15223#if 0
15224 status = rpc_call(RPC_TEST_CXX, ...);
15225#endif
15226
15227 return status;
15228}
15229
15230static std::atomic_bool gAllowedHostsEnabled(false);
15231static std::vector<std::string> gAllowedHosts;
15232static std::mutex gAllowedHostsMutex;
15233
15234/********************************************************************/
15236/********************************************************************\
15237 Routine: rpc_clear_allowed_hosts
15238
15239 Purpose: Clear list of allowed hosts and permit connections from anybody
15240
15241 Input:
15242 none
15243
15244 Output:
15245 none
15246
15247 Function value:
15248 RPC_SUCCESS Successful completion
15249
15250\********************************************************************/
15251{
15252 gAllowedHostsMutex.lock();
15253 gAllowedHosts.clear();
15254 gAllowedHostsEnabled = false;
15255 gAllowedHostsMutex.unlock();
15256 return RPC_SUCCESS;
15257}
15258
15259/********************************************************************/
15260INT rpc_add_allowed_host(const char *hostname)
15261/********************************************************************\
15262 Routine: rpc_add_allowed_host
15263
15264 Purpose: Permit connections from listed hosts only
15265
15266 Input:
15267 none
15268
15269 Output:
15270 none
15271
15272 Function value:
15273 RPC_SUCCESS Successful completion
15274 RPC_NO_MEMORY Too many allowed hosts
15275
15276\********************************************************************/
15277{
15278 //cm_msg(MINFO, "rpc_add_allowed_host", "Adding allowed host \'%s\'", hostname);
15279
15280 gAllowedHostsMutex.lock();
15281 gAllowedHosts.push_back(hostname);
15282 gAllowedHostsEnabled = true;
15283 gAllowedHostsMutex.unlock();
15284
15285 return RPC_SUCCESS;
15286}
15287
15288/********************************************************************/
15289INT rpc_check_allowed_host(const char *hostname)
15290/********************************************************************\
15291 Routine: rpc_check_allowed_host
15292
15293 Purpose: Check if hostname is permitted to connect
15294
15295 Function value:
15296 RPC_SUCCESS hostname is permitted to connect
15297 RPC_NOT_REGISTERED hostname not permitted to connect
15298
15299\********************************************************************/
15300{
15301 //printf("rpc_check_allowed_host: enabled %d, hostname [%s]\n", gAllowedHostsEnabled.load(), hostname);
15302
15304 return RPC_SUCCESS;
15305
15306 if (strcmp(hostname, "localhost") == 0)
15307 return RPC_SUCCESS;
15308
15309 if (strcmp(hostname, "localhost.localdomain") == 0)
15310 return RPC_SUCCESS;
15311
15312 if (strcmp(hostname, "localhost6") == 0) // RedHat el6, el7
15313 return RPC_SUCCESS;
15314
15315 if (strcmp(hostname, "ip6-localhost") == 0) // Ubuntu-22
15316 return RPC_SUCCESS;
15317
15319
15320 gAllowedHostsMutex.lock();
15321
15322 for (const auto& h: gAllowedHosts) {
15323 if (h == hostname) {
15325 break;
15326 }
15327 }
15328
15329 gAllowedHostsMutex.unlock();
15330
15331 //if (status != RPC_SUCCESS)
15332 // printf("rpc_check_allowed_host: enabled %d, hostname [%s] not found\n", gAllowedHostsEnabled.load(), hostname);
15333
15334 return status;
15335}
15336
15337/*------------------------------------------------------------------*/
15339{
15340 std::string hostname;
15341
15342 int status = ss_socket_get_peer_name(sock, &hostname, NULL);
15343
15344 if (status != SS_SUCCESS)
15345 return status;
15346
15347 status = rpc_check_allowed_host(hostname.c_str());
15348
15349 if (status == RPC_SUCCESS)
15350 return RPC_SUCCESS;
15351
15352 static std::atomic_int max_report(10);
15353 if (max_report > 0) {
15354 max_report--;
15355 if (max_report == 0) {
15356 cm_msg(MERROR, "rpc_socket_check_allowed_host", "rejecting connection from unallowed host \'%s\', this message will no longer be reported", hostname.c_str());
15357 } else {
15358 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());
15359 }
15360 }
15361
15362 return RPC_NET_ERROR;
15363}
15364
15365/********************************************************************/
15367/********************************************************************\
15368
15369 Routine: rpc_server_accept
15370
15371 Purpose: Accept new incoming connections
15372
15373 Input:
15374 INT lscok Listen socket
15375
15376 Output:
15377 none
15378
15379 Function value:
15380 RPC_SUCCESS Successful completion
15381 RPC_NET_ERROR Error in socket call
15382 RPC_CONNCLOSED Connection was closed
15383 RPC_SHUTDOWN Listener shutdown
15384 RPC_EXCEED_BUFFER Not enough memory for network buffer
15385
15386\********************************************************************/
15387{
15388 INT i;
15389 INT sock;
15390 char version[NAME_LENGTH], v1[32];
15391 char experiment[NAME_LENGTH];
15392 INT port1, port2, port3;
15393 char *ptr;
15394 char net_buffer[256];
15395 struct linger ling;
15396
15397 static struct callback_addr callback;
15398
15399 if (lsock > 0) {
15400 sock = accept(lsock, NULL, NULL);
15401
15402 if (sock == -1)
15403 return RPC_NET_ERROR;
15404 } else {
15405 /* lsock is stdin -> already connected from inetd */
15406
15407 sock = lsock;
15408 }
15409
15410 /* check access control list */
15413
15414 if (status != RPC_SUCCESS) {
15415 ss_socket_close(&sock);
15416 return RPC_NET_ERROR;
15417 }
15418 }
15419
15420 /* receive string with timeout */
15421 i = recv_string(sock, net_buffer, 256, 10000);
15422 rpc_debug_printf("Received command: %s", net_buffer);
15423
15424 if (i > 0) {
15425 char command = (char) toupper(net_buffer[0]);
15426
15427 //printf("rpc_server_accept: command [%c]\n", command);
15428
15429 switch (command) {
15430 case 'S': {
15431
15432 /*----------- shutdown listener ----------------------*/
15433 ss_socket_close(&sock);
15434 return RPC_SHUTDOWN;
15435 }
15436 case 'I': {
15437
15438 /*----------- return available experiments -----------*/
15439#ifdef LOCAL_ROUTINES
15440 exptab_struct exptab;
15441 cm_read_exptab(&exptab); // thread safe!
15442 for (unsigned i=0; i<exptab.exptab.size(); i++) {
15443 rpc_debug_printf("Return experiment: %s", exptab.exptab[i].name.c_str());
15444 const char* str = exptab.exptab[i].name.c_str();
15445 send(sock, str, strlen(str) + 1, 0);
15446 }
15447 send(sock, "", 1, 0);
15448#endif
15449 ss_socket_close(&sock);
15450 break;
15451 }
15452 case 'C': {
15453
15454 /*----------- connect to experiment -----------*/
15455
15456 /* get callback information */
15457 callback.experiment[0] = 0;
15458 port1 = port2 = version[0] = 0;
15459
15460 //printf("rpc_server_accept: net buffer \'%s\'\n", net_buffer);
15461
15462 /* parse string in format "C port1 port2 port3 version expt" */
15463 /* example: C 51046 45838 56832 2.0.0 alpha */
15464
15465 port1 = strtoul(net_buffer + 2, &ptr, 0);
15466 port2 = strtoul(ptr, &ptr, 0);
15467 port3 = strtoul(ptr, &ptr, 0);
15468
15469 while (*ptr == ' ')
15470 ptr++;
15471
15472 i = 0;
15473 for (; *ptr != 0 && *ptr != ' ' && i < (int) sizeof(version) - 1;)
15474 version[i++] = *ptr++;
15475
15476 // ensure that we do not overwrite buffer "version"
15477 assert(i < (int) sizeof(version));
15478 version[i] = 0;
15479
15480 // skip wjatever is left from the "version" string
15481 for (; *ptr != 0 && *ptr != ' ';)
15482 ptr++;
15483
15484 while (*ptr == ' ')
15485 ptr++;
15486
15487 i = 0;
15488 for (; *ptr != 0 && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && i < (int) sizeof(experiment) - 1;)
15489 experiment[i++] = *ptr++;
15490
15491 // ensure that we do not overwrite buffer "experiment"
15492 assert(i < (int) sizeof(experiment));
15493 experiment[i] = 0;
15494
15496
15497 /* print warning if version patch level doesn't agree */
15498 mstrlcpy(v1, version, sizeof(v1));
15499 if (strchr(v1, '.'))
15500 if (strchr(strchr(v1, '.') + 1, '.'))
15501 *strchr(strchr(v1, '.') + 1, '.') = 0;
15502
15503 char str[100];
15504 mstrlcpy(str, cm_get_version(), sizeof(str));
15505 if (strchr(str, '.'))
15506 if (strchr(strchr(str, '.') + 1, '.'))
15507 *strchr(strchr(str, '.') + 1, '.') = 0;
15508
15509 if (strcmp(v1, str) != 0) {
15510 cm_msg(MERROR, "rpc_server_accept", "client MIDAS version %s differs from local version %s", version, cm_get_version());
15511 cm_msg(MERROR, "rpc_server_accept", "received string: %s", net_buffer + 2);
15512 }
15513
15514 callback.host_port1 = (short) port1;
15515 callback.host_port2 = (short) port2;
15516 callback.host_port3 = (short) port3;
15518
15520
15521 if (status != SS_SUCCESS) {
15522 ss_socket_close(&sock);
15523 break;
15524 }
15525
15526#ifdef LOCAL_ROUTINES
15527 /* update experiment definition */
15528 exptab_struct exptab;
15529 cm_read_exptab(&exptab); // thread safe!
15530
15531 unsigned idx = 0;
15532 bool found = false;
15533 /* lookup experiment */
15534 if (equal_ustring(callback.experiment.c_str(), "Default")) {
15535 found = true;
15536 idx = 0;
15537 } else {
15538 for (idx = 0; idx < exptab.exptab.size(); idx++) {
15539 if (exptab.exptab[idx].name == callback.experiment) {
15540 if (ss_dir_exist(exptab.exptab[idx].directory.c_str())) {
15541 found = true;
15542 break;
15543 }
15544 }
15545 }
15546 }
15547
15548 if (!found) {
15549 cm_msg(MERROR, "rpc_server_accept", "experiment \'%s\' not defined in exptab file \'%s\'", callback.experiment.c_str(), exptab.filename.c_str());
15550
15551 send(sock, "2", 2, 0); /* 2 means exp. not found */
15552 ss_socket_close(&sock);
15553 break;
15554 }
15555
15556 callback.directory = exptab.exptab[idx].directory;
15557 callback.user = exptab.exptab[idx].user;
15558
15559 /* create a new process */
15560 char host_port1_str[30], host_port2_str[30], host_port3_str[30];
15561 char debug_str[30];
15562
15563 sprintf(host_port1_str, "%d", callback.host_port1);
15564 sprintf(host_port2_str, "%d", callback.host_port2);
15565 sprintf(host_port3_str, "%d", callback.host_port3);
15566 sprintf(debug_str, "%d", callback.debug);
15567
15568 const char *mserver_path = rpc_get_mserver_path();
15569
15570 const char *argv[10];
15571 argv[0] = mserver_path;
15572 argv[1] = callback.host_name.c_str();
15573 argv[2] = host_port1_str;
15574 argv[3] = host_port2_str;
15575 argv[4] = host_port3_str;
15576 argv[5] = debug_str;
15577 argv[6] = callback.experiment.c_str();
15578 argv[7] = callback.directory.c_str();
15579 argv[8] = callback.user.c_str();
15580 argv[9] = NULL;
15581
15582 rpc_debug_printf("Spawn: %s %s %s %s %s %s %s %s %s %s",
15583 argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8],
15584 argv[9]);
15585
15586 status = ss_spawnv(P_NOWAIT, mserver_path, argv);
15587
15588 if (status != SS_SUCCESS) {
15589 rpc_debug_printf("Cannot spawn subprocess: %s\n", strerror(errno));
15590
15591 sprintf(str, "3"); /* 3 means cannot spawn subprocess */
15592 send(sock, str, strlen(str) + 1, 0);
15593 ss_socket_close(&sock);
15594 break;
15595 }
15596
15597 sprintf(str, "1 %s", cm_get_version()); /* 1 means ok */
15598 send(sock, str, strlen(str) + 1, 0);
15599#endif // LOCAL_ROUTINES
15600 ss_socket_close(&sock);
15601
15602 break;
15603 }
15604 default: {
15605 cm_msg(MERROR, "rpc_server_accept", "received unknown command '%c' code %d", command, command);
15606 ss_socket_close(&sock);
15607 break;
15608 }
15609 }
15610 } else { /* if i>0 */
15611
15612 /* lingering needed for PCTCP */
15613 ling.l_onoff = 1;
15614 ling.l_linger = 0;
15615 setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
15616 ss_socket_close(&sock);
15617 }
15618
15619 return RPC_SUCCESS;
15620}
15621
15622/********************************************************************/
15624/********************************************************************\
15625
15626 Routine: rpc_client_accept
15627
15628 Purpose: midas program accept new RPC connection (run transitions, etc)
15629
15630 Input:
15631 INT lsock Listen socket
15632
15633 Output:
15634 none
15635
15636 Function value:
15637 RPC_SUCCESS Successful completion
15638 RPC_NET_ERROR Error in socket call
15639 RPC_CONNCLOSED Connection was closed
15640 RPC_SHUTDOWN Listener shutdown
15641 RPC_EXCEED_BUFFER Not enough memory for network buffer
15642
15643\********************************************************************/
15644{
15645 INT i, status;
15646 INT client_hw_type = 0, hw_type;
15647 std::string client_program;
15648 std::string host_name;
15649 INT convert_flags;
15650 char net_buffer[256], *p;
15651
15652 int sock = accept(lsock, NULL, NULL);
15653
15654 if (sock == -1)
15655 return RPC_NET_ERROR;
15656
15657 /* check access control list */
15660
15661 if (status != RPC_SUCCESS) {
15662 ss_socket_close(&sock);
15663 return RPC_NET_ERROR;
15664 }
15665 }
15666
15667 host_name = "(unknown)";
15668 client_program = "(unknown)";
15669
15670 /* receive string with timeout */
15671 i = recv_string(sock, net_buffer, sizeof(net_buffer), 10000);
15672 if (i <= 0) {
15673 ss_socket_close(&sock);
15674 return RPC_NET_ERROR;
15675 }
15676
15677 /* get remote computer info */
15678 p = strtok(net_buffer, " ");
15679 if (p != NULL) {
15680 client_hw_type = atoi(p);
15681 p = strtok(NULL, " ");
15682 }
15683 if (p != NULL) {
15684 //version = atoi(p);
15685 p = strtok(NULL, " ");
15686 }
15687 if (p != NULL) {
15688 client_program = p;
15689 p = strtok(NULL, " ");
15690 }
15691 if (p != NULL) {
15692 host_name = p;
15693 p = strtok(NULL, " ");
15694 }
15695
15696 //printf("rpc_client_accept: client_hw_type %d, version %d, client_name \'%s\', hostname \'%s\'\n", client_hw_type, version, client_program, host_name);
15697
15699
15700 /* save information in _server_acception structure */
15701 sa->recv_sock = sock;
15702 sa->send_sock = 0;
15703 sa->event_sock = 0;
15704 sa->remote_hw_type = client_hw_type;
15705 sa->host_name = host_name;
15706 sa->prog_name = client_program;
15708 sa->watchdog_timeout = 0;
15709 sa->is_mserver = FALSE;
15710
15711 /* send my own computer id */
15712 hw_type = rpc_get_hw_type();
15713 std::string str = msprintf("%d %s", hw_type, cm_get_version());
15714 status = send(sock, str.c_str(), str.length() + 1, 0);
15715 if (status != (INT) str.length() + 1)
15716 return RPC_NET_ERROR;
15717
15718 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
15719 sa->convert_flags = convert_flags;
15720
15722
15723 return RPC_SUCCESS;
15724}
15725
15726/********************************************************************/
15728/********************************************************************\
15729
15730 Routine: rpc_server_callback
15731
15732 Purpose: Callback a remote client. Setup _server_acception entry
15733 with optional conversion flags and establish two-way
15734 TCP connection.
15735
15736 Input:
15737 callback_addr pcallback Pointer to a callback structure
15738
15739 Output:
15740 none
15741
15742 Function value:
15743 RPC_SUCCESS Successful completion
15744
15745\********************************************************************/
15746{
15747 INT status;
15748 int recv_sock, send_sock, event_sock;
15749 char str[100];
15750 std::string client_program;
15751 INT client_hw_type, hw_type;
15752 INT convert_flags;
15753 char net_buffer[256];
15754 char *p;
15755 int flag;
15756
15757 /* copy callback information */
15758 struct callback_addr callback = *pcallback;
15759 //idx = callback.index;
15760
15761 std::string errmsg;
15762
15763 /* create new sockets for TCP */
15764 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port1, &recv_sock, &errmsg);
15765
15766 if (status != SS_SUCCESS) {
15767 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());
15768 ss_socket_close(&recv_sock);
15769 //ss_socket_close(&send_sock);
15770 //ss_socket_close(&event_sock);
15771 return RPC_NET_ERROR;
15772 }
15773
15774 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port2, &send_sock, &errmsg);
15775
15776 if (status != SS_SUCCESS) {
15777 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());
15778 ss_socket_close(&recv_sock);
15779 ss_socket_close(&send_sock);
15780 //ss_socket_close(&event_sock);
15781 return RPC_NET_ERROR;
15782 }
15783
15784 status = ss_socket_connect_tcp(callback.host_name.c_str(), callback.host_port3, &event_sock, &errmsg);
15785
15786 if (status != SS_SUCCESS) {
15787 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());
15788 ss_socket_close(&recv_sock);
15789 ss_socket_close(&send_sock);
15790 ss_socket_close(&event_sock);
15791 return RPC_NET_ERROR;
15792 }
15793#ifndef OS_ULTRIX /* crashes ULTRIX... */
15794 /* increase send buffer size to 2 Mbytes, on Linux also limited by sysctl net.ipv4.tcp_rmem and net.ipv4.tcp_wmem */
15795 flag = 2 * 1024 * 1024;
15796 status = setsockopt(event_sock, SOL_SOCKET, SO_RCVBUF, (char *) &flag, sizeof(INT));
15797 if (status != 0)
15798 cm_msg(MERROR, "rpc_server_callback", "cannot setsockopt(SOL_SOCKET, SO_RCVBUF), errno %d (%s)", errno,
15799 strerror(errno));
15800#endif
15801
15802 if (recv_string(recv_sock, net_buffer, 256, _rpc_connect_timeout) <= 0) {
15803 cm_msg(MERROR, "rpc_server_callback", "timeout on receive remote computer info");
15804 ss_socket_close(&recv_sock);
15805 ss_socket_close(&send_sock);
15806 ss_socket_close(&event_sock);
15807 return RPC_NET_ERROR;
15808 }
15809 //printf("rpc_server_callback: \'%s\'\n", net_buffer);
15810
15811 /* get remote computer info */
15812 client_hw_type = strtoul(net_buffer, &p, 0);
15813
15814 while (*p == ' ')
15815 p++;
15816
15817 client_program = p;
15818
15819 //printf("hw type %d, name \'%s\'\n", client_hw_type, client_program);
15820
15821 std::string host_name;
15822
15823 status = ss_socket_get_peer_name(recv_sock, &host_name, NULL);
15824
15825 if (status != SS_SUCCESS)
15826 host_name = "unknown";
15827
15828 //printf("rpc_server_callback: mserver acception\n");
15829
15831
15832 /* save information in _server_acception structure */
15833 sa->recv_sock = recv_sock;
15834 sa->send_sock = send_sock;
15835 sa->event_sock = event_sock;
15836 sa->remote_hw_type = client_hw_type;
15837 sa->host_name = host_name;
15838 sa->prog_name = client_program;
15840 sa->watchdog_timeout = 0;
15841 sa->is_mserver = TRUE;
15842
15843 assert(_mserver_acception == NULL);
15844
15845 _mserver_acception = sa;
15846
15847 //printf("rpc_server_callback: _mserver_acception %p\n", _mserver_acception);
15848
15849 /* send my own computer id */
15850 hw_type = rpc_get_hw_type();
15851 sprintf(str, "%d", hw_type);
15852 send(recv_sock, str, strlen(str) + 1, 0);
15853
15854 rpc_calc_convert_flags(hw_type, client_hw_type, &convert_flags);
15855 sa->convert_flags = convert_flags;
15856
15858
15859 if (rpc_is_mserver()) {
15860 rpc_debug_printf("Connection to %s:%s established\n", sa->host_name.c_str(), sa->prog_name.c_str());
15861 }
15862
15863 return RPC_SUCCESS;
15864}
15865
15866
15867/********************************************************************/
15869/********************************************************************\
15870
15871 Routine: rpc_server_loop
15872
15873 Purpose: mserver main event loop
15874
15875\********************************************************************/
15876{
15877 while (1) {
15878 int status = ss_suspend(1000, 0);
15879
15880 if (status == SS_ABORT || status == SS_EXIT)
15881 break;
15882
15884 break;
15885
15886 /* check alarms, etc */
15888
15890 }
15891
15892 return RPC_SUCCESS;
15893}
15894
15895/********************************************************************/
15897/********************************************************************\
15898
15899 Routine: rpc_server_receive_rpc
15900
15901 Purpose: Receive rpc commands and execute them. Close the connection
15902 if client has broken TCP pipe.
15903
15904 Function value:
15905 RPC_SUCCESS Successful completion
15906 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
15907 SS_EXIT Server connection was closed
15908 SS_ABORT Server connection was broken
15909
15910\********************************************************************/
15911{
15912 int status = 0;
15913 int remaining = 0;
15914
15915 char *buf = NULL;
15916 int bufsize = 0;
15917
15918 do {
15919 int n_received = recv_net_command_realloc(idx, &buf, &bufsize, &remaining);
15920
15921 if (n_received <= 0) {
15922 status = SS_ABORT;
15923 cm_msg(MERROR, "rpc_server_receive_rpc", "recv_net_command() returned %d, abort", n_received);
15924 goto error;
15925 }
15926
15927 status = rpc_execute(sa->recv_sock, buf, sa->convert_flags);
15928
15929 if (status == SS_ABORT) {
15930 cm_msg(MERROR, "rpc_server_receive_rpc", "rpc_execute() returned %d, abort", status);
15931 goto error;
15932 }
15933
15934 if (status == SS_EXIT || status == RPC_SHUTDOWN) {
15935 if (rpc_is_mserver())
15936 rpc_debug_printf("Connection to %s:%s closed\n", sa->host_name.c_str(), sa->prog_name.c_str());
15937 goto exit;
15938 }
15939
15940 } while (remaining);
15941
15942 if (buf) {
15943 free(buf);
15944 buf = NULL;
15945 bufsize = 0;
15946 }
15947
15948 return RPC_SUCCESS;
15949
15950 error:
15951
15952 {
15953 char str[80];
15954 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
15955 if (strchr(str, '.'))
15956 *strchr(str, '.') = 0;
15957 cm_msg(MTALK, "rpc_server_receive_rpc", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
15958 }
15959
15960 exit:
15961
15963
15964 if (buf) {
15965 free(buf);
15966 buf = NULL;
15967 bufsize = 0;
15968 }
15969
15970 /* disconnect from experiment as MIDAS server */
15971 if (rpc_is_mserver()) {
15972 HNDLE hDB, hKey;
15973
15975
15976 /* only disconnect from experiment if previously connected.
15977 Necessary for pure RPC servers (RPC_SRVR) */
15978 if (hDB) {
15982
15984
15986 }
15987 }
15988
15989 bool is_mserver = sa->is_mserver;
15990
15991 sa->close();
15992
15993 /* signal caller a shutdonw */
15994 if (status == RPC_SHUTDOWN)
15995 return status;
15996
15997 /* only the mserver should stop on server connection closure */
15998 if (!is_mserver) {
15999 return SS_SUCCESS;
16000 }
16001
16002 return status;
16003}
16004
16005/********************************************************************/
16007/********************************************************************\
16008
16009 Routine: rpc_server_receive_event
16010
16011 Purpose: Receive event and dispatch it
16012
16013 Function value:
16014 RPC_SUCCESS Successful completion
16015 RPC_EXCEED_BUFFER Not enough memeory to allocate buffer
16016 SS_EXIT Server connection was closed
16017 SS_ABORT Server connection was broken
16018
16019\********************************************************************/
16020{
16021 int status = 0;
16022
16023 DWORD start_time = ss_millitime();
16024
16025 //
16026 // THIS IS NOT THREAD SAFE!!!
16027 //
16028 // IT IS ONLY USED BY THE MSERVER
16029 // MSERVER IS SINGLE-THREADED!!!
16030 //
16031
16032 static char *xbuf = NULL;
16033 static int xbufsize = 0;
16034 static bool xbufempty = true;
16035
16036 // short cut
16037 if (sa == NULL && xbufempty)
16038 return RPC_SUCCESS;
16039
16040 static bool recurse = false;
16041
16042 if (recurse) {
16043 cm_msg(MERROR, "rpc_server_receive_event", "internal error: called recursively");
16044 // do not do anything if we are called recursively
16045 // via recursive ss_suspend() or otherwise. K.O.
16046 if (xbufempty)
16047 return RPC_SUCCESS;
16048 else
16049 return BM_ASYNC_RETURN;
16050 }
16051
16052 recurse = true;
16053
16054 do {
16055 if (xbufempty && sa) {
16056 int n_received = recv_event_server_realloc(idx, sa, &xbuf, &xbufsize);
16057
16058 if (n_received < 0) {
16059 status = SS_ABORT;
16060 cm_msg(MERROR, "rpc_server_receive_event", "recv_event_server_realloc() returned %d, abort", n_received);
16061 goto error;
16062 }
16063
16064 if (n_received == 0) {
16065 // no more data in the tcp socket
16066 recurse = false;
16067 return RPC_SUCCESS;
16068 }
16069
16070 xbufempty = false;
16071 }
16072
16073 if (xbufempty) {
16074 // no event in xbuf buffer
16075 recurse = false;
16076 return RPC_SUCCESS;
16077 }
16078
16079 /* send event to buffer */
16080 INT *pbh = (INT *) xbuf;
16081 EVENT_HEADER *pevent = (EVENT_HEADER *) (pbh + 1);
16082
16083 status = bm_send_event(*pbh, pevent, 0, timeout_msec);
16084
16085 //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);
16086
16087 if (status == SS_ABORT) {
16088 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d (SS_ABORT), abort", status);
16089 goto error;
16090 }
16091
16092 if (status == BM_ASYNC_RETURN) {
16093 //cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, event buffer is full", status);
16094 recurse = false;
16095 return status;
16096 }
16097
16098 if (status != BM_SUCCESS) {
16099 cm_msg(MERROR, "rpc_server_receive_event", "bm_send_event() error %d, mserver dropped this event", status);
16100 }
16101
16102 xbufempty = true;
16103
16104 /* repeat for maximum 0.5 sec */
16105 } while (ss_millitime() - start_time < 500);
16106
16107 recurse = false;
16108 return RPC_SUCCESS;
16109
16110 error:
16111
16112 {
16113 char str[80];
16114 mstrlcpy(str, sa->host_name.c_str(), sizeof(str));
16115 if (strchr(str, '.'))
16116 *strchr(str, '.') = 0;
16117 cm_msg(MTALK, "rpc_server_receive_event", "Program \'%s\' on host \'%s\' aborted", sa->prog_name.c_str(), str);
16118 }
16119
16120 //exit:
16121
16123
16124 /* disconnect from experiment as MIDAS server */
16125 if (rpc_is_mserver()) {
16126 HNDLE hDB, hKey;
16127
16129
16130 /* only disconnect from experiment if previously connected.
16131 Necessary for pure RPC servers (RPC_SRVR) */
16132 if (hDB) {
16136
16138
16140 }
16141 }
16142
16143 bool is_mserver = sa->is_mserver;
16144
16145 sa->close();
16146
16147 /* signal caller a shutdonw */
16148 if (status == RPC_SHUTDOWN)
16149 return status;
16150
16151 /* only the mserver should stop on server connection closure */
16152 if (!is_mserver) {
16153 return SS_SUCCESS;
16154 }
16155
16156 return status;
16157}
16158
16159
16160/********************************************************************/
16161int rpc_flush_event_socket(int timeout_msec)
16162/********************************************************************\
16163
16164 Routine: rpc_flush_event_socket
16165
16166 Purpose: Receive and en-buffer events from the mserver event socket
16167
16168 Function value:
16169 BM_SUCCESS Event socket is empty, all data was read an en-buffered
16170 BM_ASYNC_RETURN Event socket has unread data or event buffer is full and rpc_server_receive_event() has an un-buffered event.
16171 SS_EXIT Server connection was closed
16172 SS_ABORT Server connection was broken
16173
16174\********************************************************************/
16175{
16176 bool has_data = ss_event_socket_has_data();
16177
16178 //printf("ss_event_socket_has_data() returned %d\n", has_data);
16179
16180 if (has_data) {
16181 if (timeout_msec == BM_NO_WAIT) {
16182 return BM_ASYNC_RETURN;
16183 } else if (timeout_msec == BM_WAIT) {
16184 return BM_ASYNC_RETURN;
16185 } else {
16186 int status = ss_suspend(timeout_msec, MSG_BM);
16187 if (status == SS_ABORT || status == SS_EXIT)
16188 return status;
16189 return BM_ASYNC_RETURN;
16190 }
16191 }
16192
16193 int status = rpc_server_receive_event(0, NULL, timeout_msec);
16194
16195 //printf("rpc_server_receive_event() status %d\n", status);
16196
16197 if (status == BM_ASYNC_RETURN) {
16198 return BM_ASYNC_RETURN;
16199 }
16200
16201 if (status == SS_ABORT || status == SS_EXIT)
16202 return status;
16203
16204 return BM_SUCCESS;
16205}
16206
16207/********************************************************************/
16209/********************************************************************\
16210
16211 Routine: rpc_server_shutdown
16212
16213 Purpose: Shutdown RPC server, abort all connections
16214
16215 Input:
16216 none
16217
16218 Output:
16219 none
16220
16221 Function value:
16222 RPC_SUCCESS Successful completion
16223
16224\********************************************************************/
16225{
16226 //printf("rpc_server_shutdown!\n");
16227
16228 struct linger ling;
16229
16230 /* close all open connections */
16231 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16232 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock != 0) {
16234 /* lingering needed for PCTCP */
16235 ling.l_onoff = 1;
16236 ling.l_linger = 0;
16237 setsockopt(sa->recv_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16239
16240 if (sa->send_sock) {
16241 setsockopt(sa->send_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16243 }
16244
16245 if (sa->event_sock) {
16246 setsockopt(sa->event_sock, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling));
16248 }
16249 }
16250 }
16251
16252 /* avoid memory leak */
16253 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16255 if (sa) {
16256 //printf("rpc_server_shutdown: %d %p %p\n", idx, sa, _mserver_acception);
16257 if (sa == _mserver_acception) {
16258 // do not leave behind a stale pointer!
16259 _mserver_acception = NULL;
16260 }
16261 delete sa;
16262 _server_acceptions[idx] = NULL;
16263 }
16264 }
16265
16266 if (_rpc_registered) {
16269 }
16270
16271 /* free suspend structures */
16273
16274 return RPC_SUCCESS;
16275}
16276
16277
16278/********************************************************************/
16280/********************************************************************\
16281
16282 Routine: rpc_check_channels
16283
16284 Purpose: Check open rpc channels by sending watchdog messages
16285
16286 Input:
16287 none
16288
16289 Output:
16290 none
16291
16292 Function value:
16293 RPC_SUCCESS Channel is still alive
16294 RPC_NET_ERROR Connection is broken
16295
16296\********************************************************************/
16297{
16298 INT status;
16299 NET_COMMAND nc;
16300 fd_set readfds;
16301 struct timeval timeout;
16302
16303 //printf("rpc_check_channels!\n");
16304
16305 for (unsigned idx = 0; idx < _server_acceptions.size(); idx++) {
16306 if (_server_acceptions[idx] && _server_acceptions[idx]->recv_sock) {
16308 if (sa == NULL)
16309 continue;
16310
16311 if (sa->watchdog_timeout == 0) {
16312 continue;
16313 }
16314
16315 DWORD elapsed = ss_millitime() - sa->last_activity;
16316
16317 //printf("rpc_check_channels: idx %d, watchdog_timeout %d, last_activity %d, elapsed %d\n", idx, sa->watchdog_timeout, sa->last_activity, elapsed);
16318
16319 if (sa->watchdog_timeout && (elapsed > (DWORD)sa->watchdog_timeout)) {
16320
16321 //printf("rpc_check_channels: send watchdog message to %s on %s\n", sa->prog_name.c_str(), sa->host_name.c_str());
16322
16323 /* send a watchdog message */
16325 nc.header.param_size = 0;
16326
16327 int convert_flags = sa->convert_flags;
16328 if (convert_flags) {
16331 }
16332
16333 /* send the header to the client */
16334 int i = send_tcp(sa->send_sock, (char *) &nc, sizeof(NET_COMMAND_HEADER), 0);
16335
16336 if (i < 0) {
16337 cm_msg(MINFO, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, send_tcp() returned %d",
16338 sa->prog_name.c_str(),
16339 sa->host_name.c_str(),
16340 sa->watchdog_timeout / 1000,
16341 i);
16342
16343 /* disconnect from experiment */
16344 if (rpc_is_mserver()) {
16346 return RPC_NET_ERROR;
16347 }
16348
16349 sa->close();
16350 return RPC_NET_ERROR;
16351 }
16352
16353 DWORD timeout_end_ms = ss_millitime() + sa->watchdog_timeout;
16354
16355 while (1) {
16356 FD_ZERO(&readfds);
16357 FD_SET(sa->send_sock, &readfds);
16358 FD_SET(sa->recv_sock, &readfds);
16359
16360 timeout.tv_sec = 1;
16361 timeout.tv_usec = 0;
16362
16363 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
16364
16365 DWORD now = ss_millitime();
16366
16367 //printf("waiting for reply: %d %d, diff %d, select() status %d\n", now, timeout_end_ms, timeout_end_ms - now, status);
16368
16369 if (now > timeout_end_ms) // timeout
16370 break;
16371
16372 if (status > 0) // select has something to read
16373 break;
16374
16375 // select() returned 0, timeout
16376 // select() returned -1, error, likely EAGAIN or EINTR
16377
16380 }
16381
16382 if (!FD_ISSET(sa->send_sock, &readfds) &&
16383 !FD_ISSET(sa->recv_sock, &readfds)) {
16384
16385 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec",
16386 sa->prog_name.c_str(),
16387 sa->host_name.c_str(),
16388 sa->watchdog_timeout / 1000);
16389
16390 /* disconnect from experiment */
16391 if (rpc_is_mserver()) {
16393 return RPC_NET_ERROR;
16394 }
16395
16396 sa->close();
16397 return RPC_NET_ERROR;
16398 }
16399
16400 /* receive result on send socket */
16401 if (FD_ISSET(sa->send_sock, &readfds)) {
16402 i = recv_tcp(sa->send_sock, (char *) &nc, sizeof(nc), 0);
16403 if (i <= 0) {
16404 cm_msg(MERROR, "rpc_check_channels", "client \"%s\" on host \"%s\" failed watchdog test after %d sec, recv_tcp() returned %d",
16405 sa->prog_name.c_str(),
16406 sa->host_name.c_str(),
16407 sa->watchdog_timeout / 1000,
16408 i);
16409
16410 /* disconnect from experiment */
16411 if (rpc_is_mserver()) {
16413 return RPC_NET_ERROR;
16414 }
16415
16416 sa->close();
16417 return RPC_NET_ERROR;
16418 }
16419 }
16420 }
16421 }
16422 }
16423
16424 return RPC_SUCCESS;
16425}
16426
16434/********************************************************************\
16435* *
16436* Bank functions *
16437* *
16438\********************************************************************/
16439
16440/********************************************************************/
16446void bk_init(void *event) {
16447 ((BANK_HEADER *) event)->data_size = 0;
16448 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION;
16449}
16450
16452#ifndef DOXYGEN_SHOULD_SKIP_THIS
16453
16454/********************************************************************/
16455BOOL bk_is32(const void *event)
16456/********************************************************************\
16457
16458 Routine: bk_is32
16459
16460 Purpose: Return true if banks inside event are 32-bit banks
16461
16462 Input:
16463 void *event pointer to the event
16464
16465 Output:
16466 none
16467
16468 Function value:
16469 none
16470
16471\********************************************************************/
16472{
16473 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_32BIT) > 0);
16474}
16475
16476/********************************************************************/
16477BOOL bk_is32a(const void *event)
16478/********************************************************************\
16479
16480 Routine: bk_is32a
16481
16482 Purpose: Return true if banks inside event are 32-bit banks
16483 and banks are 64-bit aligned
16484
16485 Input:
16486 void *event pointer to the event
16487
16488 Output:
16489 none
16490
16491 Function value:
16492 none
16493
16494\********************************************************************/
16495{
16496 return ((((BANK_HEADER *) event)->flags & BANK_FORMAT_64BIT_ALIGNED) > 0);
16497}
16498
16500#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16501
16502/********************************************************************/
16509void bk_init32(void *event) {
16510 ((BANK_HEADER *) event)->data_size = 0;
16511 ((BANK_HEADER *) event)->flags = BANK_FORMAT_VERSION | BANK_FORMAT_32BIT;
16512}
16513
16514/********************************************************************/
16522void bk_init32a(void *event) {
16523 ((BANK_HEADER *) event)->data_size = 0;
16525}
16526
16527/********************************************************************/
16535INT bk_size(const void *event) {
16536 return ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER);
16537}
16538
16539static void copy_bk_name(char* dst, const char* src)
16540{
16541 // copy 4 byte bank name from "src" to "dst", set unused bytes of "dst" to NUL.
16542
16543 if (src[0] == 0) {
16544 // invalid empty name
16545 dst[0] = 0;
16546 dst[1] = 0;
16547 dst[2] = 0;
16548 dst[3] = 0;
16549 return;
16550 }
16551
16552 dst[0] = src[0];
16553
16554 if (src[1] == 0) {
16555 dst[1] = 0;
16556 dst[2] = 0;
16557 dst[3] = 0;
16558 return;
16559 }
16560
16561 dst[1] = src[1];
16562
16563 if (src[2] == 0) {
16564 dst[2] = 0;
16565 dst[3] = 0;
16566 return;
16567 }
16568
16569 dst[2] = src[2];
16570
16571 if (src[3] == 0) {
16572 dst[3] = 0;
16573 return;
16574 }
16575
16576 dst[3] = src[3];
16577}
16578
16579/********************************************************************/
16601void bk_create(void *event, const char *name, WORD type, void **pdata) {
16602 if (bk_is32a((BANK_HEADER *) event)) {
16603 if (((PTYPE) event & 0x07) != 0) {
16604 cm_msg(MERROR, "bk_create", "Bank %s created with unaligned event pointer", name);
16605 return;
16606 }
16607 BANK32A *pbk32a;
16608
16609 pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16610 copy_bk_name(pbk32a->name, name);
16611 pbk32a->type = type;
16612 pbk32a->data_size = 0;
16613 *pdata = pbk32a + 1;
16614 } else if (bk_is32((BANK_HEADER *) event)) {
16615 BANK32 *pbk32;
16616
16617 pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16618 copy_bk_name(pbk32->name, name);
16619 pbk32->type = type;
16620 pbk32->data_size = 0;
16621 *pdata = pbk32 + 1;
16622 } else {
16623 BANK *pbk;
16624
16625 pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16626 copy_bk_name(pbk->name, name);
16627 pbk->type = type;
16628 pbk->data_size = 0;
16629 *pdata = pbk + 1;
16630 }
16631}
16632
16634#ifndef DOXYGEN_SHOULD_SKIP_THIS
16635
16636/********************************************************************/
16644INT bk_copy(char *pevent, char *psrce, const char *bkname) {
16645
16646 INT status;
16647 DWORD bklen, bktype, bksze;
16648 BANK_HEADER *psBkh;
16649 BANK *psbkh;
16650 char *pdest;
16651 void *psdata;
16652
16653 // source pointing on the BANKxx
16654 psBkh = (BANK_HEADER *) ((EVENT_HEADER *) psrce + 1);
16655 // Find requested bank
16656 status = bk_find(psBkh, bkname, &bklen, &bktype, &psdata);
16657 // Return 0 if not found
16658 if (status != SUCCESS) return 0;
16659
16660 // Check bank type...
16661 // You cannot mix BANK and BANK32 so make sure all the FE use either
16662 // bk_init(pevent) or bk_init32(pevent).
16663 if (bk_is32a(psBkh)) {
16664
16665 // pointer to the source bank header
16666 BANK32A *psbkh32a = ((BANK32A *) psdata - 1);
16667 // Data size in the bank
16668 bksze = psbkh32a->data_size;
16669
16670 // Get to the end of the event
16671 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16672 // Copy from BANK32 to end of Data
16673 memmove(pdest, (char *) psbkh32a, ALIGN8(bksze) + sizeof(BANK32A));
16674 // Bring pointer to the next free location
16675 pdest += ALIGN8(bksze) + sizeof(BANK32A);
16676
16677 } else if (bk_is32(psBkh)) {
16678
16679 // pointer to the source bank header
16680 BANK32 *psbkh32 = ((BANK32 *) psdata - 1);
16681 // Data size in the bank
16682 bksze = psbkh32->data_size;
16683
16684 // Get to the end of the event
16685 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16686 // Copy from BANK32 to end of Data
16687 memmove(pdest, (char *) psbkh32, ALIGN8(bksze) + sizeof(BANK32));
16688 // Bring pointer to the next free location
16689 pdest += ALIGN8(bksze) + sizeof(BANK32);
16690
16691 } else {
16692
16693 // pointer to the source bank header
16694 psbkh = ((BANK *) psdata - 1);
16695 // Data size in the bank
16696 bksze = psbkh->data_size;
16697
16698 // Get to the end of the event
16699 pdest = (char *) (((BANK_HEADER *) pevent) + 1) + ((BANK_HEADER *) pevent)->data_size;
16700 // Copy from BANK to end of Data
16701 memmove(pdest, (char *) psbkh, ALIGN8(bksze) + sizeof(BANK));
16702 // Bring pointer to the next free location
16703 pdest += ALIGN8(bksze) + sizeof(BANK);
16704 }
16705
16706 // Close bank (adjust BANK_HEADER size)
16707 bk_close(pevent, pdest);
16708 // Adjust EVENT_HEADER size
16709 ((EVENT_HEADER *) pevent - 1)->data_size = ((BANK_HEADER *) pevent)->data_size + sizeof(BANK_HEADER);
16710 return SUCCESS;
16711}
16712
16713/********************************************************************/
16714int bk_delete(void *event, const char *name)
16715/********************************************************************\
16716
16717 Routine: bk_delete
16718
16719 Purpose: Delete a MIDAS bank inside an event
16720
16721 Input:
16722 void *event pointer to the event
16723 char *name Name of bank (exactly four letters)
16724
16725 Function value:
16726 CM_SUCCESS Bank has been deleted
16727 0 Bank has not been found
16728
16729\********************************************************************/
16730{
16731 BANK *pbk;
16732 DWORD dname;
16733 int remaining;
16734
16735 if (bk_is32a((BANK_HEADER *) event)) {
16736 /* locate bank */
16737 BANK32A *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16738 copy_bk_name((char *) &dname, name);
16739 do {
16740 if (*((DWORD *) pbk32a->name) == dname) {
16741 /* bank found, delete it */
16742 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16743 sizeof(BANK_HEADER)) - ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16744
16745 /* reduce total event size */
16746 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32a->data_size);
16747
16748 /* copy remaining bytes */
16749 if (remaining > 0)
16750 memmove(pbk32a, (char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size), remaining);
16751 return CM_SUCCESS;
16752 }
16753
16754 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16755 } while ((DWORD) ((char *) pbk32a - (char *) event) <
16756 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16757 } else if (bk_is32((BANK_HEADER *) event)) {
16758 /* locate bank */
16759 BANK32 *pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16760 copy_bk_name((char *) &dname, name);
16761 do {
16762 if (*((DWORD *) pbk32->name) == dname) {
16763 /* bank found, delete it */
16764 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16765 sizeof(BANK_HEADER)) - ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16766
16767 /* reduce total event size */
16768 ((BANK_HEADER *) event)->data_size -= sizeof(BANK32) + ALIGN8(pbk32->data_size);
16769
16770 /* copy remaining bytes */
16771 if (remaining > 0)
16772 memmove(pbk32, (char *) (pbk32 + 1) + ALIGN8(pbk32->data_size), remaining);
16773 return CM_SUCCESS;
16774 }
16775
16776 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16777 } while ((DWORD) ((char *) pbk32 - (char *) event) <
16778 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16779 } else {
16780 /* locate bank */
16781 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16782 copy_bk_name((char *) &dname, name);
16783 do {
16784 if (*((DWORD *) pbk->name) == dname) {
16785 /* bank found, delete it */
16786 remaining = ((char *) event + ((BANK_HEADER *) event)->data_size +
16787 sizeof(BANK_HEADER)) - ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16788
16789 /* reduce total event size */
16790 ((BANK_HEADER *) event)->data_size -= sizeof(BANK) + ALIGN8(pbk->data_size);
16791
16792 /* copy remaining bytes */
16793 if (remaining > 0)
16794 memmove(pbk, (char *) (pbk + 1) + ALIGN8(pbk->data_size), remaining);
16795 return CM_SUCCESS;
16796 }
16797
16798 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16799 } while ((DWORD) ((char *) pbk - (char *) event) <
16800 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER));
16801 }
16802
16803 return 0;
16804}
16805
16807#endif /* DOXYGEN_SHOULD_SKIP_THIS */
16808
16809/********************************************************************/
16820INT bk_close(void *event, void *pdata) {
16821 if (bk_is32a((BANK_HEADER *) event)) {
16822 BANK32A *pbk32a = (BANK32A *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16823 pbk32a->data_size = (DWORD) ((char *) pdata - (char *) (pbk32a + 1));
16824 if (pbk32a->type == TID_STRUCT && pbk32a->data_size == 0)
16825 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]);
16826 ((BANK_HEADER *) event)->data_size += sizeof(BANK32A) + ALIGN8(pbk32a->data_size);
16827 return pbk32a->data_size;
16828 } else if (bk_is32((BANK_HEADER *) event)) {
16829 BANK32 *pbk32 = (BANK32 *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16830 pbk32->data_size = (DWORD) ((char *) pdata - (char *) (pbk32 + 1));
16831 if (pbk32->type == TID_STRUCT && pbk32->data_size == 0)
16832 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]);
16833 ((BANK_HEADER *) event)->data_size += sizeof(BANK32) + ALIGN8(pbk32->data_size);
16834 return pbk32->data_size;
16835 } else {
16836 BANK *pbk = (BANK *) ((char *) (((BANK_HEADER *) event) + 1) + ((BANK_HEADER *) event)->data_size);
16837 uint32_t size = (uint32_t) ((char *) pdata - (char *) (pbk + 1));
16838 if (size > 0xFFFF) {
16839 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16840 size = 0;
16841 }
16842 pbk->data_size = (WORD) (size);
16843 if (pbk->type == TID_STRUCT && pbk->data_size == 0)
16844 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]);
16845 size = ((BANK_HEADER *) event)->data_size + sizeof(BANK) + ALIGN8(pbk->data_size);
16846 if (size > 0xFFFF) {
16847 printf("Error: Bank size %d exceeds 16-bit limit of 65526, please use bk_init32() to create a 32-bit bank\n", size);
16848 size = 0;
16849 }
16850 ((BANK_HEADER *) event)->data_size = size;
16851 return pbk->data_size;
16852 }
16853}
16854
16855/********************************************************************/
16880INT bk_list(const void *event, char *bklist) { /* Full event */
16881 INT nbk;
16882 BANK *pmbk = NULL;
16883 BANK32 *pmbk32 = NULL;
16884 BANK32A *pmbk32a = NULL;
16885 char *pdata;
16886
16887 /* compose bank list */
16888 bklist[0] = 0;
16889 nbk = 0;
16890 do {
16891 /* scan all banks for bank name only */
16892 if (bk_is32a(event)) {
16893 bk_iterate32a(event, &pmbk32a, &pdata);
16894 if (pmbk32a == NULL)
16895 break;
16896 } else if (bk_is32(event)) {
16897 bk_iterate32(event, &pmbk32, &pdata);
16898 if (pmbk32 == NULL)
16899 break;
16900 } else {
16901 bk_iterate(event, &pmbk, &pdata);
16902 if (pmbk == NULL)
16903 break;
16904 }
16905 nbk++;
16906
16907 if (nbk > BANKLIST_MAX) {
16908 cm_msg(MINFO, "bk_list", "over %i banks -> truncated", BANKLIST_MAX);
16909 return (nbk - 1);
16910 }
16911 if (bk_is32a(event))
16912 strncat(bklist, (char *) pmbk32a->name, 4);
16913 else if (bk_is32(event))
16914 strncat(bklist, (char *) pmbk32->name, 4);
16915 else
16916 strncat(bklist, (char *) pmbk->name, 4);
16917 } while (1);
16918 return (nbk);
16919}
16920
16921/********************************************************************/
16929INT bk_locate(const void *event, const char *name, void *pdata) {
16930 BANK *pbk;
16931 BANK32 *pbk32;
16932 BANK32A *pbk32a;
16933 DWORD dname;
16934
16935 if (bk_is32a(event)) {
16936 pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
16937 copy_bk_name((char *) &dname, name);
16938 while ((DWORD) ((char *) pbk32a - (char *) event) <
16939 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16940 if (*((DWORD *) pbk32a->name) == dname) {
16941 *((void **) pdata) = pbk32a + 1;
16942 if (tid_size[pbk32a->type & 0xFF] == 0)
16943 return pbk32a->data_size;
16944 return pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
16945 }
16946 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
16947 }
16948 } else if (bk_is32(event)) {
16949 pbk32 = (BANK32 *) (((BANK_HEADER *) event) + 1);
16950 copy_bk_name((char *) &dname, name);
16951 while ((DWORD) ((char *) pbk32 - (char *) event) <
16952 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16953 if (*((DWORD *) pbk32->name) == dname) {
16954 *((void **) pdata) = pbk32 + 1;
16955 if (tid_size[pbk32->type & 0xFF] == 0)
16956 return pbk32->data_size;
16957 return pbk32->data_size / tid_size[pbk32->type & 0xFF];
16958 }
16959 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
16960 }
16961 } else {
16962 pbk = (BANK *) (((BANK_HEADER *) event) + 1);
16963 copy_bk_name((char *) &dname, name);
16964 while ((DWORD) ((char *) pbk - (char *) event) <
16965 ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
16966 if (*((DWORD *) pbk->name) == dname) {
16967 *((void **) pdata) = pbk + 1;
16968 if (tid_size[pbk->type & 0xFF] == 0)
16969 return pbk->data_size;
16970 return pbk->data_size / tid_size[pbk->type & 0xFF];
16971 }
16972 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
16973 }
16974
16975 }
16976
16977 /* bank not found */
16978 *((void **) pdata) = NULL;
16979 return 0;
16980}
16981
16982/********************************************************************/
16992INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata) {
16993 DWORD dname;
16994
16995 if (bk_is32a(pbkh)) {
16996 BANK32A *pbk32a = (BANK32A *) (pbkh + 1);
16997 copy_bk_name((char *) &dname, name);
16998 do {
16999 if (*((DWORD *) pbk32a->name) == dname) {
17000 *((void **) pdata) = pbk32a + 1;
17001 if (tid_size[pbk32a->type & 0xFF] == 0)
17002 *bklen = pbk32a->data_size;
17003 else
17004 *bklen = pbk32a->data_size / tid_size[pbk32a->type & 0xFF];
17005
17006 *bktype = pbk32a->type;
17007 return 1;
17008 }
17009 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17010 } while ((DWORD) ((char *) pbk32a - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17011 } else if (bk_is32(pbkh)) {
17012 BANK32 *pbk32 = (BANK32 *) (pbkh + 1);
17013 copy_bk_name((char *) &dname, name);
17014 do {
17015 if (*((DWORD *) pbk32->name) == dname) {
17016 *((void **) pdata) = pbk32 + 1;
17017 if (tid_size[pbk32->type & 0xFF] == 0)
17018 *bklen = pbk32->data_size;
17019 else
17020 *bklen = pbk32->data_size / tid_size[pbk32->type & 0xFF];
17021
17022 *bktype = pbk32->type;
17023 return 1;
17024 }
17025 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17026 } while ((DWORD) ((char *) pbk32 - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17027 } else {
17028 BANK *pbk = (BANK *) (pbkh + 1);
17029 copy_bk_name((char *) &dname, name);
17030 do {
17031 if (*((DWORD *) pbk->name) == dname) {
17032 *((void **) pdata) = pbk + 1;
17033 if (tid_size[pbk->type & 0xFF] == 0)
17034 *bklen = pbk->data_size;
17035 else
17036 *bklen = pbk->data_size / tid_size[pbk->type & 0xFF];
17037
17038 *bktype = pbk->type;
17039 return 1;
17040 }
17041 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17042 } while ((DWORD) ((char *) pbk - (char *) pbkh) < pbkh->data_size + sizeof(BANK_HEADER));
17043 }
17044
17045 /* bank not found */
17046 *((void **) pdata) = NULL;
17047 return 0;
17048}
17049
17050/********************************************************************/
17086INT bk_iterate(const void *event, BANK **pbk, void *pdata) {
17087 if (*pbk == NULL)
17088 *pbk = (BANK *) (((BANK_HEADER *) event) + 1);
17089 else
17090 *pbk = (BANK *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17091
17092 *((void **) pdata) = (*pbk) + 1;
17093
17094 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17095 *pbk = *((BANK **) pdata) = NULL;
17096 return 0;
17097 }
17098
17099 return (*pbk)->data_size;
17100}
17101
17102
17104#ifndef DOXYGEN_SHOULD_SKIP_THIS
17105
17106/********************************************************************/
17107INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
17108/********************************************************************\
17109
17110 Routine: bk_iterate32
17111
17112 Purpose: Iterate through 32 bit MIDAS banks inside an event
17113
17114 Input:
17115 void *event pointer to the event
17116 BANK32 **pbk32 must be NULL for the first call to bk_iterate
17117
17118 Output:
17119 BANK32 **pbk32 pointer to the bank header
17120 void *pdata pointer to data area of the bank
17121
17122 Function value:
17123 INT size of the bank in bytes
17124
17125\********************************************************************/
17126{
17127 if (*pbk == NULL)
17128 *pbk = (BANK32 *) (((BANK_HEADER *) event) + 1);
17129 else
17130 *pbk = (BANK32 *) ((char *) (*pbk + 1) + ALIGN8((*pbk)->data_size));
17131
17132 *((void **) pdata) = (*pbk) + 1;
17133
17134 if ((DWORD) ((char *) *pbk - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17135 *pbk = NULL;
17136 pdata = NULL;
17137 return 0;
17138 }
17139
17140 return (*pbk)->data_size;
17141}
17142
17143INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
17144/********************************************************************\
17145
17146 Routine: bk_iterate32a
17147
17148 Purpose: Iterate through 64-bit aliggned 32 bit MIDAS banks inside an event
17149
17150 Input:
17151 void *event pointer to the event
17152 BANK32A **pbk32a must be NULL for the first call to bk_iterate
17153
17154 Output:
17155 BANK32A **pbk32 pointer to the bank header
17156 void *pdata pointer to data area of the bank
17157
17158 Function value:
17159 INT size of the bank in bytes
17160
17161\********************************************************************/
17162{
17163 if (*pbk32a == NULL)
17164 *pbk32a = (BANK32A *) (((BANK_HEADER *) event) + 1);
17165 else
17166 *pbk32a = (BANK32A *) ((char *) (*pbk32a + 1) + ALIGN8((*pbk32a)->data_size));
17167
17168 *((void **) pdata) = (*pbk32a) + 1;
17169
17170 if ((DWORD) ((char *) *pbk32a - (char *) event) >= ((BANK_HEADER *) event)->data_size + sizeof(BANK_HEADER)) {
17171 *pbk32a = NULL;
17172 pdata = NULL;
17173 return 0;
17174 }
17175
17176 return (*pbk32a)->data_size;
17177}
17178
17180#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17181
17182/********************************************************************/
17197INT bk_swap(void *event, BOOL force) {
17198 BANK_HEADER *pbh;
17199 BANK *pbk;
17200 BANK32 *pbk32;
17201 BANK32A *pbk32a;
17202 void *pdata;
17203 WORD type;
17204
17205 pbh = (BANK_HEADER *) event;
17206
17207 /* only swap if flags in high 16-bit */
17208 if (pbh->flags < 0x10000 && !force)
17209 return 0;
17210
17211 /* swap bank header */
17212 DWORD_SWAP(&pbh->data_size);
17213 DWORD_SWAP(&pbh->flags);
17214
17215 pbk = (BANK *) (pbh + 1);
17216 pbk32 = (BANK32 *) pbk;
17217 pbk32a = (BANK32A *) pbk;
17218
17219 /* scan event */
17220 while ((char *) pbk - (char *) pbh < (INT) pbh->data_size + (INT) sizeof(BANK_HEADER)) {
17221 /* swap bank header */
17222 if (bk_is32a(event)) {
17223 DWORD_SWAP(&pbk32a->type);
17224 DWORD_SWAP(&pbk32a->data_size);
17225 pdata = pbk32a + 1;
17226 type = (WORD) pbk32a->type;
17227 } else if (bk_is32(event)) {
17228 DWORD_SWAP(&pbk32->type);
17229 DWORD_SWAP(&pbk32->data_size);
17230 pdata = pbk32 + 1;
17231 type = (WORD) pbk32->type;
17232 } else {
17233 WORD_SWAP(&pbk->type);
17234 WORD_SWAP(&pbk->data_size);
17235 pdata = pbk + 1;
17236 type = pbk->type;
17237 }
17238
17239 /* pbk points to next bank */
17240 if (bk_is32a(event)) {
17241 pbk32a = (BANK32A *) ((char *) (pbk32a + 1) + ALIGN8(pbk32a->data_size));
17242 pbk = (BANK *) pbk32a;
17243 } else if (bk_is32(event)) {
17244 pbk32 = (BANK32 *) ((char *) (pbk32 + 1) + ALIGN8(pbk32->data_size));
17245 pbk = (BANK *) pbk32;
17246 } else {
17247 pbk = (BANK *) ((char *) (pbk + 1) + ALIGN8(pbk->data_size));
17248 pbk32 = (BANK32 *) pbk;
17249 }
17250
17251 switch (type) {
17252 case TID_UINT16:
17253 case TID_INT16:
17254 while ((char *) pdata < (char *) pbk) {
17255 WORD_SWAP(pdata);
17256 pdata = (void *) (((WORD *) pdata) + 1);
17257 }
17258 break;
17259
17260 case TID_UINT32:
17261 case TID_INT32:
17262 case TID_BOOL:
17263 case TID_FLOAT:
17264 while ((char *) pdata < (char *) pbk) {
17265 DWORD_SWAP(pdata);
17266 pdata = (void *) (((DWORD *) pdata) + 1);
17267 }
17268 break;
17269
17270 case TID_DOUBLE:
17271 case TID_INT64:
17272 case TID_UINT64:
17273 while ((char *) pdata < (char *) pbk) {
17274 QWORD_SWAP(pdata);
17275 pdata = (void *) (((double *) pdata) + 1);
17276 }
17277 break;
17278 }
17279 }
17280
17281 return CM_SUCCESS;
17282}
17283
/* end of bkfunctionc */
17287
17288
17295#ifndef DOXYGEN_SHOULD_SKIP_THIS
17296/********************************************************************/
17297
17298/********************************************************************\
17299* *
17300* Ring buffer functions *
17301* *
17302* Provide an inter-thread buffer scheme for handling front-end *
17303* events. This code allows concurrent data acquisition, calibration *
17304* and network transfer on a multi-CPU machine. One thread reads *
17305* out the data, passes it vis the ring buffer functions *
17306* to another thread running on the other CPU, which can then *
17307* calibrate and/or send the data over the network. *
17308* *
17309\********************************************************************/
17310
17311typedef struct {
17312 unsigned char *buffer;
17313 unsigned int size;
17314 unsigned int max_event_size;
17315 unsigned char *rp;
17316 unsigned char *wp;
17317 unsigned char *ep;
17318} RING_BUFFER;
17319
17320#define MAX_RING_BUFFER 100
17321
17323
17324static volatile int _rb_nonblocking = 0;
17325
17327#endif /* DOXYGEN_SHOULD_SKIP_THIS */
17328
17329/********************************************************************/
17336/********************************************************************\
17337
17338 Routine: rb_set_nonblocking
17339
17340 Purpose: Set all rb_get_xx to nonblocking. Needed in multi-thread
17341 environments for stopping all theads without deadlock
17342
17343 Input:
17344 NONE
17345
17346 Output:
17347 NONE
17348
17349 Function value:
17350 DB_SUCCESS Successful completion
17351
17352\********************************************************************/
17353{
17354 _rb_nonblocking = 1;
17355
17356 return DB_SUCCESS;
17357}
17358
17359/********************************************************************/
17376int rb_create(int size, int max_event_size, int *handle)
17377/********************************************************************\
17378
17379 Routine: rb_create
17380
17381 Purpose: Create a ring buffer with a given size
17382
17383 Input:
17384 int size Size of ring buffer, must be larger than
17385 2*max_event_size
17386 int max_event_size Maximum event size to be placed into
17387 ring buffer
17388 Output:
17389 int *handle Handle to ring buffer
17390
17391 Function value:
17392 DB_SUCCESS Successful completion
17393 DB_NO_MEMORY Maximum number of ring buffers exceeded
17394 DB_INVALID_PARAM Invalid event size specified
17395
17396\********************************************************************/
17397{
17398 int i;
17399
17400 for (i = 0; i < MAX_RING_BUFFER; i++)
17401 if (rb[i].buffer == NULL)
17402 break;
17403
17404 if (i == MAX_RING_BUFFER)
17405 return DB_NO_MEMORY;
17406
17407 if (size < max_event_size * 2)
17408 return DB_INVALID_PARAM;
17409
17410 memset(&rb[i], 0, sizeof(RING_BUFFER));
17411 rb[i].buffer = (unsigned char *) M_MALLOC(size);
17412 assert(rb[i].buffer);
17413 rb[i].size = size;
17415 rb[i].rp = rb[i].buffer;
17416 rb[i].wp = rb[i].buffer;
17417 rb[i].ep = rb[i].buffer;
17418
17419 *handle = i + 1;
17420
17421 return DB_SUCCESS;
17422}
17423
17424/********************************************************************/
17430int rb_delete(int handle)
17431/********************************************************************\
17432
17433 Routine: rb_delete
17434
17435 Purpose: Delete a ring buffer
17436
17437 Input:
17438 none
17439 Output:
17440 int handle Handle to ring buffer
17441
17442 Function value:
17443 DB_SUCCESS Successful completion
17444
17445\********************************************************************/
17446{
17447 if (handle < 0 || handle >= MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17448 return DB_INVALID_HANDLE;
17449
17450 M_FREE(rb[handle - 1].buffer);
17451 rb[handle - 1].buffer = NULL;
17452 memset(&rb[handle - 1], 0, sizeof(RING_BUFFER));
17453
17454 return DB_SUCCESS;
17455}
17456
17457/********************************************************************/
17467int rb_get_wp(int handle, void **p, int millisec)
17468/********************************************************************\
17469
17470Routine: rb_get_wp
17471
17472 Purpose: Retrieve write pointer where new data can be written
17473
17474 Input:
17475 int handle Ring buffer handle
17476 int millisec Optional timeout in milliseconds if
17477 buffer is full. Zero to not wait at
17478 all (non-blocking)
17479
17480 Output:
17481 char **p Write pointer
17482
17483 Function value:
17484 DB_SUCCESS Successful completion
17485
17486\********************************************************************/
17487{
17488 int h, i;
17489 unsigned char *rp;
17490
17491 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17492 return DB_INVALID_HANDLE;
17493
17494 h = handle - 1;
17495
17496 for (i = 0; i <= millisec / 10; i++) {
17497
17498 rp = rb[h].rp; // keep local copy for convenience
17499
17500 /* check if enough size for wp >= rp without wrap-around */
17501 if (rb[h].wp >= rp
17502 && rb[h].wp + rb[h].max_event_size <= rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17503 *p = rb[h].wp;
17504 return DB_SUCCESS;
17505 }
17506
17507 /* check if enough size for wp >= rp with wrap-around */
17508 if (rb[h].wp >= rp && rb[h].wp + rb[h].max_event_size > rb[h].buffer + rb[h].size - rb[h].max_event_size &&
17509 rp > rb[h].buffer) { // next increment of wp wraps around, so need space at beginning
17510 *p = rb[h].wp;
17511 return DB_SUCCESS;
17512 }
17513
17514 /* check if enough size for wp < rp */
17515 if (rb[h].wp < rp && rb[h].wp + rb[h].max_event_size < rp) {
17516 *p = rb[h].wp;
17517 return DB_SUCCESS;
17518 }
17519
17520 if (millisec == 0)
17521 return DB_TIMEOUT;
17522
17523 if (_rb_nonblocking)
17524 return DB_TIMEOUT;
17525
17526 /* wait one time slice */
17527 ss_sleep(10);
17528 }
17529
17530 return DB_TIMEOUT;
17531}
17532
17533/********************************************************************/
17542int rb_increment_wp(int handle, int size)
17543/********************************************************************\
17544
17545 Routine: rb_increment_wp
17546
17547 Purpose: Increment current write pointer, making the data at
17548 the write pointer available to the receiving thread
17549
17550 Input:
17551 int handle Ring buffer handle
17552 int size Number of bytes placed at the WP
17553
17554 Output:
17555 NONE
17556
17557 Function value:
17558 DB_SUCCESS Successful completion
17559 DB_INVALID_PARAM Event size too large or invalid handle
17560\********************************************************************/
17561{
17562 int h;
17563 unsigned char *new_wp;
17564
17565 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17566 return DB_INVALID_HANDLE;
17567
17568 h = handle - 1;
17569
17570 if ((DWORD) size > rb[h].max_event_size) {
17571 cm_msg(MERROR, "rb_increment_wp", "event size of %d MB larger than max_event_size of %d MB",
17572 size/1024/1024, rb[h].max_event_size/1024/1024);
17573 abort();
17574 }
17575
17576 new_wp = rb[h].wp + size;
17577
17578 /* wrap around wp if not enough space */
17579 if (new_wp > rb[h].buffer + rb[h].size - rb[h].max_event_size) {
17580 rb[h].ep = new_wp;
17581 new_wp = rb[h].buffer;
17582 assert(rb[h].rp != rb[h].buffer);
17583 } else
17584 if (new_wp > rb[h].ep)
17585 rb[h].ep = new_wp;
17586
17587 rb[h].wp = new_wp;
17588
17589 return DB_SUCCESS;
17590}
17591
17592/********************************************************************/
17608int rb_get_rp(int handle, void **p, int millisec)
17609/********************************************************************\
17610
17611 Routine: rb_get_rp
17612
17613 Purpose: Obtain the current read pointer at which new data is
17614 available with optional timeout
17615
17616 Input:
17617 int handle Ring buffer handle
17618 int millisec Optional timeout in milliseconds if
17619 buffer is full. Zero to not wait at
17620 all (non-blocking)
17621
17622 Output:
17623 char **p Address of pointer pointing to newly
17624 available data. If p == NULL, only
17625 return status.
17626
17627 Function value:
17628 DB_SUCCESS Successful completion
17629
17630\********************************************************************/
17631{
17632 int i, h;
17633
17634 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17635 return DB_INVALID_HANDLE;
17636
17637 h = handle - 1;
17638
17639 for (i = 0; i <= millisec / 10; i++) {
17640
17641 if (rb[h].wp != rb[h].rp) {
17642 if (p != NULL)
17643 *p = rb[handle - 1].rp;
17644 return DB_SUCCESS;
17645 }
17646
17647 if (millisec == 0)
17648 return DB_TIMEOUT;
17649
17650 if (_rb_nonblocking)
17651 return DB_TIMEOUT;
17652
17653 /* wait one time slice */
17654 ss_sleep(10);
17655 }
17656
17657 return DB_TIMEOUT;
17658}
17659
17660/********************************************************************/
17670int rb_increment_rp(int handle, int size)
17671/********************************************************************\
17672
17673 Routine: rb_increment_rp
17674
17675 Purpose: Increment current read pointer, freeing up space for
17676 the writing thread.
17677
17678 Input:
17679 int handle Ring buffer handle
17680 int size Number of bytes to free up at current
17681 read pointer
17682
17683 Output:
17684 NONE
17685
17686 Function value:
17687 DB_SUCCESS Successful completion
17688 DB_INVALID_PARAM Event size too large or invalid handle
17689
17690\********************************************************************/
17691{
17692 int h;
17693
17694 unsigned char *new_rp;
17695 unsigned char *ep;
17696
17697 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17698 return DB_INVALID_HANDLE;
17699
17700 h = handle - 1;
17701
17702 if ((DWORD) size > rb[h].max_event_size)
17703 return DB_INVALID_PARAM;
17704
17705 new_rp = rb[h].rp + size;
17706 ep = rb[h].ep; // keep local copy of end pointer, rb[h].ep might be changed by other thread
17707
17708 /* wrap around if end pointer reached */
17709 if (new_rp >= ep && rb[h].wp < ep)
17710 new_rp = rb[h].buffer;
17711
17712 rb[handle - 1].rp = new_rp;
17713
17714 return DB_SUCCESS;
17715}
17716
17717/********************************************************************/
17725int rb_get_buffer_level(int handle, int *n_bytes)
17726/********************************************************************\
17727
17728 Routine: rb_get_buffer_level
17729
17730 Purpose: Return number of bytes in a ring buffer
17731
17732 Input:
17733 int handle Handle of the buffer to get the info
17734
17735 Output:
17736 int *n_bytes Number of bytes in buffer
17737
17738 Function value:
17739 DB_SUCCESS Successful completion
17740 DB_INVALID_HANDLE Buffer handle is invalid
17741
17742\********************************************************************/
17743{
17744 int h;
17745
17746 if (handle < 1 || handle > MAX_RING_BUFFER || rb[handle - 1].buffer == NULL)
17747 return DB_INVALID_HANDLE;
17748
17749 h = handle - 1;
17750
17751 if (rb[h].wp >= rb[h].rp)
17752 *n_bytes = (POINTER_T) rb[h].wp - (POINTER_T) rb[h].rp;
17753 else
17754 *n_bytes =
17755 (POINTER_T) rb[h].ep - (POINTER_T) rb[h].rp + (POINTER_T) rb[h].wp - (POINTER_T) rb[h].buffer;
17756
17757 return DB_SUCCESS;
17758}
17759
/* end of rbfunctionc */
17761
17762
17764{
17765 if (format == FORMAT_FIXED) {
17766 int status;
17767 status = db_set_record(hDB, hKey, (char *) (pevent + 1), pevent->data_size, 0);
17768 if (status != DB_SUCCESS) {
17769 cm_msg(MERROR, "cm_write_event_to_odb", "event %d ODB record size mismatch, db_set_record() status %d", pevent->event_id, status);
17770 return status;
17771 }
17772 return SUCCESS;
17773 } else if (format == FORMAT_MIDAS) {
17774 INT size, i, status, n_data;
17775 int n;
17776 char *pdata, *pdata0;
17777
17778 char name[5];
17779 BANK_HEADER *pbh;
17780 BANK *pbk;
17781 BANK32 *pbk32;
17782 BANK32A *pbk32a;
17783 DWORD bkname;
17784 WORD bktype;
17785 HNDLE hKeyRoot, hKeyl, *hKeys;
17786 KEY key;
17787
17788 pbh = (BANK_HEADER *) (pevent + 1);
17789 pbk = NULL;
17790 pbk32 = NULL;
17791 pbk32a = NULL;
17792
17793 /* count number of banks */
17794 for (n=0 ; ; n++) {
17795 if (bk_is32a(pbh)) {
17796 bk_iterate32a(pbh, &pbk32a, &pdata);
17797 if (pbk32a == NULL)
17798 break;
17799 } else if (bk_is32(pbh)) {
17800 bk_iterate32(pbh, &pbk32, &pdata);
17801 if (pbk32 == NULL)
17802 break;
17803 } else {
17804 bk_iterate(pbh, &pbk, &pdata);
17805 if (pbk == NULL)
17806 break;
17807 }
17808 }
17809
17810 /* build array of keys */
17811 hKeys = (HNDLE *)malloc(sizeof(HNDLE) * n);
17812
17813 pbk = NULL;
17814 pbk32 = NULL;
17815 n = 0;
17816 do {
17817 /* scan all banks */
17818 if (bk_is32a(pbh)) {
17819 size = bk_iterate32a(pbh, &pbk32a, &pdata);
17820 if (pbk32a == NULL)
17821 break;
17822 bkname = *((DWORD *) pbk32a->name);
17823 bktype = (WORD) pbk32a->type;
17824 } else if (bk_is32(pbh)) {
17825 size = bk_iterate32(pbh, &pbk32, &pdata);
17826 if (pbk32 == NULL)
17827 break;
17828 bkname = *((DWORD *) pbk32->name);
17829 bktype = (WORD) pbk32->type;
17830 } else {
17831 size = bk_iterate(pbh, &pbk, &pdata);
17832 if (pbk == NULL)
17833 break;
17834 bkname = *((DWORD *) pbk->name);
17835 bktype = (WORD) pbk->type;
17836 }
17837
17838 n_data = size;
17839 if (rpc_tid_size(bktype & 0xFF))
17840 n_data /= rpc_tid_size(bktype & 0xFF);
17841
17842 /* get bank key */
17843 *((DWORD *) name) = bkname;
17844 name[4] = 0;
17845 /* record the start of the data in case it is struct */
17846 pdata0 = pdata;
17847 if (bktype == TID_STRUCT) {
17848 status = db_find_key(hDB, hKey, name, &hKeyRoot);
17849 if (status != DB_SUCCESS) {
17850 cm_msg(MERROR, "cm_write_event_to_odb", "please define bank \"%s\" in BANK_LIST in frontend", name);
17851 continue;
17852 }
17853
17854 /* write structured bank */
17855 for (i = 0;; i++) {
17856 status = db_enum_key(hDB, hKeyRoot, i, &hKeyl);
17858 break;
17859
17860 db_get_key(hDB, hKeyl, &key);
17861
17862 /* adjust for alignment */
17863 if (key.type != TID_STRING && key.type != TID_LINK)
17864 pdata = (pdata0 + VALIGN(pdata-pdata0, MIN(ss_get_struct_align(), key.item_size)));
17865
17867 if (status != DB_SUCCESS) {
17868 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17869 continue;
17870 }
17871 hKeys[n++] = hKeyl;
17872
17873 /* shift data pointer to next item */
17874 pdata += key.item_size * key.num_values;
17875 }
17876 } else {
17877 /* write variable length bank */
17878 status = db_find_key(hDB, hKey, name, &hKeyRoot);
17879 if (status != DB_SUCCESS) {
17880 status = db_create_key(hDB, hKey, name, bktype);
17881 if (status != DB_SUCCESS) {
17882 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);
17883 continue;
17884 }
17885 status = db_find_key(hDB, hKey, name, &hKeyRoot);
17886 if (status != DB_SUCCESS) {
17887 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);
17888 continue;
17889 }
17890 }
17891 if (n_data > 0) {
17892 status = db_set_data1(hDB, hKeyRoot, pdata, size, n_data, bktype & 0xFF);
17893 if (status != DB_SUCCESS) {
17894 cm_msg(MERROR, "cm_write_event_to_odb", "cannot write bank \"%s\" to ODB, db_set_data1() status %d", name, status);
17895 }
17896 hKeys[n++] = hKeyRoot;
17897 }
17898 }
17899 } while (1);
17900
17901 /* notify all hot-lined clients in one go */
17902 db_notify_clients_array(hDB, hKeys, n*sizeof(INT));
17903
17904 free(hKeys);
17905
17906 return SUCCESS;
17907 } else {
17908 cm_msg(MERROR, "cm_write_event_to_odb", "event format %d is not supported (see midas.h definitions of FORMAT_xxx)", format);
17909 return CM_DB_ERROR;
17910 }
17911}
17912
17913/* emacs
17914 * Local Variables:
17915 * tab-width: 8
17916 * c-basic-offset: 3
17917 * indent-tabs-mode: nil
17918 * End:
17919 */
#define FALSE
Definition cfortran.h:309
std::string host_name
Definition midas.cxx:11460
std::atomic_bool connected
Definition midas.cxx:11459
std::string client_name
Definition midas.cxx:11464
BUFFER * get_pbuf() const
Definition midas.cxx:3196
bool is_error() const
Definition midas.cxx:3186
int get_status() const
Definition midas.cxx:3191
bool is_locked() const
Definition midas.cxx:3181
bm_lock_buffer_guard(BUFFER *pbuf, bool do_not_lock=false)
Definition midas.cxx:3100
bm_lock_buffer_guard & operator=(const bm_lock_buffer_guard &)=delete
bm_lock_buffer_guard(const bm_lock_buffer_guard &)=delete
void set_string_size(std::string s, int size)
Definition odbxx.cxx:1664
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:860
INT al_check()
Definition alarm.cxx:614
void bk_init32a(void *event)
Definition midas.cxx:16522
INT bk_close(void *event, void *pdata)
Definition midas.cxx:16820
INT bk_iterate32a(const void *event, BANK32A **pbk32a, void *pdata)
Definition midas.cxx:17143
static void copy_bk_name(char *dst, const char *src)
Definition midas.cxx:16539
INT bk_swap(void *event, BOOL force)
Definition midas.cxx:17197
BOOL bk_is32a(const void *event)
Definition midas.cxx:16477
int bk_delete(void *event, const char *name)
Definition midas.cxx:16714
BOOL bk_is32(const void *event)
Definition midas.cxx:16455
INT bk_iterate32(const void *event, BANK32 **pbk, void *pdata)
Definition midas.cxx:17107
INT bk_locate(const void *event, const char *name, void *pdata)
Definition midas.cxx:16929
void bk_init(void *event)
Definition midas.cxx:16446
INT bk_list(const void *event, char *bklist)
Definition midas.cxx:16880
INT bk_copy(char *pevent, char *psrce, const char *bkname)
Definition midas.cxx:16644
INT bk_iterate(const void *event, BANK **pbk, void *pdata)
Definition midas.cxx:17086
void bk_init32(void *event)
Definition midas.cxx:16509
void bk_create(void *event, const char *name, WORD type, void **pdata)
Definition midas.cxx:16601
INT bk_find(const BANK_HEADER *pbkh, const char *name, DWORD *bklen, DWORD *bktype, void **pdata)
Definition midas.cxx:16992
INT bk_size(const void *event)
Definition midas.cxx:16535
static void bm_wakeup_producers_locked(const BUFFER_HEADER *pheader, const BUFFER_CLIENT *pc)
Definition midas.cxx:8797
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:10508
INT bm_open_buffer(const char *buffer_name, INT buffer_size, INT *buffer_handle)
Definition midas.cxx:6727
static BOOL bm_validate_rp(const char *who, const BUFFER_HEADER *pheader, int rp)
Definition midas.cxx:6206
INT bm_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, int timeout_msec)
Definition midas.cxx:9688
static int bm_flush_cache_rpc(int buffer_handle, int timeout_msec)
Definition midas.cxx:9997
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:6474
static int bm_skip_event(BUFFER *pbuf)
Definition midas.cxx:10858
static INT bm_flush_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:10088
INT bm_write_statistics_to_odb(void)
Definition midas.cxx:7290
#define MAX_DEFRAG_EVENTS
Definition midas.cxx:11297
INT bm_delete_request(INT request_id)
Definition midas.cxx:8594
INT bm_close_all_buffers(void)
Definition midas.cxx:7253
INT bm_poll_event()
Definition midas.cxx:11151
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:8324
static void bm_write_buffer_statistics_to_odb(HNDLE hDB, BUFFER *pbuf, BOOL force)
Definition midas.cxx:6596
static int bm_incr_rp_no_check(const BUFFER_HEADER *pheader, int rp, int total_size)
Definition midas.cxx:6240
INT bm_receive_event_vec(INT buffer_handle, std::vector< char > *pvec, int timeout_msec)
Definition midas.cxx:10834
static void bm_notify_reader_locked(BUFFER_HEADER *pheader, BUFFER_CLIENT *pc, int old_write_pointer, int request_id)
Definition midas.cxx:9613
static int bm_find_first_request_locked(BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:9599
static BOOL bm_check_requests(const BUFFER_CLIENT *pc, const EVENT_HEADER *pevent)
Definition midas.cxx:8976
static void bm_cleanup_buffer_locked(BUFFER *pbuf, const char *who, DWORD actual_time)
Definition midas.cxx:6083
INT bm_empty_buffers()
Definition midas.cxx:11265
static BOOL bm_update_read_pointer_locked(const char *caller_name, BUFFER_HEADER *pheader)
Definition midas.cxx:8730
static void bm_convert_event_header(EVENT_HEADER *pevent, int convert_flags)
Definition midas.cxx:9079
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:8475
static int bm_validate_client_index_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:5939
INT bm_set_cache_size(INT buffer_handle, size_t read_size, size_t write_size)
Definition midas.cxx:8150
static int bm_validate_buffer_locked(const BUFFER *pbuf)
Definition midas.cxx:6324
INT bm_receive_event(INT buffer_handle, void *destination, INT *buf_size, int timeout_msec)
Definition midas.cxx:10675
INT bm_check_buffers()
Definition midas.cxx:10979
static int bm_fill_read_cache_locked(bm_lock_buffer_guard &pbuf_guard, int timeout_msec)
Definition midas.cxx:9002
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:8302
INT bm_remove_event_request(INT buffer_handle, INT request_id)
Definition midas.cxx:8528
static double _bm_mutex_timeout_sec
Definition midas.cxx:5937
INT bm_close_buffer(INT buffer_handle)
Definition midas.cxx:7106
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:9788
INT bm_compose_event(EVENT_HEADER *event_header, short int event_id, short int trigger_mask, DWORD data_size, DWORD serial)
Definition midas.cxx:8291
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:9514
static EVENT_DEFRAG_BUFFER defrag_buffer[MAX_DEFRAG_EVENTS]
Definition midas.cxx:11306
static int bm_next_rp(const char *who, const BUFFER_HEADER *pheader, const char *pdata, int rp)
Definition midas.cxx:6273
INT bm_match_event(short int event_id, short int trigger_mask, const EVENT_HEADER *pevent)
Definition midas.cxx:6032
static void bm_read_from_buffer_locked(const BUFFER_HEADER *pheader, int rp, char *buf, int event_size)
Definition midas.cxx:8946
static BOOL bm_peek_read_cache_locked(BUFFER *pbuf, EVENT_HEADER **ppevent, int *pevent_size, int *ptotal_size)
Definition midas.cxx:8875
static void bm_update_last_activity(DWORD millitime)
Definition midas.cxx:6134
static void bm_incr_read_cache_locked(BUFFER *pbuf, int total_size)
Definition midas.cxx:8865
static DWORD _bm_max_event_size
Definition midas.cxx:5931
static void bm_clear_buffer_statistics(HNDLE hDB, BUFFER *pbuf)
Definition midas.cxx:6425
INT bm_flush_cache(int buffer_handle, int timeout_msec)
Definition midas.cxx:10232
static void bm_dispatch_event(int buffer_handle, EVENT_HEADER *pevent)
Definition midas.cxx:8833
static void bm_validate_client_pointers_locked(const BUFFER_HEADER *pheader, BUFFER_CLIENT *pclient)
Definition midas.cxx:8632
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:10289
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:9090
INT bm_get_buffer_handle(const char *buffer_name, INT *buffer_handle)
Definition midas.cxx:7085
int bm_send_event_vec(int buffer_handle, const std::vector< char > &event, int timeout_msec)
Definition midas.cxx:9715
static int _bm_lock_timeout
Definition midas.cxx:5936
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:8901
INT bm_receive_event_alloc(INT buffer_handle, EVENT_HEADER **ppevent, int timeout_msec)
Definition midas.cxx:10756
static void bm_reset_buffer_locked(BUFFER *pbuf)
Definition midas.cxx:6408
void bm_remove_client_locked(BUFFER_HEADER *pheader, int j)
Definition midas.cxx:6052
static INT bm_push_buffer(BUFFER *pbuf, int buffer_handle)
Definition midas.cxx:10927
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:9402
INT cm_set_path(const char *path)
Definition midas.cxx:1512
INT cm_register_transition(INT transition, INT(*func)(INT, char *), INT sequence_number)
Definition midas.cxx:3616
INT cm_shutdown(const char *name, BOOL bUnique)
Definition midas.cxx:7410
static int cm_transition_call(TrState *s, int idx)
Definition midas.cxx:4188
INT cm_disconnect_client(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:2848
static void load_rpc_hosts(HNDLE hDB, HNDLE hKey, int index, void *info)
Definition midas.cxx:3376
static std::atomic< std::thread * > _watchdog_thread
Definition midas.cxx:7333
static int cm_transition_detach(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4105
INT cm_yield(INT millisec)
Definition midas.cxx:5659
INT cm_get_experiment_database(HNDLE *hDB, HNDLE *hKeyClient)
Definition midas.cxx:3026
INT cm_list_experiments_remote(const char *host_name, STRING_LIST *exp_names)
Definition midas.cxx:2627
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3340
static void bm_defragment_event(HNDLE buffer_handle, HNDLE request_id, EVENT_HEADER *pevent, void *pdata, EVENT_HANDLER *dispatcher)
Definition midas.cxx:11309
INT cm_connect_client(const char *client_name, HNDLE *hConn)
Definition midas.cxx:2781
INT cm_connect_experiment(const char *host_name, const char *exp_name, const char *client_name, void(*func)(char *))
Definition midas.cxx:2293
static BUFFER_CLIENT * bm_get_my_client_locked(bm_lock_buffer_guard &pbuf_guard)
Definition midas.cxx:6016
INT cm_list_experiments_local(STRING_LIST *exp_names)
Definition midas.cxx:2601
INT cm_start_watchdog_thread()
Definition midas.cxx:7365
static INT bm_push_event(const char *buffer_name)
Definition midas.cxx:10943
INT cm_get_experiment_semaphore(INT *semaphore_alarm, INT *semaphore_elog, INT *semaphore_history, INT *semaphore_msg)
Definition midas.cxx:3048
static int tr_finish(HNDLE hDB, TrState *tr, int transition, int status, const char *errorstr)
Definition midas.cxx:4019
INT cm_set_client_run_state(INT state)
Definition midas.cxx:3806
static int bm_lock_buffer_read_cache(BUFFER *pbuf)
Definition midas.cxx:7914
static void write_tr_client_to_odb(HNDLE hDB, const TrClient *tr_client)
Definition midas.cxx:4052
INT cm_transition(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5303
INT cm_stop_watchdog_thread()
Definition midas.cxx:7380
std::string cm_asctime()
Definition midas.cxx:1427
INT cm_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:5807
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:2312
INT cm_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:1884
static int xbm_lock_buffer(BUFFER *pbuf)
Definition midas.cxx:7985
INT cm_periodic_tasks()
Definition midas.cxx:5596
static void xbm_unlock_buffer(BUFFER *pbuf)
Definition midas.cxx:8047
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5402
static INT cm_transition1(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:5255
INT cm_select_experiment_remote(const char *host_name, std::string *exp_name)
Definition midas.cxx:2734
INT cm_register_server(void)
Definition midas.cxx:3475
static BOOL _ctrlc_pressed
Definition midas.cxx:5456
static void init_rpc_hosts(HNDLE hDB)
Definition midas.cxx:3427
void cm_ack_ctrlc_pressed()
Definition midas.cxx:5473
INT cm_execute(const char *command, char *result, INT bufsize)
Definition midas.cxx:5740
INT cm_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition midas.cxx:3359
INT cm_cleanup(const char *client_name, BOOL ignore_timeout)
Definition midas.cxx:7620
void cm_check_connect(void)
Definition midas.cxx:2216
static BUFFER * bm_get_buffer(const char *who, INT buffer_handle, int *pstatus)
Definition midas.cxx:6632
INT cm_select_experiment_local(std::string *exp_name)
Definition midas.cxx:2685
std::string cm_expand_env(const char *str)
Definition midas.cxx:7720
std::string cm_get_client_name()
Definition midas.cxx:2074
static int bm_lock_buffer_mutex(BUFFER *pbuf)
Definition midas.cxx:7956
int cm_exec_script(const char *odb_path_to_script)
Definition midas.cxx:5478
INT EXPRT cm_get_path_string(std::string *path)
Definition midas.cxx:1560
static void rpc_client_shutdown()
Definition midas.cxx:12657
static std::atomic< bool > _watchdog_thread_is_running
Definition midas.cxx:7332
static exptab_struct _exptab
Definition midas.cxx:1620
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:1908
INT cm_disconnect_experiment(void)
Definition midas.cxx:2861
static void bm_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition midas.cxx:6169
static INT bm_notify_client(const char *buffer_name, int s)
Definition midas.cxx:11073
int cm_get_exptab(const char *expname, std::string *dir, std::string *user)
Definition midas.cxx:1814
static void xcm_watchdog_thread()
Definition midas.cxx:7359
static bool test_cm_expand_env1(const char *str, const char *expected)
Definition midas.cxx:7752
INT cm_synchronize(DWORD *seconds)
Definition midas.cxx:1384
std::string cm_get_exptab_filename()
Definition midas.cxx:1803
std::string cm_get_path()
Definition midas.cxx:1552
static DWORD _deferred_transition_mask
Definition midas.cxx:3842
std::string cm_get_history_path(const char *history_channel)
Definition midas.cxx:5860
void cm_test_expand_env()
Definition midas.cxx:7767
INT cm_register_deferred_transition(INT transition, BOOL(*func)(INT, BOOL))
Definition midas.cxx:3860
int cm_set_experiment_local(const char *exp_name)
Definition midas.cxx:2181
static INT _requested_transition
Definition midas.cxx:3841
INT cm_get_environment(char *host_name, int host_name_size, char *exp_name, int exp_name_size)
Definition midas.cxx:2149
const char * cm_get_version()
Definition midas.cxx:1491
INT cm_read_exptab(exptab_struct *exptab)
Definition midas.cxx:1629
static int bm_lock_buffer_write_cache(BUFFER *pbuf)
Definition midas.cxx:7935
INT cm_deregister_transition(INT transition)
Definition midas.cxx:3692
INT cm_check_deferred_transition()
Definition midas.cxx:3912
std::string cm_get_experiment_name()
Definition midas.cxx:1595
INT cm_set_transition_sequence(INT transition, INT sequence_number)
Definition midas.cxx:3746
static bool tr_compare(const std::unique_ptr< TrClient > &arg1, const std::unique_ptr< TrClient > &arg2)
Definition midas.cxx:3999
INT cm_delete_client_info(HNDLE hDB, INT pid)
Definition midas.cxx:1867
static std::atomic< bool > _watchdog_thread_run
Definition midas.cxx:7331
const char * cm_get_revision()
Definition midas.cxx:1499
INT cm_watchdog_thread(void *unused)
Definition midas.cxx:7339
INT cm_set_experiment_database(HNDLE hDB, HNDLE hKeyClient)
Definition midas.cxx:2954
BOOL cm_is_ctrlc_pressed()
Definition midas.cxx:5469
static INT tr_main_thread(void *param)
Definition midas.cxx:5271
void cm_ctrlc_handler(int sig)
Definition midas.cxx:5458
INT cm_set_watchdog_params_local(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3259
INT cm_time(DWORD *t)
Definition midas.cxx:1449
INT cm_transition_cleanup()
Definition midas.cxx:5284
INT cm_set_watchdog_params(BOOL call_watchdog, DWORD timeout)
Definition midas.cxx:3298
static int cm_transition_call_direct(TrClient *tr_client)
Definition midas.cxx:4423
INT cm_exist(const char *name, BOOL bUnique)
Definition midas.cxx:7530
INT cm_set_experiment_semaphore(INT semaphore_alarm, INT semaphore_elog, INT semaphore_history, INT semaphore_msg)
Definition midas.cxx:2973
static INT cm_transition2(INT transition, INT run_number, char *errstr, INT errstr_size, INT async_flag, INT debug_flag)
Definition midas.cxx:4546
INT cm_set_experiment_name(const char *name)
Definition midas.cxx:1573
#define CM_SUCCESS
Definition midas.h:582
#define CM_UNDEF_EXP
Definition midas.h:586
#define CM_INVALID_TRANSITION
Definition midas.h:594
#define CM_SET_ERROR
Definition midas.h:583
#define CM_TRUNCATED
Definition midas.h:596
#define CM_DEFERRED_TRANSITION
Definition midas.h:591
#define CM_TRANSITION_IN_PROGRESS
Definition midas.h:592
#define CM_WRONG_PASSWORD
Definition midas.h:589
#define CM_NO_CLIENT
Definition midas.h:584
#define CM_TRANSITION_CANCELED
Definition midas.h:597
#define CM_DB_ERROR
Definition midas.h:585
#define CM_VERSION_MISMATCH
Definition midas.h:587
#define CM_UNDEF_ENVIRON
Definition midas.h:590
#define BM_INVALID_PARAM
Definition midas.h:619
#define BM_MORE_EVENTS
Definition midas.h:620
#define BM_TRUNCATED
Definition midas.h:614
#define BM_NOT_FOUND
Definition midas.h:612
#define BM_NO_MEMORY
Definition midas.h:607
#define BM_INVALID_MIXING
Definition midas.h:621
#define BM_NO_SLOT
Definition midas.h:610
#define BM_ASYNC_RETURN
Definition midas.h:613
#define BM_TIMEOUT
Definition midas.h:625
#define BM_INVALID_HANDLE
Definition midas.h:609
#define BM_INVALID_SIZE
Definition midas.h:624
#define BM_SUCCESS
Definition midas.h:605
#define BM_CORRUPTED
Definition midas.h:623
#define BM_NO_SHM
Definition midas.h:622
#define BM_CREATED
Definition midas.h:606
#define BM_NO_SEMAPHORE
Definition midas.h:611
#define DB_INVALID_HANDLE
Definition midas.h:636
#define DB_INVALID_PARAM
Definition midas.h:640
#define DB_SUCCESS
Definition midas.h:632
#define DB_NO_MEMORY
Definition midas.h:634
#define DB_NO_KEY
Definition midas.h:643
#define DB_TYPE_MISMATCH
Definition midas.h:646
#define DB_CREATED
Definition midas.h:633
#define DB_TIMEOUT
Definition midas.h:656
#define DB_NO_MORE_SUBKEYS
Definition midas.h:647
#define SS_SUCCESS
Definition midas.h:664
#define SS_ABORT
Definition midas.h:678
#define SS_FILE_ERROR
Definition midas.h:670
#define SS_TIMEOUT
Definition midas.h:675
#define SS_CREATED
Definition midas.h:665
#define SS_EXIT
Definition midas.h:679
#define RPC_SHUTDOWN
Definition midas.h:708
#define RPC_SUCCESS
Definition midas.h:699
#define RPC_EXCEED_BUFFER
Definition midas.h:704
#define RPC_INVALID_ID
Definition midas.h:707
#define RPC_DOUBLE_DEFINED
Definition midas.h:710
#define RPC_NOT_REGISTERED
Definition midas.h:705
#define RPC_MUTEX_TIMEOUT
Definition midas.h:711
#define RPC_TIMEOUT
Definition midas.h:703
#define RPC_NO_CONNECTION
Definition midas.h:701
#define RPC_NET_ERROR
Definition midas.h:702
#define AL_TRIGGERED
Definition midas.h:759
#define FE_ERR_HW
Definition midas.h:720
unsigned short int WORD
Definition mcstd.h:49
unsigned int DWORD
Definition mcstd.h:51
#define SUCCESS
Definition mcstd.h:54
#define TR_RESUME
Definition midas.h:408
#define GET_NONBLOCKING
Definition midas.h:322
#define TR_PAUSE
Definition midas.h:407
#define TID_DOUBLE
Definition midas.h:343
#define MT_LOG_STR
Definition midas.h:555
#define TID_KEY
Definition midas.h:349
#define GET_ALL
Definition midas.h:321
#define TID_BOOL
Definition midas.h:340
#define TRIGGER_ALL
Definition midas.h:538
#define TR_START
Definition midas.h:405
#define TR_SYNC
Definition midas.h:358
#define GET_RECENT
Definition midas.h:323
#define MT_ALL
Definition midas.h:549
#define BM_NO_WAIT
Definition midas.h:366
#define TID_UINT64
Definition midas.h:352
#define FORMAT_FIXED
Definition midas.h:314
#define MT_INFO_STR
Definition midas.h:552
#define TID_INT64
Definition midas.h:351
#define TR_MTHREAD
Definition midas.h:361
#define MT_INFO
Definition midas.h:543
#define TR_STARTABORT
Definition midas.h:409
#define RPC_HNDLE_CONNECT
Definition midas.h:394
#define STATE_STOPPED
Definition midas.h:305
#define MINFO
Definition midas.h:560
#define MODE_DELETE
Definition midas.h:372
#define MT_DEBUG_STR
Definition midas.h:553
#define MT_TALK
Definition midas.h:547
#define RPC_NO_REPLY
Definition midas.h:396
#define TID_STRUCT
Definition midas.h:348
#define MT_USER
Definition midas.h:545
#define RPC_HNDLE_MSERVER
Definition midas.h:393
#define MLOG
Definition midas.h:563
#define MT_USER_STR
Definition midas.h:554
#define TID_INT32
Definition midas.h:339
#define TR_DETACH
Definition midas.h:360
#define MT_LOG
Definition midas.h:546
#define TID_UINT8
Definition midas.h:328
#define TID_LINK
Definition midas.h:350
#define STATE_PAUSED
Definition midas.h:306
#define MT_TALK_STR
Definition midas.h:556
#define TID_STRING
Definition midas.h:346
#define MODE_WRITE
Definition midas.h:371
#define EVENTID_ALL
Definition midas.h:537
#define TR_DEFERRED
Definition midas.h:410
#define MERROR
Definition midas.h:559
#define STATE_RUNNING
Definition midas.h:307
#define MODE_READ
Definition midas.h:370
#define TID_INT8
Definition midas.h:330
#define FORMAT_MIDAS
Definition midas.h:311
#define TID_ARRAY
Definition midas.h:347
#define MT_DEBUG
Definition midas.h:544
#define TID_CHAR
Definition midas.h:331
#define MTALK
Definition midas.h:564
#define MDEBUG
Definition midas.h:561
#define TID_UINT32
Definition midas.h:337
#define TID_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:919
INT() RPC_HANDLER(INT index, void *prpc_param[])
Definition midas.h:923
std::string ss_gethostname()
Definition system.cxx:5784
INT ss_suspend(INT millisec, INT msg)
Definition system.cxx:4615
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3229
INT ss_suspend_init_odb_port()
Definition system.cxx:4377
bool ss_event_socket_has_data()
Definition system.cxx:4592
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3437
DWORD ss_millitime()
Definition system.cxx:3465
int ss_file_exist(const char *path)
Definition system.cxx:7196
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2532
INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
Definition system.cxx:5634
INT ss_suspend_set_client_listener(int listen_socket)
Definition system.cxx:4356
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5318
int ss_file_link_exist(const char *path)
Definition system.cxx:7232
std::string ss_getcwd()
Definition system.cxx:5848
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3283
int ss_socket_wait(int sock, INT millisec)
Definition system.cxx:4970
INT ss_suspend_set_server_acceptions(RPC_SERVER_ACCEPTION_LIST *acceptions)
Definition system.cxx:4370
INT ss_getpid(void)
Definition system.cxx:1379
DWORD ss_settime(DWORD seconds)
Definition system.cxx:3547
char * ss_getpass(const char *prompt)
Definition system.cxx:7518
INT ss_suspend_set_client_connection(RPC_SERVER_CONNECTION *connection)
Definition system.cxx:4363
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3013
void ss_tzset()
Definition system.cxx:3427
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:326
INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
Definition system.cxx:5471
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5424
int ss_dir_exist(const char *path)
Definition system.cxx:7264
bool ss_timed_mutex_wait_for_sec(std::timed_mutex &mutex, const char *mutex_name, double timeout_sec)
Definition system.cxx:3337
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2853
std::string ss_get_cmdline(void)
Definition system.cxx:1519
int ss_file_copy(const char *src, const char *dst, bool append)
Definition system.cxx:7297
DWORD ss_time()
Definition system.cxx:3534
INT ss_suspend_exit()
Definition system.cxx:4298
INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5526
INT ss_resume(INT port, const char *message)
Definition system.cxx:4916
midas_thread_t ss_gettid(void)
Definition system.cxx:1591
std::string ss_asctime()
Definition system.cxx:3621
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2941
INT ss_sleep(INT millisec)
Definition system.cxx:3700
INT ss_socket_connect_tcp(const char *hostname, int tcp_port, int *sockp, std::string *error_msg_p)
Definition system.cxx:5039
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2711
INT ss_socket_listen_tcp(bool listen_localhost, int tcp_port, int *sockp, int *tcp_port_p, std::string *error_msg_p)
Definition system.cxx:5134
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7969
INT ss_spawnv(INT mode, const char *cmdname, const char *const argv[])
Definition system.cxx:1702
INT ss_suspend_get_buffer_port(midas_thread_t thread_id, INT *port)
Definition system.cxx:4425
INT ss_socket_close(int *sockp)
Definition system.cxx:5303
char * ss_gets(char *string, int size)
Definition system.cxx:7848
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5707
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:757
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5357
void * ss_ctrlc_handler(void(*func)(int))
Definition system.cxx:3971
BOOL ss_pid_exists(int pid)
Definition system.cxx:1442
INT ss_system(const char *command)
Definition system.cxx:2188
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3109
INT ss_file_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6791
static int cm_msg_retrieve1(const char *filename, time_t t, INT n_messages, char **messages, int *length, int *allocated, int *num_messages)
Definition midas.cxx:1111
INT cm_msg1(INT message_type, const char *filename, INT line, const char *facility, const char *routine, const char *format,...)
Definition midas.cxx:988
int cm_msg_early_init(void)
Definition midas.cxx:480
INT EXPRT cm_msg_facilities(STRING_LIST *list)
Definition midas.cxx:517
int cm_msg_open_buffer(void)
Definition midas.cxx:487
int cm_msg_close_buffer(void)
Definition midas.cxx:500
static std::mutex gMsgBufMutex
Definition midas.cxx:872
static void add_message(char **messages, int *length, int *allocated, time_t tstamp, const char *new_message)
Definition midas.cxx:1080
INT cm_msg_register(EVENT_HANDLER *func)
Definition midas.cxx:1066
INT cm_msg_log(INT message_type, const char *facility, const char *message)
Definition midas.cxx:677
INT cm_msg_flush_buffer()
Definition midas.cxx:880
static std::deque< msg_buffer_entry > gMsgBuf
Definition midas.cxx:871
static INT cm_msg_send_event(DWORD ts, INT message_type, const char *send_message)
Definition midas.cxx:839
std::string cm_get_error(INT code)
Definition midas.cxx:468
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:930
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:764
INT cm_msg_retrieve(INT n_message, char *message, INT buf_size)
Definition midas.cxx:1349
INT cm_msg_retrieve2(const char *facility, time_t t, INT n_message, char **messages, int *num_messages)
Definition midas.cxx:1279
void cm_msg_get_logfile(const char *fac, time_t t, std::string *filename, std::string *linkname, std::string *linktarget)
Definition midas.cxx:552
INT cm_set_msg_print(INT system_mask, INT user_mask, int(*func)(const char *))
Definition midas.cxx:660
#define WORD_SWAP(x)
Definition msystem.h:65
#define QWORD_SWAP(x)
Definition msystem.h:86
#define DWORD_SWAP(x)
Definition msystem.h:74
struct rpc_server_acception_struct RPC_SERVER_ACCEPTION
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3285
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2306
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6664
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3933
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3143
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5185
INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size, WORD access_mode, void(*dispatcher)(INT, INT, void *), void *info)
Definition odb.cxx:13069
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1820
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2911
std::string strcomb1(const char **list)
Definition odb.cxx:668
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6310
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3392
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:7787
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:5790
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:13714
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3098
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7415
INT db_close_all_records()
Definition odb.cxx:13293
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13592
INT db_close_all_databases(void)
Definition odb.cxx:2398
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:6986
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:3999
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10612
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2776
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7084
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5028
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4256
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13331
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:3060
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13378
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2981
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2876
static DATABASE * db_lock_database(HNDLE hDB, int *pstatus, const char *caller, bool check_attached=true)
Definition odb.cxx:2508
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2440
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12401
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12067
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5357
static void db_unlock_database(DATABASE *pdb, const char *caller)
Definition odb.cxx:2670
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12578
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:13785
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2748
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7270
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3251
int rb_get_rp(int handle, void **p, int millisec)
Definition midas.cxx:17608
#define MAX_RING_BUFFER
Definition midas.cxx:17320
int rb_delete(int handle)
Definition midas.cxx:17430
int rb_get_wp(int handle, void **p, int millisec)
Definition midas.cxx:17467
int rb_increment_rp(int handle, int size)
Definition midas.cxx:17670
int rb_set_nonblocking()
Definition midas.cxx:17335
static volatile int _rb_nonblocking
Definition midas.cxx:17324
int rb_increment_wp(int handle, int size)
Definition midas.cxx:17542
int rb_create(int size, int max_event_size, int *handle)
Definition midas.cxx:17376
int rb_get_buffer_level(int handle, int *n_bytes)
Definition midas.cxx:17725
static RING_BUFFER rb[MAX_RING_BUFFER]
Definition midas.cxx:17322
INT rpc_add_allowed_host(const char *hostname)
Definition midas.cxx:15260
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11731
#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:12037
#define RPC_BM_ADD_EVENT_REQUEST
Definition mrpc.h:43
INT rpc_register_server(int port, int *plsock, int *pport)
Definition midas.cxx:14563
#define RPC_CM_EXIST
Definition mrpc.h:31
static int tls_size
Definition midas.cxx:14672
#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:14412
INT rpc_get_opt_tcp_size()
Definition midas.cxx:13897
INT rpc_client_disconnect(HNDLE hConn, BOOL bShutdown)
Definition midas.cxx:12701
#define RPC_BM_SEND_EVENT
Definition mrpc.h:45
static int _opt_tcp_size
Definition midas.cxx:11601
INT rpc_client_call(HNDLE hConn, DWORD routine_id,...)
Definition midas.cxx:13497
INT rpc_register_functions(const RPC_LIST *new_list, RPC_HANDLER func)
Definition midas.cxx:11852
static std::atomic_bool gAllowedHostsEnabled(false)
INT rpc_server_callback(struct callback_addr *pcallback)
Definition midas.cxx:15727
static std::mutex _client_connections_mutex
Definition midas.cxx:11520
INT rpc_set_timeout(HNDLE hConn, int timeout_msec, int *old_timeout_msec)
Definition midas.cxx:13023
#define RPC_CM_SYNCHRONIZE
Definition mrpc.h:27
static std::mutex gAllowedHostsMutex
Definition midas.cxx:15232
INT recv_tcp_check(int sock)
Definition midas.cxx:14382
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15896
#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:13076
static RPC_SERVER_ACCEPTION * rpc_get_server_acception(int idx)
Definition midas.cxx:11530
RPC_SERVER_ACCEPTION * rpc_get_mserver_acception()
Definition midas.cxx:11538
void rpc_calc_convert_flags(INT hw_type, INT remote_hw_type, INT *convert_flags)
Definition midas.cxx:11608
INT rpc_server_connect(const char *host_name, const char *exp_name)
Definition midas.cxx:12406
std::string rpc_get_name()
Definition midas.cxx:13109
#define RPC_ID_WATCHDOG
Definition mrpc.h:133
static RPC_SERVER_ACCEPTION * rpc_new_server_acception()
Definition midas.cxx:11543
#define RPC_BM_REMOVE_EVENT_REQUEST
Definition mrpc.h:44
bool rpc_is_remote(void)
Definition midas.cxx:12786
void rpc_debug_printf(const char *format,...)
Definition midas.cxx:13186
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11796
static int _tr_fifo_rp
Definition midas.cxx:14079
int cm_query_transition(int *transition, int *run_number, int *trans_time)
Definition midas.cxx:14145
#define RPC_RC_TRANSITION
Definition mrpc.h:116
void rpc_va_arg(va_list *arg_ptr, INT arg_type, void *arg)
Definition midas.cxx:13218
#define RPC_ID_EXIT
Definition mrpc.h:135
INT rpc_server_loop(void)
Definition midas.cxx:15868
INT rpc_clear_allowed_hosts()
Definition midas.cxx:15235
int rpc_test_rpc()
Definition midas.cxx:15071
std::string rpc_get_mserver_hostname(void)
Definition midas.cxx:12830
INT rpc_get_hw_type()
Definition midas.cxx:12859
INT rpc_deregister_functions()
Definition midas.cxx:11895
bool rpc_is_connected(void)
Definition midas.cxx:12808
INT rpc_set_mserver_path(const char *path)
Definition midas.cxx:13089
static std::vector< RPC_LIST > rpc_list
Definition midas.cxx:11598
#define RPC_BM_CLOSE_BUFFER
Definition mrpc.h:37
static TLS_POINTER * tls_buffer
Definition midas.cxx:14671
#define RPC_BM_SET_CACHE_SIZE
Definition mrpc.h:42
static std::vector< RPC_CLIENT_CONNECTION * > _client_connections
Definition midas.cxx:11521
#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:13256
static RPC_CLIENT_CONNECTION * rpc_get_locked_client_connection(HNDLE hConn)
Definition midas.cxx:12637
static std::vector< std::string > gAllowedHosts
Definition midas.cxx:15231
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:13688
static std::mutex rpc_list_mutex
Definition midas.cxx:11599
void rpc_client_check()
Definition midas.cxx:12295
const char * rpc_tid_name(INT id)
Definition midas.cxx:11789
INT rpc_flush_event()
Definition midas.cxx:14063
INT rpc_register_client(const char *name, RPC_LIST *list)
Definition midas.cxx:11833
static std::vector< RPC_SERVER_ACCEPTION * > _server_acceptions
Definition midas.cxx:11527
static INT rpc_socket_check_allowed_host(int sock)
Definition midas.cxx:15338
#define RPC_BM_CLOSE_ALL_BUFFERS
Definition mrpc.h:38
INT rpc_server_shutdown(void)
Definition midas.cxx:16208
static int _tr_fifo_wp
Definition midas.cxx:14078
int rpc_flush_event_socket(int timeout_msec)
Definition midas.cxx:16161
INT rpc_send_event(INT buffer_handle, const EVENT_HEADER *pevent, int unused, INT async_flag, INT mode)
Definition midas.cxx:13926
static TR_FIFO _tr_fifo[10]
Definition midas.cxx:14077
static std::string _mserver_path
Definition midas.cxx:13073
INT rpc_get_timeout(HNDLE hConn)
Definition midas.cxx:12998
INT rpc_server_disconnect()
Definition midas.cxx:12730
INT rpc_set_debug(void(*func)(const char *), INT mode)
Definition midas.cxx:13159
INT rpc_client_accept(int lsock)
Definition midas.cxx:15623
void rpc_vax2ieee_float(float *var)
Definition midas.cxx:11651
INT rpc_execute(INT sock, char *buffer, INT convert_flags)
Definition midas.cxx:14675
INT rpc_set_opt_tcp_size(INT tcp_size)
Definition midas.cxx:13889
#define RPC_BM_GET_BUFFER_LEVEL
Definition mrpc.h:40
int rpc_name_tid(const char *name)
Definition midas.cxx:11803
INT rpc_register_listener(int port, RPC_HANDLER func, int *plsock, int *pport)
Definition midas.cxx:14604
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:16006
#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:15366
static std::mutex _tr_fifo_mutex
Definition midas.cxx:14076
bool rpc_is_mserver(void)
Definition midas.cxx:12843
static RPC_SERVER_ACCEPTION * _mserver_acception
Definition midas.cxx:11528
static int recv_net_command_realloc(INT idx, char **pbuf, int *pbufsize, INT *remaining)
Definition midas.cxx:14222
void rpc_ieee2vax_float(float *var)
Definition midas.cxx:11636
#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:13944
#define RPC_BM_RECEIVE_EVENT
Definition mrpc.h:47
static bool _rpc_is_remote
Definition midas.cxx:11524
static int rpc_call_decode(va_list &ap, const RPC_LIST &rl, const char *buf, size_t buf_size)
Definition midas.cxx:13427
INT rpc_send_event_sg(INT buffer_handle, int sg_n, const char *const sg_ptr[], const size_t sg_len[])
Definition midas.cxx:13950
INT rpc_set_name(const char *name)
Definition midas.cxx:13133
INT rpc_register_function(INT id, INT(*func)(INT, void **))
Definition midas.cxx:11922
#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:11970
#define RPC_BM_INIT_BUFFER_COUNTERS
Definition mrpc.h:41
static RPC_SERVER_CONNECTION _server_connection
Definition midas.cxx:11523
INT rpc_check_channels(void)
Definition midas.cxx:16279
static INT rpc_transition_dispatch(INT idx, void *prpc_param[])
Definition midas.cxx:14081
#define RPC_CM_EXECUTE
Definition mrpc.h:26
static int handle_msg_odb(int n, const NET_COMMAND *nc)
Definition midas.cxx:11956
#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:11686
void rpc_vax2ieee_double(double *var)
Definition midas.cxx:11667
#define RPC_CM_GET_WATCHDOG_INFO
Definition mrpc.h:24
INT rpc_get_convert_flags(void)
Definition midas.cxx:13055
INT rpc_tid_size(INT id)
Definition midas.cxx:11782
INT rpc_check_allowed_host(const char *hostname)
Definition midas.cxx:15289
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11706
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:448
INT _semaphore_alarm
Definition midas.cxx:1477
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:1471
int(* MessagePrintCallback)(const char *)
Definition midas.cxx:444
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:1473
static std::string _path_name
Definition midas.cxx:1475
INT _database_entries
static std::string _client_name
Definition midas.cxx:1474
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:1476
INT bm_get_buffer_info(INT buffer_handle, BUFFER_HEADER *buffer_header)
Definition midas.cxx:7801
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:446
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:449
static std::mutex _request_list_mutex
Definition midas.cxx:219
INT _semaphore_elog
Definition midas.cxx:1478
INT bm_get_buffer_level(INT buffer_handle, INT *n_bytes)
Definition midas.cxx:7848
INT _semaphore_history
Definition midas.cxx:1479
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:1472
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:17763
static INT _msg_buffer
Definition midas.cxx:198
DATABASE * _database
INT bm_init_buffer_counters(INT buffer_handle)
Definition midas.cxx:8073
#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:1610
#define M_MALLOC(x)
Definition midas.h:1551
#define CINT(_i)
Definition midas.h:1621
#define RPC_OUT
Definition midas.h:1580
#define HOST_NAME_LENGTH
Definition midas.h:273
INT midas_thread_t
Definition midas.h:179
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define RPC_IN
Definition midas.h:1579
#define DEFAULT_WATCHDOG_TIMEOUT
Definition midas.h:290
#define RPC_MIN_ID
Definition midas.h:1605
#define OPT_TCP_SIZE
Definition midas.h:267
#define RPC_MAX_ID
Definition midas.h:1606
#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:1594
#define PROGRAM_INFO_STR(_name)
Definition midas.h:1447
#define MIN_WRITE_CACHE_SIZE
Definition midas.h:257
#define EVENTID_FRAG
Definition midas.h:908
#define RPC_OUTGOING
Definition midas.h:1584
#define MAX_WRITE_CACHE_EVENT_SIZE_DIV
Definition midas.h:259
#define MIDAS_TCP_PORT
Definition midas.h:283
#define CSTRING(_i)
Definition midas.h:1645
#define DEFAULT_MAX_EVENT_SIZE
Definition midas.h:254
#define PTYPE
Definition midas.h:170
#define RPC_POINTER
Definition midas.h:1581
#define EVENTID_MESSAGE
Definition midas.h:903
#define MIDAS_VERSION
Definition midas.h:37
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define M_FREE(x)
Definition midas.h:1553
#define TRUE
Definition midas.h:182
#define BANKLIST_MAX
Definition midas.h:278
#define EVENTID_FRAG1
Definition midas.h:907
#define CF_IEEE2VAX
Definition midas.h:1611
#define MAX_EVENT_REQUESTS
Definition midas.h:275
#define DEFAULT_ODB_SIZE
Definition midas.h:270
#define RPC_VARARRAY
Definition midas.h:1583
#define POINTER_T
Definition midas.h:166
#define BANK_FORMAT_64BIT_ALIGNED
Definition midas.h:1204
#define BANK_FORMAT_32BIT
Definition midas.h:1203
#define CF_VAX2IEEE
Definition midas.h:1612
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define MAX_WRITE_CACHE_SIZE_DIV
Definition midas.h:258
#define TRANSITION_ERROR_STRING_LENGTH
Definition midas.h:280
#define RPC_FIXARRAY
Definition midas.h:1582
#define BANK_FORMAT_VERSION
Definition midas.h:1202
#define NAME_LENGTH
Definition midas.h:272
#define trigger_mask
#define message(type, str)
#define read(n, a, f)
#define event_id
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
INT serial
Definition minife.c:20
static std::string remove(const std::string s, char c)
Definition mjsonrpc.cxx:253
int gettimeofday(struct timeval *tp, void *tzp)
struct callback_addr callback
Definition mserver.cxx:22
timeval tv
Definition msysmon.cxx:1095
int event_size
Definition msysmon.cxx:527
MUTEX_T * tm
Definition odbedit.cxx:39
char pwd[256]
Definition odbedit.cxx:24
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
DWORD status
Definition odbhist.cxx:39
char name[4]
Definition midas.h:1227
DWORD type
Definition midas.h:1228
DWORD data_size
Definition midas.h:1229
char name[4]
Definition midas.h:1221
DWORD data_size
Definition midas.h:1223
DWORD type
Definition midas.h:1222
DWORD flags
Definition midas.h:1211
DWORD data_size
Definition midas.h:1210
Definition midas.h:1214
char name[4]
Definition midas.h:1215
WORD type
Definition midas.h:1216
WORD data_size
Definition midas.h:1217
INT unused1
Definition midas.h:945
DWORD watchdog_timeout
Definition midas.h:952
INT write_wait
Definition midas.h:948
BOOL read_wait
Definition midas.h:947
DWORD last_activity
Definition midas.h:951
char name[NAME_LENGTH]
Definition midas.h:936
BOOL all_flag
Definition midas.h:950
EVENT_REQUEST event_request[MAX_EVENT_REQUESTS]
Definition midas.h:954
INT max_request_index
Definition midas.h:942
INT read_pointer
Definition midas.h:941
INT num_in_events
Definition midas.h:965
INT write_pointer
Definition midas.h:964
INT num_clients
Definition midas.h:960
char name[NAME_LENGTH]
Definition midas.h:959
INT max_client_index
Definition midas.h:961
BUFFER_CLIENT client[MAX_CLIENTS]
Definition midas.h:968
INT num_out_events
Definition midas.h:966
INT read_pointer
Definition midas.h:963
int count_sent
Definition midas.cxx:6437
int last_count_lock
Definition midas.cxx:6441
BOOL get_all_flag
Definition midas.cxx:6433
int count_lock
Definition midas.cxx:6436
BUFFER_INFO(BUFFER *pbuf)
Definition midas.cxx:6450
int count_write_wait
Definition midas.cxx:6439
double bytes_read
Definition midas.cxx:6446
int client_count_write_wait[MAX_CLIENTS]
Definition midas.cxx:6447
int wait_client_index
Definition midas.cxx:6443
DWORD time_write_wait
Definition midas.cxx:6440
int count_read
Definition midas.cxx:6445
double bytes_sent
Definition midas.cxx:6438
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.cxx:6448
DWORD wait_start_time
Definition midas.cxx:6442
int max_requested_space
Definition midas.cxx:6444
HNDLE semaphore
Definition midas.h:1004
double bytes_read
Definition midas.h:1022
std::timed_mutex buffer_mutex
Definition midas.h:989
size_t read_cache_rp
Definition midas.h:997
std::timed_mutex read_cache_mutex
Definition midas.h:994
BOOL get_all_flag
Definition midas.h:1009
int last_count_lock
Definition midas.h:1017
INT client_index
Definition midas.h:990
char client_name[NAME_LENGTH]
Definition midas.h:991
std::timed_mutex write_cache_mutex
Definition midas.h:999
size_t shm_size
Definition midas.h:1006
char * read_cache
Definition midas.h:996
int max_requested_space
Definition midas.h:1020
int count_sent
Definition midas.h:1013
size_t write_cache_rp
Definition midas.h:1002
int client_count_write_wait[MAX_CLIENTS]
Definition midas.h:1023
int count_write_wait
Definition midas.h:1015
int wait_client_index
Definition midas.h:1019
BOOL locked
Definition midas.h:1008
size_t write_cache_wp
Definition midas.h:1003
DWORD time_write_wait
Definition midas.h:1016
int count_read
Definition midas.h:1021
BOOL callback
Definition midas.h:1007
char * write_cache
Definition midas.h:1001
BUFFER_HEADER * buffer_header
Definition midas.h:993
size_t read_cache_wp
Definition midas.h:998
std::atomic< size_t > read_cache_size
Definition midas.h:995
std::atomic< size_t > write_cache_size
Definition midas.h:1000
double bytes_sent
Definition midas.h:1014
DWORD wait_start_time
Definition midas.h:1018
INT shm_handle
Definition midas.h:1005
char buffer_name[NAME_LENGTH]
Definition midas.h:992
std::atomic_bool attached
Definition midas.h:988
int count_lock
Definition midas.h:1012
DWORD client_time_write_wait[MAX_CLIENTS]
Definition midas.h:1024
void * adr
Definition midas.cxx:280
const char * string
Definition midas.cxx:267
EVENT_HEADER * pevent
Definition midas.cxx:11303
short int event_id
Definition midas.h:853
DWORD data_size
Definition midas.h:857
DWORD serial_number
Definition midas.h:855
DWORD time_stamp
Definition midas.h:856
short int trigger_mask
Definition midas.h:854
short int event_id
Definition midas.h:930
short int trigger_mask
Definition midas.h:931
INT sampling_type
Definition midas.h:932
BOOL valid
Definition midas.h:929
short int event_id
Definition midas.cxx:206
INT buffer_handle
Definition midas.cxx:205
short int trigger_mask
Definition midas.cxx:207
EVENT_HANDLER * dispatcher
Definition midas.cxx:208
void clear()
Definition midas.cxx:210
Definition midas.h:1027
INT num_values
Definition midas.h:1029
DWORD type
Definition midas.h:1028
INT last_written
Definition midas.h:1038
char name[NAME_LENGTH]
Definition midas.h:1030
INT item_size
Definition midas.h:1033
NET_COMMAND_HEADER header
Definition msystem.h:293
char param[32]
Definition msystem.h:294
unsigned char * wp
Definition midas.cxx:17316
unsigned char * buffer
Definition midas.cxx:17312
unsigned int max_event_size
Definition midas.cxx:17314
unsigned int size
Definition midas.cxx:17313
unsigned char * rp
Definition midas.cxx:17315
unsigned char * ep
Definition midas.cxx:17317
RPC_PARAM param[MAX_RPC_PARAMS]
Definition midas.h:1599
INT id
Definition midas.h:1597
RPC_HANDLER * dispatch
Definition midas.h:1600
const char * name
Definition midas.h:1598
WORD flags
Definition midas.h:1590
WORD tid
Definition midas.h:1589
INT n
Definition midas.h:1591
midas_thread_t thread_id
Definition midas.cxx:14666
int buffer_size
Definition midas.cxx:14667
char * buffer
Definition midas.cxx:14668
int transition
Definition midas.cxx:14070
time_t trans_time
Definition midas.cxx:14072
int run_number
Definition midas.cxx:14071
int sequence_number
Definition midas.cxx:14073
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:3968
int transition
Definition midas.cxx:3953
std::string host_name
Definition midas.cxx:3959
DWORD connect_end_time
Definition midas.cxx:3970
std::atomic_int status
Definition midas.cxx:3963
~TrClient()
Definition midas.cxx:3981
std::vector< int > wait_for_index
Definition midas.cxx:3958
DWORD init_time
Definition midas.cxx:3966
std::string key_name
Definition midas.cxx:3962
DWORD rpc_end_time
Definition midas.cxx:3973
std::string waiting_for_client
Definition midas.cxx:3967
DWORD rpc_timeout
Definition midas.cxx:3971
int async_flag
Definition midas.cxx:3955
int port
Definition midas.cxx:3961
DWORD rpc_start_time
Definition midas.cxx:3972
int sequence_number
Definition midas.cxx:3957
int debug_flag
Definition midas.cxx:3956
int run_number
Definition midas.cxx:3954
DWORD end_time
Definition midas.cxx:3974
DWORD connect_start_time
Definition midas.cxx:3969
std::string client_name
Definition midas.cxx:3960
std::string errorstr
Definition midas.cxx:3965
void Print() const
Definition midas.cxx:3987
std::thread * thread
Definition midas.cxx:3964
int transition
Definition midas.cxx:4006
std::vector< std::unique_ptr< TrClient > > clients
Definition midas.cxx:4014
int async_flag
Definition midas.cxx:4008
DWORD end_time
Definition midas.cxx:4013
int run_number
Definition midas.cxx:4007
int status
Definition midas.cxx:4010
DWORD start_time
Definition midas.cxx:4012
std::string errorstr
Definition midas.cxx:4011
int debug_flag
Definition midas.cxx:4009
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:1609
std::string name
Definition midas.cxx:1610
std::string directory
Definition midas.cxx:1611
std::string user
Definition midas.cxx:1612
std::string filename
Definition midas.cxx:1616
std::vector< exptab_entry > exptab
Definition midas.cxx:1617
Definition midas.cxx:865
DWORD ts
Definition midas.cxx:866
int message_type
Definition midas.cxx:867
std::string message
Definition midas.cxx:868
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