Forking a daemon on Unix requires a certain sequence of system calls. Since Python exposes a full POSIX interface, this can be done in Python, too.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | import sys, os
def main():
""" A demo daemon main routine, write a datestamp to
/tmp/daemon-log every 10 seconds.
"""
import time
f = open("/tmp/daemon-log", "w")
while 1:
f.write('%s\n' % time.ctime(time.time()))
f.flush()
time.sleep(10)
if __name__ == "__main__":
# do the UNIX double-fork magic, see Stevens' "Advanced
# Programming in the UNIX Environment" for details (ISBN 0201563177)
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)
# start the daemon main loop
main()
|
If you ask yourself what a "daemon fork" is, it decouples a process from the calling terminal so it can run on its own, even if that terminal is closed. The other visible effect of such a daemon process is that you get your prompt back immediately. Typically, a daemon is a server process that runs without further user interaction, like a web server.
For details on the Unix side of things, see [1]. Typical C code for a "daemon fork" translates more or less literally to Python, the only specialty you have to consider is that os.fork() does not return -1 on errors, but throws an OSError exception.
[1] W. Richard Stevens, "Advanced Programming in the Unix Environment", 1992, Addison-Wesley, ISBN 0-201-56317-7.
os.chdir('/') is not needed.
Sure a "cd /" is needed. If you ever wanted to delete the directory you started a demon in that does NOT have that code, you'd think otherwise.
It's also worth redirecting the standard file descriptors. Check out the UNIX Programming FAQ for the full skinny on forking a daemon:
This cookbook entry's a pretty good implementation of the above, except that one of the things the FAQ entry advises is closing the standard file descriptors and redirecting them elsewhere (eg /dev/null, or log files, etc.). I've been bitten in the ass by forgetting to do this, so it's worth remembering. :-)
In the simplest form:
The FAQ entry also does things in a slightly different order - in particular it doesn't set the umask, or chdir('/') until after the second fork - but I don't know if that's important.
It also gives further justification for calling os.chdir('/'), namely "what if the sysadmin wants to umount the filesystem the daemon was run from?". Good stuff...
More reliable i/o stream redirection. Just reassigning to the sys streams is not 100% effective if you are importing modules that write to stdin and stdout from C code. Perhaps the modules shouldn't do that, but this code will make sure that all stdin and stdout will go where you expect it to.
oops... Of course, that should be:
not
Wrapping it all together... So taking everyone's suggestions I got this. Just save this into a file called "daemonize.py". If you run it as a script it will daemonize an example main() function. It should be self-documenting...
Did I miss anything?
Noah
(comment continued...)
(...continued from previous comment)
Things can be done much easier. I took a look at the code, and I remembered that I've written various Perl daemons some time ago. So, I took their source and translated it into python code. Here's what works for me
!/usr/bin/python
import sys import os try: pid = os.fork() except: print "Could not fork" sys.exit(1) if pid: # Let parent die ! sys.exit(0) else: try: # Create new session os.setsid() except: print "Could not create new session" sys.exit(1) while 1: # Your daemon code here
That's all, so I'm asking myself why you try these double-fork stuff ?
Adding start/stop/restart behavior.
(comment continued...)
(...continued from previous comment)
Why double-fork? I'm sure the references (Stevens, FAQ) explain this. The reason is we don't like zombies.
The second fork _is_ necessary. The first fork accomplishes two things - allow the shell to return, and allow you to do a setsid().
The setsid() removes yourself from your controlling terminal. You see, before, you were still listed as a job of your previous process, and therefore the user might accidentally send you a signal. setsid() gives you a new session, and removes the existing controlling terminal.
The problem is, you are now a session leader. As a session leader, if you open a file descriptor that is a terminal, it will become your controlling terminal (oops!). Therefore, the second fork makes you NOT be a session leader. Only session leaders can acquire a controlling terminal, so you can open up any file you wish without worrying that it will make you a controlling terminal.
So - first fork - allow shell to return, and permit you to call setsid()
Second fork - prevent you from accidentally reacquiring a controlling terminal.
And here is the translation in German.
(comment continued...)
(...continued from previous comment)
(comment continued...)
(...continued from previous comment)
Restart should handle not finding the pid. This is very useful code, but there is one detail that is wrong. If startstop() gets action='restart' and pid has not been set, the daemon is not started. Instead, the code reports "Could not stop, pid file '%s' missing." and calls sys.exit(1).
The code in the next few lines handles this correctly: if the os.kill() fails then the exception handler sets action = 'start' and falls through to allow the daemon to be started.
So, the code should read:
even safer to flush first. Before dup'ing a new file into the underlying stdout/stderr file descriptors, you should flush the stdio buffers. Otherwise, it is entirely possible that pending output could get sent to the wrong file.
Thus:
More general startstop method. Made startstop more general by including the action parameter. That enables you to call it from everywhere. Also added status parameter, that performs a very simple check if process is running.
Advanced Programming in the UNIX(R) Environment (2nd Edition).
close newfd before dup2.
file(stdin) dos not work ? My python didn't accept the three lines:
.. it gave me a name error:
.. should it be '/dev/stdin':
.. or perhaps sys.stdin (which does not work) ?
Implemenation based on this recipe and discussion. Some time ago we made an implementation of this daemonizing feature.
Its a class you can use to daemonize any class providing a run method and the stdin, stdout and stderr as class attributes.
For interested people the code is available here:
here's another implementation for 2008 ;)
this focuses on the startup part, not the daemonize itself, in a way i feel makes the flow a bit more clear.