133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org#!/usr/bin/env python 233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# This code has been written by Sander Marechal and published at: 433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ 533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# where the author has placed it in the public domain (see comment #6 at 633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/#c6 733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# ). 833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# Some minor modifications have been made by the V8 authors. The work remains 933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org# in the public domain. 1033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 1133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport atexit 1233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport os 1333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgfrom signal import SIGTERM 1433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgfrom signal import SIGINT 1533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport sys 1633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgimport time 1733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 1833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 1933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.orgclass Daemon(object): 2033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 2133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org A generic daemon class. 2233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 2333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Usage: subclass the Daemon class and override the run() method 2433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 2533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def __init__(self, pidfile, stdin='/dev/null', 2633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org stdout='/dev/null', stderr='/dev/null'): 2733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.stdin = stdin 2833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.stdout = stdout 2933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.stderr = stderr 3033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.pidfile = pidfile 3133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 3233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def daemonize(self): 3333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 3433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org do the UNIX double-fork magic, see Stevens' "Advanced 3533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Programming in the UNIX Environment" for details (ISBN 0201563177) 3633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 3733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 3833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 3933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = os.fork() 4033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if pid > 0: 4133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # exit first parent 4233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(0) 4333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except OSError, e: 4433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) 4533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 4633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 4733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # decouple from parent environment 4833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.chdir("/") 4933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.setsid() 5033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.umask(0) 5133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 5233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # do second fork 5333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 5433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = os.fork() 5533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if pid > 0: 5633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # exit from second parent 5733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(0) 5833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except OSError, e: 5933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) 6033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 6133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 6233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # redirect standard file descriptors 6333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stdout.flush() 6433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stderr.flush() 6533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org si = file(self.stdin, 'r') 6633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org so = file(self.stdout, 'a+') 6733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org se = file(self.stderr, 'a+', 0) 6833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # TODO: (debug) re-enable this! 6933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org #os.dup2(si.fileno(), sys.stdin.fileno()) 7033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org #os.dup2(so.fileno(), sys.stdout.fileno()) 7133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org #os.dup2(se.fileno(), sys.stderr.fileno()) 7233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 7333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # write pidfile 7433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org atexit.register(self.delpid) 7533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = str(os.getpid()) 7633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org file(self.pidfile, 'w+').write("%s\n" % pid) 7733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 7833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def delpid(self): 7933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.remove(self.pidfile) 8033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 8133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def start(self): 8233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 8333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Start the daemon 8433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 8533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Check for a pidfile to see if the daemon already runs 8633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 8733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pf = file(self.pidfile, 'r') 8833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = int(pf.read().strip()) 8933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pf.close() 9033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except IOError: 9133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = None 9233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 9333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if pid: 9433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org message = "pidfile %s already exist. Daemon already running?\n" 9533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stderr.write(message % self.pidfile) 9633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 9733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 9833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Start the daemon 9933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.daemonize() 10033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.run() 10133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 10233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def stop(self): 10333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 10433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Stop the daemon 10533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 10633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Get the pid from the pidfile 10733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 10833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pf = file(self.pidfile, 'r') 10933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = int(pf.read().strip()) 11033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pf.close() 11133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except IOError: 11233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org pid = None 11333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 11433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if not pid: 11533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org message = "pidfile %s does not exist. Daemon not running?\n" 11633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.stderr.write(message % self.pidfile) 11733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org return # not an error in a restart 11833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 11933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Try killing the daemon process 12033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org try: 12133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org # Give the process a one-second chance to exit gracefully. 12233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.kill(pid, SIGINT) 12333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org time.sleep(1) 12433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org while 1: 12533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.kill(pid, SIGTERM) 12633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org time.sleep(0.1) 12733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org except OSError, err: 12833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org err = str(err) 12933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if err.find("No such process") > 0: 13033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org if os.path.exists(self.pidfile): 13133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org os.remove(self.pidfile) 13233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org else: 13333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org print str(err) 13433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org sys.exit(1) 13533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 13633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def restart(self): 13733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 13833e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org Restart the daemon 13933e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 14033e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.stop() 14133e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org self.start() 14233e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org 14333e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org def run(self): 14433e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 14533e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org You should override this method when you subclass Daemon. It will be 14633e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org called after the process has been daemonized by start() or restart(). 14733e09c8efd078308de3c77a88301566f65c07befverwaest@chromium.org """ 148