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