[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