[PATCH 10/17] Add an option to startProgram to reset signal handlers.

David Shea dshea at redhat.com
Sun Sep 21 19:37:02 UTC 2014


When set, reset_handlers will reset all SIG_IGN handlers to SIG_DFL
after forking the new process. This includes the SIGPIPE handler set by
Python. The option is set by default, since pretty much the only time we
want a SIG_IGN to be inherited is for X's weird startup signalling.

Add a test to make sure the option works.
---
 pyanaconda/iutil.py                  | 19 +++++++++++++++----
 tests/pyanaconda_tests/iutil_test.py | 31 +++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index a27cc21..1e5fed9 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -35,6 +35,7 @@ from threading import Thread
 from Queue import Queue, Empty
 from urllib import quote, unquote
 import gettext
+import signal
 
 from pyanaconda.flags import flags
 from pyanaconda.constants import DRACUT_SHUTDOWN_EJECT, TRANSLATIONS_UPDATE_DIR, UNSUPPORTED_HW
@@ -100,12 +101,13 @@ def setSysroot(path):
     _sysroot = path
 
 def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-        env_prune=None, **kwargs):
+        env_prune=None, reset_handlers=True, **kwargs):
     """ Start an external program and return the Popen object.
 
-        The root argument is handled by passing a preexec_fn argument to
-        subprocess.Popen, but an additional preexec_fn can still be specified
-        and will be run. The user preexec_fn will be run last.
+        The root and reset_handlers arguments are handled by passing a
+        preexec_fn argument to subprocess.Popen, but an additional preexec_fn
+        can still be specified and will be run. The user preexec_fn will be run
+        last.
 
         :param argv: The command to run and argument
         :param root: The directory to chroot to before running command.
@@ -113,6 +115,7 @@ def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subp
         :param stdout: The file object to write stdout to.
         :param stderr: The file object to write stderr to.
         :param env_prune: environment variable to remove before execution
+        :param reset_handlers: whether to reset to SIG_DFL any signal handlers set to SIG_IGN
         :param kwargs: Additional parameters to pass to subprocess.Popen
         :return: A Popen object for the running command.
     """
@@ -134,6 +137,14 @@ def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subp
             os.chroot(target_root)
             os.chdir("/")
 
+        # Signal handlers set to SIG_IGN persist across exec. Reset
+        # these to SIG_DFL if requested. In particular this will include the
+        # SIGPIPE handler set by python.
+        if reset_handlers:
+            for signum in range(1, signal.NSIG):
+                if signal.getsignal(signum) == signal.SIG_IGN:
+                    signal.signal(signum, signal.SIG_DFL)
+
         # If the user specified an additional preexec_fn argument, run it
         if preexec_fn is not None:
             preexec_fn()
diff --git a/tests/pyanaconda_tests/iutil_test.py b/tests/pyanaconda_tests/iutil_test.py
index b49d5ba..b048f9c 100644
--- a/tests/pyanaconda_tests/iutil_test.py
+++ b/tests/pyanaconda_tests/iutil_test.py
@@ -308,6 +308,37 @@ exit 0
             testfile.seek(0, os.SEEK_SET)
             self.assertEqual(testfile.read().strip(), marker_text)
 
+    def start_program_reset_handlers_test(self):
+        """Test the reset_handlers parameter of startProgram."""
+
+        with tempfile.NamedTemporaryFile() as testscript:
+            testscript.write("""#!/bin/sh
+# Just hang out and do nothing, forever
+while true ; do sleep 1 ; done
+""")
+            testscript.flush()
+
+            # Start a program with reset_handlers
+            proc = iutil.startProgram(["/bin/sh", testscript.name])
+
+            with timer(5):
+                # Kill with SIGPIPE and check that the python's SIG_IGN was not inheritted
+                # The process should die on the signal.
+                proc.send_signal(signal.SIGPIPE)
+                proc.communicate()
+                self.assertEqual(proc.returncode, -(signal.SIGPIPE))
+
+            # Start another copy without reset_handlers
+            proc = iutil.startProgram(["/bin/sh", testscript.name], reset_handlers=False)
+
+            with timer(5):
+                # Kill with SIGPIPE, then SIGTERM, and make sure SIGTERM was the one
+                # that worked.
+                proc.send_signal(signal.SIGPIPE)
+                proc.terminate()
+                proc.communicate()
+                self.assertEqual(proc.returncode, -(signal.SIGTERM))
+
 class MiscTests(unittest.TestCase):
     def get_dir_size_test(self):
         """Test the getDirSize."""
-- 
1.9.3



More information about the anaconda-patches mailing list