1 #!/usr/bin/env python 2 3 # This code has been written by Sander Marechal and published at: 4 # http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ 5 # where the author has placed it in the public domain (see comment #6 at 6 # http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/#c6 7 # ). 8 # Some minor modifications have been made by the V8 authors. The work remains 9 # in the public domain. 10 11 import atexit 12 import os 13 from signal import SIGTERM 14 from signal import SIGINT 15 import sys 16 import time 17 18 19 class Daemon(object): 20 """ 21 A generic daemon class. 22 23 Usage: subclass the Daemon class and override the run() method 24 """ 25 def __init__(self, pidfile, stdin='/dev/null', 26 stdout='/dev/null', stderr='/dev/null'): 27 self.stdin = stdin 28 self.stdout = stdout 29 self.stderr = stderr 30 self.pidfile = pidfile 31 32 def daemonize(self): 33 """ 34 do the UNIX double-fork magic, see Stevens' "Advanced 35 Programming in the UNIX Environment" for details (ISBN 0201563177) 36 http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 37 """ 38 try: 39 pid = os.fork() 40 if pid > 0: 41 # exit first parent 42 sys.exit(0) 43 except OSError, e: 44 sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) 45 sys.exit(1) 46 47 # decouple from parent environment 48 os.chdir("/") 49 os.setsid() 50 os.umask(0) 51 52 # do second fork 53 try: 54 pid = os.fork() 55 if pid > 0: 56 # exit from second parent 57 sys.exit(0) 58 except OSError, e: 59 sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) 60 sys.exit(1) 61 62 # redirect standard file descriptors 63 sys.stdout.flush() 64 sys.stderr.flush() 65 si = file(self.stdin, 'r') 66 so = file(self.stdout, 'a+') 67 se = file(self.stderr, 'a+', 0) 68 # TODO: (debug) re-enable this! 69 #os.dup2(si.fileno(), sys.stdin.fileno()) 70 #os.dup2(so.fileno(), sys.stdout.fileno()) 71 #os.dup2(se.fileno(), sys.stderr.fileno()) 72 73 # write pidfile 74 atexit.register(self.delpid) 75 pid = str(os.getpid()) 76 file(self.pidfile, 'w+').write("%s\n" % pid) 77 78 def delpid(self): 79 os.remove(self.pidfile) 80 81 def start(self): 82 """ 83 Start the daemon 84 """ 85 # Check for a pidfile to see if the daemon already runs 86 try: 87 pf = file(self.pidfile, 'r') 88 pid = int(pf.read().strip()) 89 pf.close() 90 except IOError: 91 pid = None 92 93 if pid: 94 message = "pidfile %s already exist. Daemon already running?\n" 95 sys.stderr.write(message % self.pidfile) 96 sys.exit(1) 97 98 # Start the daemon 99 self.daemonize() 100 self.run() 101 102 def stop(self): 103 """ 104 Stop the daemon 105 """ 106 # Get the pid from the pidfile 107 try: 108 pf = file(self.pidfile, 'r') 109 pid = int(pf.read().strip()) 110 pf.close() 111 except IOError: 112 pid = None 113 114 if not pid: 115 message = "pidfile %s does not exist. Daemon not running?\n" 116 sys.stderr.write(message % self.pidfile) 117 return # not an error in a restart 118 119 # Try killing the daemon process 120 try: 121 # Give the process a one-second chance to exit gracefully. 122 os.kill(pid, SIGINT) 123 time.sleep(1) 124 while 1: 125 os.kill(pid, SIGTERM) 126 time.sleep(0.1) 127 except OSError, err: 128 err = str(err) 129 if err.find("No such process") > 0: 130 if os.path.exists(self.pidfile): 131 os.remove(self.pidfile) 132 else: 133 print str(err) 134 sys.exit(1) 135 136 def restart(self): 137 """ 138 Restart the daemon 139 """ 140 self.stop() 141 self.start() 142 143 def run(self): 144 """ 145 You should override this method when you subclass Daemon. It will be 146 called after the process has been daemonized by start() or restart(). 147 """ 148