clinuxposixportingsigaction

Porting a C code to Windows from POSIX (problem with siginfo)


I am trying to compile the program 'MRCC' (developed for Linux) natively under Windows. The program is mainly written in Fortran, while the interface with the system is written in C as far as I can tell. All of the source files compile successfully with mingw64-gnu compilers except one C source that causes problems. The problem is from the type "siginfo_t" which is not implemented in mingw64.

The source file (signal.c) is:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


#ifdef __cplusplus
extern "C" {
#endif

#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif

void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);

static struct sigaction old_act;

void signalinit_() {
// initialise dmrcc's responses to signals
   struct sigaction act_term;
   memset(&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &parent_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);
}

void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
   pid_t pid_parent;
   char pidchar[10];
   char command[40];

   pid_parent = getpid();
   sprintf(pidchar, "%d", pid_parent);

   printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);

   sendSignalToChildren(SIGTERM);
   sleep(5);
   sendSignalToChildren(SIGKILL);

   printf("\n Program dmrcc terminating\n"); fflush(stdout);
   dmrccend_(&ERROR_SIGTERM);
}

void sendSignalToChildren(int sig) {
   int ownPid = getpid();
   int pid, numPids = 0;
   int *pids;
   FILE *pidfile = fopen("pids", "r");
   if (pidfile == NULL) {
      printf("Error: Could not open pids file\n");
      return;
   }

   // number of running processes other than the current process
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         numPids++;
      }
   }
   rewind(pidfile);

   // read other process' PIDs
   pids = (int *)malloc(numPids * sizeof(int));
   int i = -1;
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         pids[++i] = pid;
      }
   }

   // send signal sig to processes
   printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
   for (i = 0; i < numPids; i++) {
      kill((pid_t)pids[i], sig);
   }
   
   fclose(pidfile);
   free(pids);
}

void signalinitchild_() {
// initialise child's responses to signals
   struct sigaction act_term;
   memset (&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &child_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);

   struct sigaction act_segv;
   memset (&act_segv, '\0', sizeof(act_segv));
   act_segv.sa_sigaction = &child_sigsegv_;
   act_segv.sa_flags = SA_SIGINFO;
   sigaction(SIGSEGV, &act_segv, &old_act);
}

void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
   mrccend_(&ERROR_SIGTERM);
}

void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
   mrccend_(&ERROR_SIGSEGV);
   if(old_act.sa_flags & SA_SIGINFO)
   {
      (*old_act.sa_sigaction)(signum, siginfo, context);
   } 
   else 
   { 
      (*old_act.sa_handler)(signum);
   }
}

#ifdef __cplusplus
}
#endif

SIGTERM and SIGSEGV are supported in Windows natively. However, SIGINFO is not, so siginfo_t type is not defined. Ther compiler also throws error at the sigaction. I understand that I need to change the code so that it uses Windows API instead of POSIX API. However, I am not sure how to do that. Can I just replace the siginfo_t type with void type? And what can I do about sigaction?

[The signal.h header from mingw64 that I am using is pasted here ]


Solution

  • Rewrite it to use signal on Windows.

    (Under the hood, that's what Gnulib does for mingw platforms: reimplement sigaction in terms of signal, with a defined but effectively dummy siginfo_t structure to get past compilation issues.)

    While standard practice is to use sigaction in preference to signal — indeed, I'd say using signal is almost negligent these days — you've got to work with what you have, and you don't have a conforming SA_SIGINFO implementation. You don't have to worry about reinstalling handlers, since you intend to terminate upon the signals you do handle.

    So, when you're done it will look something like this:

    static void (*old_segv_handler)(int) = NULL;
    
    void parent_sigterm_(int s) { ... }
    
    void child_sigterm_(int s) { ... }
    
    void child_segv_(int s) {
        mrccend_(...);
        old_segv_handler(s); // FIXME: handle SIG_IGN, SIG_DFL, SIG_ERR
    }    
    
    void signalinit_() {
        signal(SIGTERM, parent_sigterm_);
    }
    void signalinitchild_() {
        signal(SIGTERM, child_sigterm_);
        old_segv_handler = signal(SIGSEGV, child_segv_);
    }
    

    Please do address the FIXME above and remove those unsafe stdio calls in your parent SIGTERM handler.