On Fri, Aug 8, 2008 at 1:42 AM, Nathaniel Smith <[EMAIL PROTECTED]> wrote:
> Revised patch attached, also committed as
> 0490573b05ba466a373ee6380c24c266423dffd6 on branch
> org.vorpus.parti.spawn-children.  Now I just need to test it...

Okay, and here's the version that works :-)

I am struck by another question now, though, i.e.: should
--survive-children be the default or not?  I can imagine people be
surprised either way...

-- Nathaniel
#
# old_revision [0490573b05ba466a373ee6380c24c266423dffd6]
#
# patch "xpra/scripts/main.py"
#  from [ab87ff74958e2549a9e817957a7fe6ed8463a558]
#    to [0a2ece3c5989579ab35b56487882b887b8e4eae2]
# 
# patch "xpra/scripts/server.py"
#  from [e8923cc866750d4196572ea0648f089c02ae69c9]
#    to [57a51c5c15dbd53a48e75c207e27dd61f0601e20]
#
============================================================
--- xpra/scripts/main.py	ab87ff74958e2549a9e817957a7fe6ed8463a558
+++ xpra/scripts/main.py	0a2ece3c5989579ab35b56487882b887b8e4eae2
@@ -27,12 +27,21 @@ def main(script_file, cmdline):
     parser.add_option("--no-daemon", action="store_false",
                       dest="daemon", default=True,
                       help="Don't daemonize when running as a server")
+    parser.add_option("--no-automatic-stop", action="store_false",
+                      dest="automatic_stop", default=True,
+                      help="Don't terminate server when client on start command exits")
     parser.add_option("--remote-xpra", action="store",
                       dest="remote_xpra", default=None, metavar="CMD",
                       help="How to run 'xpra' on the remote host")
     parser.add_option("-d", "--debug", action="store",
                       dest="debug", default=None, metavar="FILTER1,FILTER2,...",
                       help="List of categories to enable debugging for (or \"all\")")
+    parser.add_option("--start-child", action="append",
+                      dest="children",
+                      help="program to spawn in new server (may be repeated)")
+    parser.add_option("--survive-children", action="store_true",
+                      dest="survive_children", default=False,
+                      help="Don't terminate server when --start-child command(s) exit")
     (options, args) = parser.parse_args(cmdline[1:])
 
     if not args:
============================================================
--- xpra/scripts/server.py	e8923cc866750d4196572ea0648f089c02ae69c9
+++ xpra/scripts/server.py	57a51c5c15dbd53a48e75c207e27dd61f0601e20
@@ -31,6 +31,39 @@ def deadly_signal(signum, frame):
     #kill(os.getpid(), signum)
     os._exit(128 + signum)
 
+# Note that this class has async subtleties -- e.g., it is possible for a
+# child to exit and us to receive the SIGCHLD before our fork() returns (and
+# thus before we even know the pid of the child).  So be careful:
+class ChildReaper(object):
+    def __init__(self, app, survive_children):
+        self._app = app
+        self._children_pids = None
+        self._dead_pids = set()
+        self._survive_children = survive_children
+
+    def set_children_pids(self, children_pids):
+        assert self._children_pids is None
+        self._children_pids = children_pids
+        self.check()
+
+    def check(self):
+        if (self._children_pids
+            and not self._survive_children
+            and self._children_pids.issubset(self._dead_pids)):
+            print "all children have exited and --survive-children was not specified, exiting"
+            self._app.quit(False)
+
+    def __call__(self, signum, frame):
+        while 1:
+            try:
+                pid, status = os.waitpid(-1, os.WNOHANG)
+            except OSError:
+                break
+            if pid == 0:
+                break
+            self._dead_pids.add(pid)
+            self.check()
+
 def save_pid(pid):
     prop_set(gtk.gdk.get_default_root_window(),
              "_XPRA_SERVER_PID", "u32", pid)
@@ -101,7 +134,7 @@ def run_server(parser, opts, mode, xpra_
 def run_server(parser, opts, mode, xpra_file, extra_args):
     if len(extra_args) != 1:
         parser.error("need exactly 1 extra argument")
-    display_name = extra_args[0]
+    display_name = extra_args.pop(0)
 
     atexit.register(run_cleanups)
     signal.signal(signal.SIGINT, deadly_signal)
@@ -219,6 +252,17 @@ def run_server(parser, opts, mode, xpra_
         except:
             pass
     _cleanups.append(cleanup_socket)
+
+    child_reaper = ChildReaper(app, opts.survive_children)
+    # Always register the child reaper, because even if survive_children is
+    # true, we still need to reap them somehow to avoid zombies:
+    signal.signal(signal.SIGCHLD, child_reaper)
+    if opts.children:
+        children_pids = set()
+        for child_cmd in opts.children:
+            children_pids.add(subprocess.Popen(child_cmd, shell=True).pid)
+        child_reaper.set_children_pids(children_pids)
+
     if app.run():
         # Upgrading, so leave X server running
         _cleanups.remove(kill_xvfb)
_______________________________________________
Parti-discuss mailing list
[email protected]
http://lists.partiwm.org/cgi-bin/mailman/listinfo/parti-discuss

Reply via email to