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 if (lsock == -1) {
5096 if (errno == EAFNOSUPPORT) {
5097 use_inet6 = false;
5099 }
5100 }
5101#endif
5102 } else {
5104 }
5105
5106 if (lsock == -1) {
5107 *error_msg_p = msprintf("socket(AF_INET, SOCK_STREAM) failed, errno %d (%s)", errno, strerror(errno));
5108 return RPC_NET_ERROR;
5109 }
5110
5111 /* reuse address, needed if previous server stopped (30s timeout!) */
5112 int flag = 1;
5113 int status = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(int));
5114 if (status < 0) {
5115 *error_msg_p = msprintf("setsockopt(SO_REUSEADDR) failed, errno %d (%s)", errno, strerror(errno));
5116 return RPC_NET_ERROR;
5117 }
5118
5119#ifdef AF_INET6
5120#ifdef IPV6_V6ONLY
5121 if (use_inet6) {
5122 /* turn off IPV6_V6ONLY, see RFC 3493 */
5123 flag = 0;
5124 status = setsockopt(lsock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flag, sizeof(int));
5125 if (status < 0) {
5126 *error_msg_p = msprintf("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY) failed, errno %d (%s)", errno, strerror(errno));
5127 return RPC_NET_ERROR;
5128 }
5129 }
5130#else
5131#warning strange: AF_INET6 is defined, but IPV6_V6ONLY is not defined
5132#endif
5133#endif
5134
5135 if (use_inet6) {
5136#ifdef AF_INET6
5137 /* bind local node name and port to socket */
5138 struct sockaddr_in6 bind_addr6;
5139 memset(&bind_addr6, 0, sizeof(bind_addr6));
5140 bind_addr6.sin6_family = AF_INET6;
5141
5142 if (listen_localhost) {
5143 bind_addr6.sin6_addr = in6addr_loopback;
5144 } else {
5145 bind_addr6.sin6_addr = in6addr_any;
5146 }
5147
5148 if (tcp_port)
5149 bind_addr6.sin6_port = htons((short) tcp_port);
5150 else
5151 bind_addr6.sin6_port = htons(0); // OS will allocate a port number for us
5152
5153 status = bind(lsock, (struct sockaddr *) &bind_addr6, sizeof(bind_addr6));
5154 if (status < 0) {
5155 *error_msg_p = msprintf("IPv6 bind() to port %d failed, errno %d (%s)", tcp_port, errno, strerror(errno));
5156 return RPC_NET_ERROR;
5157 }
5158#endif
5159 } else {
5160 /* bind local node name and port to socket */
5161 struct sockaddr_in bind_addr;
5162 memset(&bind_addr, 0, sizeof(bind_addr));
5163 bind_addr.sin_family = AF_INET;
5164
5165 if (listen_localhost) {
5166 bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
5167 } else {
5168 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
5169 }
5170
5171 if (tcp_port)
5172 bind_addr.sin_port = htons((short) tcp_port);
5173 else
5174 bind_addr.sin_port = htons(0); // OS will allocate a port number for us
5175
5176 status = bind(lsock, (struct sockaddr *) &bind_addr, sizeof(bind_addr));
5177 if (status < 0) {
5178 *error_msg_p = msprintf("bind() to port %d failed, errno %d (%s)", tcp_port, errno, strerror(errno));
5179 return RPC_NET_ERROR;
5180 }
5181 }
5182
5183 /* listen for connection */
5184#ifdef OS_MSDOS
5185 status = listen(lsock, 1);
5186#else
5188#endif
5189 if (status < 0) {
5190 *error_msg_p = msprintf("listen() failed, errno %d (%s)", errno, strerror(errno));
5191 return RPC_NET_ERROR;
5192 }
5193
5194 if (use_inet6) {
5195#ifdef AF_INET6
5196 struct sockaddr_in6 addr;
5197 socklen_t sosize = sizeof(addr);
5198 status = getsockname(lsock, (struct sockaddr*)&addr, &sosize);
5199 if (status < 0) {
5200 *error_msg_p = msprintf("IPv6 getsockname() failed, errno %d (%s)", errno, strerror(errno));
5201 return RPC_NET_ERROR;
5202 }
5203
5204 *tcp_port_p = ntohs(addr.sin6_port);
5205#endif
5206 } else {
5207 struct sockaddr_in addr;
5208 socklen_t sosize = sizeof(addr);
5209 status = getsockname(lsock, (struct sockaddr*)&addr, &sosize);
5210 if (status < 0) {
5211 *error_msg_p = msprintf("getsockname() failed, errno %d (%s)", errno, strerror(errno));
5212 return RPC_NET_ERROR;
5213 }
5214
5215 *tcp_port_p = ntohs(addr.sin_port);
5216 }
5217
5218 *sockp = lsock;
5219
5220 if (gSocketTrace) {
5221 if (listen_localhost)
5222 fprintf(stderr, "ss_socket_listen_tcp: listening tcp port %d local connections only, new socket %d\n", *tcp_port_p, *sockp);
5223 else
5224 fprintf(stderr, "ss_socket_listen_tcp: listening tcp port %d all internet connections, socket %d\n", *tcp_port_p, *sockp);
5225 }
5226
5227 return SS_SUCCESS;
5228}
5229
5230/*------------------------------------------------------------------*/
5232{
5233 assert(sockp != NULL);
5234 if (gSocketTrace) {
5235 fprintf(stderr, "ss_socket_close: %d\n", *sockp);
5236 }
5237 int err = close(*sockp);
5238 if (err) {
5239 cm_msg(MERROR, "ss_socket_close", "unexpected error, close() returned %d, errno: %d (%s)", err, errno, strerror(errno));
5240 }
5241 *sockp = 0;
5242 return SS_SUCCESS;
5243}
5244
5245/*------------------------------------------------------------------*/
5246INT ss_socket_get_peer_name(int sock, std::string* hostp, int* portp)
5247{
5248 char addr[64];
5249
5250 unsigned size = sizeof(addr);
5251 int rv = getpeername(sock, (struct sockaddr *) &addr, &size);
5252
5253 //printf("getpeername() returned %d, size %d, buffer %d\n", rv, size, (int)sizeof(addr));
5254
5255 if (rv != 0) {
5256 cm_msg(MERROR, "ss_socket_get_peer_name", "Error: getpeername() returned %d, errno %d (%s)", rv, errno, strerror(errno));
5257 return SS_SOCKET_ERROR;
5258 }
5259
5260 char hostname[256];
5261 char servname[16];
5262
5263 int ret = getnameinfo((struct sockaddr*)&addr, size,
5264 hostname, sizeof(hostname),
5265 servname, sizeof(servname),
5267
5268 if (ret != 0) {
5269 cm_msg(MERROR, "ss_socket_get_peer_name", "Error: getnameinfo() error %d (%s)", ret, gai_strerror(ret));
5270 return SS_SOCKET_ERROR;
5271 }
5272
5273 //printf("getnameinfo() returned %d, hostname [%s], servname[%s]\n", ret, hostname, servname);
5274
5275 if (hostp)
5276 *hostp = hostname;
5277
5278 if (portp)
5279 *portp = atoi(servname);
5280
5281 return SS_SUCCESS;
5282}
5283
5284/*------------------------------------------------------------------*/
5285INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
5286/********************************************************************\
5287
5288 Routine: send_tcp
5289
5290 Purpose: Send network data over TCP port. Break buffer in smaller
5291 parts if larger than maximum TCP buffer size (usually 64k).
5292
5293 Input:
5294 INT sock Socket which was previosly opened.
5295 DWORD buffer_size Size of the buffer in bytes.
5296 INT flags Flags passed to send()
5297 0x10000 : do not send error message
5298
5299 Output:
5300 char *buffer Network receive buffer.
5301
5302 Function value:
5303 INT Same as send()
5304
5305\********************************************************************/
5306{
5307 DWORD count;
5308 INT status;
5309 //int net_tcp_size = NET_TCP_SIZE;
5310 int net_tcp_size = 1024 * 1024;
5311
5312 /* transfer fragments until complete buffer is transferred */
5313
5314 for (count = 0; (INT) count < (INT) buffer_size - net_tcp_size;) {
5315 status = send(sock, buffer + count, net_tcp_size, flags & 0xFFFF);
5316 if (status != -1)
5317 count += status;
5318 else {
5319#ifdef OS_UNIX
5320 if (errno == EINTR)
5321 continue;
5322#endif
5323 if ((flags & 0x10000) == 0)
5324 cm_msg(MERROR, "send_tcp",
5325 "send(socket=%d,size=%d) returned %d, errno: %d (%s)",
5327 return status;
5328 }
5329 }
5330
5331 while (count < buffer_size) {
5332 status = send(sock, buffer + count, buffer_size - count, flags & 0xFFFF);
5333 if (status != -1)
5334 count += status;
5335 else {
5336#ifdef OS_UNIX
5337 if (errno == EINTR)
5338 continue;
5339#endif
5340 if ((flags & 0x10000) == 0)
5341 cm_msg(MERROR, "send_tcp",
5342 "send(socket=%d,size=%d) returned %d, errno: %d (%s)",
5343 sock, (int) (buffer_size - count), status, errno, strerror(errno));
5344 return status;
5345 }
5346 }
5347
5348 return count;
5349}
5350
5351/*------------------------------------------------------------------*/
5352INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
5353/********************************************************************\
5354
5355 Routine: write_tcp
5356
5357 Purpose: Send network data over TCP port. Handle partial writes
5358
5359 Input:
5360 INT sock Socket which was previosly opened.
5361 DWORD buffer_size Size of the buffer in bytes.
5362 INT flags Flags passed to send()
5363 0x10000 : do not send error message
5364
5365 Output:
5366 char *buffer Network receive buffer.
5367
5368 Function value:
5369 SS_SUCCESS Everything was sent
5370 SS_SOCKET_ERROR There was a socket error
5371
5372\********************************************************************/
5373{
5374 size_t count = 0;
5375
5376 while (count < buffer_size) {
5377 ssize_t wr = write(sock, buffer + count, buffer_size - count);
5378
5379 if (wr == 0) {
5380 cm_msg(MERROR, "ss_write_tcp", "write(socket=%d,size=%d) returned zero, errno: %d (%s)", sock, (int) (buffer_size - count), errno, strerror(errno));
5381 return SS_SOCKET_ERROR;
5382 } else if (wr < 0) {
5383#ifdef OS_UNIX
5384 if (errno == EINTR)
5385 continue;
5386#endif
5387 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));
5388 return SS_SOCKET_ERROR;
5389 }
5390
5391 // good write
5392 count += wr;
5393 }
5394
5395 return SS_SUCCESS;
5396}
5397
5398/*------------------------------------------------------------------*/
5399INT recv_string(int sock, char *buffer, DWORD buffer_size, INT millisec)
5400/********************************************************************\
5401
5402 Routine: recv_string
5403
5404 Purpose: Receive network data over TCP port. Since sockets are
5405 operated in stream mode, a single transmission via send
5406 may not transfer the full data. Therefore, one has to check
5407 at the receiver side if the full data is received. If not,
5408 one has to issue several recv() commands.
5409
5410 The length of the data is determined by a trailing zero.
5411
5412 Input:
5413 INT sock Socket which was previosly opened.
5414 DWORD buffer_size Size of the buffer in bytes.
5415 INT millisec Timeout in ms
5416
5417 Output:
5418 char *buffer Network receive buffer.
5419
5420 Function value:
5421 INT String length
5422
5423\********************************************************************/
5424{
5425 INT i, status;
5426 DWORD n;
5427
5428 n = 0;
5429 memset(buffer, 0, buffer_size);
5430
5431 do {
5432 if (millisec > 0) {
5434 if (status != SS_SUCCESS)
5435 break;
5436 }
5437
5438 i = recv(sock, buffer + n, 1, 0);
5439
5440 if (i <= 0)
5441 break;
5442
5443 n++;
5444
5445 if (n >= buffer_size)
5446 break;
5447
5448 } while (buffer[n - 1] && buffer[n - 1] != 10);
5449
5450 return n - 1;
5451}
5452
5453/*------------------------------------------------------------------*/
5454INT recv_tcp(int sock, char *net_buffer, DWORD buffer_size, INT flags)
5455/********************************************************************\
5456
5457 Routine: recv_tcp
5458
5459 Purpose: Receive network data over TCP port. Since sockets are
5460 operated in stream mode, a single transmission via send
5461 may not transfer the full data. Therefore, one has to check
5462 at the receiver side if the full data is received. If not,
5463 one has to issue several recv() commands.
5464
5465 The length of the data is determined by the data header,
5466 which consists of two DWORDs. The first is the command code
5467 (or function id), the second is the size of the following
5468 parameters in bytes. From that size recv_tcp() determines
5469 how much data to receive.
5470
5471 Input:
5472 INT sock Socket which was previosly opened.
5473 char *net_buffer Buffer to store data to
5474 DWORD buffer_size Size of the buffer in bytes.
5475 INT flags Flags passed to recv()
5476
5477 Output:
5478 char *buffer Network receive buffer.
5479
5480 Function value:
5481 INT Same as recv()
5482
5483\********************************************************************/
5484{
5485 INT param_size, n_received, n;
5486 NET_COMMAND *nc;
5487
5488 if (buffer_size < sizeof(NET_COMMAND_HEADER)) {
5489 cm_msg(MERROR, "recv_tcp", "parameters too large for network buffer");
5490 return -1;
5491 }
5492
5493 /* first receive header */
5494 n_received = 0;
5495 do {
5496#ifdef OS_UNIX
5497 do {
5498 n = recv(sock, net_buffer + n_received, sizeof(NET_COMMAND_HEADER), flags);
5499
5500 /* don't return if an alarm signal was cought */
5501 } while (n == -1 && errno == EINTR);
5502#else
5503 n = recv(sock, net_buffer + n_received, sizeof(NET_COMMAND_HEADER), flags);
5504#endif
5505
5506 if (n == 0) {
5507 cm_msg(MERROR, "recv_tcp", "header: recv(%d) returned %d, n_received = %d, unexpected connection closure", (int)sizeof(NET_COMMAND_HEADER), n, n_received);
5508 return n;
5509 }
5510
5511 if (n < 0) {
5512 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));
5513 return n;
5514 }
5515
5516 n_received += n;
5517
5518 } while (n_received < (int) sizeof(NET_COMMAND_HEADER));
5519
5520 /* now receive parameters */
5521
5522 nc = (NET_COMMAND *) net_buffer;
5523 param_size = nc->header.param_size;
5524 n_received = 0;
5525
5526 if (param_size == 0)
5527 return sizeof(NET_COMMAND_HEADER);
5528
5529 if (param_size > (INT)buffer_size) {
5530 cm_msg(MERROR, "recv_tcp", "param: receive buffer size %d is too small for received data size %d", buffer_size, param_size);
5531 return -1;
5532 }
5533
5534 do {
5535#ifdef OS_UNIX
5536 do {
5537 n = recv(sock, net_buffer + sizeof(NET_COMMAND_HEADER) + n_received, param_size - n_received, flags);
5538
5539 /* don't return if an alarm signal was cought */
5540 } while (n == -1 && errno == EINTR);
5541#else
5542 n = recv(sock, net_buffer + sizeof(NET_COMMAND_HEADER) + n_received, param_size - n_received, flags);
5543#endif
5544
5545 if (n == 0) {
5546 cm_msg(MERROR, "recv_tcp", "param: recv() returned %d, n_received = %d, unexpected connection closure", n, n_received);
5547 return n;
5548 }
5549
5550 if (n < 0) {
5551 cm_msg(MERROR, "recv_tcp", "param: recv() returned %d, n_received = %d, errno: %d (%s)", n, n_received, errno, strerror(errno));
5552 return n;
5553 }
5554
5555 n_received += n;
5556 } while (n_received < param_size);
5557
5558 return sizeof(NET_COMMAND_HEADER) + param_size;
5559}
5560
5561/*------------------------------------------------------------------*/
5562INT recv_tcp2(int sock, char *net_buffer, int buffer_size, int timeout_ms)
5563/********************************************************************\
5564
5565 Routine: recv_tcp2
5566
5567 Purpose: Receive network data over TCP port. Since sockets are
5568 operated in stream mode, a single transmission via send
5569 may not transfer the full data. Therefore, one has to check
5570 at the receiver side if the full data is received. If not,
5571 one has to issue several recv() commands.
5572
5573 Input:
5574 INT sock Socket which was previosly opened
5575 char* net_buffer Buffer to store data
5576 int buffer_size Number of bytes to receive
5577 int timeout_ms Timeout in milliseconds
5578
5579 Output:
5580 char* net_buffer Network receive buffer
5581
5582 Function value:
5583 number of bytes received (less than buffer_size if there was a timeout), or
5584 0 : timeout and nothing was received
5585 -1 : socket error
5586
5587\********************************************************************/
5588{
5589 int n_received = 0;
5590 int flags = 0;
5591 int n;
5592
5593 //printf("recv_tcp2: %p+%d bytes, timeout %d ms!\n", net_buffer + n_received, buffer_size - n_received, timeout_ms);
5594
5595 while (n_received != buffer_size) {
5596
5597 if (timeout_ms > 0) {
5598 int status = ss_socket_wait(sock, timeout_ms);
5599 if (status == SS_TIMEOUT)
5600 return n_received;
5601 if (status != SS_SUCCESS)
5602 return -1;
5603 }
5604
5605 n = recv(sock, net_buffer + n_received, buffer_size - n_received, flags);
5606
5607 //printf("recv_tcp2: %p+%d bytes, returned %d, errno %d (%s)\n", net_buffer + n_received, buffer_size - n_received, n, errno, strerror(errno));
5608
5609#ifdef EINTR
5610 /* don't return if an alarm signal was cought */
5611 if (n == -1 && errno == EINTR)
5612 continue;
5613#endif
5614
5615 if (n == 0) {
5616 // socket closed
5617 cm_msg(MERROR, "recv_tcp2", "unexpected connection closure");
5618 return -1;
5619 }
5620
5621 if (n < 0) {
5622 // socket error
5623 cm_msg(MERROR, "recv_tcp2", "unexpected connection error, recv() errno %d (%s)", errno, strerror(errno));
5624 return -1;
5625 }
5626
5627 n_received += n;
5628 }
5629
5630 return n_received;
5631}
5632
5633
5634/*------------------------------------------------------------------*/
5635INT ss_recv_net_command(int sock, DWORD* routine_id, DWORD* param_size, char **param_ptr, int timeout_ms)
5636/********************************************************************\
5637
5638 Routine: ss_recv_net_command
5639
5640 Purpose: Receive MIDAS data packet from a TCP port. MIDAS data packet
5641 is defined by NET_COMMAND_HEADER
5642 which consists of two DWORDs. The first is the command code
5643 (or function id), the second is the size of the following
5644 parameters in bytes. From that size recv_tcp() determines
5645 how much data to receive.
5646
5647 Input:
5648 int sock Socket which was previosly opened.
5649 DWORD* routine_id routine_id from NET_COMMAND_HEADER
5650 DWORD* param_size param_size from NET_COMMAND_HEADER, size of allocated data buffer
5651 char** param_ptr pointer to allocated data buffer
5652 int timeout_ms timeout in milliseconds
5653
5654 Function value:
5655 INT SS_SUCCESS, SS_NO_MEMORY, SS_SOCKET_ERROR
5656
5657\********************************************************************/
5658{
5660 size_t n;
5661
5662 /* first receive header */
5663 n = recv_tcp2(sock, (char*)&ncbuf, sizeof(ncbuf), timeout_ms);
5664
5665 if (n == 0) {
5666 cm_msg(MERROR, "ss_recv_net_command", "timeout receiving network command header");
5667 return SS_TIMEOUT;
5668 }
5669
5670 if (n != sizeof(ncbuf)) {
5671 cm_msg(MERROR, "ss_recv_net_command", "error receiving network command header, see messages");
5672 return SS_SOCKET_ERROR;
5673 }
5674
5675 // FIXME: where is the big-endian/little-endian conversion?
5676 *routine_id = ncbuf.routine_id;
5677 *param_size = ncbuf.param_size;
5678
5679 if (*param_size == 0) {
5680 *param_ptr = NULL;
5681 return SS_SUCCESS;
5682 }
5683
5684 *param_ptr = (char *)malloc(*param_size);
5685
5686 if (*param_ptr == NULL) {
5687 cm_msg(MERROR, "ss_recv_net_command", "error allocating %d bytes for network command data", *param_size);
5688 return SS_NO_MEMORY;
5689 }
5690
5691 /* first receive header */
5692 n = recv_tcp2(sock, *param_ptr, *param_size, timeout_ms);
5693
5694 if (n == 0) {
5695 cm_msg(MERROR, "ss_recv_net_command", "timeout receiving network command data");
5696 free(*param_ptr);
5697 *param_ptr = NULL;
5698 return SS_TIMEOUT;
5699 }
5700
5701 if (n != *param_size) {
5702 cm_msg(MERROR, "ss_recv_net_command", "error receiving network command data, see messages");
5703 free(*param_ptr);
5704 *param_ptr = NULL;
5705 return SS_SOCKET_ERROR;
5706 }
5707
5708 return SS_SUCCESS;
5709}
5710
5711/*------------------------------------------------------------------*/
5712std::string ss_gethostname()
5713/********************************************************************\
5714
5715 Routine: ss_gethostname
5716
5717 Purpose: Get name of local machine using gethostname() syscall
5718
5719 Input:
5720 int buffer_size Size of the buffer in bytes.
5721
5722 Output:
5723 char *buffer receive buffer
5724
5725 Function value:
5726 INT SS_SUCCESS or SS_IO_ERROR
5727
5728\********************************************************************/
5729{
5730 char buf[256];
5731 memset(buf, 0, sizeof(buf));
5732
5733 int status = gethostname(buf, sizeof(buf)-1);
5734
5735 //printf("gethostname %d (%s)\n", status, buffer);
5736
5737 if (status != 0) {
5738 cm_msg(MERROR, "ss_gethostname", "gethostname() errno %d (%s)", errno, strerror(errno));
5739 return "";
5740 }
5741
5742 return buf;
5743}
5744
5745/*------------------------------------------------------------------*/
5746INT ss_gethostname(char* buffer, int buffer_size)
5747/********************************************************************\
5748
5749 Routine: ss_gethostname
5750
5751 Purpose: Get name of local machine using gethostname() syscall
5752
5753 Input:
5754 int buffer_size Size of the buffer in bytes.
5755
5756 Output:
5757 char *buffer receive buffer
5758
5759 Function value:
5760 INT SS_SUCCESS or SS_IO_ERROR
5761
5762\********************************************************************/
5763{
5764 std::string h = ss_gethostname();
5765
5766 if (h.length() == 0) {
5767 return SS_IO_ERROR;
5768 } else {
5769 mstrlcpy(buffer, h.c_str(), buffer_size);
5770 return SS_SUCCESS;
5771 }
5772}
5773
5774/*------------------------------------------------------------------*/
5775
5776std::string ss_getcwd()
5777{
5778 char *s = getcwd(NULL, 0);
5779 if (s) {
5780 std::string cwd = s;
5781 free(s);
5782 //printf("ss_getcwd: %s\n", cwd.c_str());
5783 return cwd;
5784 } else {
5785 return "/GETCWD-FAILED-ON-US";
5786 }
5787}
5788
5789/*------------------------------------------------------------------*/
5790
5791#ifdef OS_MSDOS
5792#ifdef sopen
5793/********************************************************************\
5794 under Turbo-C, sopen is defined as a macro instead a function.
5795 Since the PCTCP library uses sopen as a function call, we supply
5796 it here.
5797\********************************************************************/
5798
5799#undef sopen
5800
5801int sopen(const char *path, int access, int shflag, int mode)
5802{
5803 return open(path, (access) | (shflag), mode);
5804}
5805
5806#endif
5807#endif
5808
5809/*------------------------------------------------------------------*/
5810/********************************************************************\
5811* *
5812* Tape functions *
5813* *
5814\********************************************************************/
5815
5816/*------------------------------------------------------------------*/
5818/********************************************************************\
5819
5820 Routine: ss_tape_open
5821
5822 Purpose: Open tape channel
5823
5824 Input:
5825 char *path Name of tape
5826 Under Windows NT, usually \\.\tape0
5827 Under UNIX, usually /dev/tape
5828 INT oflag Open flags, same as open()
5829
5830 Output:
5831 INT *channel Channel identifier
5832
5833 Function value:
5834 SS_SUCCESS Successful completion
5835 SS_NO_TAPE No tape in device
5836 SS_DEV_BUSY Device is used by someone else
5837
5838\********************************************************************/
5839{
5840#ifdef OS_UNIX
5841 //cm_enable_watchdog(FALSE);
5842
5843 *channel = open(path, oflag, 0644);
5844
5845 //cm_enable_watchdog(TRUE);
5846
5847 if (*channel < 0)
5848 cm_msg(MERROR, "ss_tape_open", "open() returned %d, errno %d (%s)", *channel, errno, strerror(errno));
5849
5850 if (*channel < 0) {
5851 if (errno == EIO)
5852 return SS_NO_TAPE;
5853 if (errno == EBUSY)
5854 return SS_DEV_BUSY;
5855 return errno;
5856 }
5857#ifdef MTSETBLK
5858 {
5859 /* set variable block size */
5860 struct mtop arg;
5861 arg.mt_op = MTSETBLK;
5862 arg.mt_count = 0;
5863
5864 ioctl(*channel, MTIOCTOP, &arg);
5865 }
5866#endif /* MTSETBLK */
5867
5868#endif /* OS_UNIX */
5869
5870#ifdef OS_WINNT
5871 INT status;
5873
5875
5876 if (*channel == (INT) INVALID_HANDLE_VALUE) {
5877 status = GetLastError();
5879 cm_msg(MERROR, "ss_tape_open", "tape is used by other process");
5880 return SS_DEV_BUSY;
5881 }
5883 cm_msg(MERROR, "ss_tape_open", "tape device \"%s\" doesn't exist", path);
5884 return SS_NO_TAPE;
5885 }
5886
5887 cm_msg(MERROR, "ss_tape_open", "unknown error %d", status);
5888 return status;
5889 }
5890
5893 cm_msg(MERROR, "ss_tape_open", "no media in drive");
5894 return SS_NO_TAPE;
5895 }
5896
5897 /* set block size */
5898 memset(&m, 0, sizeof(m));
5899 m.BlockSize = TAPE_BUFFER_SIZE;
5901
5902#endif
5903
5904 return SS_SUCCESS;
5905}
5906
5907/*------------------------------------------------------------------*/
5909/********************************************************************\
5910
5911 Routine: ss_tape_close
5912
5913 Purpose: Close tape channel
5914
5915 Input:
5916 INT channel Channel identifier
5917
5918 Output:
5919 <none>
5920
5921 Function value:
5922 SS_SUCCESS Successful completion
5923 errno Low level error number
5924
5925\********************************************************************/
5926{
5927 INT status;
5928
5929#ifdef OS_UNIX
5930
5931 status = close(channel);
5932
5933 if (status < 0) {
5934 cm_msg(MERROR, "ss_tape_close", "close() returned %d, errno %d (%s)", status, errno, strerror(errno));
5935 return errno;
5936 }
5937#endif /* OS_UNIX */
5938
5939#ifdef OS_WINNT
5940
5941 if (!CloseHandle((HANDLE) channel)) {
5942 status = GetLastError();
5943 cm_msg(MERROR, "ss_tape_close", "unknown error %d", status);
5944 return status;
5945 }
5946#endif /* OS_WINNT */
5947
5948 return SS_SUCCESS;
5949}
5950
5951/*------------------------------------------------------------------*/
5953/********************************************************************\
5954
5955 Routine: ss_tape_status
5956
5957 Purpose: Print status information about tape
5958
5959 Input:
5960 char *path Name of tape
5961
5962 Output:
5963 <print> Tape information
5964
5965 Function value:
5966 SS_SUCCESS Successful completion
5967
5968\********************************************************************/
5969{
5970#ifdef OS_UNIX
5971 int status;
5972 char str[256];
5973 /* let 'mt' do the job */
5974 sprintf(str, "mt -f %s status", path);
5975 status = system(str);
5976 if (status == -1)
5977 return SS_TAPE_ERROR;
5978 return SS_SUCCESS;
5979#endif /* OS_UNIX */
5980
5981#ifdef OS_WINNT
5983 DWORD size;
5986 double x;
5987
5989
5990 if (channel == (INT) INVALID_HANDLE_VALUE) {
5991 status = GetLastError();
5993 cm_msg(MINFO, "ss_tape_status", "tape is used by other process");
5994 return SS_SUCCESS;
5995 }
5997 cm_msg(MINFO, "ss_tape_status", "tape device \"%s\" doesn't exist", path);
5998 return SS_SUCCESS;
5999 }
6000
6001 cm_msg(MINFO, "ss_tape_status", "unknown error %d", status);
6002 return status;
6003 }
6004
6005 /* poll media changed messages */
6008
6011 cm_msg(MINFO, "ss_tape_status", "no media in drive");
6013 return SS_SUCCESS;
6014 }
6015
6018
6019 printf("Hardware error correction is %s\n", d.ECC ? "on" : "off");
6020 printf("Hardware compression is %s\n", d.Compression ? "on" : "off");
6021 printf("Tape %s write protected\n", m.WriteProtected ? "is" : "is not");
6022
6023 if (d.FeaturesLow & TAPE_DRIVE_TAPE_REMAINING) {
6024 x = ((double) m.Remaining.LowPart + (double) m.Remaining.HighPart * 4.294967295E9)
6025 / 1000.0 / 1000.0;
6026 printf("Tape capacity remaining is %d MB\n", (int) x);
6027 } else
6028 printf("Tape capacity is not reported by tape\n");
6029
6031
6032#endif
6033
6034 return SS_SUCCESS;
6035}
6036
6037/*------------------------------------------------------------------*/
6039/********************************************************************\
6040
6041 Routine: ss_tape_write
6042
6043 Purpose: Write count bytes to tape channel
6044
6045 Input:
6046 INT channel Channel identifier
6047 void *pdata Address of data to write
6048 INT count number of bytes
6049
6050 Output:
6051 <none>
6052
6053 Function value:
6054 SS_SUCCESS Successful completion
6055 SS_IO_ERROR Physical IO error
6056 SS_TAPE_ERROR Unknown tape error
6057
6058\********************************************************************/
6059{
6060#ifdef OS_UNIX
6061 INT status;
6062
6063 do {
6065/*
6066 if (status != count)
6067 printf("count: %d - %d\n", count, status);
6068*/
6069 } while (status == -1 && errno == EINTR);
6070
6071 if (status != count) {
6072 cm_msg(MERROR, "ss_tape_write", "write() returned %d, errno %d (%s)", status, errno, strerror(errno));
6073
6074 if (errno == EIO)
6075 return SS_IO_ERROR;
6076 else
6077 return SS_TAPE_ERROR;
6078 }
6079#endif /* OS_UNIX */
6080
6081#ifdef OS_WINNT
6082 INT status;
6083 DWORD written;
6084
6086 if (written != (DWORD) count) {
6087 status = GetLastError();
6088 cm_msg(MERROR, "ss_tape_write", "error %d", status);
6089
6090 return SS_IO_ERROR;
6091 }
6092#endif /* OS_WINNT */
6093
6094 return SS_SUCCESS;
6095}
6096
6097/*------------------------------------------------------------------*/
6099/********************************************************************\
6100
6101 Routine: ss_tape_write
6102
6103 Purpose: Read count bytes to tape channel
6104
6105 Input:
6106 INT channel Channel identifier
6107 void *pdata Address of data
6108 INT *count Number of bytes to read
6109
6110 Output:
6111 INT *count Number of read
6112
6113 Function value:
6114 SS_SUCCESS Successful operation
6115 <errno> Error code
6116
6117\********************************************************************/
6118{
6119#ifdef OS_UNIX
6120 INT n, status;
6121
6122 do {
6123 n = read(channel, pdata, *count);
6124 } while (n == -1 && errno == EINTR);
6125
6126 if (n == -1) {
6127 if (errno == ENOSPC || errno == EIO)
6129 else {
6130 if (n == 0 && errno == 0)
6132 else {
6133 cm_msg(MERROR, "ss_tape_read", "unexpected tape error: n=%d, errno=%d\n", n, errno);
6134 status = errno;
6135 }
6136 }
6137 } else
6139 *count = n;
6140
6141 return status;
6142
6143#elif defined(OS_WINNT) /* OS_UNIX */
6144
6145 INT status;
6146 DWORD read;
6147
6148 if (!ReadFile((HANDLE) channel, pdata, *count, &read, NULL)) {
6149 status = GetLastError();
6152 else if (status == ERROR_FILEMARK_DETECTED)
6154 else if (status == ERROR_MORE_DATA)
6156 else
6157 cm_msg(MERROR, "ss_tape_read", "unexpected tape error: n=%d, errno=%d\n", read, status);
6158 } else
6160
6161 *count = read;
6162 return status;
6163
6164#else /* OS_WINNT */
6165
6166 return SS_SUCCESS;
6167
6168#endif
6169}
6170
6171/*------------------------------------------------------------------*/
6173/********************************************************************\
6174
6175 Routine: ss_tape_write_eof
6176
6177 Purpose: Write end-of-file to tape channel
6178
6179 Input:
6180 INT *channel Channel identifier
6181
6182 Output:
6183 <none>
6184
6185 Function value:
6186 SS_SUCCESS Successful completion
6187 errno Error number
6188
6189\********************************************************************/
6190{
6191#ifdef MTIOCTOP
6192 struct mtop arg;
6193 INT status;
6194
6195 arg.mt_op = MTWEOF;
6196 arg.mt_count = 1;
6197
6198 //cm_enable_watchdog(FALSE);
6199
6200 status = ioctl(channel, MTIOCTOP, &arg);
6201
6202 //cm_enable_watchdog(TRUE);
6203
6204 if (status < 0) {
6205 cm_msg(MERROR, "ss_tape_write_eof", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6206 return errno;
6207 }
6208#endif /* OS_UNIX */
6209
6210#ifdef OS_WINNT
6211
6213 DWORD size;
6214 INT status;
6215
6216 size = sizeof(TAPE_GET_DRIVE_PARAMETERS);
6218
6219 if (d.FeaturesHigh & TAPE_DRIVE_WRITE_FILEMARKS)
6221 else if (d.FeaturesHigh & TAPE_DRIVE_WRITE_LONG_FMKS)
6223 else if (d.FeaturesHigh & TAPE_DRIVE_WRITE_SHORT_FMKS)
6225 else
6226 cm_msg(MERROR, "ss_tape_write_eof", "tape doesn't support writing of filemarks");
6227
6228 if (status != NO_ERROR) {
6229 cm_msg(MERROR, "ss_tape_write_eof", "unknown error %d", status);
6230 return status;
6231 }
6232#endif /* OS_WINNT */
6233
6234 return SS_SUCCESS;
6235}
6236
6237/*------------------------------------------------------------------*/
6239/********************************************************************\
6240
6241 Routine: ss_tape_fskip
6242
6243 Purpose: Skip count number of files on a tape
6244
6245 Input:
6246 INT *channel Channel identifier
6247 INT count Number of files to skip
6248
6249 Output:
6250 <none>
6251
6252 Function value:
6253 SS_SUCCESS Successful completion
6254 errno Error number
6255
6256\********************************************************************/
6257{
6258#ifdef MTIOCTOP
6259 struct mtop arg;
6260 INT status;
6261
6262 if (count > 0)
6263 arg.mt_op = MTFSF;
6264 else
6265 arg.mt_op = MTBSF;
6266 arg.mt_count = abs(count);
6267
6268 //cm_enable_watchdog(FALSE);
6269
6270 status = ioctl(channel, MTIOCTOP, &arg);
6271
6272 //cm_enable_watchdog(TRUE);
6273
6274 if (status < 0) {
6275 cm_msg(MERROR, "ss_tape_fskip", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6276 return errno;
6277 }
6278#endif /* OS_UNIX */
6279
6280#ifdef OS_WINNT
6281 INT status;
6282
6284
6286 return SS_END_OF_TAPE;
6287
6288 if (status != NO_ERROR) {
6289 cm_msg(MERROR, "ss_tape_fskip", "error %d", status);
6290 return status;
6291 }
6292#endif /* OS_WINNT */
6293
6294 return SS_SUCCESS;
6295}
6296
6297/*------------------------------------------------------------------*/
6299/********************************************************************\
6300
6301 Routine: ss_tape_rskip
6302
6303 Purpose: Skip count number of records on a tape
6304
6305 Input:
6306 INT *channel Channel identifier
6307 INT count Number of records to skip
6308
6309 Output:
6310 <none>
6311
6312 Function value:
6313 SS_SUCCESS Successful completion
6314 errno Error number
6315
6316\********************************************************************/
6317{
6318#ifdef MTIOCTOP
6319 struct mtop arg;
6320 INT status;
6321
6322 if (count > 0)
6323 arg.mt_op = MTFSR;
6324 else
6325 arg.mt_op = MTBSR;
6326 arg.mt_count = abs(count);
6327
6328 //cm_enable_watchdog(FALSE);
6329
6330 status = ioctl(channel, MTIOCTOP, &arg);
6331
6332 //cm_enable_watchdog(TRUE);
6333
6334 if (status < 0) {
6335 cm_msg(MERROR, "ss_tape_rskip", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6336 return errno;
6337 }
6338#endif /* OS_UNIX */
6339
6340#ifdef OS_WINNT
6341 INT status;
6342
6344 if (status != NO_ERROR) {
6345 cm_msg(MERROR, "ss_tape_rskip", "error %d", status);
6346 return status;
6347 }
6348#endif /* OS_WINNT */
6349
6350 return CM_SUCCESS;
6351}
6352
6353/*------------------------------------------------------------------*/
6355/********************************************************************\
6356
6357 Routine: ss_tape_rewind
6358
6359 Purpose: Rewind tape
6360
6361 Input:
6362 INT channel Channel identifier
6363
6364 Output:
6365 <none>
6366
6367 Function value:
6368 SS_SUCCESS Successful completion
6369 errno Error number
6370
6371\********************************************************************/
6372{
6373#ifdef MTIOCTOP
6374 struct mtop arg;
6375 INT status;
6376
6377 arg.mt_op = MTREW;
6378 arg.mt_count = 0;
6379
6380 //cm_enable_watchdog(FALSE);
6381
6382 status = ioctl(channel, MTIOCTOP, &arg);
6383
6384 //cm_enable_watchdog(TRUE);
6385
6386 if (status < 0) {
6387 cm_msg(MERROR, "ss_tape_rewind", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6388 return errno;
6389 }
6390#endif /* OS_UNIX */
6391
6392#ifdef OS_WINNT
6393 INT status;
6394
6396 if (status != NO_ERROR) {
6397 cm_msg(MERROR, "ss_tape_rewind", "error %d", status);
6398 return status;
6399 }
6400#endif /* OS_WINNT */
6401
6402 return CM_SUCCESS;
6403}
6404
6405/*------------------------------------------------------------------*/
6407/********************************************************************\
6408
6409 Routine: ss_tape_spool
6410
6411 Purpose: Spool tape forward to end of recorded data
6412
6413 Input:
6414 INT channel Channel identifier
6415
6416 Output:
6417 <none>
6418
6419 Function value:
6420 SS_SUCCESS Successful completion
6421 errno Error number
6422
6423\********************************************************************/
6424{
6425#ifdef MTIOCTOP
6426 struct mtop arg;
6427 INT status;
6428
6429#ifdef MTEOM
6430 arg.mt_op = MTEOM;
6431#else
6432 arg.mt_op = MTSEOD;
6433#endif
6434 arg.mt_count = 0;
6435
6436 //cm_enable_watchdog(FALSE);
6437
6438 status = ioctl(channel, MTIOCTOP, &arg);
6439
6440 //cm_enable_watchdog(TRUE);
6441
6442 if (status < 0) {
6443 cm_msg(MERROR, "ss_tape_rewind", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6444 return errno;
6445 }
6446#endif /* OS_UNIX */
6447
6448#ifdef OS_WINNT
6449 INT status;
6450
6452 if (status != NO_ERROR) {
6453 cm_msg(MERROR, "ss_tape_spool", "error %d", status);
6454 return status;
6455 }
6456#endif /* OS_WINNT */
6457
6458 return CM_SUCCESS;
6459}
6460
6461/*------------------------------------------------------------------*/
6463/********************************************************************\
6464
6465 Routine: ss_tape_mount
6466
6467 Purpose: Mount tape
6468
6469 Input:
6470 INT channel Channel identifier
6471
6472 Output:
6473 <none>
6474
6475 Function value:
6476 SS_SUCCESS Successful completion
6477 errno Error number
6478
6479\********************************************************************/
6480{
6481#ifdef MTIOCTOP
6482 struct mtop arg;
6483 INT status;
6484
6485#ifdef MTLOAD
6486 arg.mt_op = MTLOAD;
6487#else
6488 arg.mt_op = MTNOP;
6489#endif
6490 arg.mt_count = 0;
6491
6492 //cm_enable_watchdog(FALSE);
6493
6494 status = ioctl(channel, MTIOCTOP, &arg);
6495
6496 //cm_enable_watchdog(TRUE);
6497
6498 if (status < 0) {
6499 cm_msg(MERROR, "ss_tape_mount", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6500 return errno;
6501 }
6502#endif /* OS_UNIX */
6503
6504#ifdef OS_WINNT
6505 INT status;
6506
6508 if (status != NO_ERROR) {
6509 cm_msg(MERROR, "ss_tape_mount", "error %d", status);
6510 return status;
6511 }
6512#endif /* OS_WINNT */
6513
6514 return CM_SUCCESS;
6515}
6516
6517/*------------------------------------------------------------------*/
6519/********************************************************************\
6520
6521 Routine: ss_tape_unmount
6522
6523 Purpose: Unmount tape
6524
6525 Input:
6526 INT channel Channel identifier
6527
6528 Output:
6529 <none>
6530
6531 Function value:
6532 SS_SUCCESS Successful completion
6533 errno Error number
6534
6535\********************************************************************/
6536{
6537#ifdef MTIOCTOP
6538 struct mtop arg;
6539 INT status;
6540
6541#ifdef MTOFFL
6542 arg.mt_op = MTOFFL;
6543#else
6544 arg.mt_op = MTUNLOAD;
6545#endif
6546 arg.mt_count = 0;
6547
6548 //cm_enable_watchdog(FALSE);
6549
6550 status = ioctl(channel, MTIOCTOP, &arg);
6551
6552 //cm_enable_watchdog(TRUE);
6553
6554 if (status < 0) {
6555 cm_msg(MERROR, "ss_tape_unmount", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6556 return errno;
6557 }
6558#endif /* OS_UNIX */
6559
6560#ifdef OS_WINNT
6561 INT status;
6562
6564 if (status != NO_ERROR) {
6565 cm_msg(MERROR, "ss_tape_unmount", "error %d", status);
6566 return status;
6567 }
6568#endif /* OS_WINNT */
6569
6570 return CM_SUCCESS;
6571}
6572
6573/*------------------------------------------------------------------*/
6575/********************************************************************\
6576Routine: ss_tape_get_blockn
6577Purpose: Ask the tape channel for the present block number
6578Input:
6579INT *channel Channel identifier
6580Function value:
6581blockn: >0 = block number, =0 option not available, <0 errno
6582\********************************************************************/
6583{
6584#if defined(OS_DARWIN)
6585
6586 return 0;
6587
6588#elif defined(OS_UNIX)
6589
6590 INT status;
6591 struct mtpos arg;
6592
6593 //cm_enable_watchdog(FALSE);
6594 status = ioctl(channel, MTIOCPOS, &arg);
6595 //cm_enable_watchdog(TRUE);
6596 if (status < 0) {
6597 if (errno == EIO)
6598 return 0;
6599 else {
6600 cm_msg(MERROR, "ss_tape_get_blockn", "ioctl() failed, errno %d (%s)", errno, strerror(errno));
6601 return -errno;
6602 }
6603 }
6604 return (arg.mt_blkno);
6605
6606#elif defined(OS_WINNT)
6607
6608 INT status;
6610 unsigned long size;
6611 /* I'm not sure the partition count corresponds to the block count */
6613 return (media.PartitionCount);
6614
6615#endif
6616}
6617
6618/*------------------------------------------------------------------*/
6619/********************************************************************\
6620* *
6621* Disk functions *
6622* *
6623\********************************************************************/
6624
6625/*------------------------------------------------------------------*/
6626double ss_disk_free(const char *path)
6627/********************************************************************\
6628
6629 Routine: ss_disk_free
6630
6631 Purpose: Return free disk space
6632
6633 Input:
6634 char *path Name of a file in file system to check
6635
6636 Output:
6637
6638 Function value:
6639 doube Number of bytes free on disk
6640
6641\********************************************************************/
6642{
6643#ifdef OS_UNIX
6644#if defined(OS_OSF1)
6645 struct statfs st;
6646 statfs(path, &st, sizeof(st));
6647 return (double) st.f_bavail * st.f_bsize;
6648#elif defined(OS_LINUX)
6649 struct statfs st;
6650 int status;
6651 status = statfs(path, &st);
6652 if (status != 0)
6653 return -1;
6654 return (double) st.f_bavail * st.f_bsize;
6655#elif defined(OS_SOLARIS)
6656 struct statvfs st;
6657 statvfs(path, &st);
6658 return (double) st.f_bavail * st.f_bsize;
6659#elif defined(OS_IRIX)
6660 struct statfs st;
6661 statfs(path, &st, sizeof(struct statfs), 0);
6662 return (double) st.f_bfree * st.f_bsize;
6663#else
6664 struct fs_data st;
6665 statfs(path, &st);
6666 return (double) st.fd_otsize * st.fd_bfree;
6667#endif
6668
6669#elif defined(OS_WINNT) /* OS_UNIX */
6674 char str[80];
6675
6676 strcpy(str, path);
6677 if (strchr(str, ':') != NULL) {
6678 *(strchr(str, ':') + 1) = 0;
6681 } else
6683
6685#else /* OS_WINNT */
6686
6687 return 1e9;
6688
6689#endif
6690}
6691
6692#if defined(OS_ULTRIX) || defined(OS_WINNT)
6693int fnmatch(const char *pat, const char *str, const int flag)
6694{
6695 while (*str != '\0') {
6696 if (*pat == '*') {
6697 pat++;
6698 if ((str = strchr(str, *pat)) == NULL)
6699 return -1;
6700 }
6701 if (*pat == *str) {
6702 pat++;
6703 str++;
6704 } else
6705 return -1;
6706 }
6707 if (*pat == '\0')
6708 return 0;
6709 else
6710 return -1;
6711}
6712#endif
6713
6714#ifdef OS_WINNT
6717#endif
6718
6719INT ss_file_find(const char *path, const char *pattern, char **plist)
6720{
6722
6723 int count = ss_file_find(path, pattern, &list);
6724 if (count <= 0)
6725 return count;
6726
6727 size_t size = list.size();
6728 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6729 for (size_t i=0; i<size; i++) {
6730 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6732 }
6733
6734 return size;
6735}
6736
6737INT ss_file_find(const char *path, const char *pattern, STRING_LIST *plist)
6738/********************************************************************\
6739
6740 Routine: ss_file_find
6741
6742 Purpose: Return list of files matching 'pattern' from the 'path' location
6743
6744 Input:
6745 char *path Name of a file in file system to check
6746 char *pattern pattern string (wildcard allowed)
6747
6748 Output:
6749 char **plist pointer to the lfile list
6750
6751 Function value:
6752 int Number of files matching request
6753
6754\********************************************************************/
6755{
6756 assert(plist);
6757 // Check if the directory exists
6758 if (access(path, F_OK) != 0) {
6759 return -1; // Return -1 files if directory doesn't exist
6760 }
6761
6762#ifdef OS_UNIX
6764 struct dirent *dp;
6765
6766 plist->clear();
6767 if ((dir_pointer = opendir(path)) == NULL)
6768 return 0;
6769 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6770 if (fnmatch(pattern, dp->d_name, 0) == 0 && (dp->d_type == DT_REG || dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN)) {
6771 plist->push_back(dp->d_name);
6773 }
6774 }
6776#endif
6777#ifdef OS_WINNT
6778 char str[255];
6779 int first;
6780
6781 strcpy(str, path);
6782 strcat(str, "\\");
6783 strcat(str, pattern);
6784 first = 1;
6786 *plist->clear();
6789 return 0;
6790 first = 0;
6791 plist->push_back(lpfdata->cFileName);
6792 i++;
6793 while (FindNextFile(pffile, lpfdata)) {
6794 plist->push_back(lpfdata->cFileName);
6795 i++;
6796 }
6797 free(lpfdata);
6798#endif
6799 return plist->size();
6800}
6801
6802INT ss_dir_find(const char *path, const char *pattern, char** plist)
6803{
6805
6806 int count = ss_dir_find(path, pattern, &list);
6807 if (count <= 0)
6808 return count;
6809
6810 size_t size = list.size();
6811 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6812 for (size_t i=0; i<size; i++) {
6813 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6815 }
6816
6817 return size;
6818}
6819
6820INT ss_dir_find(const char *path, const char *pattern, STRING_LIST *plist)
6821/********************************************************************\
6822
6823 Routine: ss_dir_find
6824
6825 Purpose: Return list of direcories matching 'pattern' from the 'path' location
6826
6827 Input:
6828 char *path Name of a file in file system to check
6829 char *pattern pattern string (wildcard allowed)
6830
6831 Output:
6832 char **plist pointer to the lfile list
6833
6834 Function value:
6835 int Number of files matching request
6836
6837 \********************************************************************/
6838{
6839 assert(plist);
6840#ifdef OS_UNIX
6842 struct dirent *dp;
6843
6844 if ((dir_pointer = opendir(path)) == NULL)
6845 return 0;
6846 plist->clear();
6847 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6848 if (fnmatch(pattern, dp->d_name, 0) == 0 && dp->d_type == DT_DIR) {
6849 plist->push_back(dp->d_name);
6851 }
6852 }
6854#endif
6855#ifdef OS_WINNT
6856 char str[255];
6857 int first;
6858
6859 strcpy(str, path);
6860 strcat(str, "\\");
6861 strcat(str, pattern);
6862 first = 1;
6863 plist->clear();
6867 return 0;
6868 first = 0;
6869 plist->push_back(lpfdata->cFileName);
6870 while (FindNextFile(pffile, lpfdata)) {
6871 plist->push_back(lpfdata->cFileName);
6872 }
6873 free(lpfdata);
6874#endif
6875 return plist->size();
6876}
6877
6878INT ss_dirlink_find(const char *path, const char *pattern, char** plist)
6879{
6881
6882 int count = ss_dirlink_find(path, pattern, &list);
6883 if (count <= 0)
6884 return count;
6885
6886 size_t size = list.size();
6887 *plist = (char *) malloc(size*MAX_STRING_LENGTH);
6888 for (size_t i=0; i<size; i++) {
6889 //printf("file %d [%s]\n", (int)i, list[i].c_str());
6891 }
6892
6893 return size;
6894}
6895
6896INT ss_dirlink_find(const char *path, const char *pattern, STRING_LIST *plist)
6897/********************************************************************\
6898
6899 Routine: ss_dirlink_find
6900
6901 Purpose: Return list of direcories and links matching 'pattern' from the 'path' location
6902
6903 Input:
6904 char *path Name of a file in file system to check
6905 char *pattern pattern string (wildcard allowed)
6906
6907 Output:
6908 char **plist pointer to the lfile list
6909
6910 Function value:
6911 int Number of files matching request
6912
6913 \********************************************************************/
6914{
6915 assert(plist);
6916#ifdef OS_UNIX
6918 struct dirent *dp;
6919
6920 if ((dir_pointer = opendir(path)) == NULL)
6921 return 0;
6922 plist->clear();
6923 for (dp = readdir(dir_pointer); dp != NULL; dp = readdir(dir_pointer)) {
6924 if (fnmatch(pattern, dp->d_name, 0) == 0) {
6925 /* must have a "/" at the end, otherwise also links to files are accepted */
6926 std::string full_path = std::string(path) + "/" + dp->d_name + "/";
6927 struct stat st;
6928 if (lstat(full_path.c_str(), &st) == 0 && (S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
6929 plist->push_back(dp->d_name);
6930 }
6931 }
6932 }
6934#endif
6935#ifdef OS_WINNT
6936 char str[255];
6937 int first;
6938
6939 strcpy(str, path);
6940 strcat(str, "\\");
6941 strcat(str, pattern);
6942 first = 1;
6943 plist->clear();
6947 return 0;
6948 first = 0;
6949 plist->push_back(lpfdata->cFileName);
6950 while (FindNextFile(pffile, lpfdata)) {
6951 plist->push_back(lpfdata->cFileName);
6952 }
6953 free(lpfdata);
6954#endif
6955 return plist->size();
6956}
6957
6958INT ss_file_remove(const char *path)
6959/********************************************************************\
6960
6961 Routine: ss_file_remove
6962
6963 Purpose: remove (delete) file given through the path
6964
6965 Input:
6966 char *path Name of a file in file system to check
6967
6968 Output:
6969
6970 Function value:
6971 int function error 0= ok, -1 check errno
6972
6973\********************************************************************/
6974{
6975 return remove(path);
6976}
6977
6978double ss_file_size(const char *path)
6979/********************************************************************\
6980
6981 Routine: ss_file_size
6982
6983 Purpose: Return file size in bytes for the given path
6984
6985 Input:
6986 char *path Name of a file in file system to check
6987
6988 Output:
6989
6990 Function value:
6991 double File size
6992
6993\********************************************************************/
6994{
6995#ifdef _LARGEFILE64_SOURCE
6996 struct stat64 stat_buf;
6997 int status;
6998
6999 /* allocate buffer with file size */
7000 status = stat64(path, &stat_buf);
7001 if (status != 0)
7002 return -1;
7003 return (double) stat_buf.st_size;
7004#else
7005 struct stat stat_buf;
7006 int status;
7007
7008 /* allocate buffer with file size */
7009 status = stat(path, &stat_buf);
7010 if (status != 0)
7011 return -1;
7012 return (double) stat_buf.st_size;
7013#endif
7014}
7015
7016time_t ss_file_time(const char *path)
7017/********************************************************************\
7018
7019 Routine: ss_file_time
7020
7021 Purpose: Return time of last file modification
7022
7023 Input:
7024 char *path Name of a file in file system to check
7025
7026 Output:
7027
7028 Function value:
7029 time_t File modification time
7030
7031\********************************************************************/
7032{
7033#ifdef _LARGEFILE64_SOURCE
7034 struct stat64 stat_buf;
7035 int status;
7036
7037 /* allocate buffer with file size */
7038 status = stat64(path, &stat_buf);
7039 if (status != 0)
7040 return -1;
7041 return stat_buf.st_mtime;
7042#else
7043 struct stat stat_buf;
7044 int status;
7045
7046 /* allocate buffer with file size */
7047 status = stat(path, &stat_buf);
7048 if (status != 0)
7049 return -1;
7050 return stat_buf.st_mtime;
7051#endif
7052}
7053
7054double ss_disk_size(const char *path)
7055/********************************************************************\
7056
7057 Routine: ss_disk_size
7058
7059 Purpose: Return full disk space
7060
7061 Input:
7062 char *path Name of a file in file system to check
7063
7064 Output:
7065
7066 Function value:
7067 doube Number of bytes free on disk
7068
7069\********************************************************************/
7070{
7071#ifdef OS_UNIX
7072#if defined(OS_OSF1)
7073 struct statfs st;
7074 statfs(path, &st, sizeof(st));
7075 return (double) st.f_blocks * st.f_fsize;
7076#elif defined(OS_LINUX)
7077 int status;
7078 struct statfs st;
7079 status = statfs(path, &st);
7080 if (status != 0)
7081 return -1;
7082 return (double) st.f_blocks * st.f_bsize;
7083#elif defined(OS_SOLARIS)
7084 struct statvfs st;
7085 statvfs(path, &st);
7086 if (st.f_frsize > 0)
7087 return (double) st.f_blocks * st.f_frsize;
7088 else
7089 return (double) st.f_blocks * st.f_bsize;
7090#elif defined(OS_ULTRIX)
7091 struct fs_data st;
7092 statfs(path, &st);
7093 return (double) st.fd_btot * 1024;
7094#elif defined(OS_IRIX)
7095 struct statfs st;
7096 statfs(path, &st, sizeof(struct statfs), 0);
7097 return (double) st.f_blocks * st.f_bsize;
7098#else
7099#error ss_disk_size not defined for this OS
7100#endif
7101#endif /* OS_UNIX */
7102
7103#ifdef OS_WINNT
7108 char str[80];
7109
7110 strcpy(str, path);
7111 if (strchr(str, ':') != NULL) {
7112 *(strchr(str, ':') + 1) = 0;
7115 } else
7117
7119#endif /* OS_WINNT */
7120
7121 return 1e9;
7122}
7123
7124int ss_file_exist(const char *path)
7125/********************************************************************\
7126
7127 Routine: ss_file_exist
7128
7129 Purpose: Check if a file exists
7130
7131 Input:
7132 char *path Name of a file in file to check
7133
7134 Output:
7135
7136 Function value:
7137 int 1: file exists
7138 0: file does not exist
7139
7140 \********************************************************************/
7141{
7142#ifdef OS_UNIX
7143 struct stat buf;
7144
7145 int retval = stat(path, &buf);
7146 //printf("retval %d, errno %d (%s)\n", retval, errno, strerror(errno));
7147 if (retval < 0)
7148 return 0;
7149 if (S_ISDIR(buf.st_mode))
7150 return 0;
7151#endif
7152
7153 int fd = open(path, O_RDONLY, 0);
7154 if (fd < 0)
7155 return 0;
7156 close(fd);
7157 return 1;
7158}
7159
7160int ss_file_link_exist(const char *path)
7161/********************************************************************\
7162
7163 Routine: ss_file_link_exist
7164
7165 Purpose: Check if a symbolic link file exists
7166
7167 Input:
7168 char *path Name of a file in file to check
7169
7170 Output:
7171
7172 Function value:
7173 int 1: file exists
7174 0: file does not exist
7175
7176 \********************************************************************/
7177{
7178#ifdef OS_UNIX
7179 struct stat buf;
7180
7181 int retval = lstat(path, &buf);
7182 if (retval < 0)
7183 return 0;
7184 if (S_ISLNK(buf.st_mode))
7185 return 1;
7186 return 0;
7187#endif
7188
7189 return 0;
7190}
7191
7192int ss_dir_exist(const char *path)
7193/********************************************************************\
7194
7195 Routine: ss_dir_exist
7196
7197 Purpose: Check if a directory exists
7198
7199 Input:
7200 char *path Name of a file in file to check
7201
7202 Output:
7203
7204 Function value:
7205 int 1: file exists
7206 0: file does not exist
7207
7208 \********************************************************************/
7209{
7210#ifdef OS_UNIX
7211 struct stat buf;
7212
7213 int retval = stat(path, &buf);
7214 //printf("retval %d, errno %d (%s)\n", retval, errno, strerror(errno));
7215 if (retval < 0)
7216 return 0;
7217 if (!S_ISDIR(buf.st_mode))
7218 return 0;
7219#else
7220#warning ss_dir_exist() is not implemented!
7221#endif
7222 return 1;
7223}
7224
7225int ss_file_copy(const char *src, const char *dst, bool append)
7226/********************************************************************\
7227
7228 Routine: ss_file_copy
7229
7230 Purpose: Copy file "src" to file "dst"
7231
7232 Input:
7233 const char *src Source file name
7234 const char *dst Destination file name
7235
7236 Output:
7237
7238 Function value:
7239 int function error 0= ok, -1 check errno
7240
7241 \********************************************************************/
7242{
7243 int fd_to, fd_from;
7244 char buf[4096];
7245 ssize_t nread;
7246 int saved_errno;
7247
7248 fd_from = open(src, O_RDONLY);
7249 if (fd_from < 0)
7250 return -1;
7251
7252 if (append)
7253 fd_to = open(dst, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, 0666);
7254 else
7255 fd_to = open(dst, O_WRONLY | O_CREAT | O_EXCL, 0666);
7256 if (fd_to < 0)
7257 goto out_error;
7258
7259 while (nread = read(fd_from, buf, sizeof(buf)), nread > 0) {
7260 char *out_ptr = buf;
7262
7263 do {
7265
7266 if (nwritten >= 0) {
7267 nread -= nwritten;
7268 out_ptr += nwritten;
7269 } else if (errno != EINTR) {
7270 goto out_error;
7271 }
7272 } while (nread > 0);
7273 }
7274
7275 if (nread == 0) {
7276 if (close(fd_to) < 0) {
7277 fd_to = -1;
7278 goto out_error;
7279 }
7280 close(fd_from);
7281
7282 /* Success! */
7283 return 0;
7284 }
7285
7286 out_error:
7288
7289 close(fd_from);
7290 if (fd_to >= 0)
7291 close(fd_to);
7292
7294 return -1;
7295}
7296
7297/*------------------------------------------------------------------*/
7298/********************************************************************\
7299* *
7300* Screen functions *
7301* *
7302\********************************************************************/
7303
7304/*------------------------------------------------------------------*/
7306/********************************************************************\
7307
7308 Routine: ss_clear_screen
7309
7310 Purpose: Clear the screen
7311
7312 Input:
7313 <none>
7314
7315 Output:
7316 <none>
7317
7318 Function value:
7319 <none>
7320
7321\********************************************************************/
7322{
7323#ifdef OS_WINNT
7324
7326 COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
7327 BOOL bSuccess;
7329 CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
7330 DWORD dwConSize; /* number of character cells in the current buffer */
7331
7333
7334 /* get the number of character cells in the current buffer */
7336 dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
7337
7338 /* fill the entire screen with blanks */
7340
7341 /* put the cursor at (0, 0) */
7343 return;
7344
7345#endif /* OS_WINNT */
7346#if defined(OS_UNIX) || defined(OS_VXWORKS) || defined(OS_VMS)
7347 printf("\033[2J");
7348#endif
7349#ifdef OS_MSDOS
7350 clrscr();
7351#endif
7352}
7353
7354/*------------------------------------------------------------------*/
7355void ss_set_screen_size(int x, int y)
7356/********************************************************************\
7357
7358 Routine: ss_set_screen_size
7359
7360 Purpose: Set the screen size in character cells
7361
7362 Input:
7363 <none>
7364
7365 Output:
7366 <none>
7367
7368 Function value:
7369 <none>
7370
7371\********************************************************************/
7372{
7373#ifdef OS_WINNT
7374
7377
7378 coordSize.X = (short) x;
7379 coordSize.Y = (short) y;
7382
7383#else /* OS_WINNT */
7384#endif
7385}
7386
7387/*------------------------------------------------------------------*/
7388void ss_printf(INT x, INT y, const char *format, ...)
7389/********************************************************************\
7390
7391 Routine: ss_printf
7392
7393 Purpose: Print string at given cursor position
7394
7395 Input:
7396 INT x,y Cursor position, starting from zero,
7397 x=0 and y=0 left upper corner
7398
7399 char *format Format string for printf
7400 ... Arguments for printf
7401
7402 Output:
7403 <none>
7404
7405 Function value:
7406 <none>
7407
7408\********************************************************************/
7409{
7410 char str[256];
7412
7413 va_start(argptr, format);
7414 vsprintf(str, (char *) format, argptr);
7415 va_end(argptr);
7416
7417#ifdef OS_WINNT
7418 {
7422
7424
7425 dwWriteCoord.X = (short) x;
7426 dwWriteCoord.Y = (short) y;
7427
7429 }
7430
7431#endif /* OS_WINNT */
7432
7433#if defined(OS_UNIX) || defined(OS_VXWORKS) || defined(OS_VMS)
7434 printf("\033[%1d;%1dH", y + 1, x + 1);
7435 printf("%s", str);
7436 fflush(stdout);
7437#endif
7438
7439#ifdef OS_MSDOS
7440 gotoxy(x + 1, y + 1);
7441 cputs(str);
7442#endif
7443}
7444
7445/*------------------------------------------------------------------*/
7446char *ss_getpass(const char *prompt)
7447/********************************************************************\
7448
7449 Routine: ss_getpass
7450
7451 Purpose: Read password without echoing it at the screen
7452
7453 Input:
7454 char *prompt Prompt string
7455
7456 Output:
7457 <none>
7458
7459 Function value:
7460 char* Pointer to password
7461
7462\********************************************************************/
7463{
7464 static char password[32];
7465
7466 fprintf(stdout, "%s", prompt);
7467 fflush(stdout);
7468 memset(password, 0, sizeof(password));
7469
7470#ifdef OS_UNIX
7471 return (char *) getpass("");
7472#elif defined(OS_WINNT)
7473 {
7476
7479 ReadConsole(hConsole, password, sizeof(password), &nCharsRead, NULL);
7481 printf("\n");
7482
7483 if (password[strlen(password) - 1] == '\r')
7484 password[strlen(password) - 1] = 0;
7485
7486 return password;
7487 }
7488#elif defined(OS_MSDOS)
7489 {
7490 char c, *ptr;
7491
7492 ptr = password;
7493 while ((c = getchar()) != EOF && c != '\n')
7494 *ptr++ = c;
7495 *ptr = 0;
7496
7497 printf("\n");
7498 return password;
7499 }
7500#else
7501 {
7502 ss_gets(password, 32);
7503 return password;
7504 }
7505#endif
7506}
7507
7508/*------------------------------------------------------------------*/
7510/********************************************************************\
7511
7512 Routine: ss_getchar
7513
7514 Purpose: Read a single character
7515
7516 Input:
7517 BOOL reset Reset terminal to standard mode
7518
7519 Output:
7520 <none>
7521
7522 Function value:
7523 int 0 for no character available
7524 CH_xxs for special character
7525 n ASCII code for normal character
7526 -1 function not available on this OS
7527
7528\********************************************************************/
7529{
7530#ifdef OS_UNIX
7531
7532 static BOOL init = FALSE;
7533 static struct termios save_termios;
7534 struct termios buf;
7535 int i, fd;
7536 char c[3];
7537
7538 if (_daemon_flag)
7539 return 0;
7540
7541 fd = fileno(stdin);
7542
7543 if (reset) {
7544 if (init)
7546 init = FALSE;
7547 return 0;
7548 }
7549
7550 if (!init) {
7551 tcgetattr(fd, &save_termios);
7552 memcpy(&buf, &save_termios, sizeof(buf));
7553
7554 buf.c_lflag &= ~(ECHO | ICANON | IEXTEN);
7555
7556 buf.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON);
7557
7558 buf.c_cflag &= ~(CSIZE | PARENB);
7559 buf.c_cflag |= CS8;
7560 /* buf.c_oflag &= ~(OPOST); */
7561 buf.c_cc[VMIN] = 0;
7562 buf.c_cc[VTIME] = 0;
7563
7564 tcsetattr(fd, TCSAFLUSH, &buf);
7565 init = TRUE;
7566 }
7567
7568 memset(c, 0, 3);
7569 i = read(fd, c, 1);
7570
7571 if (i == 0)
7572 return 0;
7573
7574 /* check if ESC */
7575 if (c[0] == 27) {
7576 i = read(fd, c, 2);
7577 if (i == 0) /* return if only ESC */
7578 return 27;
7579
7580 /* cursor keys return 2 chars, others 3 chars */
7581 if (c[1] < 65) {
7582 i = read(fd, c, 1);
7583 }
7584
7585 /* convert ESC sequence to CH_xxx */
7586 switch (c[1]) {
7587 case 49:
7588 return CH_HOME;
7589 case 50:
7590 return CH_INSERT;
7591 case 51:
7592 return CH_DELETE;
7593 case 52:
7594 return CH_END;
7595 case 53:
7596 return CH_PUP;
7597 case 54:
7598 return CH_PDOWN;
7599 case 65:
7600 return CH_UP;
7601 case 66:
7602 return CH_DOWN;
7603 case 67:
7604 return CH_RIGHT;
7605 case 68:
7606 return CH_LEFT;
7607 }
7608 }
7609
7610 /* BS/DEL -> BS */
7611 if (c[0] == 127)
7612 return CH_BS;
7613
7614 return c[0];
7615
7616#elif defined(OS_WINNT)
7617
7618 static BOOL init = FALSE;
7619 static INT repeat_count = 0;
7620 static INT repeat_char;
7625
7626 /* find out if we are under W95 */
7627 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7628 GetVersionEx(&vi);
7629
7630 if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
7631 /* under W95, console doesn't work properly */
7632 int c;
7633
7634 if (!kbhit())
7635 return 0;
7636
7637 c = getch();
7638 if (c == 224) {
7639 c = getch();
7640 switch (c) {
7641 case 71:
7642 return CH_HOME;
7643 case 72:
7644 return CH_UP;
7645 case 73:
7646 return CH_PUP;
7647 case 75:
7648 return CH_LEFT;
7649 case 77:
7650 return CH_RIGHT;
7651 case 79:
7652 return CH_END;
7653 case 80:
7654 return CH_DOWN;
7655 case 81:
7656 return CH_PDOWN;
7657 case 82:
7658 return CH_INSERT;
7659 case 83:
7660 return CH_DELETE;
7661 }
7662 }
7663 return c;
7664 }
7665
7667
7668 if (reset) {
7670 init = FALSE;
7671 return 0;
7672 }
7673
7674 if (!init) {
7676 init = TRUE;
7677 }
7678
7679 if (repeat_count) {
7680 repeat_count--;
7681 return repeat_char;
7682 }
7683
7685
7686 if (nCharsRead == 0)
7687 return 0;
7688
7690
7691 if (ir.EventType != KEY_EVENT)
7692 return ss_getchar(0);
7693
7694 if (!ir.Event.KeyEvent.bKeyDown)
7695 return ss_getchar(0);
7696
7697 if (ir.Event.KeyEvent.wRepeatCount > 1) {
7698 repeat_count = ir.Event.KeyEvent.wRepeatCount - 1;
7699 repeat_char = ir.Event.KeyEvent.uChar.AsciiChar;
7700 return repeat_char;
7701 }
7702
7703 if (ir.Event.KeyEvent.uChar.AsciiChar)
7704 return ir.Event.KeyEvent.uChar.AsciiChar;
7705
7706 if (ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY)) {
7707 switch (ir.Event.KeyEvent.wVirtualKeyCode) {
7708 case 33:
7709 return CH_PUP;
7710 case 34:
7711 return CH_PDOWN;
7712 case 35:
7713 return CH_END;
7714 case 36:
7715 return CH_HOME;
7716 case 37:
7717 return CH_LEFT;
7718 case 38:
7719 return CH_UP;
7720 case 39:
7721 return CH_RIGHT;
7722 case 40:
7723 return CH_DOWN;
7724 case 45:
7725 return CH_INSERT;
7726 case 46:
7727 return CH_DELETE;
7728 }
7729
7730 return ir.Event.KeyEvent.wVirtualKeyCode;
7731 }
7732
7733 return ss_getchar(0);
7734
7735#elif defined(OS_MSDOS)
7736
7737 int c;
7738
7739 if (!kbhit())
7740 return 0;
7741
7742 c = getch();
7743 if (!c) {
7744 c = getch();
7745 switch (c) {
7746 case 71:
7747 return CH_HOME;
7748 case 72:
7749 return CH_UP;
7750 case 73:
7751 return CH_PUP;
7752 case 75:
7753 return CH_LEFT;
7754 case 77:
7755 return CH_RIGHT;
7756 case 79:
7757 return CH_END;
7758 case 80:
7759 return CH_DOWN;
7760 case 81:
7761 return CH_PDOWN;
7762 case 82:
7763 return CH_INSERT;
7764 case 83:
7765 return CH_DELETE;
7766 }
7767 }
7768 return c;
7769
7770#else
7771 return -1;
7772#endif
7773}
7774
7775/*------------------------------------------------------------------*/
7776char *ss_gets(char *string, int size)
7777/********************************************************************\
7778
7779 Routine: ss_gets
7780
7781 Purpose: Read a line from standard input. Strip trailing new line
7782 character. Return in a loop so that it cannot be interrupted
7783 by an alarm() signal (like under Sun Solaris)
7784
7785 Input:
7786 INT size Size of string
7787
7788 Output:
7789 BOOL string Return string
7790
7791 Function value:
7792 char Return string
7793
7794\********************************************************************/
7795{
7796 char *p;
7797
7798 do {
7799 p = fgets(string, size, stdin);
7800 } while (p == NULL);
7801
7802
7803 if (strlen(p) > 0 && p[strlen(p) - 1] == '\n')
7804 p[strlen(p) - 1] = 0;
7805
7806 return p;
7807}
7808
7809/*------------------------------------------------------------------*/
7810/********************************************************************\
7811* *
7812* Direct IO functions *
7813* *
7814\********************************************************************/
7815
7816/*------------------------------------------------------------------*/
7818{
7819#ifdef OS_WINNT
7820
7821 /* under Windows NT, use DirectIO driver to open ports */
7822
7824 HANDLE hdio = 0;
7825 DWORD buffer[] = { 6, 0, 0, 0 };
7826 DWORD size;
7827
7828 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7829 GetVersionEx(&vi);
7830
7831 /* use DirectIO driver under NT to gain port access */
7832 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
7833 hdio = CreateFile("\\\\.\\directio", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7834 if (hdio == INVALID_HANDLE_VALUE) {
7835 printf("hyt1331.c: Cannot access IO ports (No DirectIO driver installed)\n");
7836 return -1;
7837 }
7838
7839 /* open ports */
7840 buffer[1] = start;
7841 buffer[2] = end;
7842 if (!DeviceIoControl(hdio, (DWORD) 0x9c406000, &buffer, sizeof(buffer), NULL, 0, &size, NULL))
7843 return -1;
7844 }
7845
7846 return SS_SUCCESS;
7847#else
7848 return SS_SUCCESS;
7849#endif
7850}
7851
7852/*------------------------------------------------------------------*/
7854{
7855#ifdef OS_WINNT
7856
7857 /* under Windows NT, use DirectIO driver to lock ports */
7858
7860 HANDLE hdio;
7861 DWORD buffer[] = { 7, 0, 0, 0 };
7862 DWORD size;
7863
7864 vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
7865 GetVersionEx(&vi);
7866
7867 /* use DirectIO driver under NT to gain port access */
7868 if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
7869 hdio = CreateFile("\\\\.\\directio", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7870 if (hdio == INVALID_HANDLE_VALUE) {
7871 printf("hyt1331.c: Cannot access IO ports (No DirectIO driver installed)\n");
7872 return -1;
7873 }
7874
7875 /* lock ports */
7876 buffer[1] = start;
7877 buffer[2] = end;
7878 if (!DeviceIoControl(hdio, (DWORD) 0x9c406000, &buffer, sizeof(buffer), NULL, 0, &size, NULL))
7879 return -1;
7880 }
7881
7882 return SS_SUCCESS;
7883#else
7884 return SS_SUCCESS;
7885#endif
7886}
7887
7888/*------------------------------------------------------------------*/
7889/********************************************************************\
7890* *
7891* Encryption *
7892* *
7893\********************************************************************/
7894
7895#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
7896
7897char *ss_crypt(const char *buf, const char *salt)
7898/********************************************************************\
7899
7900 Routine: ss_crypt
7901
7902 Purpose: Simple fake of UNIX crypt(3) function, until we get
7903 a better one
7904
7905 Input:
7906 char *buf Plain password
7907 char *slalt Two random characters
7908 events. Can be used to skip events
7909
7910 Output:
7911 <none>
7912
7913 Function value:
7914 char* Encrypted password
7915
7916\********************************************************************/
7917{
7918 int i, seed;
7919 static char enc_pw[13];
7920
7921 memset(enc_pw, 0, sizeof(enc_pw));
7922 enc_pw[0] = salt[0];
7923 enc_pw[1] = salt[1];
7924
7925 for (i = 0; i < 8 && buf[i]; i++)
7926 enc_pw[i + 2] = buf[i];
7927 for (; i < 8; i++)
7928 enc_pw[i + 2] = 0;
7929
7930 seed = 123;
7931 for (i = 2; i < 13; i++) {
7932 seed = 5 * seed + 27 + enc_pw[i];
7933 enc_pw[i] = (char) bin_to_ascii(seed & 0x3F);
7934 }
7935
7936 return enc_pw;
7937}
7938
7939/*------------------------------------------------------------------*/
7940/********************************************************************\
7941* *
7942* NaN's *
7943* *
7944\********************************************************************/
7945
7946double ss_nan()
7947{
7948 double nan;
7949
7950 nan = 0;
7951 nan = 0 / nan;
7952 return nan;
7953}
7954
7955#ifdef OS_WINNT
7956#include <float.h>
7957#ifndef isnan
7958#define isnan(x) _isnan(x)
7959#endif
7960#ifndef finite
7961#define finite(x) _finite(x)
7962#endif
7963#elif defined(OS_LINUX)
7964#include <math.h>
7965#endif
7966
7967int ss_isnan(double x)
7968{
7969 return isnan(x);
7970}
7971
7972int ss_isfin(double x)
7973{
7974#ifdef FP_INFINITE
7975 /* new-style finite() */
7976 return isfinite(x);
7977#else
7978 /* old-style finite() */
7979 return finite(x);
7980#endif
7981}
7982
7983/*------------------------------------------------------------------*/
7984/********************************************************************\
7985* *
7986* Stack Trace *
7987* *
7988\********************************************************************/
7989
7990#ifndef NO_EXECINFO
7991
7992#ifdef OS_LINUX
7993#include <execinfo.h>
7994#endif
7995
7996#define N_STACK_HISTORY 500
7999
8000INT ss_stack_get(char ***string)
8001{
8002#ifdef OS_LINUX
8003#define MAX_STACK_DEPTH 16
8004
8005 void *trace[MAX_STACK_DEPTH];
8006 int size;
8007
8009 *string = backtrace_symbols(trace, size);
8010 return size;
8011#else
8012 return 0;
8013#endif
8014}
8015
8017{
8018 char **string;
8019 int i, n;
8020
8021 n = ss_stack_get(&string);
8022 for (i = 0; i < n; i++)
8023 printf("%s\n", string[i]);
8024 if (n > 0)
8025 free(string);
8026}
8027
8029{
8030 char **string;
8031 int i, n;
8032
8033 if (stack_history_pointer == -1) {
8035 memset(stack_history, 0, sizeof(stack_history));
8036 }
8039 n = ss_stack_get(&string);
8040 for (i = 2; i < n; i++) {
8043 }
8044 free(string);
8045
8046 mstrlcpy(stack_history[stack_history_pointer], "=========================", 80);
8048}
8049
8050void ss_stack_history_dump(char *filename)
8051{
8052 FILE *f;
8053 int i, j;
8054
8055 f = fopen(filename, "wt");
8056 if (f != NULL) {
8058 for (i = 0; i < N_STACK_HISTORY; i++) {
8059 if (strlen(stack_history[j]) > 0)
8060 fprintf(f, "%s\n", stack_history[j]);
8061 j = (j + 1) % N_STACK_HISTORY;
8062 }
8063 fclose(f);
8064 printf("Stack dump written to %s\n", filename);
8065 } else
8066 printf("Cannot open %s: errno=%d\n", filename, errno);
8067}
8068
8069#endif
8070
8071// Method to check if a given string is valid UTF-8. Returns 1 if it is.
8072// This method was taken from stackoverflow user Christoph, specifically
8073// http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
8074bool ss_is_valid_utf8(const char * string)
8075{
8076 assert(string);
8077
8078 // FIXME: this function over-reads the input array. K.O. May 2021
8079
8080 const unsigned char * bytes = (const unsigned char *)string;
8081 while(*bytes) {
8082 if( (// ASCII
8083 // use bytes[0] <= 0x7F to allow ASCII control characters
8084 bytes[0] == 0x09 ||
8085 bytes[0] == 0x0A ||
8086 bytes[0] == 0x0D ||
8087 (0x20 <= bytes[0] && bytes[0] <= 0x7E)
8088 )
8089 ) {
8090 bytes += 1;
8091 continue;
8092 }
8093
8094 if( (// non-overlong 2-byte
8095 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
8096 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
8097 )
8098 ) {
8099 bytes += 2;
8100 continue;
8101 }
8102
8103 if( (// excluding overlongs
8104 bytes[0] == 0xE0 &&
8105 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
8106 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8107 ) ||
8108 (// straight 3-byte
8109 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
8110 bytes[0] == 0xEE ||
8111 bytes[0] == 0xEF) &&
8112 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8113 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8114 ) ||
8115 (// excluding surrogates
8116 bytes[0] == 0xED &&
8117 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
8118 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8119 )
8120 ) {
8121 bytes += 3;
8122 continue;
8123 }
8124
8125 if( (// planes 1-3
8126 bytes[0] == 0xF0 &&
8127 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
8128 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8129 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8130 ) ||
8131 (// planes 4-15
8132 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
8133 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8134 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8135 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8136 ) ||
8137 (// plane 16
8138 bytes[0] == 0xF4 &&
8139 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
8140 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8141 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8142 )
8143 ) {
8144 bytes += 4;
8145 continue;
8146 }
8147
8148 //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);
8149 //abort();
8150
8151 return false;
8152 }
8153
8154 return true;
8155}
8156
8157bool ss_repair_utf8(char* string)
8158{
8159 assert(string);
8160
8161 bool modified = false;
8162
8163 //std::string original = string;
8164
8165 // FIXME: this function over-reads the input array. K.O. May 2021
8166
8167 unsigned char * bytes = (unsigned char *)string;
8168 while(*bytes) {
8169 if( (// ASCII
8170 // use bytes[0] <= 0x7F to allow ASCII control characters
8171 bytes[0] == 0x09 ||
8172 bytes[0] == 0x0A ||
8173 bytes[0] == 0x0D ||
8174 (0x20 <= bytes[0] && bytes[0] <= 0x7E)
8175 )
8176 ) {
8177 bytes += 1;
8178 continue;
8179 }
8180
8181 if( (// non-overlong 2-byte
8182 (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
8183 (0x80 <= bytes[1] && bytes[1] <= 0xBF)
8184 )
8185 ) {
8186 bytes += 2;
8187 continue;
8188 }
8189
8190 if( (// excluding overlongs
8191 bytes[0] == 0xE0 &&
8192 (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
8193 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8194 ) ||
8195 (// straight 3-byte
8196 ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
8197 bytes[0] == 0xEE ||
8198 bytes[0] == 0xEF) &&
8199 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8200 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8201 ) ||
8202 (// excluding surrogates
8203 bytes[0] == 0xED &&
8204 (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
8205 (0x80 <= bytes[2] && bytes[2] <= 0xBF)
8206 )
8207 ) {
8208 bytes += 3;
8209 continue;
8210 }
8211
8212 if( (// planes 1-3
8213 bytes[0] == 0xF0 &&
8214 (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
8215 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8216 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8217 ) ||
8218 (// planes 4-15
8219 (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
8220 (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
8221 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8222 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8223 ) ||
8224 (// plane 16
8225 bytes[0] == 0xF4 &&
8226 (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
8227 (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
8228 (0x80 <= bytes[3] && bytes[3] <= 0xBF)
8229 )
8230 ) {
8231 bytes += 4;
8232 continue;
8233 }
8234
8235 if (bytes[0] == 0) // end of string
8236 break;
8237
8238 bytes[0] = '?';
8239 bytes += 1;
8240
8241 modified = true;
8242 }
8243
8244 //if (modified) {
8245 // printf("ss_repair_utf8(): invalid UTF8 string [%s] changed to [%s]\n", original.c_str(), string);
8246 //} else {
8247 // //printf("ss_repair_utf8(): string [%s] is ok\n", string);
8248 //}
8249
8250 return modified;
8251}
8252
8253bool ss_repair_utf8(std::string& s)
8254{
8255 // C++11 std::string data() is same as c_str(), NUL-terminated.
8256 // C++17 std::string data() is not "const".
8257 // https://en.cppreference.com/w/cpp/string/basic_string/data
8258 return ss_repair_utf8((char*)s.data()); // FIXME: C++17 or newer, do not need to drop the "const". K.O. May 2021
8259}
8260
8261std::chrono::time_point<std::chrono::high_resolution_clock> ss_us_start()
8262{
8263 return std::chrono::high_resolution_clock::now();
8264}
8265
8266unsigned int ss_us_since(std::chrono::time_point<std::chrono::high_resolution_clock> start) {
8267 auto elapsed = std::chrono::high_resolution_clock::now() - start;
8268 return std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
8269}
8270
/* end of msfunctionc */
8272/* emacs
8273 * Local Variables:
8274 * tab-width: 8
8275 * c-basic-offset: 3
8276 * indent-tabs-mode: nil
8277 * End:
8278 */
#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:5393
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:5712
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:8074
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:6802
static midas_thread_t _ss_server_thread
Definition system.cxx:3989
void ss_stack_print()
Definition system.cxx:8016
int ss_isnan(double x)
Definition system.cxx:7967
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:7054
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:7124
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:6298
INT ss_tape_write(INT channel, void *pdata, INT count)
Definition system.cxx:6038
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:5562
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:7972
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:6878
INT ss_getchar(BOOL reset)
Definition system.cxx:7509
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:6354
INT ss_shm_flush_thread(void *p)
Definition system.cxx:1136
INT ss_tape_fskip(INT channel, INT count)
Definition system.cxx:6238
INT ss_tape_close(INT channel)
Definition system.cxx:5908
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:7996
double ss_disk_free(const char *path)
Definition system.cxx:6626
time_t ss_file_time(const char *path)
Definition system.cxx:7016
INT ss_socket_get_peer_name(int sock, std::string *hostp, int *portp)
Definition system.cxx:5246
static int ss_socket_check(int sock)
Definition system.cxx:4493
int ss_file_link_exist(const char *path)
Definition system.cxx:7160
std::string ss_getcwd()
Definition system.cxx:5776
INT ss_tape_get_blockn(INT channel)
Definition system.cxx:6574
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:6978
char stack_history[N_STACK_HISTORY][80]
Definition system.cxx:7997
INT ss_tape_status(char *path)
Definition system.cxx:5952
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:8261
INT ss_tape_mount(INT channel)
Definition system.cxx:6462
char * ss_getpass(const char *prompt)
Definition system.cxx:7446
INT ss_directio_give_port(INT start, INT end)
Definition system.cxx:7817
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:8266
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:5399
INT ss_write_tcp(int sock, const char *buffer, size_t buffer_size)
Definition system.cxx:5352
int ss_dir_exist(const char *path)
Definition system.cxx:7192
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:7355
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:8028
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:6406
INT ss_stack_get(char ***string)
Definition system.cxx:8000
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:6958
void ss_stack_history_dump(char *filename)
Definition system.cxx:8050
INT ss_tape_read(INT channel, void *pdata, INT *count)
Definition system.cxx:6098
int ss_file_copy(const char *src, const char *dst, bool append)
Definition system.cxx:7225
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:5454
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:6518
double ss_nan()
Definition system.cxx:7946
static RPC_SERVER_ACCEPTION_LIST * _ss_server_acceptions
Definition system.cxx:3990
INT ss_tape_write_eof(INT channel)
Definition system.cxx:6172
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:8157
static std::atomic_int s_semaphore_nest_level
Definition system.cxx:2458
void ss_clear_screen()
Definition system.cxx:7305
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:7897
INT ss_tape_open(char *path, INT oflag, INT *channel)
Definition system.cxx:5817
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:7388
#define bin_to_ascii(c)
Definition system.cxx:7895
INT ss_socket_close(int *sockp)
Definition system.cxx:5231
INT ss_directio_lock_port(INT start, INT end)
Definition system.cxx:7853
struct suspend_struct SUSPEND_STRUCT
char * ss_gets(char *string, int size)
Definition system.cxx:7776
INT ss_recv_net_command(int sock, DWORD *routine_id, DWORD *param_size, char **param_ptr, int timeout_ms)
Definition system.cxx:5635
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:5285
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:7998
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:6719
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:14365
INT rpc_server_receive_rpc(int idx, RPC_SERVER_ACCEPTION *sa)
Definition midas.cxx:15879
INT rpc_client_accept(int lsock)
Definition midas.cxx:15606
INT rpc_server_receive_event(int idx, RPC_SERVER_ACCEPTION *sa, int timeout_msec)
Definition midas.cxx:15989
INT rpc_server_accept(int lsock)
Definition midas.cxx:15349
INT rpc_client_dispatch(int sock)
Definition midas.cxx:11953
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