make open() return when signal is caught

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;
}

Upvotes: 4

Views: 366

Answers (3)

Luis Colorado
Luis Colorado

Reputation: 12708

you can use O_NONBLOCK in which case open() will return immediately, and you will block as soon as you fcntl(2) cancelling the O_NONBLOCK.

Read the man page, as probably you have some way to make open(2) return -1 and errno equal to EINTR. But the normal usage is what has been described, the call is reissued, so the signal handler doesn't make the calll to be interrupted (much code depends on this behaviour). I'm not sure about this, but I think only pause(2) and select(2) and friends are interrupted (and return) when a non-ignored signal is received.

Is worth noting that only the thread that is blocked in an interruptible call and receives the signal is awaken and the call interrupted, and the thread receiving the interrupt can be any of the ones you have started in your process.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 181714

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).

Upvotes: 2

Erki Aring
Erki Aring

Reputation: 2106

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;
}

Upvotes: 0

Related Questions