Home > Software design >  make open() return when signal is caught
make open() return when signal is caught

Time:10-22

When I call open("./fifo",O_RDONLY), the syscall will block because no one is writing to the fifo ./fifo. If a signal is received during that time that has no signal handler, the process ends instantly. So far so good. But when a signal is received that has a signal handler, the signal handler is executed and the open() syscall is still blocking.

How can I make open() return when I catch the signal?

I tried to block the signal, that does not work because there is no sigmask argument for open() like there is for pselect(). Using O_NONBLOCK does not work either, because then open() will return with an error, whether there is a signal or not. Removing the signal handler is also no good because I want to be able to react to the signal.

My test code:

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static volatile bool end=0;

static void catchSignal(int signal)
{
  (void)signal;
  const char *s="Catched Signal\n";
  write(STDERR_FILENO,s,strlen(s));
  end=1;
}

static int openFile(void)
{
  int fd=open("./in",O_RDONLY);
  if(fd<0)
  {
    perror("can't open file");
    exit(1);
  }
  return fd;
}


int main()
{
  if(SIG_ERR==signal(SIGTERM,catchSignal))
  {
    perror("cant set up signal handler");
    return -1;
  }
  int fd = openFile();
  while(end==0)
  {
    puts("Still running");
    usleep(300UL*1000);
  }
  puts("End now");
  if(fd>0)
  {
    close(fd);
  }
  return 0;
}

CodePudding user response:

The signal() function is problematic because of a history of implementations with different details. According to its Linux manual page:

The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.

(Emphasis in the original)

Instead of signal(), you should be using sigaction():

  struct sigaction sa = { .sa_handler = catchSignal };

  if (SIG_ERR == sigaction(SIGTERM, &sa, NULL))

Note that among the fields of a struct sigaction is sa_flags, a bitmask with which you can select among the various behaviors historically implemented by different versions of signal(). In particular, if you do not include the SA_RESTART flag, as the above indeed does not, then you should not see system calls automatically resume when interrupted by a signal (except for those few that are explicitly specified to do so).

CodePudding user response:

When you strace your program, you see that signal() functions sets SA_RESTART flag for the signal:

rt_sigaction(SIGTERM, {sa_handler=0x562f2a8c3249, sa_mask=[TERM], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fb504d2d210}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0

meaning, that the open() syscall will be automatically restarted after handling the signal.

You can use sigaction() to have more fine-grained control over signal handling and not set the SA_RESTART:

struct sigaction sa;
memset (&sa, 0, sizeof (sa));
sa.sa_handler = catchSignal;
sa.sa_flags = 0;
sigemptyset (&sa.sa_mask);
if (sigaction (SIGTERM, &sa, NULL) == -1) {
  perror("sigaction");
  return -1;
}
  • Related