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