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 toSIG_DFL
orSIG_IGN
. The semantics when usingsignal()
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;
}