Fix signal handling.
authorAndre Noll <maan@systemlinux.org>
Thu, 2 Feb 2012 16:07:35 +0000 (17:07 +0100)
committerAndre Noll <maan@systemlinux.org>
Thu, 23 Feb 2012 21:31:30 +0000 (22:31 +0100)
Using signal() to set the disposition of a signal is always a bad idea
as POSIX does not specify whether a system call which was interrupted
should be restarted or not.

For interactive sessions, the Linux behaviour is to automatically
restart slow system calls, specifically read(2) in case nothing had
been read before the interrupt arrived. This is rather unfortunate
as adu calls fgets(3) (hence read(2)) in an endless loop to read the
user input. Therefore automatically restarted read() calls result
in interactive sessions that can not be terminated easily, as was
noticed by Sebastian Stark.

This patch makes the signal initialization code call sigaction()
instead of signal() to set up the handlers. This buys us well-defined
semantics across all operating systems, namely to *not* restart slow
system calls. As a side effect of this change, interactive sessions
can now be terminated by sending SIGINT (e.g., by pressing CTRL+C).

adu.c
error.h

diff --git a/adu.c b/adu.c
index 7d8c9ab..0bb5aad 100644 (file)
--- a/adu.c
+++ b/adu.c
@@ -165,14 +165,24 @@ void check_signals(void)
        exit(EXIT_FAILURE);
 }
 
+static int catch_signal(int sig)
+{
+       struct sigaction act;
+
+       act.sa_handler = signal_handler;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       return sigaction(sig, &act, NULL);
+}
+
 static int init_signals(void)
 {
-       if (signal(SIGINT, &signal_handler) == SIG_ERR)
-               return -E_SIGNAL_SIG_ERR;
-       if (signal(SIGTERM, &signal_handler) == SIG_ERR)
-               return -E_SIGNAL_SIG_ERR;
-       if (signal(SIGPIPE, &signal_handler) == SIG_ERR)
-               return -E_SIGNAL_SIG_ERR;
+       if (catch_signal(SIGINT) < 0)
+               return -E_SIGACTION;
+       if (catch_signal(SIGTERM) < 0)
+               return -E_SIGACTION;
+       if (catch_signal(SIGPIPE) == SIG_ERR)
+               return -E_SIGACTION;
        return 1;
 }
 
diff --git a/error.h b/error.h
index 5a5aefb..9849916 100644 (file)
--- a/error.h
+++ b/error.h
@@ -33,7 +33,7 @@
        _ERROR(EMPTY, "file empty") \
        _ERROR(MMAP, "mmap error") \
        _ERROR(OSL, "osl error") \
-       _ERROR(SIGNAL_SIG_ERR, "signal() returned SIG_ERR") \
+       _ERROR(SIGACTION, "could not install signal handler") \
        _ERROR(OUTPUT, "error writing output") \
        _ERROR(MALFORMED_FORMAT, "malformed format string") \
        _ERROR(BAD_ALIGN_SPEC, "bad alignment specifier") \