[buildd-tools-devel] Bug#477770: [PATCH] improve SIGINT handling
Nathaniel W. Turner
nturner at exagrid.com
Mon May 10 19:07:21 UTC 2010
From: Nathaniel W. Turner <nate at houseofnate.net>
This addresses the case where a Ctrl-C is intended for a process running
in the chroot; before, schroot would always exit without cleaning up
(the most visible symptom being left-over bind-mounts in
/var/lib/schroot/mount). Now, cleanup occurs, and an appropriate error
message is displayed ("E: Child terminated by signal ‘Interrupt’").
This patch does not cover all the cases described by bug 477770, but I
think it is a step in the right direction.
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=477770
Signed-off-by: Nathaniel W. Turner <nate at houseofnate.net>
---
sbuild/sbuild-session.cc | 52 +++++++++++++++++++++++++++++++++++++++++++++-
sbuild/sbuild-session.h | 16 ++++++++++++++
2 files changed, 67 insertions(+), 1 deletions(-)
diff --git a/sbuild/sbuild-session.cc b/sbuild/sbuild-session.cc
index 9d83ca1..685a0bd 100644
--- a/sbuild/sbuild-session.cc
+++ b/sbuild/sbuild-session.cc
@@ -184,6 +184,7 @@ namespace
}
volatile bool sighup_called = false;
+ volatile bool sigint_called = false;
volatile bool sigterm_called = false;
/**
@@ -199,6 +200,29 @@ namespace
}
/**
+ * Handle the SIGINT signal.
+ *
+ * @param ignore the signal number.
+ *
+ * We explicitly do nothing with SIGINT, and rely on the exit status of child
+ * processes to determine if we should care. We want to make sure any child
+ * process (which will also have received SIGINT) has exited before we do
+ * anything, and some child processes (for example, emacs) may expect SIGINT
+ * during normal operation. See http://www.cons.org/cracauer/sigint.html for
+ * a good discussion of SIGINT handling.
+ */
+ void
+ sigint_handler (int ignore)
+ {
+ /*
+ * Allows us to detect if an interrupted waitpid() was interrupted due to
+ * SIGINT or something else. We may also want to use this at exit time to
+ * see if we should re-kill ourselves with SIGINT.
+ */
+ sigint_called = true;
+ }
+
+ /**
* Handle the SIGTERM signal.
*
* @param ignore the signal number.
@@ -242,6 +266,7 @@ session::session (std::string const& service,
session_id(),
force(false),
saved_sighup_signal(),
+ saved_sigint_signal(),
saved_sigterm_signal(),
saved_termios(),
termios_ok(false),
@@ -571,6 +596,8 @@ session::run_impl ()
{
sighup_called = false;
set_sighup_handler();
+ sigint_called = false;
+ set_sigint_handler();
sigterm_called = false;
set_sigterm_handler();
@@ -695,6 +722,7 @@ session::run_impl ()
catch (error const& e)
{
clear_sigterm_handler();
+ clear_sigint_handler();
clear_sighup_handler();
/* If a command was not run, but something failed, the exit
@@ -705,6 +733,7 @@ session::run_impl ()
}
clear_sigterm_handler();
+ clear_sigint_handler();
clear_sighup_handler();
}
@@ -1268,6 +1297,14 @@ session::wait_for_child (pid_t pid,
while (1)
{
+ /*
+ * If we (the parent process) gets SIGHUP or SIGTERM, pass this signal on
+ * to the child (once). Note that we do not handle SIGINT this way,
+ * because when a user presses Ctrl-C, the SIGINT is sent to all
+ * processes attached to that TTY (so the child will already have gotten
+ * it). In any case, once the child gets the signal, we just have to
+ * continue waiting for it to exit.
+ */
if ((sighup_called || sigterm_called) && !child_killed)
{
if (sighup_called)
@@ -1290,7 +1327,7 @@ session::wait_for_child (pid_t pid,
if (waitpid(pid, &status, 0) == -1)
{
- if (errno == EINTR && (sighup_called || sigterm_called))
+ if (errno == EINTR && (sighup_called || sigterm_called || sigint_called))
continue; // Kill child and wait again.
else
throw error(CHILD_WAIT, strerror(errno));
@@ -1305,6 +1342,7 @@ session::wait_for_child (pid_t pid,
sigterm_called = false;
throw error(SIGNAL_CATCH, strsignal(SIGTERM));
}
+ /* no need to handle the SIGINT case here; it is handled correctly below */
else
break;
}
@@ -1372,6 +1410,18 @@ session::clear_sighup_handler ()
}
void
+session::set_sigint_handler ()
+{
+ set_signal_handler(SIGINT, &this->saved_sigint_signal, sigint_handler);
+}
+
+void
+session::clear_sigint_handler ()
+{
+ clear_signal_handler(SIGINT, &this->saved_sigint_signal);
+}
+
+void
session::set_sigterm_handler ()
{
set_signal_handler(SIGTERM, &this->saved_sigterm_signal, sigterm_handler);
diff --git a/sbuild/sbuild-session.h b/sbuild/sbuild-session.h
index 888fbfe..9179a29 100644
--- a/sbuild/sbuild-session.h
+++ b/sbuild/sbuild-session.h
@@ -417,6 +417,20 @@ namespace sbuild
clear_sighup_handler ();
/**
+ * Set the SIGINT handler.
+ *
+ * An error will be thrown on failure.
+ */
+ void
+ set_sigint_handler ();
+
+ /**
+ * Restore the state of SIGINT prior to setting the handler.
+ */
+ void
+ clear_sigint_handler ();
+
+ /**
* Set the SIGTERM handler.
*
* An error will be thrown on failure.
@@ -474,6 +488,8 @@ namespace sbuild
bool force;
/// Signal saved while sighup handler is set.
struct sigaction saved_sighup_signal;
+ /// Signal saved while sigint handler is set.
+ struct sigaction saved_sigint_signal;
/// Signal saved while sigterm handler is set.
struct sigaction saved_sigterm_signal;
/// Saved terminal settings.
--
1.7.0.4
More information about the Buildd-tools-devel
mailing list