MIDAS
Loading...
Searching...
No Matches
system.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: system.c
4 Created by: Stefan Ritt
5
6 Contents: All operating system dependent system services. This
7 file containt routines which hide all system specific
8 behaviour to higher levels. This is done by con-
9 ditional compiling using the OS_xxx variable defined
10 in MIDAS.H.
11
12 Details about interprocess communication can be
13 found in "UNIX distributed programming" by Chris
14 Brown, Prentice Hall
15
16 $Id$
17
18\********************************************************************/
19
33#undef NDEBUG // midas required assert() to be always enabled
34
35#include <stdio.h>
36#include <math.h>
37#include <vector>
38#include <atomic> // std::atomic_int & co
39#include <thread>
40#include <array>
41#include <stdexcept>
42#include <fstream>
43
44#include "midas.h"
45#include "msystem.h"
46#include "mstrlcpy.h"
47
48#ifdef OS_UNIX
49#include <sys/mount.h>
50#endif
51
52#ifdef LOCAL_ROUTINES
53#include <signal.h>
54
55/*------------------------------------------------------------------*/
56/* globals */
57
58#if defined(OS_UNIX)
59
60#include <sys/types.h>
61#include <sys/stat.h>
62#include <sys/mman.h>
63
64#if defined(OS_DARWIN)
65#include <sys/posix_shm.h>
66#include <sys/sysctl.h>
67#endif
68
69static int shm_trace = 0;
70static int shm_count = 0;
71
72static int use_sysv_shm = 0;
73static int use_mmap_shm = 0;
74static int use_posix_shm = 0;
75static int use_posix1_shm = 0;
76static int use_posix2_shm = 0;
77static int use_posix3_shm = 0;
78static int use_posix4_shm = 0;
79
80#endif
81
82static void check_shm_type(const char* shm_type)
83{
84#ifdef OS_UNIX
85 std::string file_name;
86 char cwd[256], buf[256];
87 char* s;
88
89 std::string path = cm_get_path();
90 if (path.empty()) {
91 if (getcwd(cwd, sizeof(cwd)))
92 path = std::string(cwd);
93 path += "/";
94 }
95
96
97 file_name = path;
98 file_name += ".SHM_TYPE.TXT";
99
100 FILE* fp = fopen(file_name.c_str(), "r");
101 if (!fp) {
102 fp = fopen(file_name.c_str(), "w");
103 if (!fp) {
104 fprintf(stderr, "check_shm_type: Cannot write to config file \'%s\', errno %d (%s)", file_name.c_str(), errno, strerror(errno));
105 exit(1);
106 // DOES NOT RETURN
107 }
108
109 fprintf(fp, "%s\n", shm_type);
110 fclose(fp);
111
112 fp = fopen(file_name.c_str(), "r");
113 if (!fp) {
114 fprintf(stderr, "check_shm_type: Cannot open config file \'%s\', errno %d (%s)", file_name.c_str(), errno, strerror(errno));
115 exit(1);
116 // DOES NOT RETURN
117 }
118 }
119
120 if (!fgets(buf, sizeof(buf), fp))
121 buf[0] = 0;
122
123 fclose(fp);
124
125 s = strchr(buf, '\n');
126 if (s)
127 *s = 0;
128
129 //printf("check_shm_type: preferred %s got %s\n", shm_type, buf);
130
131 if (strcmp(buf, "SYSV_SHM") == 0) {
132 use_sysv_shm = 1;
133 return;
134 }
135
136 if (strcmp(buf, "MMAP_SHM") == 0) {
137 use_mmap_shm = 1;
138 return;
139 }
140
141 if (strcmp(buf, "POSIX_SHM") == 0) {
142 use_posix1_shm = 1;
143 use_posix_shm = 1;
144 return;
145 }
146
147 if (strcmp(buf, "POSIXv2_SHM") == 0) {
148 use_posix2_shm = 1;
149 use_posix_shm = 1;
150 return;
151 }
152
153 if (strcmp(buf, "POSIXv3_SHM") == 0) {
154 use_posix3_shm = 1;
155 use_posix_shm = 1;
156 return;
157 }
158
159 if (strcmp(buf, "POSIXv4_SHM") == 0) {
160 use_posix4_shm = 1;
161 use_posix_shm = 1;
162 return;
163 }
164
165 fprintf(stderr, "check_shm_type: Config file \"%s\" specifies unknown or unsupported shared memory type \"%s\", supported types are: SYSV_SHM, MMAP_SHM, POSIX_SHM, POSIXv2_SHM, POSIXv3_SHM, POSIXv4_SHM, default/preferred type is \"%s\"\n", file_name.c_str(), buf, shm_type);
166 exit(1);
167#endif
168}
169
170static void check_shm_host()
171{
172 std::string file_name;
173 char buf[256], cwd[256];
174 char hostname[256];
175 char* s;
176 FILE *fp;
177
178 gethostname(hostname, sizeof(hostname));
179
180 //printf("hostname [%s]\n", hostname);
181
182 std::string path = cm_get_path();
183 if (path.empty()) {
184 if (getcwd(cwd, sizeof(cwd)))
185 path = std::string(cwd);
186#if defined(OS_VMS)
187#elif defined(OS_UNIX)
188 path += "/";
189#elif defined(OS_WINNT)
190 path += "\\";
191#endif
192 }
193
194 file_name = path;
195#if defined (OS_UNIX)
196 file_name += "."; /* dot file under UNIX */
197#endif
198 file_name += "SHM_HOST.TXT";
199
200 fp = fopen(file_name.c_str(), "r");
201 if (!fp) {
202 fp = fopen(file_name.c_str(), "w");
203 if (!fp)
204 cm_msg(MERROR, "check_shm_host", "Cannot write to \'%s\', errno %d (%s)", file_name.c_str(), errno, strerror(errno));
205 assert(fp != NULL);
206 fprintf(fp, "%s\n", hostname);
207 fclose(fp);
208 return;
209 }
210
211 buf[0] = 0;
212
213 if (!fgets(buf, sizeof(buf), fp))
214 buf[0] = 0;
215
216 fclose(fp);
217
218 s = strchr(buf, '\n');
219 if (s)
220 *s = 0;
221
222 if (strlen(buf) < 1)
223 return; // success - provide user with a way to defeat this check
224
225 if (strcmp(buf, hostname) == 0)
226 return; // success!
227
228 cm_msg(MERROR, "check_shm_host", "Error: Cannot connect to MIDAS shared memory - this computer hostname is \'%s\' while \'%s\' says that MIDAS shared memory for this experiment is located on computer \'%s\'. To connect to this experiment from this computer, use the mserver. Please see the MIDAS documentation for details.", hostname, file_name.c_str(), buf);
229 exit(1);
230}
231
232static int ss_shm_name(const char* name, std::string& mem_name, std::string& file_name, std::string& shm_name)
233{
235#if defined(OS_DARWIN)
236 check_shm_type("POSIXv3_SHM"); // uid + expt name + shm name
237#elif defined(OS_UNIX)
238 check_shm_type("POSIXv4_SHM"); // uid + expt name + shm name + expt directory
239#endif
240
241 mem_name = std::string("SM_") + name;
242
243 /* append .SHM and preceed the path for the shared memory file name */
244
245 std::string exptname = cm_get_experiment_name();
246 std::string path = cm_get_path();
247
248 //printf("shm name [%s], expt name [%s], path [%s]\n", name, exptname.c_str(), path.c_str());
249
250 assert(path.length() > 0);
251 assert(exptname.length() > 0);
252
253 file_name = path;
254#if defined (OS_UNIX)
255 file_name += "."; /* dot file under UNIX */
256#endif
257 file_name += name;
258 file_name += ".SHM";
259
260#if defined(OS_UNIX)
261 shm_name = "/";
262 if (use_posix1_shm) {
264 } else if (use_posix2_shm) {
266 shm_name += "_";
267 shm_name += name;
268 shm_name += "_SHM";
269 } else if (use_posix3_shm) {
270 uid_t uid = getuid();
271 char buf[16];
272 sprintf(buf, "%d", uid);
273 shm_name += buf;
274 shm_name += "_";
276 shm_name += "_";
277 shm_name += name;
278 } else if (use_posix4_shm) {
279 uid_t uid = getuid();
280 char buf[16];
281 sprintf(buf, "%d", uid);
282 shm_name += buf;
283 shm_name += "_";
285 shm_name += "_";
286 shm_name += name;
287 shm_name += "_";
289 } else {
290 fprintf(stderr, "check_shm_host: unsupported shared memory type, bye!\n");
291 abort();
292 }
293
294 for (size_t i=1; i<shm_name.length(); i++)
295 if (shm_name[i] == '/')
296 shm_name[i] = '_';
297
298 //printf("ss_shm_name: [%s] generated [%s]\n", name, shm_name.c_str());
299#endif
300
301 return SS_SUCCESS;
302}
303
304#if defined OS_UNIX
305static int ss_shm_file_name_to_shmid(const char* file_name, int* shmid)
306{
307 int key, status;
308
309 /* create a unique key from the file name */
310 key = ftok(file_name, 'M');
311
312 /* if file doesn't exist ... */
313 if (key == -1)
314 return SS_NO_MEMORY;
315
316 status = shmget(key, 0, 0);
317 if (status == -1)
318 return SS_NO_MEMORY;
319
320 (*shmid) = status;
321 return SS_SUCCESS;
322}
323#endif
324
325/*------------------------------------------------------------------*/
326INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE * handle, BOOL get_size)
327/********************************************************************\
328
329 Routine: ss_shm_open
330
331 Purpose: Create a shared memory region which can be seen by several
332 processes which know the name.
333
334 Input:
335 char *name Name of the shared memory
336 INT size Initial size of the shared memory in bytes
337 if .SHM file doesn't exist
338 BOOL get_size If TRUE and shared memory already exists, overwrite
339 "size" parameter with existing memory size
340
341 Output:
342 void *adr Address of opened shared memory
343 HNDLE handle Handle or key to the shared memory
344 size_t shm_size Size of shared memory to use with ss_shm_close() & co
345
346 Function value:
347 SS_SUCCESS Successful completion
348 SS_CREATED Shared memory was created
349 SS_FILE_ERROR Paging file cannot be created
350 SS_NO_MEMORY Not enough memory
351 SS_SIZE_MISMATCH "size" differs from existing size and
352 get_size is FALSE
353\********************************************************************/
354{
355 INT status;
356 std::string mem_name;
357 std::string file_name;
358 std::string shm_name;
359
361
362 if (shm_trace)
363 printf("ss_shm_open(\"%s\",%d,%d), mem_name [%s], file_name [%s], shm_name [%s]\n", name, size, get_size, mem_name.c_str(), file_name.c_str(), shm_name.c_str());
364
365#ifdef OS_WINNT
366
368
369 {
371 char str[256], path[256], *p;
372 DWORD file_size;
373
374 /* make the memory name unique using the pathname. This is necessary
375 because NT doesn't use ftok. So if different experiments are
376 running in different directories, they should not see the same
377 shared memory */
378 cm_get_path(path, sizeof(path));
379 mstrlcpy(str, path, sizeof(path));
380
381 /* replace special chars by '*' */
382 while (strpbrk(str, "\\: "))
383 *strpbrk(str, "\\: ") = '*';
384 mstrlcat(str, mem_name, sizeof(path));
385
386 /* convert to uppercase */
387 p = str;
388 while (*p)
389 *p++ = (char) toupper(*p);
390
392 if (hMap == 0) {
394 if (!hFile) {
395 cm_msg(MERROR, "ss_shm_open", "CreateFile() failed");
396 return SS_FILE_ERROR;
397 }
398
399 file_size = GetFileSize(hFile, NULL);
400 if (get_size) {
401 if (file_size != 0xFFFFFFFF && file_size > 0)
402 size = file_size;
403 } else {
404 if (file_size != 0xFFFFFFFF && file_size > 0 && file_size != size) {
405 cm_msg(MERROR, "ss_shm_open", "Requested size (%d) differs from existing size (%d)", size, file_size);
406 return SS_SIZE_MISMATCH;
407 }
408 }
409
411
412 if (!hMap) {
414 cm_msg(MERROR, "ss_shm_open", "CreateFileMapping() failed, error %d", status);
415 return SS_FILE_ERROR;
416 }
417
420 }
421
422 *adr = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
423 *handle = (HNDLE) hMap;
424 *shm_size = size;
425
426 if (adr == NULL) {
427 cm_msg(MERROR, "ss_shm_open", "MapViewOfFile() failed");
428 return SS_NO_MEMORY;
429 }
430
431 return status;
432 }
433
434#endif /* OS_WINNT */
435#ifdef OS_VMS
436
438
439 {
440 int addr[2];
441 $DESCRIPTOR(memname_dsc, "dummy");
442 $DESCRIPTOR(filename_dsc, "dummy");
443 memname_dsc.dsc$w_length = strlen(mem_name);
444 memname_dsc.dsc$a_pointer = mem_name;
445 filename_dsc.dsc$w_length = file_name.length();
446 filename_dsc.dsc$a_pointer = file_name.c_str();
447
448 addr[0] = size;
449 addr[1] = 0;
450
452
453 if (status == PPL$_CREATED)
455 else if (status != PPL$_NORMAL)
457
458 *adr = (void *) addr[1];
459 *handle = 0; /* not used under VMS */
460 *shm_size = addr[0];
461
462 if (adr == NULL)
463 return SS_NO_MEMORY;
464
465 return status;
466 }
467
468#endif /* OS_VMS */
469#ifdef OS_UNIX
470
471 if (use_sysv_shm) {
472
473 int key, shmid, fh;
474 double file_size = 0;
475 struct shmid_ds buf;
476
478
479 /* create a unique key from the file name */
480 key = ftok(file_name.c_str(), 'M');
481
482 /* if file doesn't exist, create it */
483 if (key == -1) {
484 fh = open(file_name.c_str(), O_CREAT | O_TRUNC | O_BINARY | O_RDWR, 0644);
485 if (fh > 0) {
486 close(fh);
487 }
488 key = ftok(file_name.c_str(), 'M');
489
490 if (key == -1) {
491 cm_msg(MERROR, "ss_shm_open", "ftok() failed");
492 return SS_FILE_ERROR;
493 }
494
496
497 /* delete any previously created memory */
498
499 shmid = shmget(key, 0, 0);
500 shmctl(shmid, IPC_RMID, &buf);
501 } else {
502 /* if file exists, retrieve its size */
503 file_size = ss_file_size(file_name.c_str());
504 if (file_size > 0) {
505 if (get_size) {
506 size = file_size;
507 } else if (size != file_size) {
508 cm_msg(MERROR, "ss_shm_open", "Existing file \'%s\' has size %.0f, different from requested size %d", file_name.c_str(), file_size, size);
509 return SS_SIZE_MISMATCH;
510 }
511 }
512 }
513
514 if (shm_trace)
515 printf("ss_shm_open(\"%s\",%d) get_size %d, file_name %s, size %.0f\n", name, size, get_size, file_name.c_str(), file_size);
516
517 /* get the shared memory, create if not existing */
518 shmid = shmget(key, size, 0);
519 if (shmid == -1) {
520 //cm_msg(MINFO, "ss_shm_open", "Creating shared memory segment, key: 0x%x, size: %d",key,size);
521 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL);
522 if (shmid == -1 && errno == EEXIST) {
523 cm_msg(MERROR, "ss_shm_open",
524 "Shared memory segment with key 0x%x already exists, please remove it manually: ipcrm -M 0x%x",
525 key, key);
526 return SS_NO_MEMORY;
527 }
529 }
530
531 if (shmid == -1) {
532 cm_msg(MERROR, "ss_shm_open", "shmget(key=0x%x,size=%d) failed, errno %d (%s)", key, size, errno, strerror(errno));
533 return SS_NO_MEMORY;
534 }
535
536 memset(&buf, 0, sizeof(buf));
537 buf.shm_perm.uid = getuid();
538 buf.shm_perm.gid = getgid();
539 buf.shm_perm.mode = 0666;
540 shmctl(shmid, IPC_SET, &buf);
541
542 *adr = shmat(shmid, 0, 0);
543
544 if ((*adr) == (void *) (-1)) {
545 cm_msg(MERROR, "ss_shm_open", "shmat(shmid=%d) failed, errno %d (%s)", shmid, errno, strerror(errno));
546 return SS_NO_MEMORY;
547 }
548
549 *handle = (HNDLE) shmid;
550 *shm_size = size;
551
552 /* if shared memory was created, try to load it from file */
553 if (status == SS_CREATED && file_size > 0) {
554 fh = open(file_name.c_str(), O_RDONLY, 0644);
555 if (fh == -1)
556 fh = open(file_name.c_str(), O_CREAT | O_RDWR, 0644);
557 else {
558 int rd = read(fh, *adr, size);
559 if (rd != size)
560 cm_msg(MERROR, "ss_shm_open", "File size mismatch shared memory \'%s\' size %d, file \'%s\' read %d, errno %d (%s)", name, size, file_name.c_str(), rd, errno, strerror(errno));
561 }
562 close(fh);
563 }
564
565 return status;
566 }
567
568 if (use_mmap_shm) {
569
570 int ret;
571 int fh, file_size;
572
573 if (1) {
574 static int once = 1;
575 if (once && strstr(file_name.c_str(), "ODB")) {
576 once = 0;
577 cm_msg(MINFO, "ss_shm_open", "WARNING: This version of MIDAS system.c uses the experimental mmap() based implementation of MIDAS shared memory.");
578 }
579 }
580
581 if (shm_trace)
582 printf("ss_shm_open(\"%s\",%d) get_size %d, file_name %s\n", name, size, get_size, file_name.c_str());
583
585
586 fh = open(file_name.c_str(), O_RDWR | O_BINARY | O_LARGEFILE, 0644);
587
588 if (fh < 0) {
589 if (errno == ENOENT) { // file does not exist
590 fh = open(file_name.c_str(), O_CREAT | O_RDWR | O_BINARY | O_LARGEFILE, 0644);
591 }
592
593 if (fh < 0) {
594 cm_msg(MERROR, "ss_shm_open", "Cannot create shared memory file \'%s\', errno %d (%s)", file_name.c_str(), errno, strerror(errno));
595 return SS_FILE_ERROR;
596 }
597
598 ret = lseek(fh, size - 1, SEEK_SET);
599
600 if (ret == (off_t) - 1) {
601 cm_msg(MERROR, "ss_shm_open",
602 "Cannot create shared memory file \'%s\', size %d, lseek() errno %d (%s)",
603 file_name.c_str(), size, errno, strerror(errno));
604 return SS_FILE_ERROR;
605 }
606
607 ret = 0;
608 ret = write(fh, &ret, 1);
609 assert(ret == 1);
610
611 ret = lseek(fh, 0, SEEK_SET);
612 assert(ret == 0);
613
614 //cm_msg(MINFO, "ss_shm_open", "Created shared memory file \'%s\', size %d", file_name.c_str(), size);
615
617 }
618
619 /* if file exists, retrieve its size */
620 file_size = (INT) ss_file_size(file_name.c_str());
621 if (file_size < size) {
622 cm_msg(MERROR, "ss_shm_open",
623 "Shared memory file \'%s\' size %d is smaller than requested size %d. Please remove it and try again",
624 file_name.c_str(), file_size, size);
625 return SS_NO_MEMORY;
626 }
627
628 size = file_size;
629
630 *adr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fh, 0);
631
632 if ((*adr) == MAP_FAILED) {
633 cm_msg(MERROR, "ss_shm_open", "mmap() failed, errno %d (%s)", errno, strerror(errno));
634 return SS_NO_MEMORY;
635 }
636
637 *handle = ++shm_count;
638 *shm_size = size;
639
640 return status;
641 }
642
643 if (use_posix_shm) {
644
645 int sh;
646 int fh;
647 int created = 0;
648 double file_size = -1;
649
650 fh = open(file_name.c_str(), O_RDONLY | O_BINARY | O_LARGEFILE, 0777);
651
652 if (fh >= 0) {
653 file_size = ss_file_size(file_name.c_str());
654 }
655
656 if (shm_trace)
657 printf("ss_shm_open(\"%s\",%d) get_size %d, file_name %s, size %.0f\n", name, size, get_size, file_name.c_str(), file_size);
658
659 if (file_size > 0) {
660 if (get_size)
661 size = file_size;
662
663 if (file_size != size) {
664 cm_msg(MERROR, "ss_shm_open", "Shared memory file \'%s\' size %.0f is different from requested size %d. Please backup and remove this file and try again", file_name.c_str(), file_size, size);
665 if (fh >= 0)
666 close(fh);
667 return SS_NO_MEMORY;
668 }
669 }
670
671 int mode = 0600; // 0777: full access for everybody (minus umask!), 0600: current user: read+write, others: no permission
672
673 sh = shm_open(shm_name.c_str(), O_RDWR, mode);
674
675 if (sh < 0) {
676 // cannot open, try to create new one
677
678 sh = shm_open(shm_name.c_str(), O_RDWR | O_CREAT, mode);
679
680 //printf("ss_shm_open: name [%s], return %d, errno %d (%s)\n", shm_name, sh, errno, strerror(errno));
681
682 if (sh < 0) {
683#ifdef ENAMETOOLONG
684 if (errno == ENAMETOOLONG) {
685 fprintf(stderr, "ss_shm_open: Cannot create shared memory for \"%s\": shared memory object name \"%s\" is too long for shm_open(), please try to use shorter experiment name or shorter event buffer name or a shared memory type that uses shorter names, in this order: POSIXv3_SHM, POSIXv2_SHM or POSIX_SHM (as specified in config file .SHM_TYPE.TXT). Sorry, bye!\n", name, shm_name.c_str());
686 exit(1);
687 }
688#endif
689#ifdef EACCES
690 if (errno == EACCES) {
691 fprintf(stderr, "ss_shm_open: Cannot create shared memory for \"%s\" with shared memory object name \"%s\", shm_open() errno %d (%s), please inspect file permissions in \"ls -l /dev/shm\", and if this is a conflict with a different user using the same experiment name, please change shared memory type to the POSIXv4_SHM or POSIXv3_SHM (on MacOS) (as specified in config file .SHM_TYPE.TXT). Sorry, bye!\n", name, shm_name.c_str(), errno, strerror(errno));
692 exit(1);
693 }
694#endif
695 cm_msg(MERROR, "ss_shm_open", "Cannot create shared memory segment \'%s\', shm_open() errno %d (%s)", shm_name.c_str(), errno, strerror(errno));
696 if (fh >= 0)
697 close(fh);
698 return SS_NO_MEMORY;
699 }
700
701 status = ftruncate(sh, size);
702 if (status < 0) {
703 cm_msg(MERROR, "ss_shm_open", "Cannot resize shared memory segment \'%s\', ftruncate(%d) errno %d (%s)", shm_name.c_str(), size, errno, strerror(errno));
704 if (fh >= 0)
705 close(fh);
706 return SS_NO_MEMORY;
707 }
708
709 //cm_msg(MINFO, "ss_shm_open", "Created shared memory segment \'%s\', size %d", shm_name.c_str(), size);
710
711 created = 1;
712 }
713
714 *adr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, sh, 0);
715
716 if ((*adr) == MAP_FAILED) {
717 cm_msg(MERROR, "ss_shm_open", "Cannot mmap() shared memory \'%s\', errno %d (%s)", shm_name.c_str(), errno, strerror(errno));
718 close(sh);
719 if (fh >= 0)
720 close(fh);
721 return SS_NO_MEMORY;
722 }
723
724 close(sh);
725
726 /* if shared memory was created, try to load it from file */
727
728 if (created && fh >= 0 && file_size > 0) {
729 if (shm_trace)
730 printf("ss_shm_open(\"%s\"), loading contents of file [%s], size %.0f\n", name, file_name.c_str(), file_size);
731
732 status = read(fh, *adr, size);
733 if (status != size) {
734 cm_msg(MERROR, "ss_shm_open", "Cannot read \'%s\', read() returned %d instead of %d, errno %d (%s)", file_name.c_str(), status, size, errno, strerror(errno));
735 close(fh);
736 return SS_NO_MEMORY;
737 }
738 }
739
740 close(fh);
741
742 *handle = ++shm_count;
743 *shm_size = size;
744
745 if (created)
746 return SS_CREATED;
747 else
748 return SS_SUCCESS;
749 }
750
751#endif /* OS_UNIX */
752
753 return SS_FILE_ERROR;
754}
755
756/*------------------------------------------------------------------*/
757INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
758/********************************************************************\
759
760 Routine: ss_shm_close
761
762 Purpose: Close a shared memory region.
763
764 Input:
765 char *name Name of the shared memory
766 void *adr Base address of shared memory
767 size_t shm_size Size of shared memory shm_size returned by ss_shm_open()
768 HNDLE handle Handle of shared memeory
769 BOOL destroy Shared memory has to be destroyd and
770 flushed to the mapping file.
771
772 Output:
773 none
774
775 Function value:
776 SS_SUCCESS Successful completion
777 SS_INVALID_ADDRESS Invalid base address
778 SS_FILE_ERROR Cannot write shared memory file
779 SS_INVALID_HANDLE Invalid shared memory handle
780
781\********************************************************************/
782{
783 char mem_name[256], cwd[256];
784 std::string file_name;
785
786 /*
787 append a leading SM_ to the memory name to resolve name conflicts
788 with mutex or semaphore names
789 */
790 sprintf(mem_name, "SM_%s", name);
791
792 /* append .SHM and preceed the path for the shared memory file name */
793 std::string path = cm_get_path();
794 if (path.empty()) {
795 if (getcwd(cwd, sizeof(cwd)))
796 path = std::string(cwd);
797#if defined(OS_VMS)
798#elif defined(OS_UNIX)
799 path += "/";
800#elif defined(OS_WINNT)
801 path += "\\";
802#endif
803 }
804
805 file_name = path;
806#if defined (OS_UNIX)
807 file_name += "."; /* dot file under UNIX */
808#endif
809 file_name += std::string(name);
810 file_name += ".SHM";
811
812 if (shm_trace)
813 printf("ss_shm_close(\"%s\",%p,%.0f,%d,destroy_flag=%d), file_name [%s]\n", name, adr, (double)shm_size, handle, destroy_flag, file_name.c_str());
814
815#ifdef OS_WINNT
816
817 if (!UnmapViewOfFile(adr))
818 return SS_INVALID_ADDRESS;
819
820 CloseHandle((HANDLE) handle);
821
822 return SS_SUCCESS;
823
824#endif /* OS_WINNT */
825#ifdef OS_VMS
826/* outcommented because ppl$delete... makes privilege violation
827 {
828 int addr[2], flags, status;
829 char mem_name[100];
830 $DESCRIPTOR(memname_dsc, mem_name);
831
832 strcpy(mem_name, "SM_");
833 strcat(mem_name, name);
834 memname_dsc.dsc$w_length = strlen(mem_name);
835
836 flags = PPL$M_FLUSH | PPL$M_NOUNI;
837
838 addr[0] = 0;
839 addr[1] = adr;
840
841 status = ppl$delete_shared_memory( &memname_dsc, addr, &flags);
842
843 if (status == PPL$_NORMAL)
844 return SS_SUCCESS;
845
846 return SS_INVALID_ADDRESS;
847 }
848*/
849 return SS_INVALID_ADDRESS;
850
851#endif /* OS_VMS */
852#ifdef OS_UNIX
853
854 if (use_sysv_shm) {
855
856 struct shmid_ds buf;
857
858 /* get info about shared memory */
859 memset(&buf, 0, sizeof(buf));
860 if (shmctl(handle, IPC_STAT, &buf) < 0) {
861 cm_msg(MERROR, "ss_shm_close", "shmctl(shmid=%d,IPC_STAT) failed, errno %d (%s)",
862 handle, errno, strerror(errno));
863 return SS_INVALID_HANDLE;
864 }
865
866 destroy_flag = (buf.shm_nattch == 1);
867
868 if (shm_trace)
869 printf("ss_shm_close(\"%s\"), destroy_flag %d, shmid %d, shm_nattach %d\n", name, destroy_flag, handle, (int)buf.shm_nattch);
870
871 if (shmdt(adr) < 0) {
872 cm_msg(MERROR, "ss_shm_close", "shmdt(shmid=%d) failed, errno %d (%s)", handle, errno, strerror(errno));
873 return SS_INVALID_ADDRESS;
874 }
875
876 if (destroy_flag) {
878 if (status != SS_SUCCESS)
879 return status;
880 }
881
882 return SS_SUCCESS;
883 }
884
886 int status;
887
888 if (shm_trace)
889 printf("ss_shm_close(\"%s\"), destroy_flag %d\n", name, destroy_flag);
890
891 status = munmap(adr, shm_size);
892 if (status != 0) {
893 cm_msg(MERROR, "ss_shm_close", "Cannot unmap shared memory \'%s\', munmap() errno %d (%s)", name, errno, strerror(errno));
894 return SS_INVALID_ADDRESS;
895 }
896
897 if (destroy_flag) {
899 if (status != SS_SUCCESS)
900 return status;
901 }
902
903 return SS_SUCCESS;
904 }
905#endif /* OS_UNIX */
906
907 return SS_FILE_ERROR;
908}
909
910/*------------------------------------------------------------------*/
912/********************************************************************\
913
914 Routine: ss_shm_delete
915
916 Purpose: Delete shared memory segment from memory.
917
918 Input:
919 char *name Name of the shared memory
920
921 Output:
922 none
923
924 Function value:
925 SS_SUCCESS Successful completion
926 SS_NO_MEMORY Shared memory segment does not exist
927
928\********************************************************************/
929{
930 int status;
931 std::string mem_name;
932 std::string file_name;
933 std::string shm_name;
934
936
937 if (shm_trace)
938 printf("ss_shm_delete(\"%s\") file_name [%s] shm_name [%s]\n", name, file_name.c_str(), shm_name.c_str());
939
940#ifdef OS_WINNT
941 /* no shared memory segments to delete */
942 return SS_SUCCESS;
943#endif /* OS_WINNT */
944
945#ifdef OS_VMS
946 assert(!"not implemented!");
947 return SS_NO_MEMORY;
948#endif /* OS_VMS */
949
950#ifdef OS_UNIX
951
952 if (use_sysv_shm) {
953 int shmid = -1;
954 struct shmid_ds buf;
955
957
958 if (shm_trace)
959 printf("ss_shm_delete(\"%s\") file_name %s, shmid %d\n", name, file_name.c_str(), shmid);
960
961 if (status != SS_SUCCESS)
962 return status;
963
964 status = shmctl(shmid, IPC_RMID, &buf);
965
966 if (status == -1) {
967 cm_msg(MERROR, "ss_shm_delete", "Cannot delete shared memory \'%s\', shmctl(IPC_RMID) failed, errno %d (%s)", name, errno, strerror(errno));
968 return SS_FILE_ERROR;
969 }
970
971 return SS_SUCCESS;
972 }
973
974 if (use_mmap_shm) {
975 /* no shared memory segments to delete */
976
977 if (shm_trace)
978 printf("ss_shm_delete(\"%s\") file_name %s (no-op)\n", name, file_name.c_str());
979
980 return SS_SUCCESS;
981 }
982
983 if (use_posix_shm) {
984
985 if (shm_trace)
986 printf("ss_shm_delete(\"%s\") shm_name %s\n", name, shm_name.c_str());
987
988 status = shm_unlink(shm_name.c_str());
989 if (status < 0) {
990 if (errno != ENOENT) {
991 cm_msg(MERROR, "ss_shm_delete", "shm_unlink(%s) nexpexted error, status %d, errno %d (%s)", shm_name.c_str(), status, errno, strerror(errno));
992 }
993 return SS_NO_MEMORY;
994 }
995
996 return SS_SUCCESS;
997 }
998
999#endif /* OS_UNIX */
1000
1001 return SS_FILE_ERROR;
1002}
1003
1004/*------------------------------------------------------------------*/
1005INT ss_shm_protect(HNDLE handle, void *adr, size_t shm_size)
1006/********************************************************************\
1007
1008 Routine: ss_shm_protect
1009
1010 Purpose: Protect a shared memory region, disallow read and write
1011 access to it by this process
1012
1013 Input:
1014 HNDLE handle Handle of shared memeory
1015 void *adr Address of shared memory
1016 size_t shm_size Size of shared memory
1017
1018 Output:
1019 none
1020
1021 Function value:
1022 SS_SUCCESS Successful completion
1023 SS_INVALID_ADDRESS Invalid base address
1024
1025\********************************************************************/
1026{
1027 if (shm_trace)
1028 printf("ss_shm_protect() handle %d, adr %p, size %.0f\n", handle, adr, (double)shm_size);
1029
1030#ifdef OS_WINNT
1031
1032 if (!UnmapViewOfFile(adr))
1033 return SS_INVALID_ADDRESS;
1034
1035#endif /* OS_WINNT */
1036#ifdef OS_UNIX
1037
1038 if (use_sysv_shm) {
1039
1040 if (shmdt(adr) < 0) {
1041 cm_msg(MERROR, "ss_shm_protect", "shmdt() failed");
1042 return SS_INVALID_ADDRESS;
1043 }
1044 }
1045
1046 if (use_mmap_shm || use_posix_shm) {
1047 assert(shm_size > 0);
1048
1049 int ret = mprotect(adr, shm_size, PROT_NONE);
1050 if (ret != 0) {
1051 cm_msg(MERROR, "ss_shm_protect", "Cannot mprotect(PROT_NONE): return value %d, errno %d (%s)", ret, errno, strerror(errno));
1052 return SS_INVALID_ADDRESS;
1053 }
1054 }
1055
1056#endif // OS_UNIX
1057
1058 return SS_SUCCESS;
1059}
1060
1061/*------------------------------------------------------------------*/
1062INT ss_shm_unprotect(HNDLE handle, void **adr, size_t shm_size, BOOL read, BOOL write, const char* caller_name)
1063/********************************************************************\
1064
1065 Routine: ss_shm_unprotect
1066
1067 Purpose: Unprotect a shared memory region so that it can be accessed
1068 by this process
1069
1070 Input:
1071 HNDLE handle Handle or key to the shared memory, must
1072 be obtained with ss_shm_open
1073 size_t shm_size Size of shared memory shm_size returned by ss_shm_open()
1074
1075 Output:
1076 void *adr Address of opened shared memory
1077
1078 Function value:
1079 SS_SUCCESS Successful completion
1080 SS_NO_MEMORY Memory mapping failed
1081
1082\********************************************************************/
1083{
1084 if (shm_trace)
1085 printf("ss_shm_unprotect() handle %d, adr %p, size %.0f, read %d, write %d, caller %s\n", handle, *adr, (double)shm_size, read, write, caller_name);
1086
1087#ifdef OS_WINNT
1088
1089 *adr = MapViewOfFile((HANDLE) handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
1090
1091 if (*adr == NULL) {
1092 cm_msg(MERROR, "ss_shm_unprotect", "MapViewOfFile() failed");
1093 return SS_NO_MEMORY;
1094 }
1095#endif /* OS_WINNT */
1096#ifdef OS_UNIX
1097
1098 if (use_sysv_shm) {
1099
1100 *adr = shmat(handle, 0, 0);
1101
1102 if ((*adr) == (void *) (-1)) {
1103 cm_msg(MERROR, "ss_shm_unprotect", "shmat() failed, errno = %d", errno);
1104 return SS_NO_MEMORY;
1105 }
1106 }
1107
1108 if (use_mmap_shm || use_posix_shm) {
1109 assert(shm_size > 0);
1110
1111 int mode = 0;
1112 if (read)
1113 mode |= PROT_READ;
1114 if (write)
1116
1117 int ret = mprotect(*adr, shm_size, mode);
1118 if (ret != 0) {
1119 cm_msg(MERROR, "ss_shm_unprotect", "Cannot mprotect(%d): return value %d, errno %d (%s)", mode, ret, errno, strerror(errno));
1120 return SS_INVALID_ADDRESS;
1121 }
1122 }
1123
1124#endif // OS_UNIX
1125
1126 return SS_SUCCESS;
1127}
1128
1129/*------------------------------------------------------------------*/
1130
1131typedef struct {
1132 std::string file_name;
1133 int fd;
1134 void *buf;
1135 int size;
1136} FL_PARAM;
1137
1139{
1140 FL_PARAM *param = (FL_PARAM *)p;
1141
1142 //fprintf(stderr, "flush start!\n");
1143
1144 uint32_t start = ss_time();
1145
1146 /* write shared memory to file */
1147 ssize_t wr = write(param->fd, param->buf, param->size);
1148 if ((size_t)wr != (size_t)param->size) {
1149 cm_msg(MERROR, "ss_shm_flush", "Cannot write to file \'%s\', write() returned %d instead of %d, errno %d (%s)",
1150 param->file_name.c_str(), (int)wr, (int)param->size, errno, strerror(errno));
1151 close(param->fd);
1152 free(param->buf);
1153 param->buf = nullptr;
1154 return -1;
1155 }
1156
1157 int ret = close(param->fd);
1158 if (ret < 0) {
1159 cm_msg(MERROR, "ss_shm_flush", "Cannot write to file \'%s\', close() errno %d (%s)",
1160 param->file_name.c_str(), errno, strerror(errno));
1161 free(param->buf);
1162 param->buf = nullptr;
1163 return -1;
1164 }
1165
1166 free(param->buf);
1167 param->buf = nullptr;
1168
1169 if (ss_time() - start > 4)
1170 cm_msg(MINFO, "ss_shm_flush", "Flushing shared memory took %d seconds", ss_time() - start);
1171
1172 //fprintf(stderr, "flush end!\n");
1173
1174 return 0;
1175}
1176
1177
1178INT ss_shm_flush(const char *name, const void *adr, size_t size, HNDLE handle, bool wait_for_thread)
1179/********************************************************************\
1180
1181 Routine: ss_shm_flush
1182
1183 Purpose: Flush a shared memory region to its disk file.
1184
1185 Input:
1186 char *name Name of the shared memory
1187 void *adr Base address of shared memory
1188 INT size Size of shared memeory
1189 HNDLE handle Handle of shared memory
1190
1191 Output:
1192 none
1193
1194 Function value:
1195 SS_SUCCESS Successful completion
1196 SS_INVALID_ADDRESS Invalid base address
1197
1198\********************************************************************/
1199{
1200 std::string mem_name;
1201 std::string file_name;
1202 std::string shm_name;
1203
1205
1206 if (shm_trace)
1207 printf("ss_shm_flush(\"%s\",%p,%.0f,%d), file_name [%s]\n", name, adr, (double)size, handle, file_name.c_str());
1208
1209#ifdef OS_WINNT
1210
1211 if (!FlushViewOfFile(adr, size))
1212 return SS_INVALID_ADDRESS;
1213
1214 return SS_SUCCESS;
1215
1216#endif /* OS_WINNT */
1217#ifdef OS_VMS
1218
1219 return SS_SUCCESS;
1220
1221#endif /* OS_VMS */
1222#ifdef OS_UNIX
1223
1224 if (use_sysv_shm || use_posix_shm) {
1225
1226 assert(size > 0);
1227
1228 int fd = open(file_name.c_str(), O_RDWR | O_CREAT, 0777);
1229 if (fd < 0) {
1230 cm_msg(MERROR, "ss_shm_flush", "Cannot write to file \'%s\', fopen() errno %d (%s)", file_name.c_str(), errno, strerror(errno));
1231 return SS_NO_MEMORY;
1232 }
1233
1234 /* try to make a copy of the shared memory */
1235 void *buffer = malloc(size);
1236 if (buffer != nullptr) {
1237 memcpy(buffer, adr, size);
1238 static std::thread* thread = NULL; // THIS IS NOT THREAD SAFE!
1239 if (thread) { // reap the long finished thread from the previous flush
1240 thread->join();
1241 delete thread;
1242 thread = NULL;
1243 }
1244 static FL_PARAM param; // this is safe, thread is no longer running. K.O.
1246 param.fd = fd;
1247 param.buf = buffer;
1248 param.size = size;
1249
1250 thread = new std::thread(ss_shm_flush_thread, &param);
1251
1252 if (wait_for_thread) {
1253 //fprintf(stderr, "waiting for flush thread!\n");
1254 thread->join();
1255 delete thread;
1256 thread = NULL;
1257 //fprintf(stderr, "thread joined!\n");
1258 }
1259
1260 // buffer gets freed in ss_shm_flush_thread, so we don't have to free() it here...
1261 } else {
1262
1263 /* not enough memory for ODB copy buffer, so write directly */
1264 uint32_t start = ss_time();
1265 ssize_t wr = write(fd, adr, size);
1266 if ((size_t)wr != size) {
1267 cm_msg(MERROR, "ss_shm_flush", "Cannot write to file \'%s\', write() returned %d instead of %d, errno %d (%s)", file_name.c_str(), (int)wr, (int)size, errno, strerror(errno));
1268 close(fd);
1269 return SS_NO_MEMORY;
1270 }
1271
1272 int ret = close(fd);
1273 if (ret < 0) {
1274 cm_msg(MERROR, "ss_shm_flush", "Cannot write to file \'%s\', close() errno %d (%s)",
1275 file_name.c_str(), errno, strerror(errno));
1276 return SS_NO_MEMORY;
1277 }
1278
1279 if (ss_time() - start > 4)
1280 cm_msg(MINFO, "ss_shm_flush", "Flushing shared memory took %d seconds", ss_time() - start);
1281
1282 }
1283
1284 return SS_SUCCESS;
1285 }
1286
1287 if (use_mmap_shm) {
1288
1289 assert(size > 0);
1290
1291 if (shm_trace)
1292 printf("ss_shm_flush(\"%s\") size %.0f, mmap file_name [%s]\n", name, (double)size, file_name.c_str());
1293
1294 int ret = msync((void *)adr, size, MS_ASYNC);
1295 if (ret != 0) {
1296 cm_msg(MERROR, "ss_shm_flush", "Cannot msync(MS_ASYNC): return value %d, errno %d (%s)", ret, errno, strerror(errno));
1297 return SS_INVALID_ADDRESS;
1298 }
1299 return SS_SUCCESS;
1300 }
1301
1302
1303#endif // OS_UNIX
1304
1305 return SS_SUCCESS;
1306}
1307
1308#endif /* LOCAL_ROUTINES */
1309
1310/*------------------------------------------------------------------*/
1311static struct {
1312 char c;
1313 double d;
1315
1316static struct {
1317 double d;
1318 char c;
1320
1322/********************************************************************\
1323
1324 Routine: ss_get_struct_align
1325
1326 Purpose: Returns compiler alignment of structures. In C, structures
1327 can be byte aligned, word or even quadword aligned. This
1328 can usually be set with compiler switches. This routine
1329 tests this alignment during runtime and returns 1 for
1330 byte alignment, 2 for word alignment, 4 for dword alignment
1331 and 8 for quadword alignment.
1332
1333 Input:
1334 <none>
1335
1336 Output:
1337 <none>
1338
1339 Function value:
1340 INT Structure alignment
1341
1342\********************************************************************/
1343{
1344 return (POINTER_T) (&test_align.d) - (POINTER_T) & test_align.c;
1345}
1346
1348/********************************************************************\
1349
1350 Routine: ss_get_struct_padding
1351
1352 Purpose: Returns compiler padding of structures. Under some C
1353 compilers and architectures, C structures can be padded at the
1354 end to have a size of muliples of 4 or 8. This routine returns
1355 this number, like 8 if all structures are padded with 0-7 bytes
1356 to lie on an 8 byte boundary.
1357
1358 Input:
1359 <none>
1360
1361 Output:
1362 <none>
1363
1364 Function value:
1365 INT Structure alignment
1366
1367 \********************************************************************/
1368{
1369 return (INT) sizeof(test_padding) - 8;
1370}
1371
1372/********************************************************************\
1373* *
1374* Process functions *
1375* *
1376\********************************************************************/
1377
1378/*------------------------------------------------------------------*/
1380/********************************************************************\
1381
1382 Routine: ss_getpid
1383
1384 Purpose: Return process ID of current process
1385
1386 Input:
1387 none
1388
1389 Output:
1390 none
1391
1392 Function value:
1393 INT Process ID
1394
1395\********************************************************************/
1396{
1397#ifdef OS_WINNT
1398
1399 return (int) GetCurrentProcessId();
1400
1401#endif /* OS_WINNT */
1402#ifdef OS_VMS
1403
1404 return getpid();
1405
1406#endif /* OS_VMS */
1407#ifdef OS_UNIX
1408
1409 return getpid();
1410
1411#endif /* OS_UNIX */
1412#ifdef OS_VXWORKS
1413
1414 return 0;
1415
1416#endif /* OS_VXWORKS */
1417#ifdef OS_MSDOS
1418
1419 return 0;
1420
1421#endif /* OS_MSDOS */
1422}
1423
1424#ifdef LOCAL_ROUTINES
1425
1426/******************************************************************** \
1427
1428 Routine: ss_pid_exists
1429
1430 Purpose: Check if given pid still exists
1431
1432 Input:
1433 pid - process id returned by ss_getpid()
1434
1435 Output:
1436 none
1437
1438 Function value:
1439 BOOL TRUE or FALSE
1440
1441\********************************************************************/
1443{
1444#ifdef ESRCH
1445 /* Only enable this for systems that define ESRCH and hope that they also support kill(pid,0) */
1446 int status = kill(pid, 0);
1447 //printf("kill(%d,0) returned %d, errno %d\n", pid, status, errno);
1448 if ((status != 0) && (errno == ESRCH)) {
1449 return FALSE;
1450 }
1451#else
1452#warning Missing ESRCH for ss_pid_exists()
1453#endif
1454 return TRUE;
1455}
1456
1457/********************************************************************\
1458
1459 Routine: ss_kill
1460
1461 Purpose: Kill given process, ensure it is not running anymore
1462
1463 Input:
1464 pid - process id returned by ss_getpid()
1465
1466 Output:
1467 none
1468
1469 Function value:
1470 void - none
1471
1472\********************************************************************/
1473void ss_kill(int pid)
1474{
1475#ifdef SIGKILL
1476 kill(pid, SIGKILL);
1477#else
1478#warning Missing SIGKILL for ss_kill()
1479#endif
1480}
1481
1482#endif // LOCAL_ROUTINES
1483
1484/*------------------------------------------------------------------*/
1485
1486#if defined(OS_DARWIN)
1487#include <mach-o/dyld.h>
1488#endif
1489
1490std::string ss_get_executable(void)
1491/********************************************************************\
1492
1493 Routine: ss_get_executable()
1494
1495 Purpose: Return full path of current executable
1496
1497 Function value:
1498 std::string Name of executable
1499
1500\********************************************************************/
1501{
1502 char path[PATH_MAX];
1503
1504#if defined(OS_DARWIN)
1505 uint32_t size = sizeof(path);
1506 if (_NSGetExecutablePath(path, &size) == 0)
1507 return path;
1508#elif defined(OS_LINUX)
1509
1510 ssize_t count = readlink("/proc/self/exe", path, PATH_MAX);
1511 if (count != -1) {
1512 path[count] = '\0'; // Null-terminate the string
1513 return std::string(path);
1514 }
1515#endif
1516 return "";
1517}
1518
1519std::string ss_get_cmdline(void)
1520/********************************************************************\
1521
1522 Routine: ss_get_cmdline()
1523
1524 Purpose: Return command line for current executable
1525
1526 Function value:
1527 std::string Command line
1528
1529\********************************************************************/
1530{
1531#if defined(OS_DARWIN)
1532 int mib[3] = {CTL_KERN, KERN_PROCARGS2, getpid()};
1533 size_t len;
1534
1535 if (sysctl(mib, 3, nullptr, &len, nullptr, 0) == -1) {
1536 perror("sysctl (size)");
1537 return {};
1538 }
1539
1540 std::vector<char> buf(len);
1541 if (sysctl(mib, 3, buf.data(), &len, nullptr, 0) == -1) {
1542 perror("sysctl (data)");
1543 return {};
1544 }
1545
1546 int argc = *reinterpret_cast<int*>(buf.data());
1547 char* ptr = buf.data() + sizeof(int);
1548 char* end = buf.data() + len;
1549
1550 // Skip the executable path
1551 while (ptr < end && *ptr != '\0') {
1552 ptr++;
1553 }
1554 // Skip over any trailing NULs until the real argv[1] begins
1555 while (ptr < end && *ptr == '\0') {
1556 ptr++;
1557 }
1558
1559 std::string result;
1560 for (int i = 1; i <= argc && ptr < end; i++) {
1561 std::string s(ptr);
1562 if (!result.empty()) result += " ";
1563 result += s;
1564 ptr += s.size() + 1;
1565 }
1566
1567 return result;
1568#elif defined(OS_LINUX)
1569 std::ifstream in("/proc/self/cmdline", std::ios::binary);
1570 if (!in)
1571 return {};
1572
1573 std::string data((std::istreambuf_iterator<char>(in)),
1574 std::istreambuf_iterator<char>());
1575 if (data.empty())
1576 return {};
1577
1578 // Replace NULs with spaces and trim a trailing space if present
1579 for (char &c : data)
1580 if (c == '\0')
1581 c = ' ';
1582 if (!data.empty() && data.back() == ' ')
1583 data.pop_back();
1584 return data;
1585#endif
1586 return {};
1587}
1588
1589/*------------------------------------------------------------------*/
1590
1592/********************************************************************\
1593
1594 Routine: ss_gettid
1595
1596 Purpose: Return thread ID of current thread
1597
1598 Input:
1599 none
1600
1601 Output:
1602 none
1603
1604 Function value:
1605 INT thread ID
1606
1607\********************************************************************/
1608{
1609#if defined OS_MSDOS
1610
1611 return 0;
1612
1613#elif defined OS_WINNT
1614
1615 return GetCurrentThreadId();
1616
1617#elif defined OS_VMS
1618
1619 return ss_getpid();
1620
1621#elif defined OS_DARWIN
1622
1623 return pthread_self();
1624
1625#elif defined OS_CYGWIN
1626
1627 return pthread_self();
1628
1629#elif defined OS_UNIX
1630
1631 return pthread_self();
1632 //return syscall(SYS_gettid);
1633
1634#elif defined OS_VXWORKS
1635
1636 return ss_getpid();
1637
1638#else
1639#error Do not know how to do ss_gettid()
1640#endif
1641}
1642
1643std::string ss_tid_to_string(midas_thread_t thread_id)
1644{
1645#if defined OS_MSDOS
1646
1647 return "0";
1648
1649#elif defined OS_WINNT
1650
1651#error Do not know how to do ss_tid_to_string()
1652 return "???";
1653
1654#elif defined OS_VMS
1655
1656 char buf[256];
1657 sprintf(buf, "%d", thread_id);
1658 return buf;
1659
1660#elif defined OS_DARWIN
1661
1662 char buf[256];
1663 sprintf(buf, "%p", thread_id);
1664 return buf;
1665
1666#elif defined OS_CYGWIN
1667
1668 char buf[256];
1669 sprintf(buf, "%p", thread_id);
1670 return buf;
1671
1672#elif defined OS_UNIX
1673
1674 char buf[256];
1675 sprintf(buf, "%lu", thread_id);
1676 return buf;
1677
1678#elif defined OS_VXWORKS
1679
1680 char buf[256];
1681 sprintf(buf, "%d", thread_id);
1682 return buf;
1683
1684#else
1685#error Do not know how to do ss_tid_to_string()
1686#endif
1687}
1688
1689/*------------------------------------------------------------------*/
1690
1691#ifdef OS_UNIX
1692void catch_sigchld(int signo)
1693{
1694 int status;
1695
1696 status = signo; /* avoid compiler warning */
1697 wait(&status);
1698 return;
1699}
1700#endif
1701
1702INT ss_spawnv(INT mode, const char *cmdname, const char* const argv[])
1703/********************************************************************\
1704
1705 Routine: ss_spawnv
1706
1707 Purpose: Spawn a subprocess or detached process
1708
1709 Input:
1710 INT mode One of the following modes:
1711 P_WAIT Wait for the subprocess to compl.
1712 P_NOWAIT Don't wait for subprocess to compl.
1713 P_DETACH Create detached process.
1714 char cmdname Program name to execute
1715 char *argv[] Optional program arguments
1716
1717 Output:
1718 none
1719
1720 Function value:
1721 SS_SUCCESS Successful completeion
1722 SS_INVALID_NAME Command could not be executed;
1723
1724\********************************************************************/
1725{
1726#ifdef OS_WINNT
1727
1728 if (spawnvp(mode, cmdname, argv) < 0)
1729 return SS_INVALID_NAME;
1730
1731 return SS_SUCCESS;
1732
1733#endif /* OS_WINNT */
1734
1735#ifdef OS_MSDOS
1736
1737 spawnvp((int) mode, cmdname, argv);
1738
1739 return SS_SUCCESS;
1740
1741#endif /* OS_MSDOS */
1742
1743#ifdef OS_VMS
1744
1745 {
1746 char cmdstring[500], *pc;
1747 INT i, flags, status;
1749
1750 $DESCRIPTOR(cmdstring_dsc, "dummy");
1751
1752 if (mode & P_DETACH) {
1753 cmdstring_dsc.dsc$w_length = strlen(cmdstring);
1754 cmdstring_dsc.dsc$a_pointer = cmdstring;
1755
1756 status = sys$creprc(0, &cmdstring_dsc, 0, 0, 0, 0, 0, NULL, 4, 0, 0, PRC$M_DETACH);
1757 } else {
1758 flags = (mode & P_NOWAIT) ? 1 : 0;
1759
1760 for (pc = argv[0] + strlen(argv[0]); *pc != ']' && pc != argv[0]; pc--);
1761 if (*pc == ']')
1762 pc++;
1763
1764 strcpy(cmdstring, pc);
1765
1766 if (strchr(cmdstring, ';'))
1767 *strchr(cmdstring, ';') = 0;
1768
1769 strcat(cmdstring, " ");
1770
1771 for (i = 1; argv[i] != NULL; i++) {
1773 strcat(cmdstring, " ");
1774 }
1775
1776 cmdstring_dsc.dsc$w_length = strlen(cmdstring);
1777 cmdstring_dsc.dsc$a_pointer = cmdstring;
1778
1779 status = lib$spawn(&cmdstring_dsc, 0, 0, &flags, NULL, 0, 0, 0, 0, 0, 0, 0, 0);
1780 }
1781
1782 return BM_SUCCESS;
1783 }
1784
1785#endif /* OS_VMS */
1786#ifdef OS_UNIX
1788
1789#ifdef OS_ULTRIX
1790 union wait *status;
1791#else
1792 int status;
1793#endif
1794
1795#ifdef NO_FORK
1796 assert(!"support for fork() disabled by NO_FORK");
1797#else
1798 if ((child_pid = fork()) < 0)
1799 return (-1);
1800#endif
1801
1802 if (child_pid == 0) {
1803 /* now we are in the child process ... */
1804 int error = execvp(cmdname, (char*const*)argv);
1805 fprintf(stderr, "ss_spawnv: Cannot execute command \"%s\": execvp() returned %d, errno %d (%s), aborting!\n", cmdname, error, errno, strerror(errno));
1806 // NB: this is the forked() process, if it returns back to the caller, we will have
1807 // a duplicate process for whoever called us. Very bad! So must abort. K.O.
1808 abort();
1809 // NOT REACHED
1810 return SS_SUCCESS;
1811 } else {
1812 /* still in parent process */
1813 if (mode == P_WAIT) {
1814#ifdef OS_ULTRIX
1816#else
1818#endif
1819
1820 } else {
1821 /* catch SIGCHLD signal to avoid <defunc> processes */
1823 }
1824 }
1825
1826 return SS_SUCCESS;
1827
1828#endif /* OS_UNIX */
1829}
1830
1831/*------------------------------------------------------------------*/
1832INT ss_shell(int sock)
1833/********************************************************************\
1834
1835 Routine: ss_shell
1836
1837 Purpose: Execute shell via socket (like telnetd)
1838
1839 Input:
1840 int sock Socket
1841
1842 Output:
1843 none
1844
1845 Function value:
1846 SS_SUCCESS Successful completeion
1847
1848\********************************************************************/
1849{
1850#ifdef OS_WINNT
1851
1854
1858 char buffer[256], cmd[256];
1861 struct timeval timeout;
1862
1863 /* Set the bInheritHandle flag so pipe handles are inherited. */
1864 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
1865 saAttr.bInheritHandle = TRUE;
1866 saAttr.lpSecurityDescriptor = NULL;
1867
1868 /* Save the handle to the current STDOUT. */
1870
1871 /* Create a pipe for the child's STDOUT. */
1873 return 0;
1874
1875 /* Set a write handle to the pipe to be STDOUT. */
1877 return 0;
1878
1879
1880 /* Save the handle to the current STDERR. */
1882
1883 /* Create a pipe for the child's STDERR. */
1885 return 0;
1886
1887 /* Set a read handle to the pipe to be STDERR. */
1889 return 0;
1890
1891
1892 /* Save the handle to the current STDIN. */
1894
1895 /* Create a pipe for the child's STDIN. */
1897 return 0;
1898
1899 /* Set a read handle to the pipe to be STDIN. */
1901 return 0;
1902
1903 /* Duplicate the write handle to the pipe so it is not inherited. */
1906 return 0;
1907
1909
1910 /* Now create the child process. */
1911 memset(&siStartInfo, 0, sizeof(siStartInfo));
1912 siStartInfo.cb = sizeof(STARTUPINFO);
1913 siStartInfo.lpReserved = NULL;
1914 siStartInfo.lpReserved2 = NULL;
1915 siStartInfo.cbReserved2 = 0;
1916 siStartInfo.lpDesktop = NULL;
1917 siStartInfo.dwFlags = 0;
1918
1919 if (!CreateProcess(NULL, "cmd /Q", /* command line */
1920 NULL, /* process security attributes */
1921 NULL, /* primary thread security attributes */
1922 TRUE, /* handles are inherited */
1923 0, /* creation flags */
1924 NULL, /* use parent's environment */
1925 NULL, /* use parent's current directory */
1926 &siStartInfo, /* STARTUPINFO pointer */
1927 &piProcInfo)) /* receives PROCESS_INFORMATION */
1928 return 0;
1929
1930 /* After process creation, restore the saved STDIN and STDOUT. */
1934
1935 i_cmd = 0;
1936
1937 do {
1938 /* query stderr */
1939 do {
1940 if (!PeekNamedPipe(hChildStderrRd, buffer, 256, &dwRead, &dwAvail, NULL))
1941 break;
1942
1943 if (dwRead > 0) {
1944 ReadFile(hChildStderrRd, buffer, 256, &dwRead, NULL);
1945 send(sock, buffer, dwRead, 0);
1946 }
1947 } while (dwAvail > 0);
1948
1949 /* query stdout */
1950 do {
1951 if (!PeekNamedPipe(hChildStdoutRd, buffer, 256, &dwRead, &dwAvail, NULL))
1952 break;
1953 if (dwRead > 0) {
1954 ReadFile(hChildStdoutRd, buffer, 256, &dwRead, NULL);
1955 send(sock, buffer, dwRead, 0);
1956 }
1957 } while (dwAvail > 0);
1958
1959
1960 /* check if subprocess still alive */
1961 if (!GetExitCodeProcess(piProcInfo.hProcess, &i))
1962 break;
1963 if (i != STILL_ACTIVE)
1964 break;
1965
1966 /* query network socket */
1967 FD_ZERO(&readfds);
1968 FD_SET(sock, &readfds);
1969 timeout.tv_sec = 0;
1970 timeout.tv_usec = 100;
1971 select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
1972
1973 if (FD_ISSET(sock, &readfds)) {
1974 i = recv(sock, cmd + i_cmd, 1, 0);
1975 if (i <= 0)
1976 break;
1977
1978 /* backspace */
1979 if (cmd[i_cmd] == 8) {
1980 if (i_cmd > 0) {
1981 send(sock, "\b \b", 3, 0);
1982 i_cmd -= 1;
1983 }
1984 } else if (cmd[i_cmd] >= ' ' || cmd[i_cmd] == 13 || cmd[i_cmd] == 10) {
1985 send(sock, cmd + i_cmd, 1, 0);
1986 i_cmd += i;
1987 }
1988 }
1989
1990 /* linefeed triggers new command */
1991 if (cmd[i_cmd - 1] == 10) {
1993 i_cmd = 0;
1994 }
1995
1996 } while (TRUE);
1997
2002
2003 return SS_SUCCESS;
2004
2005#endif /* OS_WINNT */
2006
2007#ifdef OS_UNIX
2008#ifndef NO_PTY
2009 pid_t pid;
2010 int i, p;
2011 char line[32], buffer[1024], shell[32];
2013
2014#ifdef NO_FORK
2015 assert(!"support for forkpty() disabled by NO_FORK");
2016#else
2017 pid = forkpty(&p, line, NULL, NULL);
2018#endif
2019 if (pid < 0)
2020 return 0;
2021 else if (pid > 0) {
2022 /* parent process */
2023
2024 do {
2025 FD_ZERO(&readfds);
2026 FD_SET(sock, &readfds);
2027 FD_SET(p, &readfds);
2028
2030
2031 if (FD_ISSET(sock, &readfds)) {
2032 memset(buffer, 0, sizeof(buffer));
2033 i = recv(sock, buffer, sizeof(buffer), 0);
2034 if (i <= 0)
2035 break;
2036 if (write(p, buffer, i) != i)
2037 break;
2038 }
2039
2040 if (FD_ISSET(p, &readfds)) {
2041 memset(buffer, 0, sizeof(buffer));
2042 i = read(p, buffer, sizeof(buffer));
2043 if (i <= 0)
2044 break;
2045 send(sock, buffer, i, 0);
2046 }
2047
2048 } while (1);
2049 } else {
2050 /* child process */
2051
2052 if (getenv("SHELL"))
2053 mstrlcpy(shell, getenv("SHELL"), sizeof(shell));
2054 else
2055 strcpy(shell, "/bin/sh");
2056 int error = execl(shell, shell, NULL);
2057 // NB: execl() does not return unless there is an error.
2058 fprintf(stderr, "ss_shell: Cannot execute command \"%s\": execl() returned %d, errno %d (%s), aborting!\n", shell, error, errno, strerror(errno));
2059 abort();
2060 }
2061#else
2062 send(sock, "not implemented\n", 17, 0);
2063#endif /* NO_PTY */
2064
2065 return SS_SUCCESS;
2066
2067#endif /* OS_UNIX */
2068}
2069
2070/*------------------------------------------------------------------*/
2072
2074/********************************************************************\
2075
2076 Routine: ss_daemon_init
2077
2078 Purpose: Become a daemon
2079
2080 Input:
2081 none
2082
2083 Output:
2084 none
2085
2086 Function value:
2087 SS_SUCCESS Successful completeion
2088 SS_ABORT fork() was not successful, or other problem
2089
2090\********************************************************************/
2091{
2092#ifdef OS_UNIX
2093
2094 /* only implemented for UNIX */
2095 int i, fd, pid;
2096
2097#ifdef NO_FORK
2098 assert(!"support for fork() disabled by NO_FORK");
2099#else
2100 if ((pid = fork()) < 0)
2101 return SS_ABORT;
2102 else if (pid != 0)
2103 exit(0); /* parent finished */
2104#endif
2105
2106 /* child continues here */
2107
2109
2110 /* try and use up stdin, stdout and stderr, so other
2111 routines writing to stdout etc won't cause havoc. Copied from smbd */
2112 for (i = 0; i < 3; i++) {
2113 if (keep_stdout && ((i == 1) || (i == 2)))
2114 continue;
2115
2116 close(i);
2117 fd = open("/dev/null", O_RDWR, 0);
2118 if (fd < 0)
2119 fd = open("/dev/null", O_WRONLY, 0);
2120 if (fd < 0) {
2121 cm_msg(MERROR, "ss_daemon_init", "Can't open /dev/null");
2122 return SS_ABORT;
2123 }
2124 if (fd != i) {
2125 cm_msg(MERROR, "ss_daemon_init", "Did not get file descriptor");
2126 return SS_ABORT;
2127 }
2128 }
2129
2130 setsid(); /* become session leader */
2131
2132#endif
2133
2134 return SS_SUCCESS;
2135}
2136
2137#ifdef LOCAL_ROUTINES
2138
2139/*------------------------------------------------------------------*/
2141/********************************************************************\
2142
2143 Routine: ss_existpid
2144
2145 Purpose: Execute a Kill sig=0 which return success if pid found.
2146
2147 Input:
2148 pid : pid to check
2149
2150 Output:
2151 none
2152
2153 Function value:
2154 TRUE PID found
2155 FALSE PID not found
2156
2157\********************************************************************/
2158{
2159#ifdef OS_UNIX
2160 /* only implemented for UNIX */
2161 return (kill(pid, 0) == 0 ? TRUE : FALSE);
2162#else
2163 cm_msg(MINFO, "ss_existpid", "implemented for UNIX only");
2164 return FALSE;
2165#endif
2166}
2167
2168#endif // LOCAL_ROUTINES
2169
2170/********************************************************************/
2188INT ss_system(const char *command)
2189{
2190#ifdef OS_UNIX
2191 INT childpid;
2192
2193 return ss_exec(command, &childpid);
2194
2195#else
2196
2197 system(command);
2198 return SS_SUCCESS;
2199
2200#endif
2201}
2202
2203/*------------------------------------------------------------------*/
2204INT ss_exec(const char *command, INT * pid)
2205/********************************************************************\
2206
2207 Routine: ss_exec
2208
2209 Purpose: Execute command in a separate process, close all open
2210 file descriptors, return the pid of the child process.
2211
2212 Input:
2213 char * command Command to execute
2214 INT * pid Returned PID of the spawned process.
2215 Output:
2216 none
2217
2218 Function value:
2219 SS_SUCCESS Successful completion
2220 SS_ABORT fork() was not successful, or other problem
2221
2222\********************************************************************/
2223{
2224#ifdef OS_UNIX
2225
2226 /* only implemented for UNIX */
2227 int i, fd;
2228
2229#ifdef NO_FORK
2230 assert(!"support for fork() disabled by NO_FORK");
2231#else
2232 *pid = fork();
2233#endif
2234 if (*pid < 0)
2235 return SS_ABORT;
2236 else if (*pid != 0) {
2237 /* avoid <defunc> parent processes */
2239 return SS_SUCCESS; /* parent returns */
2240 }
2241
2242 /* child continues here... */
2243
2244 /* close all open file descriptors */
2245 for (i = 0; i < 256; i++)
2246 close(i);
2247
2248 /* try and use up stdin, stdout and stderr, so other
2249 routines writing to stdout etc won't cause havoc */
2250 for (i = 0; i < 3; i++) {
2251 fd = open("/dev/null", O_RDWR, 0);
2252 if (fd < 0)
2253 fd = open("/dev/null", O_WRONLY, 0);
2254 if (fd < 0) {
2255 cm_msg(MERROR, "ss_exec", "Can't open /dev/null");
2256 return SS_ABORT;
2257 }
2258 if (fd != i) {
2259 cm_msg(MERROR, "ss_exec", "Did not get file descriptor");
2260 return SS_ABORT;
2261 }
2262 }
2263
2264 setsid(); /* become session leader */
2265 /* chdir("/"); *//* change working directory (not on NFS!) */
2266
2267 /* execute command */
2268 int error = execl("/bin/sh", "sh", "-c", command, NULL);
2269 // NB: execl() does not return unless there is an error. K.O.
2270 fprintf(stderr, "ss_shell: Cannot execute /bin/sh for command \"%s\": execl() returned %d, errno %d (%s), aborting!\n", command, error, errno, strerror(errno));
2271 abort();
2272
2273#else
2274
2275 system(command);
2276
2277#endif
2278
2279 return SS_SUCCESS;
2280}
2281
2282/*------------------------------------------------------------------*/
2283
2284std::string ss_replace_env_variables(const std::string& inputPath) {
2285 std::string result;
2286 size_t startPos = 0;
2287 size_t dollarPos;
2288
2289 while ((dollarPos = inputPath.find('$', startPos)) != std::string::npos) {
2290 result.append(inputPath, startPos, dollarPos - startPos);
2291
2292 size_t varEndPos = inputPath.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", dollarPos + 1);
2293 size_t varLength = varEndPos - dollarPos - 1;
2294 std::string varName = inputPath.substr(dollarPos + 1, varLength);
2295
2296 char* varValue = std::getenv(varName.c_str());
2297 if (varValue != nullptr) {
2298 result.append(varValue);
2299 }
2300
2302 }
2303
2304 result.append(inputPath.c_str(), startPos, std::string::npos);
2305 return result;
2306}
2307
2308/*------------------------------------------------------------------*/
2309std::string ss_execs(const char *cmd)
2310/********************************************************************\
2311
2312 Routine: ss_execs
2313
2314 Purpose: Execute shell command and return result in a string
2315
2316 Input:
2317 const char *command Command to execute
2318
2319
2320 Function value:
2321 std::string Result of shell commaand
2322
2323\********************************************************************/
2324{
2325#ifdef OS_UNIX
2326 std::array<char, 256> buffer{};
2327 std::string result;
2328 auto pclose_deleter = [](FILE* f) { pclose(f); };
2329 auto pipe = std::unique_ptr<FILE, decltype(pclose_deleter)>(
2330 popen(cmd, "r"),
2332 );
2333
2334 if (!pipe) {
2335 throw std::runtime_error("popen() failed!");
2336 }
2337
2338 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
2339 result += buffer.data();
2340 }
2341
2342 return result;
2343#else
2344 fprintf(stderr, "ss_execs: Function not supported on this OS, aborting!\n");
2345 abort();
2346#endif
2347}
2348
2349/********************************************************************/
2383{
2384#if defined(OS_WINNT)
2385
2386 HANDLE status;
2387 DWORD thread_id;
2388
2389 if (thread_func == NULL) {
2390 return 0;
2391 }
2392
2394
2395 return status == NULL ? 0 : (midas_thread_t) thread_id;
2396
2397#elif defined(OS_MSDOS)
2398
2399 return 0;
2400
2401#elif defined(OS_VMS)
2402
2403 return 0;
2404
2405#elif defined(OS_VXWORKS)
2406
2407/* taskSpawn which could be considered as a thread under VxWorks
2408 requires several argument beside the thread args
2409 taskSpawn (taskname, priority, option, stacksize, entry_point
2410 , arg1, arg2, ... , arg9, arg10)
2411 all the arg will have to be retrieved from the param list.
2412 through a structure to be simpler */
2413
2414 INT status;
2416
2417 ts = (VX_TASK_SPAWN *) param;
2418 status =
2419 taskSpawn(ts->name, ts->priority, ts->options, ts->stackSize,
2420 (FUNCPTR) thread_func, ts->arg1, ts->arg2, ts->arg3,
2421 ts->arg4, ts->arg5, ts->arg6, ts->arg7, ts->arg8, ts->arg9, ts->arg10);
2422
2423 return status == ERROR ? 0 : status;
2424
2425#elif defined(OS_UNIX)
2426
2427 INT status;
2428 pthread_t thread_id;
2429
2430 status = pthread_create(&thread_id, NULL, (void* (*)(void*))thread_func, param);
2431
2432 return status != 0 ? 0 : thread_id;
2433
2434#endif
2435}
2436
2437/********************************************************************/
2456{
2457#if defined(OS_WINNT)
2458
2459 DWORD status;
2460 HANDLE th;
2461
2462 th = OpenThread(THREAD_TERMINATE, FALSE, (DWORD)thread_id);
2463 if (th == 0)
2464 status = GetLastError();
2465
2467
2468 if (status == 0)
2469 status = GetLastError();
2470
2471 return status != 0 ? SS_SUCCESS : SS_NO_THREAD;
2472
2473#elif defined(OS_MSDOS)
2474
2475 return 0;
2476
2477#elif defined(OS_VMS)
2478
2479 return 0;
2480
2481#elif defined(OS_VXWORKS)
2482
2483 INT status;
2484 status = taskDelete(thread_id);
2485 return status == OK ? 0 : ERROR;
2486
2487#elif defined(OS_UNIX)
2488
2489 INT status;
2490 status = pthread_kill(thread_id, SIGKILL);
2491 return status == 0 ? SS_SUCCESS : SS_NO_THREAD;
2492
2493#endif
2494}
2495
2496/*------------------------------------------------------------------*/
2497
2499{
2500#if defined(OS_DARWIN)
2501
2502 pthread_setname_np(name.c_str());
2503 return SS_SUCCESS;
2504
2505#elif defined(OS_UNIX)
2506
2508 pthread_setname_np(thread, name.c_str());
2509 return SS_SUCCESS;
2510
2511#else
2512 return 0;
2513#endif
2514}
2515
2517{
2518#if defined(OS_UNIX)
2519 char str[256];
2521 pthread_getname_np(thread, str, sizeof(str));
2522 return std::string(str);
2523#else
2524 return "";
2525#endif
2526}
2527
2528/*------------------------------------------------------------------*/
2529static std::atomic_bool s_semaphore_trace{false};
2530static std::atomic_int s_semaphore_nest_level{0}; // must be signed int!
2531
2533/********************************************************************\
2534
2535 Routine: ss_semaphore_create
2536
2537 Purpose: Create a semaphore with a specific name
2538
2539 Remark: Under VxWorks the specific semaphore handling is
2540 different than other OS. But VxWorks provides
2541 the POSIX-compatible semaphore interface.
2542 Under POSIX, no timeout is supported.
2543 So for the time being, we keep the pure VxWorks
2544 The semaphore type is a Binary instead of mutex
2545 as the binary is an optimized mutex.
2546
2547 Input:
2548 char *name Name of the semaphore to create.
2549 Special blank name "" creates a local semaphore for
2550 syncronization between threads in multithreaded applications.
2551
2552 Output:
2553 HNDLE *semaphore_handle Handle of the created semaphore
2554
2555 Function value:
2556 SS_CREATED semaphore was created
2557 SS_SUCCESS semaphore existed already and was attached
2558 SS_NO_SEMAPHORE Cannot create semaphore
2559
2560\********************************************************************/
2561{
2562 char semaphore_name[256];
2563
2564 /* Add a leading MX_ to the semaphore name */
2565 sprintf(semaphore_name, "MX_%s", name);
2566
2567#ifdef OS_VXWORKS
2568
2569 /* semBCreate is a Binary semaphore which is under VxWorks a optimized mutex
2570 refering to the programmer's Guide 5.3.1 */
2572 return SS_NO_MUTEX;
2573 return SS_CREATED;
2574
2575#endif /* OS_VXWORKS */
2576
2577#ifdef OS_WINNT
2578
2580
2581 if (*semaphore_handle == 0)
2582 return SS_NO_SEMAPHORE;
2583
2584 return SS_CREATED;
2585
2586#endif /* OS_WINNT */
2587#ifdef OS_VMS
2588
2589 /* VMS has to use lock manager... */
2590
2591 {
2592 INT status;
2594 semaphorename_dsc.dsc$w_length = strlen(semaphore_name);
2595 semaphorename_dsc.dsc$a_pointer = semaphore_name;
2596
2598
2599 status = sys$enqw(0, LCK$K_NLMODE, *semaphore_handle, 0, &semaphorename_dsc, 0, 0, 0, 0, 0, 0);
2600
2601 if (status != SS$_NORMAL) {
2602 free((void *) *semaphore_handle);
2603 *semaphore_handle = 0;
2604 }
2605
2606 if (*semaphore_handle == 0)
2607 return SS_NO_SEMAPHORE;
2608
2609 return SS_CREATED;
2610 }
2611
2612#endif /* OS_VMS */
2613#ifdef OS_UNIX
2614
2615 {
2617 int status;
2618 struct semid_ds buf;
2619
2620 if (name[0] != 0) {
2621 int fh;
2622 char cwd[256];
2623 std::string file_name;
2624
2625 /* Build the filename out of the path and the name of the semaphore */
2626 std::string path = cm_get_path();
2627 if (path.empty()) {
2628 if (getcwd(cwd, sizeof(cwd)))
2629 path = std::string(cwd);
2630#if defined(OS_VMS)
2631#elif defined(OS_UNIX)
2632 path += "/";
2633#elif defined(OS_WINNT)
2634 path += "\\";
2635#endif
2636 }
2637
2638 file_name = path;
2639 file_name += ".";
2640 file_name += std::string(name);
2641 file_name += ".SHM";
2642
2643 /* create a unique key from the file name */
2644 key = ftok(file_name.c_str(), 'M');
2645 if (key < 0) {
2646 fh = open(file_name.c_str(), O_CREAT, 0644);
2647 close(fh);
2648 key = ftok(file_name.c_str(), 'M');
2650 }
2651 }
2652
2653#if (defined(OS_LINUX) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(OS_CYGWIN)) || defined(OS_FREEBSD)
2654 union semun arg;
2655#else
2656 union semun {
2657 INT val;
2658 struct semid_ds *buf;
2659 ushort *array;
2660 } arg;
2661#endif
2662
2664
2665 /* create or get semaphore */
2666 *semaphore_handle = (HNDLE) semget(key, 1, 0);
2667 //printf("create1 key 0x%x, id %d, errno %d (%s)\n", key, *semaphore_handle, errno, strerror(errno));
2668 if (*semaphore_handle < 0) {
2670 //printf("create2 key 0x%x, id %d, errno %d (%s)\n", key, *semaphore_handle, errno, strerror(errno));
2672 }
2673
2674 if (*semaphore_handle < 0) {
2675 cm_msg(MERROR, "ss_semaphore_create", "Cannot create semaphore \'%s\', semget(0x%x) failed, errno %d (%s)", name, key, errno, strerror(errno));
2676
2677 fprintf(stderr, "ss_semaphore_create: Cannot create semaphore \'%s\', semget(0x%x) failed, errno %d (%s)", name, key, errno, strerror(errno));
2678 abort(); // does not return
2679 return SS_NO_SEMAPHORE;
2680 }
2681
2682 memset(&buf, 0, sizeof(buf));
2683 buf.sem_perm.uid = getuid();
2684 buf.sem_perm.gid = getgid();
2685 buf.sem_perm.mode = 0666;
2686 arg.buf = &buf;
2687
2688 semctl(*semaphore_handle, 0, IPC_SET, arg);
2689
2690 /* if semaphore was created, set value to one */
2691 if (key == IPC_PRIVATE || status == SS_CREATED) {
2692 arg.val = 1;
2693 if (semctl(*semaphore_handle, 0, SETVAL, arg) < 0)
2694 return SS_NO_SEMAPHORE;
2695 }
2696
2697 if (s_semaphore_trace) {
2698 fprintf(stderr, "name %d %d %d %s\n", *semaphore_handle, (int)time(NULL), getpid(), name);
2699 }
2700
2701 return SS_SUCCESS;
2702 }
2703#endif /* OS_UNIX */
2704
2705#ifdef OS_MSDOS
2706 return SS_NO_SEMAPHORE;
2707#endif
2708}
2709
2710/*------------------------------------------------------------------*/
2712/********************************************************************\
2713
2714 Routine: ss_semaphore_wait_for
2715
2716 Purpose: Wait for a semaphore to get owned
2717
2718 Input:
2719 HNDLE *semaphore_handle Handle of the semaphore
2720 DWORD timeout_millisec Timeout in ms, zero for no timeout
2721
2722 Output:
2723 none
2724
2725 Function value:
2726 SS_SUCCESS Successful completion
2727 SS_NO_SEMAPHORE Invalid semaphore handle
2728 SS_TIMEOUT Timeout
2729
2730\********************************************************************/
2731{
2732 INT status;
2733
2734#ifdef OS_WINNT
2735
2737 if (status == WAIT_FAILED)
2738 return SS_NO_SEMAPHORE;
2739 if (status == WAIT_TIMEOUT)
2740 return SS_TIMEOUT;
2741
2742 return SS_SUCCESS;
2743#endif /* OS_WINNT */
2744#ifdef OS_VMS
2745 status = sys$enqw(0, LCK$K_EXMODE, semaphore_handle, LCK$M_CONVERT, 0, 0, 0, 0, 0, 0, 0);
2746 if (status != SS$_NORMAL)
2747 return SS_NO_SEMAPHORE;
2748 return SS_SUCCESS;
2749
2750#endif /* OS_VMS */
2751#ifdef OS_VXWORKS
2752 /* convert timeout in ticks (1/60) = 1000/60 ~ 1/16 = >>4 */
2754 if (status == ERROR)
2755 return SS_NO_SEMAPHORE;
2756 return SS_SUCCESS;
2757
2758#endif /* OS_VXWORKS */
2759#ifdef OS_UNIX
2760 {
2761 struct sembuf sb;
2762
2763#if (defined(OS_LINUX) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(OS_CYGWIN)) || defined(OS_FREEBSD)
2764 union semun arg;
2765#else
2766 union semun {
2767 INT val;
2768 struct semid_ds *buf;
2769 ushort *array;
2770 } arg;
2771#endif
2772
2773 sb.sem_num = 0;
2774 sb.sem_op = -1; /* decrement semaphore */
2775 sb.sem_flg = SEM_UNDO;
2776
2777 memset(&arg, 0, sizeof(arg));
2778
2779 DWORD start_time = ss_millitime();
2780
2781 if (s_semaphore_trace) {
2782 fprintf(stderr, "waitlock %d %d %d nest %d\n", semaphore_handle, ss_millitime(), getpid(), int(s_semaphore_nest_level));
2783 }
2784
2785 do {
2786#if defined(OS_DARWIN)
2788#elif defined(OS_LINUX)
2789 struct timespec ts;
2790 if (timeout_millisec >= 1000 || timeout_millisec == 0) {
2791 ts.tv_sec = 1;
2792 ts.tv_nsec = 0;
2793 } else {
2794 ts.tv_sec = 0;
2795 ts.tv_nsec = (timeout_millisec+10)*1000*1000;
2796 }
2797
2799#else
2801#endif
2802
2803 /* return on success */
2804 if (status == 0) {
2805 //DWORD milli_now = ss_millitime();
2806 //DWORD dt = milli_now - start_time;
2807 //fprintf(stderr, "ss_semaphore_wait_for: locked ok, start time 0x%08x, now 0x%08x, dt 0x%08x, timeout 0x%08x ms\n", start_time, milli_now, dt, timeout_millisec);
2808 //ss_sleep(100);
2809 break;
2810 }
2811
2812 /* retry if interrupted by a ss_wake signal */
2813 if (errno == EINTR || errno == EAGAIN) {
2814 //if (1) {
2815 // DWORD milli_now = ss_millitime();
2816 // DWORD dt = milli_now - start_time;
2817 // fprintf(stderr, "ss_semaphore_wait_for: semop/semtimedop(%d) returned %d, errno %d (%s), start time 0x%08x, now 0x%08x, dt 0x%08x, timeout 0x%08x ms\n", semaphore_handle, status, errno, strerror(errno), start_time, milli_now, dt, timeout_millisec);
2818 // abort();
2819 //}
2820
2821 /* return if timeout expired */
2822 if (timeout_millisec > 0) {
2824 DWORD dt = milli_now - start_time;
2825 if (dt > timeout_millisec) {
2826 fprintf(stderr, "ss_semaphore_wait_for: semop/semtimedop(%d) returned %d, errno %d (%s), start time 0x%08x, now 0x%08x, dt 0x%08x, timeout 0x%08x ms, SEMAPHORE TIMEOUT!\n", semaphore_handle, status, errno, strerror(errno), start_time, milli_now, dt, timeout_millisec);
2827 return SS_TIMEOUT;
2828 }
2829 }
2830
2831 continue;
2832 }
2833
2834 fprintf(stderr, "ss_semaphore_wait_for: semop/semtimedop(%d) returned %d, errno %d (%s)\n", semaphore_handle, status, errno, strerror(errno));
2835 return SS_NO_SEMAPHORE;
2836 } while (1);
2837
2838 if (s_semaphore_trace) {
2840 fprintf(stderr, "lock %d %d %d nest %d\n", semaphore_handle, ss_millitime(), getpid(), int( s_semaphore_nest_level));
2841 }
2842
2843 return SS_SUCCESS;
2844 }
2845#endif /* OS_UNIX */
2846
2847#ifdef OS_MSDOS
2848 return SS_NO_SEMAPHORE;
2849#endif
2850}
2851
2852/*------------------------------------------------------------------*/
2854/********************************************************************\
2855
2856 Routine: ss_semaphore_release
2857
2858 Purpose: Release ownership of a semaphore
2859
2860 Input:
2861 HNDLE *semaphore_handle Handle of the semaphore
2862
2863 Output:
2864 none
2865
2866 Function value:
2867 SS_SUCCESS Successful completion
2868 SS_NO_SEMAPHORE Invalid semaphore handle
2869
2870\********************************************************************/
2871{
2872 INT status;
2873
2874#ifdef OS_WINNT
2875
2877
2878 if (status == FALSE)
2879 return SS_NO_SEMAPHORE;
2880
2881 return SS_SUCCESS;
2882
2883#endif /* OS_WINNT */
2884#ifdef OS_VMS
2885
2886 status = sys$enqw(0, LCK$K_NLMODE, semaphore_handle, LCK$M_CONVERT, 0, 0, 0, 0, 0, 0, 0);
2887
2888 if (status != SS$_NORMAL)
2889 return SS_NO_SEMAPHORE;
2890
2891 return SS_SUCCESS;
2892
2893#endif /* OS_VMS */
2894
2895#ifdef OS_VXWORKS
2896
2898 return SS_NO_SEMAPHORE;
2899 return SS_SUCCESS;
2900#endif /* OS_VXWORKS */
2901
2902#ifdef OS_UNIX
2903 {
2904 struct sembuf sb;
2905
2906 sb.sem_num = 0;
2907 sb.sem_op = 1; /* increment semaphore */
2908 sb.sem_flg = SEM_UNDO;
2909
2910 if (s_semaphore_trace) {
2911 fprintf(stderr, "unlock %d %d %d nest %d\n", semaphore_handle, ss_millitime(), getpid(), int(s_semaphore_nest_level));
2912 assert(s_semaphore_nest_level > 0);
2914 }
2915
2916 do {
2918
2919 /* return on success */
2920 if (status == 0)
2921 break;
2922
2923 /* retry if interrupted by a ss_wake signal */
2924 if (errno == EINTR)
2925 continue;
2926
2927 fprintf(stderr, "ss_semaphore_release: semop/semtimedop(%d) returned %d, errno %d (%s)\n", semaphore_handle, status, errno, strerror(errno));
2928 return SS_NO_SEMAPHORE;
2929 } while (1);
2930
2931 return SS_SUCCESS;
2932 }
2933#endif /* OS_UNIX */
2934
2935#ifdef OS_MSDOS
2936 return SS_NO_SEMAPHORE;
2937#endif
2938}
2939
2940/*------------------------------------------------------------------*/
2942/********************************************************************\
2943
2944 Routine: ss_semaphore_delete
2945
2946 Purpose: Delete a semaphore
2947
2948 Input:
2949 HNDLE *semaphore_handle Handle of the semaphore
2950
2951 Output:
2952 none
2953
2954 Function value:
2955 SS_SUCCESS Successful completion
2956 SS_NO_SEMAPHORE Invalid semaphore handle
2957
2958\********************************************************************/
2959{
2960#ifdef OS_WINNT
2961
2963 return SS_NO_SEMAPHORE;
2964
2965 return SS_SUCCESS;
2966
2967#endif /* OS_WINNT */
2968#ifdef OS_VMS
2969
2970 free((void *) semaphore_handle);
2971 return SS_SUCCESS;
2972
2973#endif /* OS_VMS */
2974
2975#ifdef OS_VXWORKS
2976 /* no code for VxWorks destroy yet */
2978 return SS_NO_SEMAPHORE;
2979 return SS_SUCCESS;
2980#endif /* OS_VXWORKS */
2981
2982#ifdef OS_UNIX
2983#if (defined(OS_LINUX) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(OS_CYGWIN)) || defined(OS_FREEBSD)
2984 union semun arg;
2985#else
2986 union semun {
2987 INT val;
2988 struct semid_ds *buf;
2989 ushort *array;
2990 } arg;
2991#endif
2992
2993 memset(&arg, 0, sizeof(arg));
2994
2995 if (destroy_flag) {
2996 int status = semctl(semaphore_handle, 0, IPC_RMID, arg);
2997 //printf("semctl(ID=%d, IPC_RMID) returned %d, errno %d (%s)\n", semaphore_handle, status, errno, strerror(errno));
2998 if (status < 0)
2999 return SS_NO_SEMAPHORE;
3000 }
3001
3002 return SS_SUCCESS;
3003
3004#endif /* OS_UNIX */
3005
3006#ifdef OS_MSDOS
3007 return SS_NO_SEMAPHORE;
3008#endif
3009}
3010
3011/*------------------------------------------------------------------*/
3012
3014/********************************************************************\
3015
3016 Routine: ss_mutex_create
3017
3018 Purpose: Create a mutex for inter-thread locking
3019
3020 Output:
3021 MUTEX_T mutex Address of pointer to mutex
3022
3023 Function value:
3024 SS_CREATED Mutex was created
3025 SS_NO_SEMAPHORE Cannot create mutex
3026
3027\********************************************************************/
3028{
3029#ifdef OS_VXWORKS
3030
3031 /* semBCreate is a Binary semaphore which is under VxWorks a optimized mutex
3032 refering to the programmer's Guide 5.3.1 */
3034 return SS_NO_MUTEX;
3035 return SS_CREATED;
3036
3037#endif /* OS_VXWORKS */
3038
3039#ifdef OS_WINNT
3040
3041 *mutex = (MUTEX_T *)malloc(sizeof(HANDLE));
3042 **mutex = CreateMutex(NULL, FALSE, NULL);
3043
3044 if (**mutex == 0)
3045 return SS_NO_MUTEX;
3046
3047 return SS_CREATED;
3048
3049#endif /* OS_WINNT */
3050#ifdef OS_UNIX
3051
3052 {
3053 int status;
3055
3056 attr = (pthread_mutexattr_t*)malloc(sizeof(*attr));
3057 assert(attr);
3058
3060 if (status != 0) {
3061 fprintf(stderr, "ss_mutex_create: pthread_mutexattr_init() returned errno %d (%s)\n", status, strerror(status));
3062 }
3063
3064 if (recursive) {
3066 if (status != 0) {
3067 fprintf(stderr, "ss_mutex_create: pthread_mutexattr_settype() returned errno %d (%s)\n", status, strerror(status));
3068 }
3069 }
3070
3071 *mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
3072 assert(*mutex);
3073
3074 status = pthread_mutex_init(*mutex, attr);
3075 if (status != 0) {
3076 fprintf(stderr, "ss_mutex_create: pthread_mutex_init() returned errno %d (%s), aborting...\n", status, strerror(status));
3077 abort(); // does not return
3078 return SS_NO_MUTEX;
3079 }
3080
3081 free(attr);
3082
3083 if (recursive) {
3084 // test recursive locks
3085
3086 status = pthread_mutex_trylock(*mutex);
3087 assert(status == 0);
3088
3089 status = pthread_mutex_trylock(*mutex);
3090 assert(status == 0); // EBUSY if PTHREAD_MUTEX_RECURSIVE does not work
3091
3092 status = pthread_mutex_unlock(*mutex);
3093 assert(status == 0);
3094
3095 status = pthread_mutex_unlock(*mutex);
3096 assert(status == 0);
3097 }
3098
3099 return SS_SUCCESS;
3100 }
3101#endif /* OS_UNIX */
3102
3103#ifdef OS_MSDOS
3104 return SS_NO_SEMAPHORE;
3105#endif
3106}
3107
3108/*------------------------------------------------------------------*/
3110/********************************************************************\
3111
3112 Routine: ss_mutex_wait_for
3113
3114 Purpose: Wait for a mutex to get owned
3115
3116 Input:
3117 MUTEX_T *mutex Pointer to mutex
3118 INT timeout Timeout in ms, zero for no timeout
3119
3120 Output:
3121 none
3122
3123 Function value:
3124 SS_SUCCESS Successful completion
3125 SS_NO_MUTEX Invalid mutex handle
3126 SS_TIMEOUT Timeout
3127
3128\********************************************************************/
3129{
3130 INT status;
3131
3132#ifdef OS_WINNT
3133
3134 status = WaitForSingleObject(*mutex, timeout == 0 ? INFINITE : timeout);
3135
3136 if (status == WAIT_TIMEOUT) {
3137 return SS_TIMEOUT;
3138 }
3139
3140 if (status == WAIT_FAILED) {
3141 fprintf(stderr, "ss_mutex_wait_for: WaitForSingleObject() failed, status = %d", status);
3142 abort(); // does not return
3143 return SS_NO_MUTEX;
3144 }
3145
3146 return SS_SUCCESS;
3147#endif /* OS_WINNT */
3148#ifdef OS_VXWORKS
3149 /* convert timeout in ticks (1/60) = 1000/60 ~ 1/16 = >>4 */
3150 status = semTake((SEM_ID) mutex, timeout == 0 ? WAIT_FOREVER : timeout >> 4);
3151 if (status == ERROR)
3152 return SS_NO_MUTEX;
3153 return SS_SUCCESS;
3154
3155#endif /* OS_VXWORKS */
3156#if defined(OS_UNIX)
3157
3158#if defined(OS_DARWIN)
3159
3160 if (timeout > 0) {
3161 // emulate pthread_mutex_timedlock under OS_DARWIN
3162 DWORD wait = 0;
3163 while (1) {
3165 if (status == 0) {
3166 return SS_SUCCESS;
3167 } else if (status == EBUSY) {
3168 ss_sleep(10);
3169 wait += 10;
3170 } else {
3171 fprintf(stderr, "ss_mutex_wait_for: fatal error: pthread_mutex_trylock() returned errno %d (%s), aborting...\n", status, strerror(status));
3172 abort(); // does not return
3173 }
3174 if (wait > timeout) {
3175 fprintf(stderr, "ss_mutex_wait_for: fatal error: timeout waiting for mutex, timeout was %d millisec, aborting...\n", timeout);
3176 abort(); // does not return
3177 }
3178 }
3179 } else {
3180 status = pthread_mutex_lock(mutex);
3181 }
3182
3183 if (status != 0) {
3184 fprintf(stderr, "ss_mutex_wait_for: pthread_mutex_lock() returned errno %d (%s), aborting...\n", status, strerror(status));
3185 abort(); // does not return
3186 }
3187
3188 return SS_SUCCESS;
3189
3190#else // OS_DARWIN
3191 if (timeout > 0) {
3193 struct timespec st;
3194
3196 st.tv_sec += timeout / 1000;
3197 st.tv_nsec += (timeout % 1000) * 1000000;
3199
3200 if (status == ETIMEDOUT) {
3201 fprintf(stderr, "ss_mutex_wait_for: fatal error: timeout waiting for mutex, timeout was %d millisec, aborting...\n", timeout);
3202 abort();
3203 }
3204
3205 // Make linux timeout do same as MacOS timeout: abort() the program
3206 //if (status == ETIMEDOUT)
3207 // return SS_TIMEOUT;
3208 //return SS_SUCCESS;
3209 } else {
3210 status = pthread_mutex_lock(mutex);
3211 }
3212
3213 if (status != 0) {
3214 fprintf(stderr, "ss_mutex_wait_for: pthread_mutex_lock() returned errno %d (%s), aborting...\n", status, strerror(status));
3215 abort();
3216 }
3217
3218 return SS_SUCCESS;
3219#endif
3220
3221#endif /* OS_UNIX */
3222
3223#ifdef OS_MSDOS
3224 return SS_NO_MUTEX;
3225#endif
3226}
3227
3228/*------------------------------------------------------------------*/
3230/********************************************************************\
3231
3232 Routine: ss_mutex_release
3233
3234 Purpose: Release ownership of a mutex
3235
3236 Input:
3237 MUTEX_T *mutex Pointer to mutex
3238
3239 Output:
3240 none
3241
3242 Function value:
3243 SS_SUCCESS Successful completion
3244 SS_NO_MUTES Invalid mutes handle
3245
3246\********************************************************************/
3247{
3248 INT status;
3249
3250#ifdef OS_WINNT
3251
3252 status = ReleaseMutex(*mutex);
3253 if (status == FALSE)
3254 return SS_NO_SEMAPHORE;
3255
3256 return SS_SUCCESS;
3257
3258#endif /* OS_WINNT */
3259#ifdef OS_VXWORKS
3260
3261 if (semGive((SEM_ID) mutes_handle) == ERROR)
3262 return SS_NO_MUTEX;
3263 return SS_SUCCESS;
3264#endif /* OS_VXWORKS */
3265#ifdef OS_UNIX
3266
3268 if (status != 0) {
3269 fprintf(stderr, "ss_mutex_release: pthread_mutex_unlock() returned error %d (%s), aborting...\n", status, strerror(status));
3270 abort(); // does not return
3271 return SS_NO_MUTEX;
3272 }
3273
3274 return SS_SUCCESS;
3275#endif /* OS_UNIX */
3276
3277#ifdef OS_MSDOS
3278 return SS_NO_MUTEX;
3279#endif
3280}
3281
3282/*------------------------------------------------------------------*/
3284/********************************************************************\
3285
3286 Routine: ss_mutex_delete
3287
3288 Purpose: Delete a mutex
3289
3290 Input:
3291 MUTEX_T *mutex Pointer to mutex
3292
3293 Output:
3294 none
3295
3296 Function value:
3297 SS_SUCCESS Successful completion
3298 SS_NO_MUTEX Invalid mutex handle
3299
3300\********************************************************************/
3301{
3302#ifdef OS_WINNT
3303
3304 if (CloseHandle(*mutex) == FALSE)
3305 return SS_NO_SEMAPHORE;
3306
3307 free(mutex);
3308
3309 return SS_SUCCESS;
3310
3311#endif /* OS_WINNT */
3312#ifdef OS_VXWORKS
3313 /* no code for VxWorks destroy yet */
3315 return SS_NO_MUTEX;
3316 return SS_SUCCESS;
3317#endif /* OS_VXWORKS */
3318
3319#ifdef OS_UNIX
3320 {
3321 int status;
3322
3324 if (status != 0) {
3325 fprintf(stderr, "ss_mutex_delete: pthread_mutex_destroy() returned errno %d (%s), aborting...\n", status, strerror(status));
3326 abort(); // do not return
3327 return SS_NO_MUTEX;
3328 }
3329
3330 free(mutex);
3331 return SS_SUCCESS;
3332 }
3333#endif /* OS_UNIX */
3334}
3335
3336/*------------------------------------------------------------------*/
3337bool ss_timed_mutex_wait_for_sec(std::timed_mutex& mutex, const char* mutex_name, double timeout_sec)
3338/********************************************************************\
3339
3340 Routine: ss_timed_mutex_wait_for_sec
3341
3342 Purpose: Lock C++11 timed mutex with a timeout
3343
3344 Input:
3345 std::timed_mutex& mutex Pointer to mutex
3346 double timeout_sec Timeout in seconds, zero to wait forever
3347
3348 Function value:
3349 true Successful completion
3350 false Timeout
3351
3352\********************************************************************/
3353{
3354 if (timeout_sec <= 0) {
3355 mutex.lock();
3356 return true;
3357 }
3358
3359 double starttime = ss_time_sec();
3360 double endtime = starttime + timeout_sec;
3361
3362 // NB: per timed mutex try_lock_for(), one must always
3363 // look waiting for successful lock because it is permitted
3364 // to return "false" even if timeout did not yet expire. (cannot
3365 // tell permitted spurious failure from normal timeout). K.O.
3366
3367 double locktime = starttime;
3368
3369 while (1) {
3370 bool ok = mutex.try_lock_for(std::chrono::milliseconds(1000));
3371
3372 if (ok) {
3373 //double now = ss_time_sec();
3374 //fprintf(stderr, "ss_timed_mutex_wait_for_sec: mutex %s locked in %.1f seconds. timeout %.1f seconds\n", mutex_name, now-starttime, timeout_sec);
3375 return true;
3376 }
3377
3378 double now = ss_time_sec();
3379
3380 if (mutex_name) {
3381 if (now-locktime < 0.2) {
3382 // mutex.try_lock_for() is permitted spuriously fail: return false before the 1 sec timeout expires, we should not print any messages about it. K.O.
3383 //fprintf(stderr, "ss_timed_mutex_wait_for_sec: short try_lock_for(1000). %.3f seconds\n", now-locktime);
3384 } else {
3385 fprintf(stderr, "ss_timed_mutex_wait_for_sec: long wait for mutex %s, %.1f seconds. %.1f seconds until timeout\n", mutex_name, now-starttime, endtime-now);
3386 }
3387 }
3388
3389 if (now > endtime)
3390 return false;
3391
3392 locktime = now;
3393 }
3394}
3395
3396//
3397// thread-safe versions of tzset() and mktime().
3398//
3399// as of ubuntu 20.04, tzset() and mktime() are not thread safe,
3400// easy to see by source code inspection. there is no reeader lock
3401// in mktime() to protect global time zone data against modification
3402// by tzset() executing in another thread. (on stackoverflow people
3403// argue that as long as system time zone never changes, this violation of
3404// thread safety is benign).
3405//
3406// calling mktime() is quite expensive, easy to see by inspecting the source code:
3407// each call to mktime() will call tzset(), inside tzset(), "old_tz" is always
3408// reallocated by a free() and strdup() pair and a stat() syscall is made
3409// to check that file /etc/localtime did not change. These overheads can be turned off
3410// by setting setenv("TZ") to a time zone name (i.e. "UTC") or to the value "/etc/localtime".
3411//
3412// tzset() itself is thread-safe, it uses a lock to protect global
3413// time zone data against another tzset() running in a different thread.
3414// however this lock is not instrumented by the thread sanitizer and
3415// causes false positive data race warnings.
3416//
3417// in MIDAS, we choose this solution to avoid the thread sanitizer false positive
3418// warning about tzset() - introduce ss_tzset() to protect calls to tzset() with
3419// a mutex and introduce ss_mktime() to add same protection to tzset() called by mktime()
3420// internally. It also makes calls to ss_mktime() explicitely thread-safe.
3421//
3422// K.O. 2022-Mar-10.
3423//
3424
3425static std::mutex gTzMutex;
3426
3428{
3429 std::lock_guard<std::mutex> lock(gTzMutex);
3430 //defeat tzset() error trap from msystem.h
3431 //#ifdef tzset
3432 //#undef tzset
3433 //#endif
3434 tzset();
3435}
3436
3438{
3439 std::lock_guard<std::mutex> lock(gTzMutex);
3440 //defeat mktime() error trap from msystem.h
3441 //#ifdef mktime
3442 //#undef mktime
3443 //#endif
3444 return mktime(tms);
3445}
3446
3447/********************************************************************/
3466{
3467#ifdef OS_WINNT
3468
3469 return (int) GetTickCount();
3470
3471#endif /* OS_WINNT */
3472#ifdef OS_MSDOS
3473
3474 return clock() * 55;
3475
3476#endif /* OS_MSDOS */
3477#ifdef OS_VMS
3478
3479 {
3480 char time[8];
3481 DWORD lo, hi;
3482
3483 sys$gettim(time);
3484
3485 lo = *((DWORD *) time);
3486 hi = *((DWORD *) (time + 4));
3487
3488/* return *lo / 10000; */
3489
3490 return lo / 10000 + hi * 429496.7296;
3491
3492 }
3493
3494#endif /* OS_VMS */
3495#ifdef OS_UNIX
3496 {
3497 struct timeval tv;
3498
3499 gettimeofday(&tv, NULL);
3500
3501 DWORD m = tv.tv_sec * 1000 + tv.tv_usec / 1000;
3502 //m += 0x137e0000; // adjust milltime for testing 32-bit wrap-around
3503 return m;
3504 }
3505
3506#endif /* OS_UNIX */
3507#ifdef OS_VXWORKS
3508 {
3509 int count;
3510 static int ticks_per_msec = 0;
3511
3512 if (ticks_per_msec == 0)
3513 ticks_per_msec = 1000 / sysClkRateGet();
3514
3515 return tickGet() * ticks_per_msec;
3516 }
3517#endif /* OS_VXWORKS */
3518}
3519
3520/********************************************************************/
3535{
3536 return (DWORD) time(NULL);
3537}
3538
3540{
3541 struct timeval tv;
3542 gettimeofday(&tv, NULL);
3543 return tv.tv_sec*1.0 + tv.tv_usec/1000000.0;
3544}
3545
3546/*------------------------------------------------------------------*/
3548/********************************************************************\
3549
3550 Routine: ss_settime
3551
3552 Purpose: Set local time. Used to synchronize different computers
3553
3554 Input:
3555 INT Time in seconds since 1.1.1970 UTC.
3556
3557 Output:
3558 none
3559
3560 Function value:
3561
3562\********************************************************************/
3563{
3564#if defined(OS_WINNT)
3565 SYSTEMTIME st;
3566 struct tm ltm;
3567
3568 ss_tzset();
3569 localtime_r((time_t *) & seconds, &ltm);
3570
3571 st.wYear = ltm.tm_year + 1900;
3572 st.wMonth = ltm.tm_mon + 1;
3573 st.wDay = ltm.tm_mday;
3574 st.wHour = ltm.tm_hour;
3575 st.wMinute = ltm.tm_min;
3576 st.wSecond = ltm.tm_sec;
3577 st.wMilliseconds = 0;
3578
3579 SetLocalTime(&st);
3580
3581#elif defined(OS_DARWIN) && defined(CLOCK_REALTIME)
3582
3583 struct timespec ltm;
3584
3585 ltm.tv_sec = seconds;
3586 ltm.tv_nsec = 0;
3588
3589#elif defined(OS_CYGWIN) && defined(CLOCK_REALTIME)
3590
3591 struct timespec ltm;
3592
3593 ltm.tv_sec = seconds;
3594 ltm.tv_nsec = 0;
3596 return SS_NO_DRIVER;
3597
3598#elif defined(OS_UNIX) && defined(CLOCK_REALTIME)
3599
3600 struct timespec ltm;
3601
3602 ltm.tv_sec = seconds;
3603 ltm.tv_nsec = 0;
3605
3606#elif defined(OS_VXWORKS)
3607
3608 struct timespec ltm;
3609
3610 ltm.tv_sec = seconds;
3611 ltm.tv_nsec = 0;
3613
3614#else
3615#warning ss_settime() is not supported!
3616#endif
3617 return SS_SUCCESS;
3618}
3619
3620/*------------------------------------------------------------------*/
3621std::string ss_asctime()
3622/********************************************************************\
3623
3624 Routine: ss_asctime
3625
3626 Purpose: Returns the local actual time as a string
3627
3628 Input:
3629 none
3630
3631 Output:
3632 none
3633
3634 Function value:
3635 char * Time string
3636
3637\********************************************************************/
3638{
3639 ss_tzset(); // required for localtime_t()
3641 struct tm tms;
3643 char str[32];
3644 asctime_r(&tms, str);
3645 /* strip new line */
3646 str[24] = 0;
3647
3648 return str;
3649}
3650
3651/*------------------------------------------------------------------*/
3653/********************************************************************\
3654
3655 Routine: ss_timezone
3656
3657 Purpose: Returns difference in seconds between coordinated universal
3658 time and local time.
3659
3660 Input:
3661 none
3662
3663 Output:
3664 none
3665
3666 Function value:
3667 INT Time difference in seconds
3668
3669\********************************************************************/
3670{
3671#if defined(OS_DARWIN) || defined(OS_VXWORKS)
3672 return 0;
3673#else
3674 return (INT) timezone; /* on Linux, comes from "#include <time.h>". */
3675#endif
3676}
3677
3678
3679/*------------------------------------------------------------------*/
3680
3681#ifdef OS_UNIX
3682/* dummy function for signal() call */
3683void ss_cont(int signum)
3684{
3685}
3686#endif
3687
3688/********************************************************************/
3701{
3702 if (millisec == 0) {
3703#ifdef OS_WINNT
3705#endif
3706#ifdef OS_VMS
3707 sys$hiber();
3708#endif
3709#ifdef OS_UNIX
3711 pause();
3712#endif
3713 return SS_SUCCESS;
3714 }
3715#ifdef OS_WINNT
3716 Sleep(millisec);
3717#endif
3718#ifdef OS_UNIX
3719 struct timespec ts;
3720 int status;
3721
3722 ts.tv_sec = millisec / 1000;
3723 ts.tv_nsec = (millisec % 1000) * 1E6;
3724
3725 do {
3726 status = nanosleep(&ts, &ts);
3727 if ((int)ts.tv_sec < 0)
3728 break; // can be negative under OSX
3729 } while (status == -1 && errno == EINTR);
3730#endif
3731
3732 return SS_SUCCESS;
3733}
3734
3735/*------------------------------------------------------------------*/
3737/********************************************************************\
3738
3739 Routine: ss_kbhit
3740
3741 Purpose: Returns TRUE if a key is pressed
3742
3743 Input:
3744 none
3745
3746 Output:
3747 none
3748
3749 Function value:
3750 FALSE No key has been pressed
3751 TRUE Key has been pressed
3752
3753\********************************************************************/
3754{
3755#ifdef OS_MSDOS
3756
3757 return kbhit();
3758
3759#endif /* OS_MSDOS */
3760#ifdef OS_WINNT
3761
3762 return kbhit();
3763
3764#endif /* OS_WINNT */
3765#ifdef OS_VMS
3766
3767 return FALSE;
3768
3769#endif /* OS_VMS */
3770#ifdef OS_UNIX
3771
3772 int n;
3773
3774 if (_daemon_flag)
3775 return 0;
3776
3777 ioctl(0, FIONREAD, &n);
3778 return (n > 0);
3779
3780#endif /* OS_UNIX */
3781#ifdef OS_VXWORKS
3782
3783 int n;
3784 ioctl(0, FIONREAD, (long) &n);
3785 return (n > 0);
3786
3787#endif /* OS_UNIX */
3788}
3789
3790
3791/*------------------------------------------------------------------*/
3792#ifdef LOCAL_ROUTINES
3793
3794/*------------------------------------------------------------------*/
3795#ifdef OS_WINNT
3796
3797static void (*UserCallback) (int);
3798static UINT _timer_id = 0;
3799
3801{
3802 _timer_id = 0;
3803 if (UserCallback != NULL)
3804 UserCallback(0);
3805}
3806
3807#endif /* OS_WINNT */
3808
3809INT ss_alarm(INT millitime, void (*func) (int))
3810/********************************************************************\
3811
3812 Routine: ss_alarm
3813
3814 Purpose: Schedules an alarm. Call function referenced by *func
3815 after the specified seconds.
3816
3817 Input:
3818 INT millitime Time in milliseconds
3819 void (*func)() Function to be called after the spe-
3820 cified time.
3821
3822 Output:
3823 none
3824
3825 Function value:
3826 SS_SUCCESS Successful completion
3827
3828\********************************************************************/
3829{
3830#ifdef OS_WINNT
3831
3832 UserCallback = func;
3833 if (millitime > 0)
3835 else {
3836 if (_timer_id)
3838 _timer_id = 0;
3839 }
3840
3841 return SS_SUCCESS;
3842
3843#endif /* OS_WINNT */
3844#ifdef OS_VMS
3845
3846 signal(SIGALRM, func);
3847 alarm(millitime / 1000);
3848 return SS_SUCCESS;
3849
3850#endif /* OS_VMS */
3851#ifdef OS_UNIX
3852
3853 signal(SIGALRM, func);
3854 alarm(millitime / 1000);
3855 return SS_SUCCESS;
3856
3857#endif /* OS_UNIX */
3858}
3859
3860/*------------------------------------------------------------------*/
3862
3863#ifdef OS_WINNT
3864
3866{
3869
3871}
3872
3874{
3877
3878 raise(sig);
3879
3880 return 0;
3881}
3882
3883/*
3884INT _matherr(struct _exception *except)
3885{
3886 if (MidasExceptionHandler != NULL)
3887 MidasExceptionHandler();
3888
3889 return 0;
3890}
3891*/
3892
3893#endif /* OS_WINNT */
3894
3895#ifdef OS_VMS
3896
3898{
3901
3902 return (SS$_RESIGNAL);
3903}
3904
3905void MidasExceptionSignal(INT sig)
3906{
3909
3910 kill(getpid(), sig);
3911}
3912
3913#endif /* OS_VMS */
3914
3915/*------------------------------------------------------------------*/
3916INT ss_exception_handler(void (*func) (void))
3917/********************************************************************\
3918
3919 Routine: ss_exception_handler
3920
3921 Purpose: Establish new exception handler which is called before
3922 the program is aborted due to a Ctrl-Break or an access
3923 violation. This handler may clean up things which may
3924 otherwise left in an undefined state.
3925
3926 Input:
3927 void (*func)() Address of handler function
3928 Output:
3929 none
3930
3931 Function value:
3932 BM_SUCCESS Successful completion
3933
3934\********************************************************************/
3935{
3936#ifdef OS_WINNT
3937
3938 MidasExceptionHandler = func;
3939/* SetUnhandledExceptionFilter(
3940 (LPTOP_LEVEL_EXCEPTION_FILTER) MidasExceptionFilter);
3941
3942 signal(SIGINT, MidasExceptionSignal);
3943 signal(SIGILL, MidasExceptionSignal);
3944 signal(SIGFPE, MidasExceptionSignal);
3945 signal(SIGSEGV, MidasExceptionSignal);
3946 signal(SIGTERM, MidasExceptionSignal);
3947 signal(SIGBREAK, MidasExceptionSignal);
3948 signal(SIGABRT, MidasExceptionSignal); */
3949
3950#elif defined (OS_VMS)
3951
3952 MidasExceptionHandler = func;
3954
3961
3962#else /* OS_VMS */
3963#endif
3964
3965 return SS_SUCCESS;
3966}
3967
3968#endif /* LOCAL_ROUTINES */
3969
3970/*------------------------------------------------------------------*/
3971void *ss_ctrlc_handler(void (*func) (int))
3972/********************************************************************\
3973
3974 Routine: ss_ctrlc_handler
3975
3976 Purpose: Establish new exception handler which is called before
3977 the program is aborted due to a Ctrl-Break. This handler may
3978 clean up things which may otherwise left in an undefined state.
3979
3980 Input:
3981 void (*func)(int) Address of handler function, if NULL
3982 install default handler
3983
3984 Output:
3985 none
3986
3987 Function value:
3988 same as signal()
3989
3990\********************************************************************/
3991{
3992#ifdef OS_WINNT
3993
3994 if (func == NULL) {
3996 return signal(SIGINT, SIG_DFL);
3997 } else {
3998 signal(SIGBREAK, func);
3999 return signal(SIGINT, func);
4000 }
4001 return NULL;
4002
4003#endif /* OS_WINNT */
4004#ifdef OS_VMS
4005
4006 return signal(SIGINT, func);
4007
4008#endif /* OS_WINNT */
4009
4010#ifdef OS_UNIX
4011
4012 if (func == NULL) {
4014 return (void *) signal(SIGINT, SIG_DFL);
4015 } else {
4016 signal(SIGTERM, func);
4017 return (void *) signal(SIGINT, func);
4018 }
4019
4020#endif /* OS_UNIX */
4021}
4022
4023/*------------------------------------------------------------------*/
4024/********************************************************************\
4025* *
4026* Suspend/resume functions *
4027* *
4028\********************************************************************/
4029
4030/*------------------------------------------------------------------*/
4031/* globals */
4032
4033/*
4034 The suspend structure is used in a multithread environment
4035 (multi thread server) where each thread may resume another thread.
4036 Since all threads share the same global memory, the ports and
4037 sockets for suspending and resuming must be stored in a array
4038 which keeps one entry for each thread.
4039*/
4040
4048
4049static std::vector<SUSPEND_STRUCT*> _ss_suspend_vector;
4050
4053
4055static int _ss_server_listen_socket = 0; // mserver listening for connections
4056static int _ss_client_listen_socket = 0; // normal midas program listening for rpc connections for run transitions, etc
4057
4059static RPC_SERVER_CONNECTION* _ss_client_connection = NULL; // client-side connection to the mserver
4060
4062static RPC_SERVER_ACCEPTION_LIST* _ss_server_acceptions = NULL; // server side RPC connections (run transitions, etc)
4063
4064/*------------------------------------------------------------------*/
4066{
4067 if (tid1 == 0)
4068 return true;
4069 if (tid1 == tid2)
4070 return true;
4071 return false;
4072}
4073
4075{
4076 _ss_listen_thread = thread_id; // this thread handles listen()/accept() activity
4077 _ss_client_thread = thread_id; // this thread reads the mserver connection, handles ODB and event buffer notifications (db_watch->db_update_record_local(), bm_poll_event())
4078 _ss_server_thread = thread_id; // this thread reads and executes RPC requests
4079 _ss_odb_thread = thread_id; // this thread reads and dispatches ODB notifications (db_watch & co)
4080 return SS_SUCCESS;
4081}
4082
4083/*------------------------------------------------------------------*/
4085/********************************************************************\
4086
4087 Routine: ss_suspend_init_struct
4088
4089 Purpose: Create sockets used in the suspend/resume mechanism.
4090
4091 Input:
4092 SUSPEND_STRUCT* psuspend structure to initialize
4093
4094 Function value:
4095 SS_SUCCESS Successful completion
4096 SS_SOCKET_ERROR Error in socket routines
4097 SS_NO_MEMORY Not enough memory
4098
4099\********************************************************************/
4100{
4101 INT status, sock;
4102 unsigned int size;
4103 struct sockaddr_in bind_addr;
4104 //int udp_bind_hostname = 0; // bind to localhost or bind to hostname or bind to INADDR_ANY?
4105
4106 //printf("ss_suspend_init_struct: thread %s\n", ss_tid_to_string(psuspend->thread_id).c_str());
4107
4108 assert(psuspend->thread_id != 0);
4109
4110#ifdef OS_WINNT
4111 {
4113
4114 /* Start windows sockets */
4115 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
4116 return SS_SOCKET_ERROR;
4117 }
4118#endif
4119
4120 /*--------------- create UDP receive socket -------------------*/
4121 sock = socket(AF_INET, SOCK_DGRAM, 0);
4122 if (sock == -1)
4123 return SS_SOCKET_ERROR;
4124
4125 /* let OS choose port for socket */
4126 memset(&bind_addr, 0, sizeof(bind_addr));
4127 bind_addr.sin_family = AF_INET;
4128 bind_addr.sin_addr.s_addr = 0;
4129 bind_addr.sin_port = 0;
4130
4131 /* decide if UDP sockets are bound to localhost (they are only use for local communications)
4132 or to hostname (for compatibility with old clients - their hotlinks will not work) */
4133 {
4134 std::string path = cm_get_path();
4135 path += ".UDP_BIND_HOSTNAME";
4136
4137 //cm_msg(MERROR, "ss_suspend_init_ipc", "check file [%s]", path.c_str());
4138
4139 FILE *fp = fopen(path.c_str(), "r");
4140 if (fp) {
4141 cm_msg(MERROR, "ss_suspend_init_ipc", "Support for UDP_BIND_HOSTNAME was removed. Please delete file \"%s\"", path.c_str());
4142 //udp_bind_hostname = 1;
4143 fclose(fp);
4144 fp = NULL;
4145 }
4146 }
4147
4148 //#ifdef OS_VXWORKS
4149 //{
4150 // char local_host_name[HOST_NAME_LENGTH];
4151 // INT host_addr;
4152 //
4153 // gethostname(local_host_name, sizeof(local_host_name));
4154 //
4155 //host_addr = hostGetByName(local_host_name);
4156 // memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
4157 //}
4158 //#else
4159 //if (udp_bind_hostname) {
4160 // char local_host_name[HOST_NAME_LENGTH];
4161 // struct hostent *phe = gethostbyname(local_host_name);
4162 // if (phe == NULL) {
4163 // cm_msg(MERROR, "ss_suspend_init_ipc", "cannot get IP address for host name \'%s\'", local_host_name);
4164 // return SS_SOCKET_ERROR;
4165 // }
4166 // memcpy((char *) &(bind_addr.sin_addr), phe->h_addr, phe->h_length);
4167 //} else {
4168 bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4169 //}
4170 //#endif
4171
4172 status = bind(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
4173 if (status < 0)
4174 return SS_SOCKET_ERROR;
4175
4176 /* find out which port OS has chosen */
4177 size = sizeof(bind_addr);
4178#ifdef OS_WINNT
4179 getsockname(sock, (struct sockaddr *) &bind_addr, (int *) &size);
4180#else
4181 getsockname(sock, (struct sockaddr *) &bind_addr, &size);
4182#endif
4183
4184 // ipc receive socket must be set to non-blocking mode, see explanation
4185 // in ss_suspend_process_ipc(). K.O. July 2022.
4186
4187 int flags = fcntl(sock, F_GETFL, 0);
4188 status = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
4189
4190 if (status < 0) {
4191 fprintf(stderr, "ss_suspend_init_struct: cannot set non-blocking mode of ipc receive socket, fcntl() returned %d, errno %d (%s)\n", status, errno, strerror(errno));
4192 return SS_SOCKET_ERROR;
4193 }
4194
4195 psuspend->ipc_recv_socket = sock;
4196 psuspend->ipc_recv_port = ntohs(bind_addr.sin_port);
4197
4198 /*--------------- create UDP send socket ----------------------*/
4199 sock = socket(AF_INET, SOCK_DGRAM, 0);
4200
4201 if (sock == -1)
4202 return SS_SOCKET_ERROR;
4203
4204 /* fill out bind struct pointing to local host */
4205 memset(&bind_addr, 0, sizeof(bind_addr));
4206 bind_addr.sin_family = AF_INET;
4207 bind_addr.sin_addr.s_addr = 0;
4208
4209 //#ifdef OS_VXWORKS
4210 //{
4211 // INT host_addr;
4212 //
4213 // host_addr = hostGetByName(local_host_name);
4214 //memcpy((char *) &(bind_addr.sin_addr), &host_addr, 4);
4215 //}
4216 //#else
4217 //if (udp_bind_hostname) {
4218 // // nothing
4219 //} else {
4220 bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
4221
4222 status = bind(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
4223 if (status < 0)
4224 return SS_SOCKET_ERROR;
4225 //}
4226 //#endif
4227
4228 memcpy(&(psuspend->bind_addr), &bind_addr, sizeof(bind_addr));
4229 psuspend->ipc_send_socket = sock;
4230
4231 //printf("ss_suspend_init_struct: thread %s, udp port %d\n", ss_tid_to_string(psuspend->thread_id).c_str(), psuspend->ipc_recv_port);
4232
4233 return SS_SUCCESS;
4234}
4235
4236/*------------------------------------------------------------------*/
4238/********************************************************************\
4239
4240 Routine: ss_suspend_get_struct
4241
4242 Purpose: Return the suspend structure for this thread.
4243
4244 Input:
4245 midas_thread_t thread_id thread is returned by ss_gettid()
4246
4247 Function value:
4248 SUSPEND_STRUCT for the given thread
4249
4250\********************************************************************/
4251{
4252 // find thread_id
4253 for (unsigned i=0; i<_ss_suspend_vector.size(); i++) {
4254 if (!_ss_suspend_vector[i])
4255 continue;
4256 if (_ss_suspend_vector[i]->thread_id == thread_id) {
4257 return _ss_suspend_vector[i];
4258 }
4259 }
4260
4261 // create new one if not found
4263 psuspend->thread_id = thread_id;
4264
4265 // place into empty slot
4266 for (unsigned i=0; i<_ss_suspend_vector.size(); i++) {
4267 if (!_ss_suspend_vector[i]) {
4269 return psuspend;
4270 }
4271 }
4272
4273 // add to vector if no empty slots
4274 _ss_suspend_vector.push_back(psuspend);
4275
4276 return psuspend;
4277}
4278
4280{
4281 if (psuspend->ipc_recv_socket) {
4282 closesocket(psuspend->ipc_recv_socket);
4283 psuspend->ipc_recv_socket = 0;
4284 }
4285
4286 if (psuspend->ipc_send_socket) {
4287 closesocket(psuspend->ipc_send_socket);
4288 psuspend->ipc_send_socket = 0;
4289 }
4290
4291 //printf("ss_suspend_close: free thread %s, udp port %d\n", ss_tid_to_string(psuspend->thread_id).c_str(), psuspend->ipc_recv_port);
4292
4293 psuspend->thread_id = 0;
4294 psuspend->ipc_recv_port = 0;
4295}
4296
4297/*------------------------------------------------------------------*/
4299/********************************************************************\
4300
4301 Routine: ss_suspend_exit
4302
4303 Purpose: Closes the sockets used in the suspend/resume mechanism.
4304 Should be called before a thread exits.
4305
4306 Input:
4307 none
4308
4309 Output:
4310 none
4311
4312 Function value:
4313 SS_SUCCESS Successful completion
4314
4315\********************************************************************/
4316{
4317 midas_thread_t thread_id = ss_gettid();
4318
4319 for (unsigned i=0; i<_ss_suspend_vector.size(); i++) {
4320 if (!_ss_suspend_vector[i])
4321 continue;
4322 if (_ss_suspend_vector[i]->thread_id == thread_id) {
4326 delete psuspend;
4327 }
4328 }
4329
4330 if (_ss_suspend_odb) {
4331 bool last = true;
4332 for (unsigned i=0; i<_ss_suspend_vector.size(); i++) {
4333 if (_ss_suspend_vector[i]) {
4334 last = false;
4335 break;
4336 }
4337 }
4338 if (last) {
4342 delete psuspend;
4343 }
4344 }
4345
4346 return SS_SUCCESS;
4347}
4348
4350{
4351 // mserver listener socket
4353 return SS_SUCCESS;
4354}
4355
4357{
4358 // midas program rpc listener socket (run transitions, etc)
4360 return SS_SUCCESS;
4361}
4362
4364{
4365 // client side of the mserver connection
4367 return SS_SUCCESS;
4368}
4369
4371{
4372 // server side of the RPC connections (run transitions, etc)
4374 return SS_SUCCESS;
4375}
4376
4378/********************************************************************\
4379
4380 Routine: ss_suspend_init_odb_port
4381
4382 Purpose: Setup UDP port to receive ODB notifications (db_watch & co)
4383
4384 Function value:
4385 SS_SUCCESS Successful completion
4386
4387\********************************************************************/
4388{
4389 if (!_ss_suspend_odb) {
4393 }
4394
4395 return SS_SUCCESS;
4396}
4397
4398/*------------------------------------------------------------------*/
4400/********************************************************************\
4401
4402 Routine: ss_suspend_get_odb_port
4403
4404 Purpose: Return the UDP port number for receiving ODB notifications (db_watch & co)
4405
4406 Input:
4407 none
4408
4409 Output:
4410 INT *port UDP port number
4411
4412 Function value:
4413 SS_SUCCESS Successful completion
4414
4415\********************************************************************/
4416{
4417 assert(_ss_suspend_odb);
4418
4420
4421 return SS_SUCCESS;
4422}
4423
4424/*------------------------------------------------------------------*/
4426/********************************************************************\
4427
4428 Routine: ss_suspend_get_buffer_port
4429
4430 Purpose: Return the UDP port number which can be used to resume
4431 the calling thread inside a ss_suspend function. The port
4432 number can then be used by another process as a para-
4433 meter to the ss_resume function to resume the thread
4434 which called ss_suspend.
4435
4436 Input:
4437 none
4438
4439 Output:
4440 INT *port UDP port number
4441
4442 Function value:
4443 SS_SUCCESS Successful completion
4444
4445\********************************************************************/
4446{
4448
4449 if (!psuspend->ipc_recv_port) {
4451 }
4452
4453 *port = psuspend->ipc_recv_port;
4454
4455 return SS_SUCCESS;
4456}
4457
4458static int ss_suspend_process_ipc(INT millisec, INT msg, int ipc_recv_socket)
4459{
4460 char buffer[80];
4461 buffer[0] = 0;
4462 /* receive IPC message */
4463 struct sockaddr from_addr;
4464 socklen_t from_addr_size = sizeof(struct sockaddr);
4465
4466 // note: ipc_recv_socket must be set in non-blocking mode:
4467 // it looks as if we come here from ss_suspend() only if select() said
4468 // that our socket has data. but this is not true. after that select(),
4469 // ss_suspend() reads other sockets, calls other handlers, which may call
4470 // ss_suspend() recursively (i.e. via bm_receive_event() RPC call to "wait_for_more_data"
4471 // call to ss_suspend()). the recursively called ss_suspend() will
4472 // also so select() and call this function to read from this socket. then it eventually
4473 // returns, all the handlers return back to the original ss_suspend(), which
4474 // happily remembers that the original select() told us we have data. but this data
4475 // was already read by the recursively call ss_suspend(), so the socket is empty
4476 // and our recvfrom() will sleep forever. inside the mserver, this makes mserver
4477 // stop (very bad!). with the socket set to non-blocking mode
4478 // recvfrom() will never sleep and this problem is avoided. K.O. July 2022
4479 // see bug report https://bitbucket.org/tmidas/midas/issues/346/rpc-timeout-in-bm_receive_event
4480
4481 // note2: in midas, there is never a situation where we wait for data
4482 // from the ipc sockets. these sockets are used for "event buffer has data" and "odb has new data"
4483 // notifications. we check them, but we do not wait for them. this setting
4484 // the socket to non-blocking mode is safe. K.O. July 2022.
4485
4486 ssize_t size = recvfrom(ipc_recv_socket, buffer, sizeof(buffer), 0, &from_addr, &from_addr_size);
4487
4488 if (size <= 0) {
4489 //fprintf(stderr, "ss_suspend_process_ipc: recvfrom() returned %zd, errno %d (%s)\n", size, errno, strerror(errno));
4490 // return 0 means we did not do anyting. K.O.
4491 return 0;
4492 }
4493
4494 // NB: ss_suspend(MSG_BM) (and ss_suspend(MSG_ODB)) are needed to break
4495 // recursive calls to the event handler (and db_watch() handler) if these
4496 // handlers call ss_suspend() again. The rootana interactive ROOT graphics
4497 // mode does this. To prevent this recursion, event handlers must always
4498 // call ss_suspend() with MSG_BM (and MSG_ODB). K.O.
4499
4500 /* return if received requested message */
4501 if (msg == MSG_BM && buffer[0] == 'B')
4502 return SS_SUCCESS;
4503 if (msg == MSG_ODB && buffer[0] == 'O')
4504 return SS_SUCCESS;
4505
4506 // NB: do not need to check thread id, the mserver is single-threaded. K.O.
4507 int mserver_client_socket = 0;
4509 for (unsigned i = 0; i < _ss_server_acceptions->size(); i++) {
4510 if ((*_ss_server_acceptions)[i]->is_mserver) {
4511 mserver_client_socket = (*_ss_server_acceptions)[i]->send_sock;
4512 }
4513 }
4514 }
4515
4516 time_t tstart = time(NULL);
4517 int return_status = 0;
4518
4519 /* receive further messages to empty UDP queue */
4520 while (1) {
4521 char buffer_tmp[80];
4522 buffer_tmp[0] = 0;
4523 from_addr_size = sizeof(struct sockaddr);
4524
4525 // note: ipc_recv_socket must be in non-blocking mode, see comments above. K.O.
4526
4527 ssize_t size_tmp = recvfrom(ipc_recv_socket, buffer_tmp, sizeof(buffer_tmp), 0, &from_addr, &from_addr_size);
4528
4529 if (size_tmp <= 0) {
4530 //fprintf(stderr, "ss_suspend_process_ipc: second recvfrom() returned %zd, errno %d (%s)\n", size, errno, strerror(errno));
4531 break;
4532 }
4533
4534 /* stop the loop if received requested message */
4535 if (msg == MSG_BM && buffer_tmp[0] == 'B') {
4537 break;
4538 }
4539 if (msg == MSG_ODB && buffer_tmp[0] == 'O') {
4541 break;
4542 }
4543
4544 /* don't forward same MSG_BM as above */
4545 if (buffer_tmp[0] != 'B' || strcmp(buffer_tmp, buffer) != 0) {
4547 }
4548
4549 if (millisec > 0) {
4550 time_t tnow = time(NULL);
4551 // make sure we do not loop for longer than our timeout
4552 if (tnow - tstart > 1 + millisec/1000) {
4553 //printf("ss_suspend - break out dt %d, %d loops\n", (int)(tnow-tstart), count);
4554 break;
4555 }
4556 }
4557 }
4558
4559 /* call dispatcher */
4561
4562 return return_status;
4563}
4564
4565static int ss_socket_check(int sock)
4566{
4567 // copied from the old rpc_server_receive()
4568
4569 /* only check if TCP connection is broken */
4570
4571 char test_buffer[256];
4572#ifdef OS_WINNT
4573 int n_received = recv(sock, test_buffer, sizeof(test_buffer), MSG_PEEK);
4574#else
4576
4577 /* check if we caught a signal */
4578 if ((n_received == -1) && (errno == EAGAIN))
4579 return SS_SUCCESS;
4580#endif
4581
4582 if (n_received == -1) {
4583 cm_msg(MERROR, "ss_socket_check", "recv(%d,MSG_PEEK) returned %d, errno: %d (%s)", (int) sizeof(test_buffer), n_received, errno, strerror(errno));
4584 }
4585
4586 if (n_received <= 0)
4587 return SS_ABORT;
4588
4589 return SS_SUCCESS;
4590}
4591
4593{
4595 for (unsigned i = 0; i < _ss_server_acceptions->size(); i++) {
4596 /* event channel */
4597 int sock = (*_ss_server_acceptions)[i]->event_sock;
4598
4599 if (!sock)
4600 continue;
4601
4602 /* check for buffered event */
4603 int status = ss_socket_wait(sock, 1);
4604
4605 if (status == SS_SUCCESS)
4606 return true;
4607 }
4608 }
4609
4610 /* no event socket or no data in event socket */
4611 return false;
4612}
4613
4614/*------------------------------------------------------------------*/
4616/********************************************************************\
4617
4618 Routine: ss_suspend
4619
4620 Purpose: Suspend the calling thread for a specified time. If
4621 timeout (in millisec.) is negative, the thead is suspended
4622 indefinitely. It can only be resumed from another thread
4623 or process which calls ss_resume or by some data which
4624 arrives on the client or server sockets.
4625
4626 If msg equals to one of MSG_BM, MSG_ODB, the function
4627 return whenever such a message is received. This is needed
4628 to break recursive calls to the event handler and db_watch() handler:
4629
4630 Avoided recursion via ss_suspend(MSG_BM):
4631
4632 ss_suspend(0) ->
4633 -> MSG_BM message arrives in the UDP socket
4634 -> ss_suspend_process_ipc()
4635 -> cm_dispatch_ipc()
4636 -> bm_push_event()
4637 -> bm_push_buffer()
4638 -> bm_read_buffer()
4639 -> bm_wait_for_more_events()
4640 -> ss_suspend(MSG_BM) <- event buffer code calls ss_suspend() with MSG_BM set
4641 -> MSG_BM arrives arrives in the UDP socket
4642 -> ss_suspend_process_ipc(MSG_BM)
4643 -> the newly arrived MSG_BM message is discarded,
4644 recursive call to cm_dispatch_ipc(), bm_push_buffer() & co avoided
4645
4646 Incorrect recursion via the event handler where user called ss_suspend() without MSG_BM:
4647
4648 analyzer ->
4649 -> cm_yield() in the main loop
4650 -> ss_suspend(0)
4651 -> MSG_BM message arrives in the UDP socket
4652 -> ss_suspend_process_ipc(0)
4653 -> cm_dispatch_ipc()
4654 -> bm_push_event()
4655 -> bm_push_buffer()
4656 -> bm_read_buffer()
4657 -> bm_dispatch_event()
4658 -> user event handler
4659 -> user event handler ROOT graphics main loop needs to sleep
4660 -> ss_suspend(0) <--- should be ss_suspend(MSG_BM)!!!
4661 -> MSG_BM message arrives in the UDP socket
4662 -> ss_suspend_process_ipc(0) <- should be ss_suspend_process_ipc(MSG_BM)!!!
4663 -> cm_dispatch_ipc() <- without MSG_BM, calling cm_dispatch_ipc() again
4664 -> bm_push_event()
4665 -> bm_push_buffer()
4666 -> bm_read_buffer()
4667 -> bm_dispatch_event()
4668 -> user event handler <---- called recursively, very bad!
4669
4670 Input:
4671 INT millisec Timeout in milliseconds
4672 INT msg Return from ss_suspend when msg (MSG_BM, MSG_ODB) is received.
4673
4674 Output:
4675 none
4676
4677 Function value:
4678 SS_SUCCESS Requested message was received
4679 SS_TIMEOUT Timeout expired
4680 SS_SERVER_RECV Server channel got data
4681 SS_CLIENT_RECV Client channel got data
4682 SS_ABORT (RPC_ABORT) Connection lost
4683 SS_EXIT Connection closed
4684
4685\********************************************************************/
4686{
4688
4689 midas_thread_t thread_id = ss_gettid();
4690
4692
4693 //printf("ss_suspend: thread %s\n", ss_tid_to_string(thread_id).c_str());
4694
4696
4697 do {
4699 FD_ZERO(&readfds);
4700
4701 if (ss_match_thread(_ss_listen_thread, thread_id)) {
4702 /* check listen sockets */
4705 //printf("ss_suspend: thread %s listen ss_server socket %d\n", ss_tid_to_string(thread_id).c_str(), _ss_server_listen_socket);
4706 }
4707
4710 //printf("ss_suspend: thread %s listen ss_client socket %d\n", ss_tid_to_string(thread_id).c_str(), _ss_client_listen_socket);
4711 }
4712 }
4713
4714 /* check server channels */
4716 //printf("ss_suspend: thread %s server acceptions %d\n", ss_tid_to_string(thread_id).c_str(), _ss_server_num_acceptions);
4717 for (unsigned i = 0; i < _ss_server_acceptions->size(); i++) {
4718 /* RPC channel */
4719 int sock = (*_ss_server_acceptions)[i]->recv_sock;
4720
4721 if (!sock)
4722 continue;
4723
4725 //if (_suspend_struct[idx].server_acception[i].tid != ss_gettid())
4726 // continue;
4727
4728 /* watch server socket if no data in cache */
4729 if (recv_tcp_check(sock) == 0)
4730 FD_SET(sock, &readfds);
4731 /* set timeout to zero if data in cache (-> just quick check IPC)
4732 and not called from inside bm_send_event (-> wait for IPC) */
4733 else if (msg == 0)
4734 millisec = 0;
4735
4736 if (msg == 0 && msg != MSG_BM) {
4737 /* event channel */
4738 sock = (*_ss_server_acceptions)[i]->event_sock;
4739
4740 if (!sock)
4741 continue;
4742
4743 /* check for buffered event */
4745
4746 if (status == BM_ASYNC_RETURN) {
4747 /* event buffer is full and rpc_server_receive_event() is holding on
4748 * to an event it cannot get rid of. Do not read more events from
4749 * the event socket, they have nowhere to go. K.O. */
4750 } else if (status == RPC_SUCCESS) {
4751 FD_SET(sock, &readfds);
4752 }
4753 }
4754 }
4755 }
4756
4757 /* watch for messages from the mserver */
4758 if (ss_match_thread(_ss_client_thread, thread_id)) {
4761 }
4762 }
4763
4764 /* watch for UDP messages in the IPC socket: buffer and odb notifications */
4765 if (ss_match_thread(_ss_odb_thread, thread_id)) {
4768 }
4769
4770 if (psuspend->ipc_recv_socket)
4771 FD_SET(psuspend->ipc_recv_socket, &readfds);
4772
4773 struct timeval timeout;
4774
4775 timeout.tv_sec = millisec / 1000;
4776 timeout.tv_usec = (millisec % 1000) * 1000;
4777
4778 do {
4779 //printf("select millisec %d, tv_sec %d, tv_usec %d\n", millisec, (int)timeout.tv_sec, (int)timeout.tv_usec);
4780
4781 if (millisec < 0)
4782 status = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); /* blocking */
4783 else
4784 status = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
4785
4786 /* if an alarm signal was cought, restart select with reduced timeout */
4787 if (status == -1 && timeout.tv_sec >= WATCHDOG_INTERVAL / 1000)
4788 timeout.tv_sec -= WATCHDOG_INTERVAL / 1000;
4789
4790 } while (status == -1); /* dont return if an alarm signal was cought */
4791
4792 /* check listener sockets */
4793
4795 //printf("ss_suspend: thread %s rpc_server_accept socket %d\n", ss_tid_to_string(thread_id).c_str(), _ss_server_listen_socket);
4797 if (status == RPC_SHUTDOWN) {
4798 return status;
4799 }
4800 }
4801
4803 //printf("ss_suspend: thread %s rpc_client_accept socket %d\n", ss_tid_to_string(thread_id).c_str(), _ss_client_listen_socket);
4805 if (status == RPC_SHUTDOWN) {
4806 return status;
4807 }
4808 }
4809
4810 /* check server channels */
4812 for (unsigned i = 0; i < _ss_server_acceptions->size(); i++) {
4813 /* rpc channel */
4814 int sock = (*_ss_server_acceptions)[i]->recv_sock;
4815
4816 if (!sock)
4817 continue;
4818
4819 //printf("rpc index %d, socket %d, hostname \'%s\', progname \'%s\'\n", i, sock, _suspend_struct[idx].server_acception[i].host_name, _suspend_struct[idx].server_acception[i].prog_name);
4820
4821 if (recv_tcp_check(sock) || FD_ISSET(sock, &readfds)) {
4822 //printf("ss_suspend: msg %d\n", msg);
4823 if (msg == MSG_BM) {
4824 status = ss_socket_check(sock);
4825 } else {
4826 //printf("ss_suspend: rpc_server_receive_rpc() call!\n");
4828 //printf("ss_suspend: rpc_server_receive_rpc() status %d\n", status);
4829 }
4830 (*_ss_server_acceptions)[i]->last_activity = ss_millitime();
4831
4832 if (status == SS_ABORT || status == SS_EXIT || status == RPC_SHUTDOWN) {
4833 return status;
4834 }
4835
4837 }
4838
4839 /* event channel */
4840 sock = (*_ss_server_acceptions)[i]->event_sock;
4841
4842 if (!sock)
4843 continue;
4844
4845 if (FD_ISSET(sock, &readfds)) {
4846 if (msg != 0) {
4847 status = ss_socket_check(sock);
4848 } else {
4849 //printf("ss_suspend: rpc_server_receive_event() call!\n");
4851 //printf("ss_suspend: rpc_server_receive_event() status %d\n", status);
4852 }
4853 (*_ss_server_acceptions)[i]->last_activity = ss_millitime();
4854
4855 if (status == SS_ABORT || status == SS_EXIT || status == RPC_SHUTDOWN) {
4856 return status;
4857 }
4858
4860 }
4861 }
4862 }
4863
4864 /* check for messages from the mserver */
4866 int sock = _ss_client_connection->recv_sock;
4867
4868 if (FD_ISSET(sock, &readfds)) {
4870
4871 if (status == SS_ABORT) {
4872 cm_msg(MINFO, "ss_suspend", "RPC connection to mserver at \'%s\' was broken", _ss_client_connection->host_name.c_str());
4873
4874 /* close client connection if link broken */
4878
4882
4884
4885 /* exit program after broken connection to MIDAS server */
4886 return SS_ABORT;
4887 }
4888
4890 }
4891 }
4892
4893 /* check ODB IPC socket */
4896 if (status) {
4897 return status;
4898 }
4899 }
4900
4901 /* check per-thread IPC socket */
4902 if (psuspend && psuspend->ipc_recv_socket && FD_ISSET(psuspend->ipc_recv_socket, &readfds)) {
4903 status = ss_suspend_process_ipc(millisec, msg, psuspend->ipc_recv_socket);
4904 if (status) {
4905 return status;
4906 }
4907 }
4908
4909
4910 } while (millisec < 0);
4911
4912 return return_status;
4913}
4914
4915/*------------------------------------------------------------------*/
4916INT ss_resume(INT port, const char *message)
4917/********************************************************************\
4918
4919 Routine: ss_resume
4920
4921 Purpose: Resume another thread or process which called ss_suspend.
4922 The port has to be transfered (shared memory or so) from
4923 the thread or process which should be resumed. In that
4924 process it can be obtained via ss_suspend_get_port.
4925
4926 Input:
4927 INT port UDP port number
4928 INT msg Mesage id & parameter transferred to
4929 INT param target process
4930
4931 Output:
4932 none
4933
4934 Function value:
4935 SS_SUCCESS Successful completion
4936 SS_SOCKET_ERROR Socket error
4937
4938\********************************************************************/
4939{
4940 assert(_ss_suspend_odb);
4941
4942 struct sockaddr_in bind_addr;
4943
4944 memcpy(&bind_addr, &_ss_suspend_odb->bind_addr, sizeof(struct sockaddr_in));
4945 bind_addr.sin_port = htons((short) port);
4946
4947 size_t message_size = strlen(message) + 1;
4948
4949 ssize_t wr = sendto(_ss_suspend_odb->ipc_send_socket, message, message_size, 0, (struct sockaddr *) &bind_addr, sizeof(struct sockaddr_in));
4950
4951 if (wr < 0) {
4952 return SS_SOCKET_ERROR;
4953 }
4954
4955 if (((size_t)wr) != message_size) {
4956 return SS_SOCKET_ERROR;
4957 }
4958
4959 return SS_SUCCESS;
4960}
4961
4962/*------------------------------------------------------------------*/
4963/********************************************************************\
4964* *
4965* Network functions *
4966* *
4967\********************************************************************/
4968
4969/*------------------------------------------------------------------*/
4971/********************************************************************\
4972
4973 Routine: ss_socket_wait
4974
4975 Purpose: Wait for data available to read from a socket
4976
4977 Input:
4978 INT sock Socket which was previosly opened.
4979 INT millisec Timeout in ms
4980
4981 Function value:
4982 SS_SUCCESS Data is available
4983 SS_TIMEOUT Timeout
4984 SS_SOCKET_ERROR Error
4985
4986\********************************************************************/
4987{
4988 INT status;
4990 struct timeval timeout;
4991 struct timeval timeout0;
4992 DWORD start_time = 0; // start_time is only used for BSD select() behaviour (MacOS)
4993 DWORD end_time = 0;
4994
4995 FD_ZERO(&readfds);
4996 FD_SET(sock, &readfds);
4997
4998 timeout.tv_sec = millisec / 1000;
4999 timeout.tv_usec = (millisec % 1000) * 1000;
5000
5001 timeout0 = timeout;
5002
5003 while (1) {
5004 status = select(sock+1, &readfds, NULL, NULL, &timeout);
5005 //printf("ss_socket_wait: millisec %d, tv_sec %d, tv_usec %d, isset %d, status %d, errno %d (%s)\n", millisec, timeout.tv_sec, timeout.tv_usec, FD_ISSET(sock, &readfds), status, errno, strerror(errno));
5006
5007#ifndef OS_WINNT
5008 if (status<0 && errno==EINTR) { /* watchdog alarm signal */
5009 /* need to determine if select() updates "timeout" (Linux) or keeps original value (BSD) */
5010 if (timeout.tv_sec == timeout0.tv_sec) {
5011 DWORD now = ss_time();
5012 if (start_time == 0) {
5013 start_time = now;
5014 end_time = start_time + (millisec+999)/1000;
5015 }
5016 //printf("ss_socket_wait: EINTR: now %d, timeout %d, wait time %d\n", now, end_time, end_time - now);
5017 if (now > end_time)
5018 return SS_TIMEOUT;
5019 }
5020 continue;
5021 }
5022#endif
5023 if (status < 0) { /* select() syscall error */
5024 cm_msg(MERROR, "ss_socket_wait", "unexpected error, select() returned %d, errno: %d (%s)", status, errno, strerror(errno));
5025 return SS_SOCKET_ERROR;
5026 }
5027 if (status == 0) /* timeout */
5028 return SS_TIMEOUT;
5029 if (!FD_ISSET(sock, &readfds))
5030 return SS_TIMEOUT;
5031 return SS_SUCCESS;
5032 }
5033 /* NOT REACHED */
5034}
5035
5036static bool gSocketTrace = false;
5037
5038/*------------------------------------------------------------------*/
5039INT ss_socket_connect_tcp(const char* hostname, int tcp_port, int* sockp, std::string* error_msg_p)
5040{
5041 assert(sockp != NULL);
5042 assert(error_msg_p != NULL);
5043 *sockp = 0;
5044
5045#ifdef OS_WINNT
5046 {
5048
5049 /* Start windows sockets */
5050 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
5051 return RPC_NET_ERROR;
5052 }
5053#endif
5054
5055 char portname[256];
5056 sprintf(portname, "%d", tcp_port);
5057
5058 struct addrinfo *ainfo = NULL;
5059
5060 int status = getaddrinfo(hostname, portname, NULL, &ainfo);
5061
5062 if (status != 0) {
5063 *error_msg_p = msprintf("cannot resolve hostname \"%s\", getaddrinfo() error %d (%s)", hostname, status, gai_strerror(status));
5064 if (ainfo)
5066 return RPC_NET_ERROR;
5067 }
5068
5069 // NOTE: ainfo must be freeed using freeaddrinfo(ainfo);
5070
5071 int sock = 0;
5072
5073 for (const struct addrinfo *r = ainfo; r != NULL; r = r->ai_next) {
5074 if (gSocketTrace) {
5075 fprintf(stderr, "ss_socket_connect_tcp: hostname [%s] port %d addrinfo: flags %d, family %d, socktype %d, protocol %d, canonname [%s]\n",
5076 hostname,
5077 tcp_port,
5078 r->ai_flags,
5079 r->ai_family,
5080 r->ai_socktype,
5081 r->ai_protocol,
5082 r->ai_canonname);
5083 }
5084
5085 // skip anything but TCP addresses
5086 if (r->ai_socktype != SOCK_STREAM) {
5087 continue;
5088 }
5089
5090 // skip anything but TCP protocol 6
5091 if (r->ai_protocol != 6) {
5092 continue;
5093 }
5094
5095 sock = ::socket(r->ai_family, r->ai_socktype, 0);
5096
5097 if (sock <= 0) {
5098 *error_msg_p = msprintf("cannot create socket, errno %d (%s)", errno, strerror(errno));
5099 continue;
5100 }
5101
5102 status = ::connect(sock, r->ai_addr, r->ai_addrlen);
5103 if (status != 0) {
5104 if (gSocketTrace) {
5105 fprintf(stderr, "ss_socket_connect_tcp: connect() status %d, errno %d (%s)\n", status, errno, strerror(errno));
5106 }
5107 *error_msg_p = msprintf("cannot connect to host \"%s\" port %d, errno %d (%s)", hostname, tcp_port, errno, strerror(errno));
5108 ::close(sock);
5109 sock = 0;
5110 continue;
5111 }
5112 // successfully connected
5113 break;
5114 }
5115
5117 ainfo = NULL;
5118
5119 if (sock == 0) {
5120 // error_msg is already set
5121 return RPC_NET_ERROR;
5122 }
5123
5124 *sockp = sock;
5125
5126 if (gSocketTrace) {
5127 fprintf(stderr, "ss_socket_connect_tcp: hostname [%s] port %d new socket %d\n", hostname, tcp_port, *sockp);
5128 }
5129
5130 return SS_SUCCESS;
5131}
5132
5133/*------------------------------------------------------------------*/
5135{
5136 assert(sockp != NULL);
5137 assert(tcp_port_p != NULL);
5138 assert(error_msg_p != NULL);
5139
5140 *sockp = 0;
5141 *tcp_port_p = 0;
5142
5143#ifdef OS_WINNT
5144 {
5146
5147 /* Start windows sockets */
5148 if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
5149 return RPC_NET_ERROR;
5150 }
5151#endif
5152
5153#ifdef AF_INET6
5154 bool use_inet6 = true;
5155#else
5156 bool use_inet6 = false;
5157#endif
5158
5159 if (listen_localhost)
5160 use_inet6 = false;
5161
5162 /* create a socket for listening */
5163 int lsock;
5164 if (use_inet6) {
5165#ifdef AF_INET6
5167 if (lsock == -1) {
5168 if (errno == EAFNOSUPPORT) {
5169 use_inet6 = false;
5171 }
5172 }
5173#endif
5174 } else {
5176 }
5177
5178 if (lsock == -1) {
5179 *error_msg_p = msprintf("socket(AF_INET, SOCK_STREAM) failed, errno %d (%s)", errno, strerror(errno));
5180 return RPC_NET_ERROR;
5181 }
5182
5183 /* reuse address, needed if previous server stopped (30s timeout!) */
5184 int flag = 1;
5185 int status = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(int));
5186 if (status < 0) {
5187 *error_msg_p = msprintf("setsockopt(SO_REUSEADDR) failed, errno %d (%s)", errno, strerror(errno));
5188 return RPC_NET_ERROR;
5189 }
5190
5191#ifdef AF_INET6
5192#ifdef IPV6_V6ONLY
5193 if (use_inet6) {
5194 /* turn off IPV6_V6ONLY, see RFC 3493 */
5195 flag = 0;
5196 status = setsockopt(lsock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flag, sizeof(int));
5197 if (status < 0) {
5198 *error_msg_p = msprintf("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY) failed, errno %d (%s)", errno, strerror(errno));
5199 return RPC_NET_ERROR;
5200 }
5201 }
5202#else
5203#warning strange: AF_INET6 is defined, but IPV6_V6ONLY is not defined
5204#endif
5205#endif
5206
5207 if (use_inet6) {
5208#ifdef AF_INET6
5209 /* bind local node name and port to socket */
5210 struct sockaddr_in6 bind_addr6;
5211 memset(&bind_addr6, 0, sizeof(bind_addr6));
5212 bind_addr6.sin6_family = AF_INET6;
5213
5214 if (listen_localhost) {
5215 bind_addr6.sin6_addr = in6addr_loopback;
5216 } else {
5217 bind_addr6.sin6_addr = in6addr_any;
5218 }
5219
5220 if (tcp_port)
5221 bind_addr6.sin6_port = htons((short) tcp_port);
5222 else
5223 bind_addr6.sin6_port = htons(0); // OS will allocate a port number for us
5224
5225 status = bind(lsock, (struct sockaddr *) &bind_addr6, sizeof(bind_addr6));
5226 if (status < 0) {
5227 *error_msg_p = msprintf("IPv6 bind() to port %d failed, errno %d (%s)", tcp_port, errno, strerror(errno));
5228 return RPC_NET_ERROR;
5229 }
5230#endif
5231 } else {
5232 /* bind local node name and port to socket */
5233 struct sockaddr_in bind_addr;
5234 memset(&bind_addr, 0, sizeof(bind_addr));
5235 bind_addr.sin_family = AF_INET;
5236
5237 if (listen_localhost) {
5238 bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
5239 } else {
5240 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
5241 }
5242
5243 if (tcp_port)
5244 bind_addr.sin_port = htons((short) tcp_port);
5245 else
5246 bind_addr.sin_port = htons(0); // OS will allocate a port number for us
5247
5248 status = bind(lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
5249 if (status < 0) {
5250 *error_msg_p = msprintf("bind() to port %d failed, errno %d (%s)", tcp_port, errno, strerror(errno));
5251 return RPC_NET_ERROR;
5252 }
5253 }
5254
5255 /* listen for connection */
5256#ifdef OS_MSDOS
5257 status = listen(lsock, 1);
5258#else
5260#endif
5261 if (status < 0) {
5262 *error_msg_p = msprintf("listen() failed, errno %d (%s)", errno, strerror(errno));
5263 return RPC_NET_ERROR;
5264 }
5265
5266 if (use_inet6) {
5267#ifdef AF_INET6
5268 struct sockaddr_in6 addr;
5269 socklen_t sosize = sizeof(addr);
5270 status = getsockname(lsock, (struct sockaddr*)&addr, &sosize);
5271 if (status < 0) {
5272 *error_msg_p = msprintf("IPv6 getsockname() failed, errno %d (%s)", errno, strerror(errno));
5273 return RPC_NET_ERROR;
5274 }
5275
5276 *tcp_port_p = ntohs(addr.sin6_port);
5277#endif
5278 } else {
5279 struct sockaddr_in addr;
5280 socklen_t sosize = sizeof(addr);
5281 status = getsockname(lsock, (struct sockaddr*)&addr, &sosize);
5282 if (status < 0) {
5283 *error_msg_p = msprintf("getsockname() failed, errno %d (%s)", errno, strerror(errno));
5284 return RPC_NET_ERROR;
5285 }
5286
5287 *tcp_port_p = ntohs(addr.sin_port);
5288 }
5289
5290 *sockp = lsock;
5291
5292 if (gSocketTrace) {
5293 if (listen_localhost)
5294 fprintf(stderr, "ss_socket_listen_tcp: listening tcp port %d local connections only, new socket %d\n", *tcp_port_p, *sockp);
5295 else
5296 fprintf(stderr, "ss_socket_listen_tcp: listening tcp port %d all internet connections, socket %d\n", *tcp_port_p, *sockp);
5297 }
5298
5299 return SS_SUCCESS;
5300}
5301
5302/*------------------------------------------------------------------*/
5304{
5305 assert(sockp != NULL);
5306 if (gSocketTrace) {
5307 fprintf(stderr, "ss_socket_close: %d\n", *sockp);
5308 }
5309 int err = close(*sockp);
5310 if (err) {
5311 cm_msg(MERROR, "ss_socket_close", "unexpected error, close() returned %d, errno: %d (%s)", err, errno, strerror(errno));
5312 }
5313 *sockp = 0;
5314 return SS_SUCCESS;
5315}
5316
5317/*------------------------------------------------------------------*/
5318INT ss_socket_get_peer_name(int sock, std::string* hostp, int* portp)
5319{
5320 char addr[64];
5321
5322 unsigned size = sizeof(addr);
5323 int rv = getpeername(sock, (struct sockaddr *) &addr, &size);
5324
5325 //printf("getpeername() returned %d, size %d, buffer %d\n", rv, size, (int)sizeof(addr));
5326
5327 if (rv != 0) {
5328 cm_msg(MERROR, "ss_socket_get_peer_name", "Error: getpeername() returned %d, errno %d (%s)", rv, errno, strerror(errno));
5329 return SS_SOCKET_ERROR;
5330 }
5331
5332 char hostname[256];
5333 char servname[16];
5334
5335 int ret = getnameinfo((struct sockaddr*)&addr, size,
5336 hostname, sizeof(hostname),
5337 servname, sizeof(servname),
5339
5340 if (ret != 0) {
5341 cm_msg(MERROR, "ss_socket_get_peer_name", "Error: getnameinfo() error %d (%s)", ret, gai_strerror(ret));
5342 return SS_SOCKET_ERROR;
5343 }
5344
5345 //printf("getnameinfo() returned %d, hostname [%s], servname[%s]\n", ret, hostname, servname);
5346
5347 if (hostp)
5348 *hostp = hostname;
5349
5350 if (portp)
5351 *portp = atoi(servname);
5352
5353 return SS_SUCCESS;
5354}
5355
5356/*------------------------------------------------------------------*/
5357INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
5358/********************************************************************\
5359
5360 Routine: send_tcp
5361
5362 Purpose: Send network data over TCP port. Break buffer in smaller
5363 parts if larger than maximum TCP buffer size (usually 64k).
5364
5365 Input:
5366 INT sock Socket which was previosly opened.
5367 DWORD buffer_size Size of the buffer in bytes.
5368 INT flags Flags passed to send()
5369 0x10000 : do not send error message
5370
5371 Output:
5372 char *buffer Network receive buffer.
5373
5374 Function value:
5375 INT Same as send()
5376
5377\********************************************************************/
5378{
5379 DWORD count;
5380 INT status;
5381 //int net_tcp_size = NET_TCP_SIZE;
5382 int net_tcp_size = 1024 * 1024;
5383
5384 /* transfer fragments until complete buffer is transferred */
5385
5386 for (count = 0; (INT) count < (INT) buffer_size - net_tcp_size;) {
5387 status = send(sock, buffer + count, net_tcp_size, flags & 0xFFFF);
5388 if (status != -1)
5389 count += status;
5390 else {
5391#ifdef OS_UNIX
5392 if (errno == EINTR)
5393 continue;
5394#endif
5395 if ((flags & 0x10000) == 0)
5396 cm_msg(MERROR, "send_tcp",
5397 "send(socket=%d,size=%d) returned %d, errno: %d (%s)",
5399 return status;
5400 }
5401 }
5402
5403 while (count < buffer_size) {
5404 status = send(sock, buffer + count, buffer_size - count, flags & 0xFFFF);
5405 if (status != -1)
5406 count += status;
5407 else {
5408#ifdef OS_UNIX
5409 if (errno == EINTR)
5410 continue;
5411#endif
5412 if ((flags & 0x10000) == 0)
5413 cm_msg(MERROR, "send_tcp",
5414 "send(socket=%d,size=%d) returned %d, errno: %d (%s)",
5415 sock, (int) (buffer_size - count), status, errno, strerror(errno));
5416 return status;
5417 }
5418 }
5419
5420 return count;
5421}
5422
5423/*------------------------------------------------------------------*/
5424INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
5425/********************************************************************\
5426
5427 Routine: write_tcp
5428
5429 Purpose: Send network data over TCP port. Handle partial writes
5430
5431 Input:
5432 INT sock Socket which was previosly opened.
5433 DWORD buffer_size Size of the buffer in bytes.
5434 INT flags Flags passed to send()
5435 0x10000 : do not send error message
5436
5437 Output:
5438 char *buffer Network receive buffer.
5439
5440 Function value:
5441 SS_SUCCESS Everything was sent
5442 SS_SOCKET_ERROR There was a socket error
5443
5444\********************************************************************/
5445{
5446 size_t count = 0;
5447
5448 while (count < buffer_size) {
5449 ssize_t wr = write(sock, buffer + count, buffer_size - count);
5450
5451 if (wr == 0) {
5452 cm_msg(MERROR, "ss_write_tcp", "write(socket=%d,size=%d) returned zero, errno: %d (%s)", sock, (int) (buffer_size - count), errno, strerror(errno));
5453 return SS_SOCKET_ERROR;
5454 } else if (wr < 0) {
5455#ifdef OS_UNIX
5456 if (errno == EINTR)
5457 continue;
5458#endif
5459 cm_msg(MERROR, "ss_write_tcp", "write(socket=%d,size=%d) returned %d, errno: %d (%s)", sock, (int) (buffer_size - count), (int)wr, errno, strerror(errno));
5460 return SS_SOCKET_ERROR;
5461 }
5462
5463 // good write
5464 count += wr;
5465 }
5466
5467 return SS_SUCCESS;
5468}
5469
5470/*------------------------------------------------------------------*/
5471INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
5472/********************************************************************\
5473
5474 Routine: recv_string
5475
5476 Purpose: Receive network data over TCP port. Since sockets are
5477 operated in stream mode, a single transmission via send
5478 may not transfer the full data. Therefore, one has to check
5479 at the receiver side if the full data is received. If not,
5480 one has to issue several recv() commands.
5481
5482 The length of the data is determined by a trailing zero.
5483
5484 Input:
5485 INT sock Socket which was previosly opened.
5486 DWORD buffer_size Size of the buffer in bytes.
5487 INT millisec Timeout in ms
5488
5489 Output:
5490 char *buffer Network receive buffer.
5491
5492 Function value:
5493 INT String length
5494
5495\********************************************************************/
5496{
5497 INT i, status;
5498 DWORD n;
5499
5500 n = 0;
5501 memset(buffer, 0, buffer_size);
5502
5503 do {
5504 if (millisec > 0) {
5506 if (status != SS_SUCCESS)
5507 break;
5508 }
5509
5510 i = recv(sock, buffer + n, 1, 0);
5511
5512 if (i <= 0)
5513 break;
5514
5515 n++;
5516
5517 if (n >= buffer_size)
5518 break;
5519
5520 } while (buffer[n - 1] && buffer[n - 1] != 10);
5521
5522 return n - 1;
5523}
5524
5525/*------------------------------------------------------------------*/
5526INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
5527/********************************************************************\
5528
5529 Routine: recv_tcp
5530
5531 Purpose: Receive network data over TCP port. Since sockets are
5532 operated in stream mode, a single transmission via send
5533 may not transfer the full data. Therefore, one has to check
5534 at the receiver side if the full data is received. If not,
5535 one has to issue several recv() commands.
5536
5537 The length of the data is determined by the data header,
5538 which consists of two DWORDs. The first is the command code
5539 (or function id), the second is the size of the following
5540 parameters in bytes. From that size recv_tcp() determines
5541 how much data to receive.
5542
5543 Input:
5544 INT sock Socket which was previosly opened.
5545 char *net_buffer Buffer to store data to
5546 DWORD buffer_size Size of the buffer in bytes.
5547 INT flags Flags passed to recv()
5548
5549 Output:
5550 char *buffer Network receive buffer.
5551
5552 Function value:
5553 INT Same as recv()
5554
5555\********************************************************************/
5556{
5557 INT param_size, n_received, n;
5558 NET_COMMAND *nc;
5559
5560 if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
5561 cm_msg(MERROR, "recv_tcp", "parameters too large for network buffer");
5562 return -1;
5563 }
5564
5565 /* first receive header */
5566 n_received = 0;
5567 do {
5568#ifdef OS_UNIX
5569 do {
5570 n = recv(sock, net_buffer + n_received, sizeof(NET_COMMAND_HEADER), flags);
5571
5572 /* don't return if an alarm signal was cought */
5573 } while (n == -1 && errno == EINTR);
5574#else
5575 n = recv(sock, net_buffer + n_received, sizeof(NET_COMMAND_HEADER), flags);
5576#endif
5577
5578 if (n == 0) {
5579 cm_msg(MERROR, "recv_tcp", "header: recv(%d) returned %d, n_received = %d, unexpected connection closure", (int)sizeof(NET_COMMAND_HEADER), n, n_received);
5580 return n;
5581 }
5582
5583 if (n < 0) {
5584 cm_msg(MERROR, "recv_tcp", "header: recv(%d) returned %d, n_received = %d, errno: %d (%s)", (int)sizeof(NET_COMMAND_HEADER), n, n_received, errno, strerror(errno));
5585 return n;
5586 }
5587
5588 n_received += n;
5589
5590 } while (n_received < (int) sizeof(NET_COMMAND_HEADER));
5591
5592 /* now receive parameters */
5593
5594 nc = (NET_COMMAND *) net_buffer;
5595 param_size = nc->header.param_size;
5596 n_received = 0;
5597
5598 if (param_size == 0)
5599 return sizeof(NET_COMMAND_HEADER);
5600
5601 if (param_size > (INT)buffer_size) {
5602 cm_msg(MERROR, "recv_tcp", "param: receive buffer size %d is too small for received data size %d", buffer_size, param_size);
5603 return -1;
5604 }
5605
5606 do {
5607#ifdef OS_UNIX
5608 do {
5609 n = recv(sock, net_buffer + sizeof(NET_COMMAND_HEADER) + n_received, param_size - n_received, flags);
5610
5611 /* don't return if an alarm signal was cought */
5612 } while (n == -1 && errno == EINTR);
5613#else
5614 n = recv(sock, net_buffer + sizeof(NET_COMMAND_HEADER) + n_received, param_size - n_received, flags);
5615#endif
5616
5617 if (n == 0) {
5618 cm_msg(MERROR, "recv_tcp", "param: recv() returned %d, n_received = %d, unexpected connection closure", n, n_received);
5619 return n;
5620 }
5621
5622 if (n < 0) {
5623 cm_msg(MERROR, "recv_tcp", "param: recv() returned %d, n_received = %d, errno: %d (%s)", n, n_received, errno, strerror(errno));
5624 return n;
5625 }
5626
5627 n_received += n;
5628 } while (n_received < param_size);
5629
5630 return sizeof(NET_COMMAND_HEADER) + param_size;
5631}
5632
5633/*------------------------------------------------------------------*/
5634INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
5635/********************************************************************\
5636
5637 Routine: recv_tcp2
5638
5639 Purpose: Receive network data over TCP port. Since sockets are
5640 operated in stream mode, a single transmission via send
5641 may not transfer the full data. Therefore, one has to check
5642 at the receiver side if the full data is received. If not,
5643 one has to issue several recv() commands.
5644
5645 Input:
5646 INT sock Socket which was previosly opened
5647 char* net_buffer Buffer to store data
5648 int buffer_size Number of bytes to receive
5649 int timeout_ms Timeout in milliseconds
5650
5651 Output:
5652 char* net_buffer Network receive buffer
5653
5654 Function value:
5655 number of bytes received (less than buffer_size if there was a timeout), or
5656 0 : timeout and nothing was received
5657 -1 : socket error
5658
5659\********************************************************************/
5660{
5661 int n_received = 0;
5662 int flags = 0;
5663 int n;
5664
5665 //printf("recv_tcp2: %p+%d bytes, timeout %d ms!\n", net_buffer + n_received, buffer_size - n_received, timeout_ms);
5666
5667 while (n_received != buffer_size) {
5668
5669 if (timeout_ms > 0) {
5670 int status = ss_socket_wait(sock, timeout_ms);
5671 if (status == SS_TIMEOUT)
5672 return n_received;
5673 if (status != SS_SUCCESS)
5674 return -1;
5675 }
5676
5677 n = recv(sock, net_buffer + n_received, buffer_size - n_received, flags);
5678
5679 //printf("recv_tcp2: %p+%d bytes, returned %d, errno %d (%s)\n", net_buffer + n_received, buffer_size - n_received, n, errno, strerror(errno));
5680
5681#ifdef EINTR
5682 /* don't return if an alarm signal was cought */
5683 if (n == -1 && errno == EINTR)
5684 continue;
5685#endif
5686
5687 if (n == 0) {
5688 // socket closed
5689 cm_msg(MERROR, "recv_tcp2", "unexpected connection closure");
5690 return -1;
5691 }
5692
5693 if (n < 0) {
5694 // socket error
5695 cm_msg(MERROR, "recv_tcp2", "unexpected connection error, recv() errno %d (%s)", errno, strerror(errno));
5696 return -1;
5697 }
5698
5699 n_received += n;
5700 }
5701
5702 return n_received;
5703}
5704
5705
5706/*------------------------------------------------------------------*/
5707INT ss_recv_net_command(int sock, DWORD* routine_id, DWORD* param_size, char **param_ptr, int timeout_ms)
5708/********************************************************************\
5709
5710 Routine: ss_recv_net_command
5711
5712 Purpose: Receive MIDAS data packet from a TCP port. MIDAS data packet
5713 is defined by NET_COMMAND_HEADER
5714 which consists of two DWORDs. The first is the command code
5715 (or function id), the second is the size of the following
5716 parameters in bytes. From that size recv_tcp() determines
5717 how much data to receive.
5718
5719 Input:
5720 int sock Socket which was previosly opened.
5721 DWORD* routine_id routine_id from NET_COMMAND_HEADER
5722 DWORD* param_size param_size from NET_COMMAND_HEADER, size of allocated data buffer
5723 char** param_ptr pointer to allocated data buffer
5724 int timeout_ms timeout in milliseconds
5725
5726 Function value:
5727 INT SS_SUCCESS, SS_NO_MEMORY, SS_SOCKET_ERROR
5728
5729\********************************************************************/
5730{
5732 size_t n;
5733
5734 /* first receive header */
5735 n = recv_tcp2(sock, (char*)&ncbuf, sizeof(ncbuf), timeout_ms);
5736
5737 if (n == 0) {
5738 cm_msg(MERROR, "ss_recv_net_command", "timeout receiving network command header");
5739 return SS_TIMEOUT;
5740 }
5741
5742 if (n != sizeof(ncbuf)) {
5743 cm_msg(MERROR, "ss_recv_net_command", "error receiving network command header, see messages");
5744 return SS_SOCKET_ERROR;
5745 }
5746
5747 // FIXME: where is the big-endian/little-endian conversion?
5748 *routine_id = ncbuf.routine_id;
5749 *param_size = ncbuf.param_size;
5750
5751 if (*param_size == 0) {
5752 *param_ptr = NULL;
5753 return SS_SUCCESS;
5754 }
5755
5756 *param_ptr = (char *)malloc(*param_size);
5757
5758 if (*param_ptr == NULL) {
5759 cm_msg(MERROR, "ss_recv_net_command", "error allocating %d bytes for network command data", *param_size);
5760 return SS_NO_MEMORY;
5761 }
5762
5763 /* first receive header */
5764 n = recv_tcp2(sock, *param_ptr, *param_size, timeout_ms);
5765
5766 if (n == 0) {
5767 cm_msg(MERROR, "ss_recv_net_command", "timeout receiving network command data");
5768 free(*param_ptr);
5769 *param_ptr = NULL;
5770 return SS_TIMEOUT;
5771 }
5772
5773 if (n != *param_size) {
5774 cm_msg(MERROR, "ss_recv_net_command", "error receiving network command data, see messages");
5775 free(*param_ptr);
5776 *param_ptr = NULL;
5777 return SS_SOCKET_ERROR;
5778 }
5779
5780 return SS_SUCCESS;
5781}
5782
5783/*------------------------------------------------------------------*/
5784std::string ss_gethostname()
5785/********************************************************************\
5786
5787 Routine: ss_gethostname
5788
5789 Purpose: Get name of local machine using gethostname() syscall
5790
5791 Input:
5792 int buffer_size Size of the buffer in bytes.
5793
5794 Output:
5795 char *buffer receive buffer
5796
5797 Function value:
5798 INT SS_SUCCESS or SS_IO_ERROR
5799
5800\********************************************************************/
5801{
5802 char buf[256];
5803 memset(buf, 0, sizeof(buf));
5804
5805 int status = gethostname(buf, sizeof(buf)-1);
5806
5807 //printf("gethostname %d (%s)\n", status, buffer);
5808
5809 if (status != 0) {
5810 cm_msg(MERROR, "ss_gethostname", "gethostname() errno %d (%s)", errno, strerror(errno));
5811 return "";
5812 }
5813
5814 return buf;
5815}
5816
5817/*------------------------------------------------------------------*/
5818INT ss_gethostname(char* buffer, int buffer_size)
5819/********************************************************************\
5820
5821 Routine: ss_gethostname
5822
5823 Purpose: Get name of local machine using gethostname() syscall
5824
5825 Input:
5826 int buffer_size Size of the buffer in bytes.
5827
5828 Output:
5829 char *buffer receive buffer
5830
5831 Function value:
5832 INT SS_SUCCESS or SS_IO_ERROR
5833
5834\********************************************************************/
5835{
5836 std::string h = ss_gethostname();
5837
5838 if (h.length() == 0) {
5839 return SS_IO_ERROR;
5840 } else {
5841 mstrlcpy(buffer, h.c_str(), buffer_size);
5842 return SS_SUCCESS;
5843 }
5844}
5845
5846/*------------------------------------------------------------------*/
5847
5848std::string ss_getcwd()
5849{
5850 char *s = getcwd(NULL, 0);
5851 if (s) {
5852 std::string cwd = s;
5853 free(s);
5854 //printf("ss_getcwd: %s\n", cwd.c_str());
5855 return cwd;
5856 } else {
5857 return "/GETCWD-FAILED-ON-US";
5858 }
5859}
5860
5861/*------------------------------------------------------------------*/
5862
5863#ifdef OS_MSDOS
5864#ifdef sopen
5865/********************************************************************\
5866 under Turbo-C, sopen is defined as a macro instead a function.
5867 Since the PCTCP library uses sopen as a function call, we supply
5868 it here.
5869\********************************************************************/
5870
5871#undef sopen
5872
5873int sopen(const char *path, int access, int shflag, int mode)
5874{
5875 return open(path, (access) | (shflag), mode);
5876}
5877
5878#endif
5879#endif
5880
5881/*------------------------------------------------------------------*/
5882/********************************************************************\
5883* *
5884* Tape functions *
5885* *
5886\********************************************************************/
5887
5888/*------------------------------------------------------------------*/
5890/********************************************************************\
5891
5892 Routine: ss_tape_open
5893
5894 Purpose: Open tape channel
5895
5896 Input:
5897 char *path Name of tape
5898 Under Windows NT, usually \\.\tape0
5899 Under UNIX, usually /dev/tape
5900 INT oflag Open flags, same as open()
5901
5902 Output:
5903 INT *channel Channel identifier
5904
5905 Function value:
5906 SS_SUCCESS Successful completion
5907 SS_NO_TAPE No tape in device
5908 SS_DEV_BUSY Device is used by someone else
5909
5910\********************************************************************/
5911{
5912#ifdef OS_UNIX
5913 //cm_enable_watchdog(FALSE);
5914
5915 *channel = open(path, oflag, 0644);
5916
5917 //cm_enable_watchdog(TRUE);
5918
5919 if (*channel < 0)
5920 cm_msg(MERROR, "ss_tape_open", "open() returned %d, errno %d (%s)", *channel, errno, strerror(errno));
5921
5922 if (*channel < 0) {
5923 if (errno == EIO)
5924 return SS_NO_TAPE;
5925 if (errno == EBUSY)
5926 return SS_DEV_BUSY;
5927 return errno;
5928 }
5929#ifdef MTSETBLK
5930 {
5931 /* set variable block size */
5932 struct mtop arg;
5933 arg.mt_op = MTSETBLK;
5934 arg.mt_count = 0;
5935
5936 ioctl(*channel, MTIOCTOP, &arg);
5937 }
5938#endif /* MTSETBLK */
5939
5940#endif /* OS_UNIX */
5941
5942#ifdef OS_WINNT
5943 INT status;
5945
5947
5948 if (*channel == (INT) INVALID_HANDLE_VALUE) {
5949 status = GetLastError();
5951 cm_msg(MERROR, "ss_tape_open", "tape is used by other process");
5952 return SS_DEV_BUSY;
5953 }
5955 cm_msg(MERROR, "ss_tape_open", "tape device \"%s\" doesn't exist", path);
5956 return SS_NO_TAPE;
5957 }
5958
5959 cm_msg(MERROR, "ss_tape_open", "unknown error %d", status);
5960 return status;
5961 }
5962
5965 cm_msg(MERROR, "ss_tape_open", "no media in drive");
5966 return SS_NO_TAPE;
5967 }
5968
5969 /* set block size */
5970 memset(&m, 0, sizeof(m));
5971 m.BlockSize = TAPE_BUFFER_SIZE;
5973
5974#endif
5975
5976 return SS_SUCCESS;
5977}
5978
5979/*------------------------------------------------------------------*/
5981/********************************************************************\
5982
5983 Routine: ss_tape_close
5984
5985 Purpose: Close tape channel
5986
5987 Input:
5988 INT channel Channel identifier
5989
5990 Output:
5991 <none>
5992
5993 Function value:
5994 SS_SUCCESS Successful completion
5995 errno Low level error number
5996
5997\********************************************************************/
5998{
5999 INT status;
6000
6001#ifdef OS_UNIX
6002
6003 status = close(channel);
6004
6005 if (status < 0) {
6006 cm_msg(MERROR, "ss_tape_close", "close() returned %d, errno %d (%s)", status, errno, strerror(errno));
6007 return errno;
6008 }
6009#endif /* OS_UNIX */
6010
6011#ifdef OS_WINNT
6012
6013 if (!CloseHandle((HANDLE) channel)) {
6014 status = GetLastError();
6015 cm_msg(MERROR, "ss_tape_close", "unknown error %d", status);
6016 return status;
6017 }
6018#endif /* OS_WINNT */
6019
6020 return SS_SUCCESS;
6021}
6022
6023/*------------------------------------------------------------------*/
6025/********************************************************************\
6026
6027 Routine: ss_tape_status
6028
6029 Purpose: Print status information about tape
6030
6031 Input:
6032 char *path Name of tape
6033
6034 Output:
6035 <print> Tape information
6036
6037 Function value:
6038 SS_SUCCESS Successful completion
6039
6040\********************************************************************/
6041{
6042#ifdef OS_UNIX
6043 int status;
6044 char str[256];
6045 /* let 'mt' do the job */
6046 sprintf(str, "mt -f %s status", path);
6047 status = system(str);
6048 if (status == -1)
6049 return SS_TAPE_ERROR;
6050 return SS_SUCCESS;
6051#endif /* OS_UNIX */
6052
6053#ifdef OS_WINNT
6055 DWORD size;
6058 double x;
6059
6061
6062 if (channel == (INT) INVALID_HANDLE_VALUE) {
6063 status = GetLastError();
6065 cm_msg(MINFO, "ss_tape_status", "tape is used by other process");
6066 return SS_SUCCESS;
6067 }
6069 cm_msg(MINFO, "ss_tape_status", "tape device \"%s\" doesn't exist", path);
6070 return SS_SUCCESS;
6071 }
6072
6073 cm_msg(MINFO, "ss_tape_status", "unknown error %d", status);
6074 return status;
6075 }
6076
6077 /* poll media changed messages */
6080
6083 cm_msg(MINFO, "ss_tape_status", "no media in drive");
6085 return SS_SUCCESS;
6086 }
6087
6090
6091 printf("Hardware error correction is %s\n", d.ECC ? "on" : "off");
6092 printf("Hardware compression is %s\n", d.Compression ? "on" : "off");
6093 printf("Tape %s write protected\n", m.WriteProtected ? "is" : "is not");
6094
6095 if (d.FeaturesLow & TAPE_DRIVE_TAPE_REMAINING) {
6096 x = ((double) m.Remaining.LowPart + (double) m.Remaining.HighPart * 4.294967295E9)
6097 / 1000.0 / 1000.0;
6098 printf("Tape capacity remaining is %d MB\n", (int) x);
6099 } else
6100 printf("Tape capacity is not reported by tape\n");
6101
6103
6104#endif
6105
6106 return SS_SUCCESS;
6107}
6108
6109/*------------------------------------------------------------------*/
6111/********************************************************************\
6112
6113 Routine: ss_tape_write
6114
6115 Purpose: Write count bytes to tape channel
6116
6117 Input:
6118 INT channel Channel identifier
6119 void *pdata Address of data to write
6120 INT count number of bytes
6121
6122 Output:
6123 <none>
6124
6125 Function value:
6126 SS_SUCCESS Successful completion
6127 SS_IO_ERROR Physical IO error
6128 SS_TAPE_ERROR Unknown tape error
6129
6130\********************************************************************/
6131{
6132#ifdef OS_UNIX
6133 INT status;
6134
6135 do {
6137/*
6138 if (status != count)
6139 printf("count: %d - %d\n", count, status);
6140*/
6141 } while (status == -1 && errno == EINTR);
6142
6143 if (status != count) {
6144 cm_msg(MERROR, "ss_tape_write", "write() returned %d, errno %d (%s)", status, errno, strerror(errno));
6145
6146 if (errno == EIO)
6147 return SS_IO_ERROR;
6148 else
6149 return SS_TAPE_ERROR;
6150 }
6151#endif /* OS_UNIX */
6152
6153#ifdef OS_WINNT
6154 INT status;
6155 DWORD written;
6156
6158 if (written != (DWORD) count) {
6159 status = GetLastError();
6160 cm_msg(MERROR, "ss_tape_write", "error %d", status);
6161
6162 return SS_IO_ERROR;
6163 }
6164#endif /* OS_WINNT */
6165
6166 return SS_SUCCESS;
6167}
6168
6169/*------------------------------------------------------------------*/
6171/********************************************************************\
6172
6173 Routine: ss_tape_write
6174
6175 Purpose: Read count bytes to tape channel
6176
6177 Input:
6178 INT channel Channel identifier
6179 void *pdata Address of data
6180 INT *count Number of bytes to read
6181
6182 Output:
6183 INT *count Number of read
6184
6185 Function value:
6186 SS_SUCCESS Successful operation
6187 <errno> Error code
6188
6189\********************************************************************/
6190{
6191#ifdef OS_UNIX
6192 INT n, status;
6193
6194 do {
6195 n = read(channel, pdata, *count);
6196 } while (n == -1 && errno == EINTR);
6197
6198 if (n == -1) {
6199 if (errno == ENOSPC || errno == EIO)
6201 else {
6202 if (n == 0 && errno == 0)
6204 else {
6205 cm_msg(MERROR, "ss_tape_read", "unexpected tape error: n=%d, errno=%d\n", n, errno);
6206 status = errno;
6207 }
6208 }
6209 } else
6211 *count = n;
6212
6213 return status;
6214
6215#elif defined(OS_WINNT) /* OS_UNIX */
6216
6217 INT status;
6218 DWORD read;
6219
6220 if (!ReadFile((HANDLE) channel, pdata, *count, &read, NULL)) {
6221 status = GetLastError();
6224 else if (status == ERROR_FILEMARK_DETECTED)
6226 else if (status == ERROR_MORE_DATA)
6228 else
6229 cm_msg(MERROR, "ss_tape_read", "unexpected tape error: n=%d, errno=%d\n", read, status);
6230 } else
6232
6233 *count = read;
6234 return status;
6235
6236#else /* OS_WINNT */
6237
6238 return SS_SUCCESS;
6239
6240#endif
6241}
6242
6243/*------------------------------------------------------------------*/
6245/********************************************************************\
6246
6247 Routine: ss_tape_write_eof
6248
6249 Purpose: Write end-of-file to tape channel
6250
6251 Input:
6252 INT *channel Channel identifier
6253
6254 Output:
6255 <none>
6256
6257 Function value:
6258 SS_SUCCESS Successful completion
6259 errno Error number
6260
6261\********************************************************************/
6262{
6263#ifdef MTIOCTOP
6264 struct mtop arg;
6265 INT status;
6266
6267 arg.mt_op = MTWEOF;
6268 arg.mt_count = 1;
6269
6270 //cm_enable_watchdog(FALSE);
6271
6272 status = ioctl(channel, MTIOCTOP, &arg);
6273
6274 //cm_enable_watchdog(TRUE);
6275
6276 if (status < 0) {
6277 cm_msg(MERROR, "ss_tape_write_eof", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6278 return errno;
6279 }
6280#endif /* OS_UNIX */
6281
6282#ifdef OS_WINNT
6283
6285 DWORD size;
6286 INT status;
6287
6288 size = sizeof(TAPE_GET_DRIVE_PARAMETERS);
6290
6291 if (d.FeaturesHigh & TAPE_DRIVE_WRITE_FILEMARKS)
6293 else if (d.FeaturesHigh & TAPE_DRIVE_WRITE_LONG_FMKS)
6295 else if (d.FeaturesHigh & TAPE_DRIVE_WRITE_SHORT_FMKS)
6297 else
6298 cm_msg(MERROR, "ss_tape_write_eof", "tape doesn't support writing of filemarks");
6299
6300 if (status != NO_ERROR) {
6301 cm_msg(MERROR, "ss_tape_write_eof", "unknown error %d", status);
6302 return status;
6303 }
6304#endif /* OS_WINNT */
6305
6306 return SS_SUCCESS;
6307}
6308
6309/*------------------------------------------------------------------*/
6311/********************************************************************\
6312
6313 Routine: ss_tape_fskip
6314
6315 Purpose: Skip count number of files on a tape
6316
6317 Input:
6318 INT *channel Channel identifier
6319 INT count Number of files to skip
6320
6321 Output:
6322 <none>
6323
6324 Function value:
6325 SS_SUCCESS Successful completion
6326 errno Error number
6327
6328\********************************************************************/
6329{
6330#ifdef MTIOCTOP
6331 struct mtop arg;
6332 INT status;
6333
6334 if (count > 0)
6335 arg.mt_op = MTFSF;
6336 else
6337 arg.mt_op = MTBSF;
6338 arg.mt_count = abs(count);
6339
6340 //cm_enable_watchdog(FALSE);
6341
6342 status = ioctl(channel, MTIOCTOP, &arg);
6343
6344 //cm_enable_watchdog(TRUE);
6345
6346 if (status < 0) {
6347 cm_msg(MERROR, "ss_tape_fskip", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6348 return errno;
6349 }
6350#endif /* OS_UNIX */
6351
6352#ifdef OS_WINNT
6353 INT status;
6354
6356
6358 return SS_END_OF_TAPE;
6359
6360 if (status != NO_ERROR) {
6361 cm_msg(MERROR, "ss_tape_fskip", "error %d", status);
6362 return status;
6363 }
6364#endif /* OS_WINNT */
6365
6366 return SS_SUCCESS;
6367}
6368
6369/*------------------------------------------------------------------*/
6371/********************************************************************\
6372
6373 Routine: ss_tape_rskip
6374
6375 Purpose: Skip count number of records on a tape
6376
6377 Input:
6378 INT *channel Channel identifier
6379 INT count Number of records to skip
6380
6381 Output:
6382 <none>
6383
6384 Function value:
6385 SS_SUCCESS Successful completion
6386 errno Error number
6387
6388\********************************************************************/
6389{
6390#ifdef MTIOCTOP
6391 struct mtop arg;
6392 INT status;
6393
6394 if (count > 0)
6395 arg.mt_op = MTFSR;
6396 else
6397 arg.mt_op = MTBSR;
6398 arg.mt_count = abs(count);
6399
6400 //cm_enable_watchdog(FALSE);
6401
6402 status = ioctl(channel, MTIOCTOP, &arg);
6403
6404 //cm_enable_watchdog(TRUE);
6405
6406 if (status < 0) {
6407 cm_msg(MERROR, "ss_tape_rskip", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6408 return errno;
6409 }
6410#endif /* OS_UNIX */
6411
6412#ifdef OS_WINNT
6413 INT status;
6414
6416 if (status != NO_ERROR) {
6417 cm_msg(MERROR, "ss_tape_rskip", "error %d", status);
6418 return status;
6419 }
6420#endif /* OS_WINNT */
6421
6422 return CM_SUCCESS;
6423}
6424
6425/*------------------------------------------------------------------*/
6427/********************************************************************\
6428
6429 Routine: ss_tape_rewind
6430
6431 Purpose: Rewind tape
6432
6433 Input:
6434 INT channel Channel identifier
6435
6436 Output:
6437 <none>
6438
6439 Function value:
6440 SS_SUCCESS Successful completion
6441 errno Error number
6442
6443\********************************************************************/
6444{
6445#ifdef MTIOCTOP
6446 struct mtop arg;
6447 INT status;
6448
6449 arg.mt_op = MTREW;
6450 arg.mt_count = 0;
6451
6452 //cm_enable_watchdog(FALSE);
6453
6454 status = ioctl(channel, MTIOCTOP, &arg);
6455
6456 //cm_enable_watchdog(TRUE);
6457
6458 if (status < 0) {
6459 cm_msg(MERROR, "ss_tape_rewind", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6460 return errno;
6461 }
6462#endif /* OS_UNIX */
6463
6464#ifdef OS_WINNT
6465 INT status;
6466
6468 if (status != NO_ERROR) {
6469 cm_msg(MERROR, "ss_tape_rewind", "error %d", status);
6470 return status;
6471 }
6472#endif /* OS_WINNT */
6473
6474 return CM_SUCCESS;
6475}
6476
6477/*------------------------------------------------------------------*/
6479/********************************************************************\
6480
6481 Routine: ss_tape_spool
6482
6483 Purpose: Spool tape forward to end of recorded data
6484
6485 Input:
6486 INT channel Channel identifier
6487
6488 Output:
6489 <none>
6490
6491 Function value:
6492 SS_SUCCESS Successful completion
6493 errno Error number
6494
6495\********************************************************************/
6496{
6497#ifdef MTIOCTOP
6498 struct mtop arg;
6499 INT status;
6500
6501#ifdef MTEOM
6502 arg.mt_op = MTEOM;
6503#else
6504 arg.mt_op = MTSEOD;
6505#endif
6506 arg.mt_count = 0;
6507
6508 //cm_enable_watchdog(FALSE);
6509
6510 status = ioctl(channel, MTIOCTOP, &arg);
6511
6512 //cm_enable_watchdog(TRUE);
6513
6514 if (status < 0) {
6515 cm_msg(MERROR, "ss_tape_rewind", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6516 return errno;
6517 }
6518#endif /* OS_UNIX */
6519
6520#ifdef OS_WINNT
6521 INT status;
6522
6524 if (status != NO_ERROR) {
6525 cm_msg(MERROR, "ss_tape_spool", "error %d", status);
6526 return status;
6527 }
6528#endif /* OS_WINNT */
6529
6530 return CM_SUCCESS;
6531}
6532
6533/*------------------------------------------------------------------*/
6535/********************************************************************\
6536
6537 Routine: ss_tape_mount
6538
6539 Purpose: Mount tape
6540
6541 Input:
6542 INT channel Channel identifier
6543
6544 Output:
6545 <none>
6546
6547 Function value:
6548 SS_SUCCESS Successful completion
6549 errno Error number
6550
6551\********************************************************************/
6552{
6553#ifdef MTIOCTOP
6554 struct mtop arg;
6555 INT status;
6556
6557#ifdef MTLOAD
6558 arg.mt_op = MTLOAD;
6559#else
6560 arg.mt_op = MTNOP;
6561#endif
6562 arg.mt_count = 0;
6563
6564 //cm_enable_watchdog(FALSE);
6565
6566 status = ioctl(channel, MTIOCTOP, &arg);
6567
6568 //cm_enable_watchdog(TRUE);
6569
6570 if (status < 0) {
6571 cm_msg(MERROR, "ss_tape_mount", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6572 return errno;
6573 }
6574#endif /* OS_UNIX */
6575
6576#ifdef OS_WINNT
6577 INT status;
6578
6580 if (status != NO_ERROR) {
6581 cm_msg(MERROR, "ss_tape_mount", "error %d", status);
6582 return status;
6583 }
6584#endif /* OS_WINNT */
6585
6586 return CM_SUCCESS;
6587}
6588
6589/*------------------------------------------------------------------*/
6591/********************************************************************\
6592
6593 Routine: ss_tape_unmount
6594
6595 Purpose: Unmount tape
6596
6597 Input:
6598 INT channel Channel identifier
6599
6600 Output:
6601 <none>
6602
6603 Function value:
6604 SS_SUCCESS Successful completion
6605 errno Error number
6606
6607\********************************************************************/
6608{
6609#ifdef MTIOCTOP
6610 struct mtop arg;
6611 INT status;
6612
6613#ifdef MTOFFL
6614 arg.mt_op = MTOFFL;
6615#else
6616 arg.mt_op = MTUNLOAD;
6617#endif
6618 arg.mt_count = 0;
6619
6620 //cm_enable_watchdog(FALSE);
6621
6622 status = ioctl(channel, MTIOCTOP, &arg);
6623
6624 //cm_enable_watchdog(TRUE);
6625
6626 if (status < 0) {
6627 cm_msg(MERROR, "ss_tape_unmount", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6628 return errno;
6629 }
6630#endif /* OS_UNIX */
6631
6632#ifdef OS_WINNT
6633 INT status;
6634
6636 if (status != NO_ERROR) {
6637 cm_msg(MERROR, "ss_tape_unmount", "error %d", status);
6638 return status;
6639 }
6640#endif /* OS_WINNT */
6641
6642 return CM_SUCCESS;
6643}
6644
6645/*------------------------------------------------------------------*/
6647/********************************************************************\
6648Routine: ss_tape_get_blockn
6649Purpose: Ask the tape channel for the present block number
6650Input:
6651INT *channel Channel identifier
6652Function value:
6653blockn: >0 = block number, =0 option not available, <0 errno
6654\********************************************************************/
6655{
6656#if defined(OS_DARWIN)
6657
6658 return 0;
6659
6660#elif defined(OS_UNIX)
6661
6662 INT status;
6663 struct mtpos arg;
6664
6665 //cm_enable_watchdog(FALSE);
6666 status = ioctl(channel, MTIOCPOS, &arg);
6667 //cm_enable_watchdog(TRUE);
6668 if (status < 0) {
6669 if (errno == EIO)
6670 return 0;
6671 else {
6672 cm_msg(MERROR, "ss_tape_get_blockn", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6673 return -errno;
6674 }
6675 }
6676 return (arg.mt_blkno);
6677
6678#elif defined(OS_WINNT)
6679
6680 INT status;
6682 unsigned long size;
6683 /* I'm not sure the partition count corresponds to the block count */
6685 return (media.PartitionCount);
6686
6687#endif
6688}
6689
6690/*------------------------------------------------------------------*/
6691/********************************************************************\
6692* *
6693* Disk functions *
6694* *
6695\********************************************************************/
6696
6697/*------------------------------------------------------------------*/
6698double ss_disk_free(const char *path)
6699/********************************************************************\
6700
6701 Routine: ss_disk_free
6702
6703 Purpose: Return free disk space
6704
6705 Input:
6706 char *path Name of a file in file system to check
6707
6708 Output:
6709
6710 Function value:
6711 doube Number of bytes free on disk
6712
6713\********************************************************************/
6714{
6715#ifdef OS_UNIX
6716#if defined(OS_OSF1)
6717 struct statfs st;
6718 statfs(path, &st, sizeof(st));
6719 return (double) st.f_bavail * st.f_bsize;
6720#elif defined(OS_LINUX)
6721 struct statfs st;
6722 int status;
6723 status = statfs(path, &st);
6724 if (status != 0)
6725 return -1;
6726 return (double) st.f_bavail * st.f_bsize;
6727#elif defined(OS_SOLARIS)
6728 struct statvfs st;
6729 statvfs(path, &st);
6730 return (double) st.f_bavail * st.f_bsize;
6731#elif defined(OS_IRIX)
6732 struct statfs st;
6733 statfs(path, &st, sizeof(struct statfs), 0);
6734 return (double) st.f_bfree * st.f_bsize;
6735#else
6736 struct fs_data st;
6737 statfs(path, &st);
6738 return (double) st.fd_otsize * st.fd_bfree;
6739#endif
6740
6741#elif defined(OS_WINNT) /* OS_UNIX */
6746 char str[80];
6747
6748 strcpy(str, path);
6749 if (strchr(str, ':') != NULL) {
6750 *(strchr(str, ':') + 1) = 0;
6753 } else
6755
6757#else /* OS_WINNT */
6758
6759 return 1e9;
6760
6761#endif
6762}
6763
6764#if defined(OS_ULTRIX) || defined(OS_WINNT)
6765int fnmatch(const char *pat, const char *str, const int flag)
6766{
6767 while (*str != '\0') {
6768 if (*pat == '*') {
6769 pat++;
6770 if ((str = strchr(str, *pat)) == NULL)
6771 return -1;
6772 }
6773 if (*pat == *str) {
6774 pat++;
6775 str++;
6776 } else
6777 return -1;
6778 }
6779 if (*pat == '\0')
6780 return 0;
6781 else
6782 return -1;
6783}
6784#endif
6785
6786#ifdef OS_WINNT
6789#endif
6790
6791INT ss_file_find(const char *path, const char *pattern, char **plist)
6792{
6794
6795 int count = ss_file_find(path, pattern, &list);
6796 if (count <= 0)
6797 return count;
6798
6799 size_t size = list.size();
6800 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6801 for (size_t i=0; i<size; i++) {
6802 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6804 }
6805
6806 return size;
6807}
6808
6809INT ss_file_find(const char *path, const char *pattern, STRING_LIST *plist)
6810/********************************************************************\
6811
6812 Routine: ss_file_find
6813
6814 Purpose: Return list of files matching 'pattern' from the 'path' location
6815
6816 Input:
6817 char *path Name of a file in file system to check
6818 char *pattern pattern string (wildcard allowed)
6819
6820 Output:
6821 char **plist pointer to the lfile list
6822
6823 Function value:
6824 int Number of files matching request
6825
6826\********************************************************************/
6827{
6828 assert(plist);
6829 // Check if the directory exists
6830 if (access(path, F_OK) != 0) {
6831 return -1; // Return -1 files if directory doesn't exist
6832 }
6833
6834#ifdef OS_UNIX
6836 struct dirent *dp;
6837
6838 plist->clear();
6839 if ((dir_pointer = opendir(path)) == NULL)
6840 return 0;
6841 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6842 if (fnmatch(pattern, dp->d_name, 0) == 0 && (dp->d_type == DT_REG || dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN)) {
6843 plist->push_back(dp->d_name);
6845 }
6846 }
6848#endif
6849#ifdef OS_WINNT
6850 char str[255];
6851 int first;
6852
6853 strcpy(str, path);
6854 strcat(str, "\\");
6855 strcat(str, pattern);
6856 first = 1;
6858 *plist->clear();
6861 return 0;
6862 first = 0;
6863 plist->push_back(lpfdata->cFileName);
6864 i++;
6865 while (FindNextFile(pffile, lpfdata)) {
6866 plist->push_back(lpfdata->cFileName);
6867 i++;
6868 }
6869 free(lpfdata);
6870#endif
6871 return plist->size();
6872}
6873
6874INT ss_dir_find(const char *path, const char *pattern, char** plist)
6875{
6877
6878 int count = ss_dir_find(path, pattern, &list);
6879 if (count <= 0)
6880 return count;
6881
6882 size_t size = list.size();
6883 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6884 for (size_t i=0; i<size; i++) {
6885 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6887 }
6888
6889 return size;
6890}
6891
6892INT ss_dir_find(const char *path, const char *pattern, STRING_LIST *plist)
6893/********************************************************************\
6894
6895 Routine: ss_dir_find
6896
6897 Purpose: Return list of direcories matching 'pattern' from the 'path' location
6898
6899 Input:
6900 char *path Name of a file in file system to check
6901 char *pattern pattern string (wildcard allowed)
6902
6903 Output:
6904 char **plist pointer to the lfile list
6905
6906 Function value:
6907 int Number of files matching request
6908
6909 \********************************************************************/
6910{
6911 assert(plist);
6912#ifdef OS_UNIX
6914 struct dirent *dp;
6915
6916 if ((dir_pointer = opendir(path)) == NULL)
6917 return 0;
6918 plist->clear();
6919 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6920 if (fnmatch(pattern, dp->d_name, 0) == 0 && dp->d_type == DT_DIR) {
6921 plist->push_back(dp->d_name);
6923 }
6924 }
6926#endif
6927#ifdef OS_WINNT
6928 char str[255];
6929 int first;
6930
6931 strcpy(str, path);
6932 strcat(str, "\\");
6933 strcat(str, pattern);
6934 first = 1;
6935 plist->clear();
6939 return 0;
6940 first = 0;
6941 plist->push_back(lpfdata->cFileName);
6942 while (FindNextFile(pffile, lpfdata)) {
6943 plist->push_back(lpfdata->cFileName);
6944 }
6945 free(lpfdata);
6946#endif
6947 return plist->size();
6948}
6949
6950INT ss_dirlink_find(const char *path, const char *pattern, char** plist)
6951{
6953
6954 int count = ss_dirlink_find(path, pattern, &list);
6955 if (count <= 0)
6956 return count;
6957
6958 size_t size = list.size();
6959 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6960 for (size_t i=0; i<size; i++) {
6961 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6963 }
6964
6965 return size;
6966}
6967
6968INT ss_dirlink_find(const char *path, const char *pattern, STRING_LIST *plist)
6969/********************************************************************\
6970
6971 Routine: ss_dirlink_find
6972
6973 Purpose: Return list of direcories and links matching 'pattern' from the 'path' location
6974
6975 Input:
6976 char *path Name of a file in file system to check
6977 char *pattern pattern string (wildcard allowed)
6978
6979 Output:
6980 char **plist pointer to the lfile list
6981
6982 Function value:
6983 int Number of files matching request
6984
6985 \********************************************************************/
6986{
6987 assert(plist);
6988#ifdef OS_UNIX
6990 struct dirent *dp;
6991
6992 if ((dir_pointer = opendir(path)) == NULL)
6993 return 0;
6994 plist->clear();
6995 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6996 if (fnmatch(pattern, dp->d_name, 0) == 0) {
6997 /* must have a "/" at the end, otherwise also links to files are accepted */
6998 std::string full_path = std::string(path) + "/" + dp->d_name + "/";
6999 struct stat st;
7000 if (lstat(full_path.c_str(), &st) == 0 && (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
7001 plist->push_back(dp->d_name);
7002 }
7003 }
7004 }
7006#endif
7007#ifdef OS_WINNT
7008 char str[255];
7009 int first;
7010
7011 strcpy(str, path);
7012 strcat(str, "\\");
7013 strcat(str, pattern);
7014 first = 1;
7015 plist->clear();
7019 return 0;
7020 first = 0;
7021 plist->push_back(lpfdata->cFileName);
7022 while (FindNextFile(pffile, lpfdata)) {
7023 plist->push_back(lpfdata->cFileName);
7024 }
7025 free(lpfdata);
7026#endif
7027 return plist->size();
7028}
7029
7030INT ss_file_remove(const char *path)
7031/********************************************************************\
7032
7033 Routine: ss_file_remove
7034
7035 Purpose: remove (delete) file given through the path
7036
7037 Input:
7038 char *path Name of a file in file system to check
7039
7040 Output:
7041
7042 Function value:
7043 int function error 0= ok, -1 check errno
7044
7045\********************************************************************/
7046{
7047 return remove(path);
7048}
7049
7050double ss_file_size(const char *path)
7051/********************************************************************\
7052
7053 Routine: ss_file_size
7054
7055 Purpose: Return file size in bytes for the given path
7056
7057 Input:
7058 char *path Name of a file in file system to check
7059
7060 Output:
7061
7062 Function value:
7063 double File size
7064
7065\********************************************************************/
7066{
7067#ifdef _LARGEFILE64_SOURCE
7068 struct stat64 stat_buf;
7069 int status;
7070
7071 /* allocate buffer with file size */
7072 status = stat64(path, &stat_buf);
7073 if (status != 0)
7074 return -1;
7075 return (double) stat_buf.st_size;
7076#else
7077 struct stat stat_buf;
7078 int status;
7079
7080 /* allocate buffer with file size */
7081 status = stat(path, &stat_buf);
7082 if (status != 0)
7083 return -1;
7084 return (double) stat_buf.st_size;
7085#endif
7086}
7087
7088time_t ss_file_time(const char *path)
7089/********************************************************************\
7090
7091 Routine: ss_file_time
7092
7093 Purpose: Return time of last file modification
7094
7095 Input:
7096 char *path Name of a file in file system to check
7097
7098 Output:
7099
7100 Function value:
7101 time_t File modification time
7102
7103\********************************************************************/
7104{
7105#ifdef _LARGEFILE64_SOURCE
7106 struct stat64 stat_buf;
7107 int status;
7108
7109 /* allocate buffer with file size */
7110 status = stat64(path, &stat_buf);
7111 if (status != 0)
7112 return -1;
7113 return stat_buf.st_mtime;
7114#else
7115 struct stat stat_buf;
7116 int status;
7117
7118 /* allocate buffer with file size */
7119 status = stat(path, &stat_buf);
7120 if (status != 0)
7121 return -1;
7122 return stat_buf.st_mtime;
7123#endif
7124}
7125
7126double ss_disk_size(const char *path)
7127/********************************************************************\
7128
7129 Routine: ss_disk_size
7130
7131 Purpose: Return full disk space
7132
7133 Input:
7134 char *path Name of a file in file system to check
7135
7136 Output:
7137
7138 Function value:
7139 doube Number of bytes free on disk
7140
7141\********************************************************************/
7142{
7143#ifdef OS_UNIX
7144#if defined(OS_OSF1)
7145 struct statfs st;
7146 statfs(path, &st, sizeof(st));
7147 return (double) st.f_blocks * st.f_fsize;
7148#elif defined(OS_LINUX)
7149 int status;
7150 struct statfs st;
7151 status = statfs(path, &st);
7152 if (status != 0)
7153 return -1;
7154 return (double) st.f_blocks * st.f_bsize;
7155#elif defined(OS_SOLARIS)
7156 struct statvfs st;
7157 statvfs(path, &st);
7158 if (st.f_frsize > 0)
7159 return (double) st.f_blocks * st.f_frsize;
7160 else
7161 return (double) st.f_blocks * st.f_bsize;
7162#elif defined(OS_ULTRIX)
7163 struct fs_data st;
7164 statfs(path, &st);
7165 return (double) st.fd_btot * 1024;
7166#elif defined(OS_IRIX)
7167 struct statfs st;
7168 statfs(path, &st, sizeof(struct statfs), 0);
7169 return (double) st.f_blocks * st.f_bsize;
7170#else
7171#error ss_disk_size not defined for this OS
7172#endif
7173#endif /* OS_UNIX */
7174
7175#ifdef OS_WINNT
7180 char str[80];
7181
7182 strcpy(str, path);
7183 if (strchr(str, ':') != NULL) {
7184 *(strchr(str, ':') + 1) = 0;
7187 } else
7189
7191#endif /* OS_WINNT */
7192
7193 return 1e9;
7194}
7195
7196int ss_file_exist(const char *path)
7197/********************************************************************\
7198
7199 Routine: ss_file_exist
7200
7201 Purpose: Check if a file exists
7202
7203 Input:
7204 char *path Name of a file in file to check
7205
7206 Output:
7207
7208 Function value:
7209 int 1: file exists
7210 0: file does not exist
7211
7212 \********************************************************************/
7213{
7214#ifdef OS_UNIX
7215 struct stat buf;
7216
7217 int retval = stat(path, &buf);
7218 //printf("retval %d, errno %d (%s)\n", retval, errno, strerror(errno));
7219 if (retval < 0)
7220 return 0;
7221 if (S_ISDIR(buf.st_mode))
7222 return 0;
7223#endif
7224
7225 int fd = open(path, O_RDONLY, 0);
7226 if (fd < 0)
7227 return 0;
7228 close(fd);
7229 return 1;
7230}
7231
7232int ss_file_link_exist(const char *path)
7233/********************************************************************\
7234
7235 Routine: ss_file_link_exist
7236
7237 Purpose: Check if a symbolic link file exists
7238
7239 Input:
7240 char *path Name of a file in file to check
7241
7242 Output:
7243
7244 Function value:
7245 int 1: file exists
7246 0: file does not exist
7247
7248 \********************************************************************/
7249{
7250#ifdef OS_UNIX
7251 struct stat buf;
7252
7253 int retval = lstat(path, &buf);
7254 if (retval < 0)
7255 return 0;
7256 if (S_ISLNK(buf.st_mode))
7257 return 1;
7258 return 0;
7259#endif
7260
7261 return 0;
7262}
7263
7264int ss_dir_exist(const char *path)
7265/********************************************************************\
7266
7267 Routine: ss_dir_exist
7268
7269 Purpose: Check if a directory exists
7270
7271 Input:
7272 char *path Name of a file in file to check
7273
7274 Output:
7275
7276 Function value:
7277 int 1: file exists
7278 0: file does not exist
7279
7280 \********************************************************************/
7281{
7282#ifdef OS_UNIX
7283 struct stat buf;
7284
7285 int retval = stat(path, &buf);
7286 //printf("retval %d, errno %d (%s)\n", retval, errno, strerror(errno));
7287 if (retval < 0)
7288 return 0;
7289 if (!S_ISDIR(buf.st_mode))
7290 return 0;
7291#else
7292#warning ss_dir_exist() is not implemented!
7293#endif
7294 return 1;
7295}
7296
7297int ss_file_copy(const char *src, const char *dst, bool append)
7298/********************************************************************\
7299
7300 Routine: ss_file_copy
7301
7302 Purpose: Copy file "src" to file "dst"
7303
7304 Input:
7305 const char *src Source file name
7306 const char *dst Destination file name
7307
7308 Output:
7309
7310 Function value:
7311 int function error 0= ok, -1 check errno
7312
7313 \********************************************************************/
7314{
7315 int fd_to, fd_from;
7316 char buf[4096];
7317 ssize_t nread;
7318 int saved_errno;
7319
7320 fd_from = open(src, O_RDONLY);
7321 if (fd_from < 0)
7322 return -1;
7323
7324 if (append)
7325 fd_to = open(dst, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, 0666);
7326 else
7327 fd_to = open(dst, O_WRONLY | O_CREAT | O_EXCL, 0666);
7328 if (fd_to < 0)
7329 goto out_error;
7330
7331 while (nread = read(fd_from, buf, sizeof(buf)), nread > 0) {
7332 char *out_ptr = buf;
7334
7335 do {
7337
7338 if (nwritten >= 0) {
7339 nread -= nwritten;
7340 out_ptr += nwritten;
7341 } else if (errno != EINTR) {
7342 goto out_error;
7343 }
7344 } while (nread > 0);
7345 }
7346
7347 if (nread == 0) {
7348 if (close(fd_to) < 0) {
7349 fd_to = -1;
7350 goto out_error;
7351 }
7352 close(fd_from);
7353
7354 /* Success! */
7355 return 0;
7356 }
7357
7358 out_error:
7360
7361 close(fd_from);
7362 if (fd_to >= 0)
7363 close(fd_to);
7364
7366 return -1;
7367}
7368
7369/*------------------------------------------------------------------*/
7370/********************************************************************\
7371* *
7372* Screen functions *
7373* *
7374\********************************************************************/
7375
7376/*------------------------------------------------------------------*/
7378/********************************************************************\
7379
7380 Routine: ss_clear_screen
7381
7382 Purpose: Clear the screen
7383
7384 Input:
7385 <none>
7386
7387 Output:
7388 <none>
7389
7390 Function value:
7391 <none>
7392
7393\********************************************************************/
7394{
7395#ifdef OS_WINNT
7396
7398 COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
7399 BOOL bSuccess;
7401 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
7402 DWORD dwConSize; /* number of character cells in the current buffer */
7403
7405
7406 /* get the number of character cells in the current buffer */
7408 dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
7409
7410 /* fill the entire screen with blanks */
7412
7413 /* put the cursor at (0, 0) */
7415 return;
7416
7417#endif /* OS_WINNT */
7418#if defined(OS_UNIX) || defined(OS_VXWORKS) || defined(OS_VMS)
7419 printf("\033[2J");
7420#endif
7421#ifdef OS_MSDOS
7422 clrscr();
7423#endif
7424}
7425
7426/*------------------------------------------------------------------*/
7427void ss_set_screen_size(int x, int y)
7428/********************************************************************\
7429
7430 Routine: ss_set_screen_size
7431
7432 Purpose: Set the screen size in character cells
7433
7434 Input:
7435 <none>
7436
7437 Output:
7438 <none>
7439
7440 Function value:
7441 <none>
7442
7443\********************************************************************/
7444{
7445#ifdef OS_WINNT
7446
7449
7450 coordSize.X = (short) x;
7451 coordSize.Y = (short) y;
7454
7455#else /* OS_WINNT */
7456#endif
7457}
7458
7459/*------------------------------------------------------------------*/
7460void ss_printf(INT x, INT y, const char *format, ...)
7461/********************************************************************\
7462
7463 Routine: ss_printf
7464
7465 Purpose: Print string at given cursor position
7466
7467 Input:
7468 INT x,y Cursor position, starting from zero,
7469 x=0 and y=0 left upper corner
7470
7471 char *format Format string for printf
7472 ... Arguments for printf
7473
7474 Output:
7475 <none>
7476
7477 Function value:
7478 <none>
7479
7480\********************************************************************/
7481{
7482 char str[256];
7484
7485 va_start(argptr, format);
7486 vsprintf(str, (char *) format, argptr);
7487 va_end(argptr);
7488
7489#ifdef OS_WINNT
7490 {
7494
7496
7497 dwWriteCoord.X = (short) x;
7498 dwWriteCoord.Y = (short) y;
7499
7501 }
7502
7503#endif /* OS_WINNT */
7504
7505#if defined(OS_UNIX) || defined(OS_VXWORKS) || defined(OS_VMS)
7506 printf("\033[%1d;%1dH", y + 1, x + 1);
7507 printf("%s", str);
7508 fflush(stdout);
7509#endif
7510
7511#ifdef OS_MSDOS
7512 gotoxy(x + 1, y + 1);
7513 cputs(str);
7514#endif
7515}
7516
7517/*------------------------------------------------------------------*/
7518char *ss_getpass(const char *prompt)
7519/********************************************************************\
7520
7521 Routine: ss_getpass
7522
7523 Purpose: Read password without echoing it at the screen
7524
7525 Input:
7526 char *prompt Prompt string
7527
7528 Output:
7529 <none>
7530
7531 Function value:
7532 char* Pointer to password
7533
7534\********************************************************************/
7535{
7536 static char password[32];
7537
7538 fprintf(stdout, "%s", prompt);
7539 fflush(stdout);
7540 memset(password, 0, sizeof(password));
7541
7542#ifdef OS_UNIX
7543 return (char *) getpass("");
7544#elif defined(OS_WINNT)
7545 {
7548
7551 ReadConsole(hConsole, password, sizeof(password), &nCharsRead, NULL);
7553 printf("\n");
7554
7555 if (password[strlen(password) - 1] == '\r')
7556 password[strlen(password) - 1] = 0;
7557
7558 return password;
7559 }
7560#elif defined(OS_MSDOS)
7561 {
7562 char c, *ptr;
7563
7564 ptr = password;
7565 while ((c = getchar()) != EOF && c != '\n')
7566 *ptr++ = c;
7567 *ptr = 0;
7568
7569 printf("\n");
7570 return password;
7571 }
7572#else
7573 {
7574 ss_gets(password, 32);
7575 return password;
7576 }
7577#endif
7578}
7579
7580/*------------------------------------------------------------------*/
7582/********************************************************************\
7583
7584 Routine: ss_getchar
7585
7586 Purpose: Read a single character
7587
7588 Input:
7589 BOOL reset Reset terminal to standard mode
7590
7591 Output:
7592 <none>
7593
7594 Function value:
7595 int 0 for no character available
7596 CH_xxs for special character
7597 n ASCII code for normal character
7598 -1 function not available on this OS
7599
7600\********************************************************************/
7601{
7602#ifdef OS_UNIX
7603
7604 static BOOL init = FALSE;
7605 static struct termios save_termios;
7606 struct termios buf;
7607 int i, fd;
7608 char c[3];
7609
7610 if (_daemon_flag)
7611 return 0;
7612
7613 fd = fileno(stdin);
7614
7615 if (reset) {
7616 if (init)
7618 init = FALSE;
7619 return 0;
7620 }
7621
7622 if (!init) {
7623 tcgetattr(fd, &save_termios);
7624 memcpy(&buf, &save_termios, sizeof(buf));
7625
7626 buf.c_lflag &= ~(ECHO | ICANON | IEXTEN);
7627
7628 buf.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON);
7629
7630 buf.c_cflag &= ~(CSIZE | PARENB);
7631 buf.c_cflag |= CS8;
7632 /* buf.c_oflag &= ~(OPOST); */
7633 buf.c_cc[VMIN] = 0;
7634 buf.c_cc[VTIME] = 0;
7635
7636 tcsetattr(fd, TCSAFLUSH, &buf);
7637 init = TRUE;
7638 }
7639
7640 memset(c, 0, 3);
7641 i = read(fd, c, 1);
7642
7643 if (i == 0)
7644 return 0;
7645
7646 /* check if ESC */
7647 if (c[0] == 27) {
7648 i = read(fd, c, 2);
7649 if (i == 0) /* return if only ESC */
7650 return 27;
7651
7652 /* cursor keys return 2 chars, others 3 chars */
7653 if (c[1] < 65) {
7654 i = read(fd, c, 1);
7655 }
7656
7657 /* convert ESC sequence to CH_xxx */
7658 switch (c[1]) {
7659 case 49:
7660 return CH_HOME;
7661 case 50:
7662 return CH_INSERT;
7663 case 51:
7664 return CH_DELETE;
7665 case 52:
7666 return CH_END;
7667 case 53:
7668 return CH_PUP;
7669 case 54:
7670 return CH_PDOWN;
7671 case 65:
7672 return CH_UP;
7673 case 66:
7674 return CH_DOWN;
7675 case 67:
7676 return CH_RIGHT;
7677 case 68:
7678 return CH_LEFT;
7679 }
7680 }
7681
7682 /* BS/DEL -> BS */
7683 if (c[0] == 127)
7684 return CH_BS;
7685
7686 return c[0];
7687
7688#elif defined(OS_WINNT)
7689
7690 static BOOL init = FALSE;
7691 static INT repeat_count = 0;
7692 static INT repeat_char;
7697
7698 /* find out if we are under W95 */
7699 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7700 GetVersionEx(&vi);
7701
7702 if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
7703 /* under W95, console doesn't work properly */
7704 int c;
7705
7706 if (!kbhit())
7707 return 0;
7708
7709 c = getch();
7710 if (c == 224) {
7711 c = getch();
7712 switch (c) {
7713 case 71:
7714 return CH_HOME;
7715 case 72:
7716 return CH_UP;
7717 case 73:
7718 return CH_PUP;
7719 case 75:
7720 return CH_LEFT;
7721 case 77:
7722 return CH_RIGHT;
7723 case 79:
7724 return CH_END;
7725 case 80:
7726 return CH_DOWN;
7727 case 81:
7728 return CH_PDOWN;
7729 case 82:
7730 return CH_INSERT;
7731 case 83:
7732 return CH_DELETE;
7733 }
7734 }
7735 return c;
7736 }
7737
7739
7740 if (reset) {
7742 init = FALSE;
7743 return 0;
7744 }
7745
7746 if (!init) {
7748 init = TRUE;
7749 }
7750
7751 if (repeat_count) {
7752 repeat_count--;
7753 return repeat_char;
7754 }
7755
7757
7758 if (nCharsRead == 0)
7759 return 0;
7760
7762
7763 if (ir.EventType != KEY_EVENT)
7764 return ss_getchar(0);
7765
7766 if (!ir.Event.KeyEvent.bKeyDown)
7767 return ss_getchar(0);
7768
7769 if (ir.Event.KeyEvent.wRepeatCount > 1) {
7770 repeat_count = ir.Event.KeyEvent.wRepeatCount - 1;
7771 repeat_char = ir.Event.KeyEvent.uChar.AsciiChar;
7772 return repeat_char;
7773 }
7774
7775 if (ir.Event.KeyEvent.uChar.AsciiChar)
7776 return ir.Event.KeyEvent.uChar.AsciiChar;
7777
7778 if (ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY)) {
7779 switch (ir.Event.KeyEvent.wVirtualKeyCode) {
7780 case 33:
7781 return CH_PUP;
7782 case 34:
7783 return CH_PDOWN;
7784 case 35:
7785 return CH_END;
7786 case 36:
7787 return CH_HOME;
7788 case 37:
7789 return CH_LEFT;
7790 case 38:
7791 return CH_UP;
7792 case 39:
7793 return CH_RIGHT;
7794 case 40:
7795 return CH_DOWN;
7796 case 45:
7797 return CH_INSERT;
7798 case 46:
7799 return CH_DELETE;
7800 }
7801
7802 return ir.Event.KeyEvent.wVirtualKeyCode;
7803 }
7804
7805 return ss_getchar(0);
7806
7807#elif defined(OS_MSDOS)
7808
7809 int c;
7810
7811 if (!kbhit())
7812 return 0;
7813
7814 c = getch();
7815 if (!c) {
7816 c = getch();
7817 switch (c) {
7818 case 71:
7819 return CH_HOME;
7820 case 72:
7821 return CH_UP;
7822 case 73:
7823 return CH_PUP;
7824 case 75:
7825 return CH_LEFT;
7826 case 77:
7827 return CH_RIGHT;
7828 case 79:
7829 return CH_END;
7830 case 80:
7831 return CH_DOWN;
7832 case 81:
7833 return CH_PDOWN;
7834 case 82:
7835 return CH_INSERT;
7836 case 83:
7837 return CH_DELETE;
7838 }
7839 }
7840 return c;
7841
7842#else
7843 return -1;
7844#endif
7845}
7846
7847/*------------------------------------------------------------------*/
7848char *ss_gets(char *string, int size)
7849/********************************************************************\
7850
7851 Routine: ss_gets
7852
7853 Purpose: Read a line from standard input. Strip trailing new line
7854 character. Return in a loop so that it cannot be interrupted
7855 by an alarm() signal (like under Sun Solaris)
7856
7857 Input:
7858 INT size Size of string
7859
7860 Output:
7861 BOOL string Return string
7862
7863 Function value:
7864 char Return string
7865
7866\********************************************************************/
7867{
7868 char *p;
7869
7870 do {
7871 p = fgets(string, size, stdin);
7872 } while (p == NULL);
7873
7874
7875 if (strlen(p) > 0 && p[strlen(p) - 1] == '\n')
7876 p[strlen(p) - 1] = 0;
7877
7878 return p;
7879}
7880
7881/*------------------------------------------------------------------*/
7882/********************************************************************\
7883* *
7884* Direct IO functions *
7885* *
7886\********************************************************************/
7887
7888/*------------------------------------------------------------------*/
7890{
7891#ifdef OS_WINNT
7892
7893 /* under Windows NT, use DirectIO driver to open ports */
7894
7896 HANDLE hdio = 0;
7897 DWORD buffer[] = { 6, 0, 0, 0 };
7898 DWORD size;
7899
7900 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7901 GetVersionEx(&vi);
7902
7903 /* use DirectIO driver under NT to gain port access */
7904 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
7905 hdio = CreateFile("\\\\.\\directio", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7906 if (hdio == INVALID_HANDLE_VALUE) {
7907 printf("hyt1331.c: Cannot access IO ports (No DirectIO driver installed)\n");
7908 return -1;
7909 }
7910
7911 /* open ports */
7912 buffer[1] = start;
7913 buffer[2] = end;
7914 if (!DeviceIoControl(hdio, (DWORD) 0x9c406000, &buffer, sizeof(buffer), NULL, 0, &size, NULL))
7915 return -1;
7916 }
7917
7918 return SS_SUCCESS;
7919#else
7920 return SS_SUCCESS;
7921#endif
7922}
7923
7924/*------------------------------------------------------------------*/
7926{
7927#ifdef OS_WINNT
7928
7929 /* under Windows NT, use DirectIO driver to lock ports */
7930
7932 HANDLE hdio;
7933 DWORD buffer[] = { 7, 0, 0, 0 };
7934 DWORD size;
7935
7936 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7937 GetVersionEx(&vi);
7938
7939 /* use DirectIO driver under NT to gain port access */
7940 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
7941 hdio = CreateFile("\\\\.\\directio", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7942 if (hdio == INVALID_HANDLE_VALUE) {
7943 printf("hyt1331.c: Cannot access IO ports (No DirectIO driver installed)\n");
7944 return -1;
7945 }
7946
7947 /* lock ports */
7948 buffer[1] = start;
7949 buffer[2] = end;
7950 if (!DeviceIoControl(hdio, (DWORD) 0x9c406000, &buffer, sizeof(buffer), NULL, 0, &size, NULL))
7951 return -1;
7952 }
7953
7954 return SS_SUCCESS;
7955#else
7956 return SS_SUCCESS;
7957#endif
7958}
7959
7960/*------------------------------------------------------------------*/
7961/********************************************************************\
7962* *
7963* Encryption *
7964* *
7965\********************************************************************/
7966
7967#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
7968
7969char *ss_crypt(const char *buf, const char *salt)
7970/********************************************************************\
7971
7972 Routine: ss_crypt
7973
7974 Purpose: Simple fake of UNIX crypt(3) function, until we get
7975 a better one
7976
7977 Input:
7978 char *buf Plain password
7979 char *slalt Two random characters
7980 events. Can be used to skip events
7981
7982 Output:
7983 <none>
7984
7985 Function value:
7986 char* Encrypted password
7987
7988\********************************************************************/
7989{
7990 int i, seed;
7991 static char enc_pw[13];
7992
7993 memset(enc_pw, 0, sizeof(enc_pw));
7994 enc_pw[0] = salt[0];
7995 enc_pw[1] = salt[1];
7996
7997 for (i = 0; i < 8 && buf[i]; i++)
7998 enc_pw[i + 2] = buf[i];
7999 for (; i < 8; i++)
8000 enc_pw[i + 2] = 0;
8001
8002 seed = 123;
8003 for (i = 2; i < 13; i++) {
8004 seed = 5 * seed + 27 + enc_pw[i];
8005 enc_pw[i] = (char) bin_to_ascii(seed & 0x3F);
8006 }
8007
8008 return enc_pw;
8009}
8010
8011/*------------------------------------------------------------------*/
8012/********************************************************************\
8013* *
8014* NaN's *
8015* *
8016\********************************************************************/
8017
8018double ss_nan()
8019{
8020 double nan;
8021
8022 nan = 0;
8023 nan = 0 / nan;
8024 return nan;
8025}
8026
8027#ifdef OS_WINNT
8028#include <float.h>
8029#ifndef isnan
8030#define isnan(x) _isnan(x)
8031#endif
8032#ifndef finite
8033#define finite(x) _finite(x)
8034#endif
8035#elif defined(OS_LINUX)
8036#include <math.h>
8037#endif
8038
8039int ss_isnan(double x)
8040{
8041 return isnan(x);
8042}
8043
8044int ss_isfin(double x)
8045{
8046#ifdef FP_INFINITE
8047 /* new-style finite() */
8048 return isfinite(x);
8049#else
8050 /* old-style finite() */
8051 return finite(x);
8052#endif
8053}
8054
8055/*------------------------------------------------------------------*/
8056/********************************************************************\
8057* *
8058* Stack Trace *
8059* *
8060\********************************************************************/
8061
8062#ifndef NO_EXECINFO
8063
8064#ifdef OS_LINUX
8065#include <execinfo.h>
8066#endif
8067
8068#define N_STACK_HISTORY 500
8071
8072INT ss_stack_get(char ***string)
8073{
8074#ifdef OS_LINUX
8075#define MAX_STACK_DEPTH 16
8076
8077 void *trace[MAX_STACK_DEPTH];
8078 int size;
8079
8081 *string = backtrace_symbols(trace, size);
8082 return size;
8083#else
8084 return 0;
8085#endif
8086}
8087
8089{
8090 char **string;
8091 int i, n;
8092
8093 n = ss_stack_get(&string);
8094 for (i = 0; i < n; i++)
8095 printf("%s\n", string[i]);
8096 if (n > 0)
8097 free(string);
8098}
8099
8101{
8102 char **string;
8103 int i, n;
8104
8105 if (stack_history_pointer == -1) {
8107 memset(stack_history, 0, sizeof(stack_history));
8108 }
8111 n = ss_stack_get(&string);
8112 for (i = 2; i < n; i++) {
8115 }
8116 free(string);
8117
8118 mstrlcpy(stack_history[stack_history_pointer], "=========================", 80);
8120}
8121
8122void ss_stack_history_dump(char *filename)
8123{
8124 FILE *f;
8125 int i, j;
8126
8127 f = fopen(filename, "wt");
8128 if (f != NULL) {
8130 for (i = 0; i < N_STACK_HISTORY; i++) {
8131 if (strlen(stack_history[j]) > 0)
8132 fprintf(f, "%s\n", stack_history[j]);
8133 j = (j + 1) % N_STACK_HISTORY;
8134 }
8135 fclose(f);
8136 printf("Stack dump written to %s\n", filename);
8137 } else
8138 printf("Cannot open %s: errno=%d\n", filename, errno);
8139}
8140
8141#endif
8142
8143// Method to check if a given string is valid UTF-8. Returns 1 if it is.
8144// This method was taken from stackoverflow user Christoph, specifically
8145// http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
8146bool ss_is_valid_utf8(const char * string)
8147{
8148 assert(string);
8149
8150 // FIXME: this function over-reads the input array. K.O. May 2021
8151
8152 const unsigned char * bytes = (const unsigned char *)string;
8153 while(*bytes) {
8154 if( (// ASCII
8155 // use bytes[0] <= 0x7F to allow ASCII control characters
8156 bytes[0] == 0x09 ||
8157 bytes[0] == 0x0A ||
8158 bytes[0] == 0x0D ||
8159 (0x20 <= bytes[0] && bytes[0] <= 0x7E)
8160 )
8161 ) {
8162 bytes += 1;
8163 continue;
8164 }
8165
8166 if( (// non-overlong 2-byte
8167 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
8168 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
8169 )
8170 ) {
8171 bytes += 2;
8172 continue;
8173 }
8174
8175 if( (// excluding overlongs
8176 bytes[0] == 0xE0 &&
8177 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
8178 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8179 ) ||
8180 (// straight 3-byte
8181 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
8182 bytes[0] == 0xEE ||
8183 bytes[0] == 0xEF) &&
8184 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8185 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8186 ) ||
8187 (// excluding surrogates
8188 bytes[0] == 0xED &&
8189 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
8190 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8191 )
8192 ) {
8193 bytes += 3;
8194 continue;
8195 }
8196
8197 if( (// planes 1-3
8198 bytes[0] == 0xF0 &&
8199 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
8200 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8201 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8202 ) ||
8203 (// planes 4-15
8204 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
8205 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8206 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8207 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8208 ) ||
8209 (// plane 16
8210 bytes[0] == 0xF4 &&
8211 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
8212 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8213 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8214 )
8215 ) {
8216 bytes += 4;
8217 continue;
8218 }
8219
8220 //printf("ss_is_valid_utf8(): string [%s], not utf8 at offset %d, byte %d, [%s]\n", string, (int)((char*)bytes-(char*)string), (int)(0xFF&bytes[0]), bytes);
8221 //abort();
8222
8223 return false;
8224 }
8225
8226 return true;
8227}
8228
8229bool ss_repair_utf8(char* string)
8230{
8231 assert(string);
8232
8233 bool modified = false;
8234
8235 //std::string original = string;
8236
8237 // FIXME: this function over-reads the input array. K.O. May 2021
8238
8239 unsigned char * bytes = (unsigned char *)string;
8240 while(*bytes) {
8241 if( (// ASCII
8242 // use bytes[0] <= 0x7F to allow ASCII control characters
8243 bytes[0] == 0x09 ||
8244 bytes[0] == 0x0A ||
8245 bytes[0] == 0x0D ||
8246 (0x20 <= bytes[0] && bytes[0] <= 0x7E)
8247 )
8248 ) {
8249 bytes += 1;
8250 continue;
8251 }
8252
8253 if( (// non-overlong 2-byte
8254 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
8255 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
8256 )
8257 ) {
8258 bytes += 2;
8259 continue;
8260 }
8261
8262 if( (// excluding overlongs
8263 bytes[0] == 0xE0 &&
8264 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
8265 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8266 ) ||
8267 (// straight 3-byte
8268 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
8269 bytes[0] == 0xEE ||
8270 bytes[0] == 0xEF) &&
8271 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8272 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8273 ) ||
8274 (// excluding surrogates
8275 bytes[0] == 0xED &&
8276 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
8277 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8278 )
8279 ) {
8280 bytes += 3;
8281 continue;
8282 }
8283
8284 if( (// planes 1-3
8285 bytes[0] == 0xF0 &&
8286 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
8287 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8288 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8289 ) ||
8290 (// planes 4-15
8291 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
8292 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8293 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8294 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8295 ) ||
8296 (// plane 16
8297 bytes[0] == 0xF4 &&
8298 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
8299 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8300 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8301 )
8302 ) {
8303 bytes += 4;
8304 continue;
8305 }
8306
8307 if (bytes[0] == 0) // end of string
8308 break;
8309
8310 bytes[0] = '?';
8311 bytes += 1;
8312
8313 modified = true;
8314 }
8315
8316 //if (modified) {
8317 // printf("ss_repair_utf8(): invalid UTF8 string [%s] changed to [%s]\n", original.c_str(), string);
8318 //} else {
8319 // //printf("ss_repair_utf8(): string [%s] is ok\n", string);
8320 //}
8321
8322 return modified;
8323}
8324
8325bool ss_repair_utf8(std::string& s)
8326{
8327 // C++11 std::string data() is same as c_str(), NUL-terminated.
8328 // C++17 std::string data() is not "const".
8329 // https://en.cppreference.com/w/cpp/string/basic_string/data
8330 return ss_repair_utf8((char*)s.data()); // FIXME: C++17 or newer, do not need to drop the "const". K.O. May 2021
8331}
8332
8333std::chrono::time_point<std::chrono::high_resolution_clock> ss_us_start()
8334{
8335 return std::chrono::high_resolution_clock::now();
8336}
8337
8338unsigned int ss_us_since(std::chrono::time_point<std::chrono::high_resolution_clock> start) {
8339 auto elapsed = std::chrono::high_resolution_clock::now() - start;
8340 return std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
8341}
8342
/* end of msfunctionc */
8344/* emacs
8345 * Local Variables:
8346 * tab-width: 8
8347 * c-basic-offset: 3
8348 * indent-tabs-mode: nil
8349 * End:
8350 */
#define FALSE
Definition cfortran.h:309
#define LONG
Definition crc32c.cxx:230
#define EXPRT
Definition esone.h:28
TRIGGER_SETTINGS ts
INT cm_dispatch_ipc(const char *message, int message_size, int client_socket)
Definition midas.cxx:5407
std::string cm_get_path()
Definition midas.cxx:1551
std::string cm_get_experiment_name()
Definition midas.cxx:1594
#define CM_SUCCESS
Definition midas.h:582
#define BM_ASYNC_RETURN
Definition midas.h:613
#define BM_SUCCESS
Definition midas.h:605
#define SS_END_OF_FILE
Definition midas.h:685
#define SS_SUCCESS
Definition midas.h:663
#define SS_TAPE_ERROR
Definition midas.h:682
#define SS_NO_MUTEX
Definition midas.h:691
#define SS_ABORT
Definition midas.h:677
#define SS_NO_THREAD
Definition midas.h:672
#define SS_FILE_ERROR
Definition midas.h:669
#define SS_NO_MEMORY
Definition midas.h:665
#define SS_SERVER_RECV
Definition midas.h:675
#define SS_INVALID_HANDLE
Definition midas.h:667
#define SS_TIMEOUT
Definition midas.h:674
#define SS_SIZE_MISMATCH
Definition midas.h:690
#define SS_NO_SEMAPHORE
Definition midas.h:670
#define SS_NO_DRIVER
Definition midas.h:683
#define SS_CREATED
Definition midas.h:664
#define SS_INVALID_NAME
Definition midas.h:666
#define SS_INVALID_ADDRESS
Definition midas.h:668
#define SS_EXIT
Definition midas.h:678
#define SS_END_OF_TAPE
Definition midas.h:684
#define SS_NO_TAPE
Definition midas.h:679
#define SS_DEV_BUSY
Definition midas.h:680
#define SS_IO_ERROR
Definition midas.h:681
#define SS_SOCKET_ERROR
Definition midas.h:673
#define SS_CLIENT_RECV
Definition midas.h:676
#define RPC_SHUTDOWN
Definition midas.h:707
#define RPC_SUCCESS
Definition midas.h:698
#define RPC_NET_ERROR
Definition midas.h:701
unsigned int DWORD
Definition mcstd.h:51
#define BM_NO_WAIT
Definition midas.h:366
#define MINFO
Definition midas.h:560
#define MERROR
Definition midas.h:559
#define O_BINARY
Definition msystem.h:226
#define MAX_STRING_LENGTH
Definition msystem.h:113
#define MSG_BM
Definition msystem.h:302
#define FD_SETSIZE
Definition msystem.h:206
#define MSG_ODB
Definition msystem.h:303
std::string ss_gethostname()
Definition system.cxx:5784
INT ss_suspend(INT millisec, INT msg)
Definition system.cxx:4615
INT ss_suspend_set_rpc_thread(midas_thread_t thread_id)
Definition system.cxx:4074
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_suspend_get_odb_port(INT *port)
Definition system.cxx:4399
bool ss_is_valid_utf8(const char *string)
Definition system.cxx:8146
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3229
INT ss_thread_kill(midas_thread_t thread_id)
Definition system.cxx:2455
INT ss_suspend_init_odb_port()
Definition system.cxx:4377
INT ss_dir_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6874
static midas_thread_t _ss_server_thread
Definition system.cxx:4061
void ss_stack_print()
Definition system.cxx:8088
int ss_isnan(double x)
Definition system.cxx:8039
BOOL ss_kbhit()
Definition system.cxx:3736
INT ss_exception_handler(void(*func)(void))
Definition system.cxx:3916
bool ss_event_socket_has_data()
Definition system.cxx:4592
INT ss_shm_flush(const char *name, const void *adr, size_t size, HNDLE handle, bool wait_for_thread)
Definition system.cxx:1178
static void ss_suspend_close(SUSPEND_STRUCT *psuspend)
Definition system.cxx:4279
double ss_disk_size(const char *path)
Definition system.cxx:7126
time_t ss_mktime(struct tm *tms)
Definition system.cxx:3437
static int _ss_server_listen_socket
Definition system.cxx:4055
DWORD ss_millitime()
Definition system.cxx:3465
int ss_file_exist(const char *path)
Definition system.cxx:7196
static void check_shm_host()
Definition system.cxx:170
INT EXPRT ss_thread_set_name(std::string name)
Definition system.cxx:2498
static int ss_suspend_process_ipc(INT millisec, INT msg, int ipc_recv_socket)
Definition system.cxx:4458
INT ss_tape_rskip(INT channel, INT count)
Definition system.cxx:6370
INT ss_tape_write(INT channel, void *pdata, INT count)
Definition system.cxx:6110
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2532
INT ss_shm_delete(const char *name)
Definition system.cxx:911
static std::mutex gTzMutex
Definition system.cxx:3425
INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
Definition system.cxx:5634
SUSPEND_STRUCT * ss_suspend_get_struct(midas_thread_t thread_id)
Definition system.cxx:4237
INT ss_suspend_set_client_listener(int listen_socket)
Definition system.cxx:4356
int ss_isfin(double x)
Definition system.cxx:8044
static RPC_SERVER_CONNECTION * _ss_client_connection
Definition system.cxx:4059
INT ss_dirlink_find(const char *path, const char *pattern, char **plist)
Definition system.cxx:6950
INT ss_getchar(BOOL reset)
Definition system.cxx:7581
static std::atomic_bool s_semaphore_trace
Definition system.cxx:2529
static BOOL _daemon_flag
Definition system.cxx:2071
INT ss_tape_rewind(INT channel)
Definition system.cxx:6426
INT ss_shm_flush_thread(void *p)
Definition system.cxx:1138
INT ss_tape_fskip(INT channel, INT count)
Definition system.cxx:6310
INT ss_tape_close(INT channel)
Definition system.cxx:5980
char c
Definition system.cxx:1318
static midas_thread_t _ss_odb_thread
Definition system.cxx:4051
static struct @3 test_align
#define N_STACK_HISTORY
Definition system.cxx:8068
double ss_disk_free(const char *path)
Definition system.cxx:6698
time_t ss_file_time(const char *path)
Definition system.cxx:7088
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5318
static int ss_socket_check(int sock)
Definition system.cxx:4565
int ss_file_link_exist(const char *path)
Definition system.cxx:7232
std::string ss_getcwd()
Definition system.cxx:5848
INT ss_tape_get_blockn(INT channel)
Definition system.cxx:6646
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
double ss_file_size(const char *path)
Definition system.cxx:7050
char stack_history[N_STACK_HISTORY][80]
Definition system.cxx:8069
INT ss_tape_status(char *path)
Definition system.cxx:6024
INT ss_getpid(void)
Definition system.cxx:1379
DWORD ss_settime(DWORD seconds)
Definition system.cxx:3547
std::chrono::time_point< std::chrono::high_resolution_clock > ss_us_start()
Definition system.cxx:8333
INT ss_tape_mount(INT channel)
Definition system.cxx:6534
char * ss_getpass(const char *prompt)
Definition system.cxx:7518
INT ss_directio_give_port(INT start, INT end)
Definition system.cxx:7889
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
static bool ss_match_thread(midas_thread_t tid1, midas_thread_t tid2)
Definition system.cxx:4065
INT ss_shell(int sock)
Definition system.cxx:1832
unsigned int ss_us_since(std::chrono::time_point< std::chrono::high_resolution_clock > start)
Definition system.cxx:8338
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
void ss_set_screen_size(int x, int y)
Definition system.cxx:7427
midas_thread_t ss_thread_create(INT(*thread_func)(void *), void *param)
Definition system.cxx:2382
void ss_stack_history_entry(char *tag)
Definition system.cxx:8100
std::string ss_execs(const char *cmd)
Definition system.cxx:2309
static struct @4 test_padding
static int _ss_client_listen_socket
Definition system.cxx:4056
static SUSPEND_STRUCT * _ss_suspend_odb
Definition system.cxx:4052
INT ss_tape_spool(INT channel)
Definition system.cxx:6478
INT ss_stack_get(char ***string)
Definition system.cxx:8072
std::string ss_get_cmdline(void)
Definition system.cxx:1519
double d
Definition system.cxx:1313
std::string ss_tid_to_string(midas_thread_t thread_id)
Definition system.cxx:1643
INT ss_file_remove(const char *path)
Definition system.cxx:7030
void ss_stack_history_dump(char *filename)
Definition system.cxx:8122
INT ss_tape_read(INT channel, void *pdata, INT *count)
Definition system.cxx:6170
int ss_file_copy(const char *src, const char *dst, bool append)
Definition system.cxx:7297
std::string ss_replace_env_variables(const std::string &inputPath)
Definition system.cxx:2284
INT ss_daemon_init(BOOL keep_stdout)
Definition system.cxx:2073
INT ss_alarm(INT millitime, void(*func)(int))
Definition system.cxx:3809
static midas_thread_t _ss_listen_thread
Definition system.cxx:4054
DWORD ss_time()
Definition system.cxx:3534
static INT ss_suspend_init_struct(SUSPEND_STRUCT *psuspend)
Definition system.cxx:4084
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
double ss_time_sec()
Definition system.cxx:3539
INT ss_get_struct_padding()
Definition system.cxx:1347
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
void(* MidasExceptionHandler)(void)
Definition system.cxx:3861
INT ss_tape_unmount(INT channel)
Definition system.cxx:6590
double ss_nan()
Definition system.cxx:8018
static RPC_SERVER_ACCEPTION_LIST * _ss_server_acceptions
Definition system.cxx:4062
INT ss_tape_write_eof(INT channel)
Definition system.cxx:6244
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
INT ss_exec(const char *command, INT *pid)
Definition system.cxx:2204
INT ss_shm_unprotect(HNDLE handle, void **adr, size_t shm_size, BOOL read, BOOL write, const char *caller_name)
Definition system.cxx:1062
bool ss_repair_utf8(char *string)
Definition system.cxx:8229
static std::atomic_int s_semaphore_nest_level
Definition system.cxx:2530
void ss_clear_screen()
Definition system.cxx:7377
std::string EXPRT ss_thread_get_name()
Definition system.cxx:2516
void ss_kill(int pid)
Definition system.cxx:1473
char * ss_crypt(const char *buf, const char *salt)
Definition system.cxx:7969
INT ss_tape_open(char *path, INT oflag, INT *channel)
Definition system.cxx:5889
BOOL ss_existpid(INT pid)
Definition system.cxx:2140
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
void ss_printf(INT x, INT y, const char *format,...)
Definition system.cxx:7460
#define bin_to_ascii(c)
Definition system.cxx:7967
INT ss_socket_close(int *sockp)
Definition system.cxx:5303
INT ss_directio_lock_port(INT start, INT end)
Definition system.cxx:7925
struct suspend_struct SUSPEND_STRUCT
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_suspend_set_server_listener(int listen_socket)
Definition system.cxx:4349
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
char c
Definition system.cxx:1312
static bool gSocketTrace
Definition system.cxx:5036
BOOL ss_pid_exists(int pid)
Definition system.cxx:1442
static midas_thread_t _ss_client_thread
Definition system.cxx:4058
std::string ss_get_executable(void)
Definition system.cxx:1490
INT ss_timezone()
Definition system.cxx:3652
static void check_shm_type(const char *shm_type)
Definition system.cxx:82
INT ss_system(const char *command)
Definition system.cxx:2188
INT ss_shm_protect(HNDLE handle, void *adr, size_t shm_size)
Definition system.cxx:1005
static std::vector< SUSPEND_STRUCT * > _ss_suspend_vector
Definition system.cxx:4049
int stack_history_pointer
Definition system.cxx:8070
static int ss_shm_name(const char *name, std::string &mem_name, std::string &file_name, std::string &shm_name)
Definition system.cxx:232
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
double d
Definition system.cxx:1317
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:929
std::vector< RPC_SERVER_ACCEPTION * > RPC_SERVER_ACCEPTION_LIST
Definition msystem.h:402
INT recv_tcp_check(int sock)
Definition midas.cxx:14379
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15893
INT rpc_client_accept(int lsock)
Definition midas.cxx:15620
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:16003
INT rpc_server_accept(int lsock)
Definition midas.cxx:15363
INT rpc_client_dispatch(int sock)
Definition midas.cxx:11967
INT channel
DWORD n[4]
Definition mana.cxx:247
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
char addr[128]
Definition mcnaf.cxx:104
double count
Definition mdump.cxx:33
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
#define closesocket(s)
Definition melog.cxx:29
std::string msprintf(const char *format,...)
Definition midas.cxx:419
INT HNDLE
Definition midas.h:132
#define O_LARGEFILE
Definition midas.h:210
INT midas_thread_t
Definition midas.h:179
#define CH_END
Definition midas.h:455
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
#define CH_DOWN
Definition midas.h:459
int INT
Definition midas.h:129
#define CH_PUP
Definition midas.h:456
#define CH_DELETE
Definition midas.h:454
#define CH_RIGHT
Definition midas.h:460
#define CH_INSERT
Definition midas.h:453
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define CH_LEFT
Definition midas.h:461
#define TRUE
Definition midas.h:182
#define TAPE_BUFFER_SIZE
Definition midas.h:264
#define CH_HOME
Definition midas.h:452
#define POINTER_T
Definition midas.h:166
#define CH_UP
Definition midas.h:458
std::vector< std::string > STRING_LIST
Definition midas.h:246
INT MUTEX_T
Definition midas.h:237
#define CH_BS
Definition midas.h:445
#define CH_PDOWN
Definition midas.h:457
#define end
#define message(type, str)
#define read(n, a, f)
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
static std::string remove(const std::string s, char c)
Definition mjsonrpc.cxx:253
static FILE * fp
#define SOMAXCONN
#define PATH_MAX
int gettimeofday(struct timeval *tp, void *tzp)
timeval tv
Definition msysmon.cxx:1095
INT thread(void *p)
Definition odbedit.cxx:43
MUTEX_T * tm
Definition odbedit.cxx:39
INT j
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
char file_name[256]
Definition odbhist.cxx:41
DWORD status
Definition odbhist.cxx:39
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
std::string file_name
Definition system.cxx:1132
void * buf
Definition system.cxx:1134
NET_COMMAND_HEADER header
Definition msystem.h:293
struct sockaddr_in bind_addr
Definition system.cxx:4046
midas_thread_t thread_id
Definition system.cxx:4042
double d
Definition system.cxx:1313
char c
Definition system.cxx:1312
@ DIR
Definition test_init.cxx:7
static te_expr * list(state *s)
Definition tinyexpr.c:567