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