Hi all,

The attached patch should make supervisor suitable for watching processes which background themselves and cannot be taught not to.

It has been lightly tested and appears to work. It comes with the usual caveat of reusing pids after a while but as long as they're assigned sequentially and the machine isn't under some insane load, it should work fine (famous last words?). The pidfile is actually reread every so often (5 seconds by default) so you actually have a chance of restarting the process without supervisor noticing. I'm not sure if it's a bug or a feature.

Does this thing have a chance of being accepted in supervisor mainline?

Best regards,
 Grzegorz Nosek
>From fee5b0a70d1c5e867d8f3dc150772deb785ff07a Mon Sep 17 00:00:00 2001
From: Grzegorz Nosek <r...@localdomain.pl>
Date: Wed, 10 Nov 2010 16:51:02 +0100
Subject: [PATCH] Support self-daemonizing processes in pidproxy

The original pidproxy script is useful for rerouting signals to a different
process but it still requires its direct child to live as long as the
interesting process.

This patch introduces the option --daemon, which makes pidproxy expect
a self-backgrounding process with a quickly exiting parent. pidproxy then
watches the pidfile and periodically checks whether the process is still
alive.
---
 src/supervisor/pidproxy.py |   53 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/src/supervisor/pidproxy.py b/src/supervisor/pidproxy.py
index 0434ca8..c7dcdb5 100755
--- a/src/supervisor/pidproxy.py
+++ b/src/supervisor/pidproxy.py
@@ -20,12 +20,16 @@ import os
 import sys
 import signal
 import time
+import errno
 
 class PidProxy:
     pid = None
     def __init__(self, args):
         self.setsignals()
         try:
+            if args[1] == '--daemon':
+                self.daemon_mode = True
+                args.pop(1)
             self.pidfile, cmdargs = args[1], args[2:]
             self.command = os.path.abspath(cmdargs[0])
             self.cmdargs = cmdargs
@@ -33,7 +37,27 @@ class PidProxy:
             self.usage()
             sys.exit(1)
 
+    def child_pid(self):
+        pid = int(open(self.pidfile, 'r').read().strip())
+        # will raise OSError (errno=ESRCH) when process is not found
+        os.kill(pid, 0)
+        return pid
+
+    def remove_pidfile(self):
+        try:
+            os.unlink(self.pidfile)
+        except OSError, e:
+            # pidfile missing is not an error worth reporting
+            if e.errno != errno.ENOENT:
+                raise
+
     def go(self):
+        if self.daemon_mode:
+            self.go_daemon()
+        else:
+            self.go_foreground()
+
+    def go_foreground(self):
         self.pid = os.spawnv(os.P_NOWAIT, self.command, self.cmdargs)
         while 1:
             time.sleep(5)
@@ -44,8 +68,30 @@ class PidProxy:
             if pid:
                 break
 
+    def go_daemon(self):
+        self.remove_pidfile()
+        self.pid = os.spawnv(os.P_NOWAIT, self.command, self.cmdargs)
+        while True:
+            # almost completely arbitrary but the child process
+            # should manage to write its pidfile in so many seconds
+            time.sleep(5)
+
+            # the direct-spawned child will probably exit quickly
+            # and is not very interesting
+            try:
+                  os.waitpid(-1, os.WNOHANG)
+            except OSError:
+                  pass
+            try:
+                  pid = self.child_pid()
+            except:
+                  pid = None
+            if not pid:
+                  break
+        self.remove_pidfile()
+
     def usage(self):
-        print "pidproxy.py <pidfile name> <command> [<cmdarg1> ...]"
+        print "pidproxy.py [--daemon] <pidfile name> <command> [<cmdarg1> ...]"
 
     def setsignals(self):
         signal.signal(signal.SIGTERM, self.passtochild)
@@ -64,7 +110,10 @@ class PidProxy:
             pid = int(open(self.pidfile, 'r').read().strip())
         except:
             pid = None
-            print "Can't read child pidfile %s!" % self.pidfile
+            if self.daemon_mode:
+                print "Can't read child pidfile %s! Is child process gone?" % self.pidfile
+            else:
+                print "Can't read child pidfile %s!" % self.pidfile
             return
         os.kill(pid, sig)
         if sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT]:
-- 
1.7.0.2

_______________________________________________
Supervisor-users mailing list
Supervisor-users@lists.supervisord.org
http://lists.supervisord.org/mailman/listinfo/supervisor-users

Reply via email to