-1

Currently I am trying to create a signal handler that, when it receives a SIGTERM signal, it closes open network sockets and file descriptors.

Here is my SigHandler function

static void SigHandler(int signo){
    if(signo == SIGTERM){
      log_trace("SIGTERM received - handling signal");
      CloseSockets();
      log_trace("SIGTERM received - All sockets closed");

      if (closeFile() == -1)
        log_trace("SIGTERM received - No File associated with XXX open - continuing with shutdown");
      else
        log_trace("SIGTERM received - Closed File Descriptor for XXX - continuing with shutdown");

      log_trace("Gracefully shutting down XXX Service");
    } else {
      log_trace("%d received - incompatible signal");
      return;
    }

    exit(0);
}

This code below sits in main

if (sigemptyset(&set) == SIGEMPTYSET_ERROR){
    log_error("Signal handling initialization failed");
  }
  else {
    if(sigaddset(&set, SIGTERM) == SIGADDSET_ERROR) {
      log_error("Signal SIGTERM not valid");
    }
    action.sa_flags = 0;
    action.sa_mask = set;
    action.sa_handler = &SigHandler;
    if (sigaction(SIGTERM, &action, NULL) == SIGACTION_ERROR) {
      log_error("SIGTERM handler initialization error");
    }
  }

When I send kill -15 PID, nothing happens. The process doesn't terminate, nor does it become a zombie process (not that it should anyway). I do see the traces printing within the SigHandler function however, so I know it is reaching that point in the code. It just seems that when it comes to exit(0), that doesn't work.

When I send SIGKILL (kill -9 PID) it kills the process just fine.

Apologies if this is vague, I'm still quite new to C and UNIX etc so I'm quite unfamiliar with most of how this works at a low level.

Shox2711
  • 139
  • 1
  • 12
  • You should have read [sigaction(2)](http://man7.org/linux/man-pages/man2/sigaction.2.html) before coding. That `man` page refers to [signal(7)](http://man7.org/linux/man-pages/man7/signal.7.html) which mentions [signal-safety(7)](http://man7.org/linux/man-pages/man7/signal-safety.7.html). Even in 2019, RTFM is a wise maxim. – Basile Starynkevitch Mar 15 '19 at 09:39
  • If the question is specific to QNX, then QNX should be mentioned in it – Basile Starynkevitch Mar 15 '19 at 09:57

2 Answers2

4

Your signal handler routine is conceptually wrong (it does not use just async-signal-safe functions). Read carefully signal(7) and signal-safety(7) to understand why. And your handler could apparently work most of the time but still be undefined behavior.

The usual trick is to set (in your signal handler) some volatile sig_atomic_t variable and test that variable outside of the signal handler. Another possible trick is the pipe(7) to self trick (the Qt documentation explains it well), with your signal handler just doing a write(2) (which is async-signal-safe) to some global file descriptor obtained by e.g. pipe(2) (or perhaps the Linux specific eventfd(2)...) at program initialization before installing that signal handler.

A Linux specific way is to use signalfd(2) for SIGTERM and handle that in your own event loop (based upon poll(2)). That trick is conceptually a variant of the pipe to self one. But signalfd has some shortcomings, that a web search will find you easily.

Signals are conceptually hard to use (some view them as a design mistake in Unix), especially in multi-threaded programs.

You might want to read the old ALP book. It has some good explanations related to your issue.

PS. If your system is QNX you should read its documentation.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

You should be using _exit from the signal handler instead, this also closes all the files.

Also read (very carefully) Basile's answer and take a long hard look at the list of async safe functions which you are allowed to use in signal handlers.

His advice about just changing a flag and testing it in your code is the best way if you need to do something you aren't allowed in the signal handler. Note that all blocking posix calls can be interrupted by signals so testing your atomic variable if you get an error on a blocking call (to say read) is a sure way to know if you have received a signal.

xception
  • 4,241
  • 1
  • 17
  • 27