[kupfer: 21/41] utils: Implement spawn_plugin_helper in kupfer.utils



commit 8226018af1091e8d79d21c2fc9e9d798189ecb4b
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Tue Apr 26 18:44:03 2011 +0200

    utils: Implement spawn_plugin_helper in kupfer.utils
    
    spawn_plugin_helper and spawn_child in kupfer.utils can spawn processes
    that should never live longer than Kupfer itself. Use (On linux)
    pr_set_deathsig to terminate the child process when kupfer exits.

 kupfer/plugin/vim/plugin.py |   39 +---------------------
 kupfer/utils.py             |   74 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 37 deletions(-)
---
diff --git a/kupfer/plugin/vim/plugin.py b/kupfer/plugin/vim/plugin.py
index cdf6008..6cbd638 100644
--- a/kupfer/plugin/vim/plugin.py
+++ b/kupfer/plugin/vim/plugin.py
@@ -15,6 +15,7 @@ from kupfer import utils
 from kupfer import kupferstring
 from kupfer import pretty
 from kupfer import plugin_support
+from kupfer import utils
 
 plugin_support.check_dbus_connection()
 
@@ -123,7 +124,7 @@ def get_plugin_service_obj(plugin_id, activate=True):
 	except dbus.DBusException as exc:
 		if activate:
 			service_id = "kupfer.plugin.%s.service" % plugin_id
-			return start_plugin_helper(service_id, True)
+			return utils.start_plugin_helper(service_id, True)
 		return None
 	proxy_iface = dbus.Interface(proxy_obj, interface_name)
 	return proxy_iface
@@ -137,42 +138,6 @@ def stop_plugin_service(plugin_id):
 		plug_iface.Exit(reply_handler=_dummy_handler,
 		                error_handler=_dummy_handler)
 
-def on_child_exit(pid, condition, user_data):
-	# @condition is the &status field of waitpid(2) (C library)
-	import os
-	argv, respawn = user_data
-	if respawn:
-		is_signal = os.WIFSIGNALED(condition)
-		if is_signal and respawn:
-			glib.timeout_add_seconds(10, spawn_child, argv, respawn)
-
-def spawn_child(argv, respawn=True):
-	"""
-	Spawn argv in the mainloop and keeping it as a child process
-	(so that it exits with the parent).
-
-	@respawn: If True, respawn if child dies abnormally
-
-	raises utils.SpawnError
-	"""
-	flags = (glib.SPAWN_SEARCH_PATH | glib.SPAWN_DO_NOT_REAP_CHILD)
-	try:
-		pid, stdin_fd, stdout_fd, stderr_fd = \
-			glib.spawn_async(argv, flags=flags)
-	except glib.GError as exc:
-		raise utils.SpawnError(unicode(exc))
-	if pid:
-		glib.child_watch_add(pid, on_child_exit, (argv, respawn))
-
-def start_plugin_helper(name, respawn):
-	"""
-	@respawn: If True, respawn if child dies abnormally
-	"""
-	argv = [sys.executable]
-	argv.extend(sys.argv)
-	argv.append('--exec-helper=%s' % name)
-	pretty.print_debug(__name__, "Spawning", argv)
-	spawn_child(argv, respawn)
 
 def _dummy_handler(*args):
 	pass
diff --git a/kupfer/utils.py b/kupfer/utils.py
index 02a786e..ed6632d 100644
--- a/kupfer/utils.py
+++ b/kupfer/utils.py
@@ -3,6 +3,7 @@ import os
 from os import path as os_path
 import locale
 import signal
+import sys
 
 import gobject
 import glib
@@ -277,6 +278,79 @@ def show_url(url):
 	except GError, exc:
 		pretty.print_error(__name__, "gtk.show_uri:", exc)
 
+def _on_child_exit(pid, condition, user_data):
+	# @condition is the &status field of waitpid(2) (C library)
+	argv, respawn = user_data
+	if respawn:
+		is_signal = os.WIFSIGNALED(condition)
+		if is_signal and respawn:
+			def callback(*args):
+				spawn_child(*args)
+				return False
+			glib.timeout_add_seconds(10, callback, argv, respawn)
+
+def _try_register_pr_pdeathsig():
+    """
+    Register PR_SET_PDEATHSIG (linux-only) for the calling process
+    which is a signal delivered when its parent dies.
+
+    This should ensure child processes die with the parent.
+    """
+    PR_SET_PDEATHSIG=1
+    SIGHUP=1
+    if sys.platform != 'linux2':
+        return
+    try:
+        import ctypes
+    except ImportError:
+        return
+    try:
+        libc = ctypes.CDLL("libc.so.6")
+        libc.prctl(PR_SET_PDEATHSIG, SIGHUP)
+    except (AttributeError, OSError):
+        pass
+
+def spawn_child(argv, respawn=True, display=None):
+	"""
+	Spawn argv in the mainloop and keeping it as a child process
+	(it will be made sure to exit with the parent).
+
+	@respawn: If True, respawn if child dies abnormally
+
+	raises utils.SpawnError
+	returns pid
+	"""
+	flags = (glib.SPAWN_SEARCH_PATH | glib.SPAWN_DO_NOT_REAP_CHILD)
+	kwargs = {}
+	if display:
+		# environment is passed as a sequence of strings
+		envd = os.environ.copy()
+		envd['DISPLAY'] = display
+		kwargs['envp'] = ['='.join((k,v)) for k,v in envd.items()]
+
+	try:
+		pid, stdin_fd, stdout_fd, stderr_fd = \
+			glib.spawn_async(argv, flags=flags,
+			                 child_setup=_try_register_pr_pdeathsig,
+			                 **kwargs)
+	except glib.GError as exc:
+		raise utils.SpawnError(unicode(exc))
+	if pid:
+		glib.child_watch_add(pid, _on_child_exit, (argv, respawn))
+	return pid
+
+def start_plugin_helper(name, respawn, display=None):
+	"""
+	@respawn: If True, respawn if child dies abnormally
+
+	raises SpawnError
+	"""
+	argv = [sys.executable]
+	argv.extend(sys.argv)
+	argv.append('--exec-helper=%s' % name)
+	pretty.print_debug(__name__, "Spawning", argv)
+	return spawn_child(argv, respawn, display=display)
+
 def show_help_url(url):
 	"""
 	Try at length to display a startup notification for the help browser.



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]