LCOV - code coverage report
Current view: top level - src - system.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 25.1 % 2044 514
Test Date: 2025-11-11 10:26:08 Functions: 29.7 % 138 41

            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, &param);
    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, &ltm);
    3570              : 
    3571              :    st.wYear = ltm.tm_year + 1900;
    3572              :    st.wMonth = ltm.tm_mon + 1;
    3573              :    st.wDay = ltm.tm_mday;
    3574              :    st.wHour = ltm.tm_hour;
    3575              :    st.wMinute = ltm.tm_min;
    3576              :    st.wSecond = ltm.tm_sec;
    3577              :    st.wMilliseconds = 0;
    3578              : 
    3579              :    SetLocalTime(&st);
    3580              : 
    3581              : #elif defined(OS_DARWIN) && defined(CLOCK_REALTIME)
    3582              : 
    3583              :    struct timespec ltm;
    3584              : 
    3585              :    ltm.tv_sec = seconds;
    3586              :    ltm.tv_nsec = 0;
    3587              :    clock_settime(CLOCK_REALTIME, &ltm);
    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, &ltm);
    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, &ltm);
    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, &ltm);
    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              :  */
        

Generated by: LCOV version 2.0-1