2008/7/19 Robert Tate <[EMAIL PROTECTED]>:
> Neat project!
>
> Attached is a patch for your consideration. It adds an optional
> argument to the "xpra server" sub-command to run a specified command
> (presumably an X client application); when this application exits, the
> server is terminated (unless --no-automatic-stop was specified).
>
> Sample usages: (Note: a -- is required before the command if it
> contains any options starting with a -.)
> xpra start :20 -- xterm -fn 10x20
> xpra --no-automatic-stop start :20 xterm
Neat patch! Rather than overload the meaning of positional arguments,
I think I like a UI like:
xpra start :20 --with-child="xterm -fn 10x20"
This also allows for people to specify multiple programs if they want
that for some reason.
Comments for future reference:
> + pid,status=posix.waitpid(-1,posix.WNOHANG)
^^ pleasetrytousespacesitmakesthingsmorereadable
> + + "\t%prog start DISPLAY [
> [--no-automatic-stop] -- command]\n"
^^ we don't normally put options explicitly into the synopsis like
this, since putting them all in would take too much space, and
duplicate the automatically generated --help besides.
> + help="Don't terminate server when client on start
> command exits")
^^ Probably better to avoid referring to these as "clients", since
that term becomes overloaded ('xpra attach' is also a client, but a
very different sort). My version uses "child" instead; maybe there's
a better term yet?
> import posix
^^ Why use posix explicitly, instead of just os? (On posix systems
posix is an alias for os.)
I also rewrote the process spawning loop to use the subprocess module
instead of fork()/exec() directly, and factored out the (now more
complicated by the addition of multiple-child support) child reaping
functionality into an object, instead of having it refer to global
state.
Revised patch attached, also committed as
0490573b05ba466a373ee6380c24c266423dffd6 on branch
org.vorpus.parti.spawn-children. Now I just need to test it...
-- Nathaniel
#
# old_revision [0490573b05ba466a373ee6380c24c266423dffd6]
#
# patch "xpra/scripts/main.py"
# from [ab87ff74958e2549a9e817957a7fe6ed8463a558]
# to [0a2ece3c5989579ab35b56487882b887b8e4eae2]
#
# patch "xpra/scripts/server.py"
# from [e8923cc866750d4196572ea0648f089c02ae69c9]
# to [c22cd72c24012c339e245f96cecc7e920624b1c5]
#
============================================================
--- 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 c22cd72c24012c339e245f96cecc7e920624b1c5
@@ -31,6 +31,37 @@ 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, survive_children):
+ 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 self._children_pids.issubset(self._dead_pids)):
+ print "all children have exited and --survive-children was not specified, exiting"
+ os.exit(0)
+
+ 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 +132,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)
@@ -211,6 +242,16 @@ def run_server(parser, opts, mode, xpra_
if xvfb_pid is not None:
save_pid(xvfb_pid)
+ child_reaper = ChildReaper(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)
+
app = XpraServer(sockpath, upgrading)
def cleanup_socket(self):
print "removing socket"
_______________________________________________
Parti-discuss mailing list
[email protected]
http://lists.partiwm.org/cgi-bin/mailman/listinfo/parti-discuss