I've written a simple Python class for creating daemons on unix/linux systems. It was pieced together for various other examples, mostly corrections to various Python Cookbook articles and a couple of examples posted to the Python mailing lists. It has support for a pidfile to keep track of the process. I hope it's useful to someone.
Below is the Daemon class. To use it, simply subclass it and implement the run() method. Download this file.
Update 2009-05-31: An anonymous contributor has written a version of the Daemon class suitable for Python 3.x. Download the Python 3.x version here. The code below is for Python 2.x
- #!/usr/bin/env python
- import sys, os, time, atexit
- from signal import SIGTERM
- class Daemon:
- """
- A generic daemon class.
- Usage: subclass the Daemon class and override the run() method
- """
- def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
- self.stdin = stdin
- self.stdout = stdout
- self.stderr = stderr
- self.pidfile = pidfile
- def daemonize(self):
- """
- do the UNIX double-fork magic, see Stevens' "Advanced
- Programming in the UNIX Environment" for details (ISBN 0201563177)
- http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
- """
- try:
- pid = os.fork()
- if pid > 0:
- # exit first parent
- sys.exit(0)
- except OSError, e:
- sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
- sys.exit(1)
- # decouple from parent environment
- os.chdir("/")
- os.setsid()
- os.umask(0)
- # do second fork
- try:
- pid = os.fork()
- if pid > 0:
- # exit from second parent
- sys.exit(0)
- except OSError, e:
- sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
- sys.exit(1)
- # redirect standard file descriptors
- sys.stdout.flush()
- sys.stderr.flush()
- si = file(self.stdin, 'r')
- so = file(self.stdout, 'a+')
- se = file(self.stderr, 'a+', 0)
- os.dup2(si.fileno(), sys.stdin.fileno())
- os.dup2(so.fileno(), sys.stdout.fileno())
- os.dup2(se.fileno(), sys.stderr.fileno())
- # write pidfile
- atexit.register(self.delpid)
- pid = str(os.getpid())
- file(self.pidfile,'w+').write("%s\n" % pid)
- def delpid(self):
- os.remove(self.pidfile)
- def start(self):
- """
- Start the daemon
- """
- # Check for a pidfile to see if the daemon already runs
- try:
- pf = file(self.pidfile,'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
- if pid:
- message = "pidfile %s already exist. Daemon already running?\n"
- sys.stderr.write(message % self.pidfile)
- sys.exit(1)
- # Start the daemon
- self.daemonize()
- self.run()
- def stop(self):
- """
- Stop the daemon
- """
- # Get the pid from the pidfile
- try:
- pf = file(self.pidfile,'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
- if not pid:
- message = "pidfile %s does not exist. Daemon not running?\n"
- sys.stderr.write(message % self.pidfile)
- return # not an error in a restart
- # Try killing the daemon process
- try:
- while 1:
- os.kill(pid, SIGTERM)
- time.sleep(0.1)
- except OSError, err:
- err = str(err)
- if err.find("No such process") > 0:
- if os.path.exists(self.pidfile:
- os.remove(self.pidfile)
- else:
- print str(err)
- sys.exit(1)
- def restart(self):
- """
- Restart the daemon
- """
- self.stop()
- self.start()
- def run(self):
- """
- You should override this method when you subclass Daemon. It will be called after the process has been
- daemonized by start() or restart().
- """
And here is an example implementation. It implements the daemon as well as it's controlling client. Simply invoke this script with start, stop or restart as it's first argument. Download this file.
- #!/usr/bin/env python
- import sys, time
- from daemon import Daemon
- class MyDaemon(Daemon):
- def run(self):
- while True:
- time.sleep(1)
- if __name__ == "__main__":
- daemon = MyDaemon('/tmp/daemon-example.pid')
- if len(sys.argv) == 2:
- if 'start' == sys.argv[1]:
- daemon.start()
- elif 'stop' == sys.argv[1]:
- daemon.stop()
- elif 'restart' == sys.argv[1]:
- daemon.restart()
- else:
- print "Unknown command"
- sys.exit(2)
- sys.exit(0)
- else:
- print "usage: %s start|stop|restart" % sys.argv[0]
- sys.exit(2)
That's it! I hope this is of some use to someone. Happy coding!


Comments
#1 Guido van Steen
#2 coolluck (http://tech.coolluck.org/)
I think it'll be better add checking pidfile existance
because atexit handler may delete pidfile before execute os.remove(self.pidfile) in exception Handler "except OSError, err"
...
except OSError, err:
err = str(err)
if err.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
...
#3 Sander Marechal (http://www.jejik.com)
#4 narthollis
And far more elegant than my previous solution.
Many Thanks :)
#5 Anonymous Coward
#6 Sander Marechal (http://www.jejik.com)
This is really basic stuff and not very original. It's Public Domain, so do with it as you please.
#7 Jacob Singh (http://pajamadesign.com)
I've got it working, but I have one problem. I'm trying to do some stuff before exiting. So, in my code:
signal.signal(signal.SIGTEM,myhandler)
def myhandler(signum,frame):
#do something
sys.exit()
I assume that my sys.exit is calling itself by raising a SIGTERM somehow which is really annoying. How can I stop this?
Thanks!
Jacob
#8 Sander Marechal (http://www.jejik.com)
So, add the stuff you want to do before shutdown to the self.delpid() function.
#9 Anonymous Coward
# redirect standard file descriptors
#sys.stdout.flush()
#sys.stderr.flush()
#si = file(self.stdin, 'r')
#so = file(self.stdout, 'a+')
#se = file(self.stderr, 'a+', 0)
#os.dup2(si.fileno(), sys.stdin.fileno())
#os.dup2(so.fileno(), sys.stdout.fileno())
#os.dup2(se.fileno(), sys.stderr.fileno())
any idea?
thx
#10 Sander Marechal (http://www.jejik.com)
#11 Anonymous Coward
#12 Sander Marechal (http://www.jejik.com)
Can you tell me what os.path.devnull is set to in Python on OSX?
#13 Preston Hunt (http://prestonhunt.com)
#14 Guilherme Gall
Something like that:
def status(self):
try:
pf = file(self.pidfile, 'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
try:
procfile = file("/proc/%d/status" % pid, 'r')
procfile.close()
except IOError:
sys.stdout.write("there is not a process with the PID specified in %s\n" % self.pidfile)
sys.exit(0)
except TypeError:
sys.stdout.write("pidfile %s does not exist\n" % self.pidfile)
sys.exit(0)
sys.stdout.write("the process with the PID %d is running\n" % pid)
#15 Jonathan Manning
if (hasattr(os, "devnull")):
DEVNULL = os.devnull
else:
DEVNULL = "/dev/null"
Then use DEVNULL instead of the string "/dev/null" in the default arguments.
~J
#16 HosipLan (http://kdyby.org)
#17 Raj (http://rajorshi.net/blog)
#18 Anonymous Coward
def run(self):
while True:
print "test1\n"
time.sleep(1)
This does not log anything in the log file specified in the constructor of Daemon. If I remove the sleep(1) call, it works. I was thinking of a buffering issue but removing the buffering to stdout in daemon.py did not change anything.
Any idea ?
#19 Sander Marechal (http://www.jejik.com)
A far better alternative however is to use the "logging" module instead.
#20 Anonymous Coward
I tried replacing the print statement with logging.info("Test"). This has the same buffer issue as print statement. Can you explain why you redirect in your code stdout if it cannot be used reliably for output ? For example, when the daemon is stopped, there is no flush or close in the stop function and information that was sent to the log file is lost.
#21 Sander Marechal (http://www.jejik.com)
Using stdout and flush() can sorta work, but you don't have a real daemon anymore. Consider this. Open a terminal, start your program and then close the terminal. What happens to stdout now? It's gone. That's why it needs to be redirected to /dev/null. And that's why all daemons use logfiles or log to the syslog.
#22 Vik
I have a question, I need to start some scripts and stop them when I want and would also like to know their status, I have attached to this daemon, but then when I am starting the daemon, in ps -aux only the daemon is shown, not that those scripts have started. Or maybe I am going in the wrong way... Can someone help me?
#23 Sander Marechal (http://www.jejik.com)
#24 Roel
#25 Sander Marechal (http://www.jejik.com)
#26 Andr
When I extract this class to a submodule it does not work. It seems that the wrong process is terminated after the first fork...
Here is my modulcode... Help would really be appriciated...
#!/usr/bin/env python try: import os import sys import time import atexit from signal import SIGTERM except StandardError, e: import sys print "Error while loading libraries: " print e sys.exit() class HcpDaemon: """ A generic daemon class. Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile def daemonize(self): try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError, e: print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent, print eventual PID before print "Daemon PID %d" % pid sys.exit(0) except OSError, e: print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) sys.exit(1) sys.stdout.flush() sys.stderr.flush() si = file(self.stdin, 'r') so = file(self.stdout, 'a+') se = file(self.stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) pid = str(os.getpid()) file(self.pidfile,'w+').write("%s\n" % pid) atexit.register(self.delpid) def delpid(self): os.remove(self.pidfile) def stop(self): try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if not pid: message = "pidfile %s does not exist. Daemon not running?\n" sys.stderr.write(message % self.pidfile) return # not an error in a restart try: while 1: os.kill(pid, SIGTERM) time.sleep(0.1) except OSError, err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print str(err) sys.exit(1) def restart(self): self.stop() self.start() def start(self): try: pf = file(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid: message = "pidfile %s already exist. Daemon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) self.daemonize() self.run() def run(self): """Override while subclassing"""#27 Andr
just stupid me forget to import a module required by the overriding run method...
#28 Sander Marechal (http://www.jejik.com)
#29 Andre
#30 Brian
#31 Robert
#32 Sander Marechal (http://www.jejik.com)
@Robert: That sounds like a great project. I always like hearing where my code ends up :-)
#33 Brian
Thanks for the very useful code! I'm using it to implement an IRC bot. Works great.
#34 Vik
close failed: [Errno 10] No child processes
I am not sure as where I am going wrong, when I looked up on google to find out that this might be a bug in python:
http://mail.python.org/pipermail/python-bugs-list/2007-August/039194.html
I wanna keep my logs and get rid of this error, the problem is the error is continuously writing to a file and that way in a certain period of time my hard drive will be full with just one text file.
Please let me know if anyone has ever faced this error. Any help is appreciated.
Thanks
Vik
#35 Anonymous Coward
"""Generic linux daemon base class for python 3.x."""
import sys, os, time, atexit, signal
class daemon:
"""A generic daemon class.
Usage: subclass the daemon class and override the run() method."""
def __init__(self, pidfile): self.pidfile = pidfile
def daemonize(self):
"""Deamonize class. UNIX double fork mechanism."""
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
sys.exit(1)
# decouple from parent environment
os.chdir('/')
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError as err:
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
sys.exit(1)
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(os.devnull, 'a+')
se = open(os.devnull, 'a+')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# write pidfile
atexit.register(self.delpid)
pid = str(os.getpid())
with open(self.pidfile,'w+') as f:
f.write(pid + '\n')
def delpid(self):
os.remove(self.pidfile)
def start(self):
"""Start the daemon."""
# Check for a pidfile to see if the daemon already runs
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if pid:
message = "pidfile {0} already exist. " + \
"Daemon already running?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# Start the daemon
self.daemonize()
self.run()
def stop(self):
"""Stop the daemon."""
# Get the pid from the pidfile
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
if not pid:
message = "pidfile {0} does not exist. " + \
"Daemon not running?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# Try killing the daemon process
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print (str(err.args))
sys.exit(1)
def restart(self):
"""Restart the daemon."""
self.stop()
self.start()
def run(self):
"""You should override this method when you subclass Daemon.
It will be called after the process has been daemonized by
start() or restart()."""
#36 Sander Marechal (http://www.jejik.com)
I suggest you look at the backtraces for your error and see where they originate. Then simply catch the OSError and act appropriately (i.e. assume the thread has died, clean up and restart it for example).
#37 Sander Marechal (http://www.jejik.com)
#38 Vik
#39 Sander Marechal (http://www.jejik.com)
The first poster had a problem implementing his own daemon. He was implementing his code after the run() function. The answer is that your code should replace the run() function. That is what overriding a function means. You create your own function that replaces the original. You should make sure that the run() function never exits. That is why the example code has an infinite loop in it. If the run() function ever exits then the daemon stops.
The second poster had a question about the pidfile. IIRC he had a problem under Python 2.4 that the PID of the first fork was written to the pidfile and not the PID from the second fork, the one that keeps running. If I get this wrong, please re-post and I promise I will press the "ham" button this time :-)
Anyway, I have no idea what would cause this. I have tested this daemon code in 2.4, 2.5 and 2.6 and it works fine for me. The code that reads the pid and created the pidfile happens after the second fork. The original process and the first fork should already be dead by the time the pidfile is written.
It may be possible that there is a bug in the 2.4 version of os.getpid() but that is pure speculation. I know of no such bug, but one hypothetical scenario is that the second fork reaches os.getpid() before the first fork has exited completely (remember, forks run in parrallel). You could try a simple time.sleep(1) after the second fork but before calling the os.getpid() function. That sleep will make sure that the first fork is dead by the time the pidfile is created.
#40 Justin
The issue I was wondering about is that the pid of the daemon is the same as the second parent's pid--that's what's actually running, and that's what's in pidfile. To see what I'm seeing, add 'print pid' after each occurrence of 'if pid > 0:' in daemonize(), just before 'sys.exit()'. If I'm understanding correctly, those should be the pids of the first and second parents, while the second child's pid is obtained further on with a call to 'os.getpid()'. In my environment, python 2.4 on CentOS 5.3, the second child's pid is the same as the second parent's pid.
It's not really a problem, but I'm obviously not getting something here. I thought the process went like this: first parent->fork->first child/second parent->fork->second child. So there should be three pids in all.
The purpose of the second fork is to make sure that the daemon isn't the process group leader of the new session, but if the second fork isn't taking a new pid, then it's still the process group leader, yes?
#41 Sander Marechal (http://www.jejik.com)
The fork() command returns 0 in the child and returns the child's pid in the parent. So, the value of pid that you are printing just after 'if pid > 0:' is the pid of the child, not the pid of the parent. You are printing the pid of the first child and second child, not of the parent and first child.
You can see that by leaving those print statements in and adding a 'print os.getpid()' all the way at the beginning of the daemonize() method, before the first fork. Now you should see three different pid's printed. The original parent, the first child and the second child.
#42 Seth
daemon.py:
"""\ Daemon base and control class. This file contains the daemon base class for a UNIX daemon and the control class for it. See test logic at the end of file and test_daemon.py to understand how to use it.\ """ import sys, os, time, atexit, signal # -- generic daemon base class ------------------------------------------ # class daemon_base: """A generic daemon base class. Usage: subclass this class and override the run() method. """ def __init__(self, pidfile, workpath='/'): """Constructor. We need the pidfile for the atexit cleanup method. The workpath is the path the daemon will operate in. Normally this is the root directory, but can be some data directory too, just make sure it exists. """ self.pidfile = pidfile self.workpath = workpath def perror(self, msg, err): """Print error message and exit. (helper method) """ msg = msg + '\n' sys.stderr.write(msg.format(err)) sys.exit(1) def daemonize(self): """Deamonize calss process. (UNIX double fork mechanism). """ if not os.path.isdir(self.workpath): self.perror('workpath does not exist!', '') try: # exit first parent process pid = os.fork() if pid > 0: sys.exit(0) except OSError as err: self.perror('fork #1 failed: {0}', err) # decouple from parent environment try: os.chdir(self.workpath) except OSError as err: self.perror('path change failed: {0}', err) os.setsid() os.umask(0) try: # exit from second parent pid = os.fork() if pid > 0: sys.exit(0) except OSError as err: self.perror('fork #2 failed: {0}', err) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(os.devnull, 'r') so = open(os.devnull, 'a+') se = open(os.devnull, 'a+') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile atexit.register(os.remove, self.pidfile) pid = str(os.getpid()) with open(self.pidfile,'w+') as f: f.write(pid + '\n') self.run() def run(self): """Worker method. It will be called after the process has been daemonized by start() or restart(). You'll have to overwrite this method with the daemon program logic. """ while True: time.sleep(1) # -- daemon control class ----------------------------------------------- # class daemon_ctl: """Control class for a daemon. Usage: >>> dc = daemon_ctl(daemon_base, '/tmp/foo.pid') >>> dc.start() This class is the control wrapper for the above (daemon_base) class. It adds start/stop/restart functionality for it withouth creating a new daemon every time. """ def __init__(self, daemon, pidfile, workdir='/'): """Constructor. @param daemon: daemon class (not instance) @param pidfile: daemon pid file @param workdir: daemon working directory """ self.daemon = daemon self.pidfile = pidfile self.workdir = workdir def start(self): """Start the daemon. """ try: # check for pidfile to see if the daemon already runs with open(self.pidfile, 'r') as pf: pid = int(pf.read().strip()) except IOError: pid = None if pid: message = "pidfile {0} already exist. " + \ "Daemon already running?\n" sys.stderr.write(message.format(self.pidfile)) sys.exit(1) # Start the daemon d = self.daemon(self.pidfile, self.workdir) d.daemonize() def stop(self): """Stop the daemon. This is purely based on the pidfile / process control and does not reference the daemon class directly. """ try: # get the pid from the pidfile with open(self.pidfile,'r') as pf: pid = int(pf.read().strip()) except IOError: pid = None if not pid: message = "pidfile {0} does not exist. " + \ "Daemon not running?\n" sys.stderr.write(message.format(self.pidfile)) return # not an error in a restart try: # try killing the daemon process while 1: os.kill(pid, signal.SIGTERM) time.sleep(0.1) except OSError as err: e = str(err.args) if e.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print (str(err.args)) sys.exit(1) def restart(self): """Restart the daemon. """ self.stop() self.start() # -- test logic --------------------------------------------------------- # if __name__ == '__main__': """Daemon test logic. This logic must be called as seperate executable (i.e. python3 daemon.py start/stop/restart). See test_daemon.py for implementation. """ usage = 'Missing parameter, usage of test logic:\n' + \ ' % python3 daemon.py start|restart|stop\n' if len(sys.argv) < 2: sys.stderr.write(usage) sys.exit(2) pidfile = '/tmp/test_daemon.pid' dc = daemon_ctl(daemon_base, pidfile) if sys.argv[1] == 'start': dc.start() elif sys.argv[1] == 'stop': dc.stop() elif sys.argv[1] == 'restart': dc.restart()I also wrote an improvised test file for this file (still ugly, but does the job), test_daemon.py:
"""\ Base daemon test file. This file tests the base daemon (runs it's test logic). The daemon is started, then stopped, then started then restarted then stopped again. The output of this file should look like this (with different pids): starting daemon listing processes 15554 ? S 0:00 python3 daemon.py start -rw-rw-rw- 1 seth seth 6 2009-06-13 14:55 /tmp/test_daemon.pid stopping daemon listing processes ls: cannot access /tmp/test_daemon.pid: No such file or directory starting daemon listing processes 15572 ? S 0:00 python3 daemon.py start -rw-rw-rw- 1 seth seth 6 2009-06-13 14:55 /tmp/test_daemon.pid restarting daemon listing processes 15582 ? S 0:00 python3 daemon.py restart -rw-rw-rw- 1 seth seth 6 2009-06-13 14:55 /tmp/test_daemon.pid stopping daemon listing processes ls: cannot access /tmp/test_daemon.pid: No such file or directory """ import os, time if __name__ == '__main__': start_cmd = 'python3 daemon.py start' stop_cmd = 'python3 daemon.py stop' restart_cmd = 'python3 daemon.py restart' os.chdir(os.path.abspath(os.path.dirname(__file__))) print('starting daemon') os.system(start_cmd) time.sleep(0.2) print('listing processes') os.system('ps x | grep "python3 daemon.py" | grep -v grep') os.system('ls -l /tmp/test_daemon.pid') print('stopping daemon') os.system(stop_cmd) time.sleep(0.2) print('listing processes') os.system('ps x | grep "python3 daemon.py" | grep -v grep') os.system('ls -l /tmp/test_daemon.pid') print('starting daemon') os.system(start_cmd) time.sleep(0.2) print('listing processes') os.system('ps x | grep "python3 daemon.py" | grep -v grep') os.system('ls -l /tmp/test_daemon.pid') print('restarting daemon') os.system(restart_cmd) time.sleep(0.2) print('listing processes') os.system('ps x | grep "python3 daemon.py" | grep -v grep') os.system('ls -l /tmp/test_daemon.pid') print('stopping daemon') os.system(stop_cmd) time.sleep(0.2) print('listing processes') os.system('ps x | grep "python3 daemon.py" | grep -v grep') os.system('ls -l /tmp/test_daemon.pid')