1

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 ]

S R Maiti
  • 237
  • 3
  • 13
  • 1
    Does https://stackoverflow.com/questions/32389905/sigaction-and-porting-linux-code-to-windows help? – Thomas Feb 24 '21 at 11:18
  • 4
    Those signal handling functions are a disaster waiting to happen. [Footnote 188 of the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#note188): "Thus, a signal handler cannot, in general, call standard library functions." POSIX allows for the calling of async-signal-safe functions from within a signal handler. Windows provides a similar list of functions. None of `printf()`, `fflush()`, `fopen()`, `fscanf()`, `fclose()`, `free()` nor `malloc()` are ***EVER*** safe to call from a signal handler on any system I've seen, and `sleep()` and `sprintf()` are often unsafe too. – Andrew Henle Feb 24 '21 at 11:30
  • @Thomas Yes I have seen that post, but I don't know enough about C to know how to apply it to my case. – S R Maiti Feb 24 '21 at 16:53
  • @AndrewHenle So, what should I do? The code is part of a package, so I can't modify all of it. There are only a very minor amount of C source code, would posting all of it help in any way? – S R Maiti Feb 24 '21 at 16:54
  • 1
    As an aside, [SIGINFO](https://www.gnu.org/software/libc/manual/html_node/Miscellaneous-Signals.html) and [`siginfo_t`](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html) are unrelated constructs. The presence or absence of one doesn't determine the presence or absence of the other. – pilcrow Mar 18 '21 at 18:22

2 Answers2

2

You can add Gnulib to your project.

"Portability problems fixed by Gnulib: struct sigaction and siginfo_t are missing on some platforms: mingw, MSVC 14." https://www.gnu.org/software/gnulib/manual/gnulib.html#signal_002eh

These links might be helpful if you encounter issues to compile your mingw project with gnulib

Cross-compile a gnulib based project to MinGW

https://courses.p2pu.org/en/groups/cross-linux-from-scratch/content/set-up-your-build-environment/

Another option is to try with cygwin which brings more POSIX compatibility.

runwuf
  • 1,701
  • 1
  • 8
  • 15
1

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.

pilcrow
  • 56,591
  • 13
  • 94
  • 135
  • Thanks for the answer btw. I can't figure out what the first line is doing though. Are you assigning null to the pointer of old_segv_handler? Sorry I am new to C so I don't understand this. Could you please explain what that line is doing? – S R Maiti Apr 12 '21 at 21:49
  • `old_segv_handler` is a pointer to a function taking an `int` and returning nothing, that is, to a basic signal handling function. – pilcrow Apr 12 '21 at 22:25
  • Thanks! Do you have any suggestions on how to deal with the kill function used in the latter half of the program? It sends the signal to the child processes, but the windows compiler does not support it. There is TerminateProcess() but it does not send signals, it just shuts down the process. I need a way to send a signal to the child processes. – S R Maiti Apr 13 '21 at 22:14
  • Can I ask again what you meant by the fixme? If I understood correctly, old_segv_handler takes the value of the previous function that would take the signal SIGEGV, so old_segv_handler would be SIG_DFL or SIG_IGN or SIG_ERR. So, how would I handle them? Can't I just pass the signal (int s) to the function? – S R Maiti Apr 30 '21 at 21:13
  • They are not functions and so cannot be called. You’ll have to simulate their behavior yourself. SIG_ERR is a bit of a red herring — it means your call to _signal_ failed. What do you want your program to do when it cannot do what you want? – pilcrow May 01 '21 at 02:32
  • So what is the original program doing at `(*old_act.sa_handler)(signum)` ? I thought it's calling the sa_handler function with signum argument? If the signal() call failed, I would want the program to shut down. – S R Maiti May 01 '21 at 09:11
  • Sounds like you might have a separate question to ask. – pilcrow May 01 '21 at 16:30