Writing a simple linux daemon type thing...
James T. Dennis
jadestar at idiom.com
Sun Jun 16 20:21:17 EDT 2002
Jones <jones at jones.jones> wrote:
> Hi everyone,
> I hope this is a simple question for you guys. I'm new to Python and Linux.
> I'm wondering if Python (and this group) can help me.
> I want a program to always be running in the background that, connects to
> PostgreSQL every few seconds, queries a table, and for each record it finds,
> shells out to the system to execute program.
> My research indicates that "nohup" might do the trick? So would it be as
> simple as writing the program in Python and then puting something like
> "nohup python myprog" in the Linux startup sequence (wherever that may be)?
Technically a daemon is different than a nohup'd background process.
The differences have to do with signal handling and session/process groups.
> That's my primary goal. Now... assuming I get past that part, I may want to
> take it a few steps further later on. I'd be happy if you guys could take a
> stab at telling me if these are possible and any tips too...
> a) Make it multi-threaded... As I said before, the number of times it
> executes the external program equals the number of records it finds in the
> table. It would be nice if I could execute the program multiple times
> concurrently in a *controlled* manner (depending on my system resources)
> instead of having to wait for each one to be done before starting another
> one.
It is unlikely that you need to be "multi-threaded" for at least
two reasons. First the time you spend establishing new connections
will probably dominate the program, it might make sense for your
deamon (or background process) to keep an open connection to the
db rather than closing and reopening it every few seconds. If the
interval was several minutes or was to be event driven then I might
offer a different suggestion. It if was responding to signals or
especially to socket connection then I might see a case for a
multi-threaded set of connections (in a pool).
> b) Make it fail-safe. I would like to figure out how I could re-launch the
> program if it was to crash.
The most straightforward way to do this on a Linux or SysV UNIX system
is to have your program launched under a respawn directive from
the /etc/inittab (but this requires sysadmin (root) intervention to
configure). Another way is to use a program like DJ Bernstein's
"supervise" (which is a daemon written in C that will launch and
"supervise" another process, respawning it as necessary.
> P.S. You're probably wondering why I don't just use a trigger. One reason
> is that I don't think I can execute an external program from PostgreSQL and
> I don't know C to write a wrapper function. Second reason is that because I
> am running an external program (out of my "control"), I'm afraid it might
> crash, etc and that record will never get processed.
> Thank You!!!
Searching google on "python daemon os.setsid" if find the following:
http://mlf.linux.rulez.org/Archivum/l-code-l-199911/msg00060.html
Which contains some text in a language that I don't recognize
(Swedish?, Slavic?, who knows) and some code in Python:
from posix import geteuid
import os import sys
def daemon(nochdir = 0,noclose = 0):
"""Runs server as INIT daemon."""
if geteuid() != 0:
print "You should be root to run an INIT daemon" #
sys.exit()
if os.fork(): os._exit(0)
os.setsid()
if nochdir == 0: os.chdir("/")
if noclose == 0:
fp = open("/dev/null", "rw")
sys.stdin = sys.__stdin__ = fp
sys.stdout = sys.__stdout__ = fp
sys.stderr = sys.__stderr__ = fp
del fp
if os.fork(): os._exit(0)
This looks reasonable. In your case you don't want to run this
as root so you can reduce that a bit to:
import os import sys
def daemon(nochdir = 0,noclose = 0):
"""Runs server as INIT daemon."""
if os.fork(): os._exit(0)
os.setsid()
if nochdir == 0: os.chdir("/")
if noclose == 0:
fp = open("/dev/null", "rw")
sys.stdin = sys.__stdin__ = fp
sys.stdout = sys.__stdout__ = fp
sys.stderr = sys.__stderr__ = fp
del fp
if os.fork(): os._exit(0)
First this code spawns a child and has the parent
commit suicide; so the child is detached from the
immediate parent. Then it makes itself the session
leader of a new process group (divorcing it from some
TTY signal handling that it inherited). BTW: the setsid
call may *require* us to do the if os.fork(): os._exit(0)
--- I got an error when I tried to skip that part in
one text and the (Linux) man page for setsid(2) suggests
that it is required.
Then it puts itself in the root directory (so the process
doesn't interfere with umounting of filesystems by keeping a
directory "busy") and replaces its file descriptors with
some on /dev/null. I'd quibble about this (they're all
opened wih "rw" which is technically wrong, stdin should be
in 'r' mode and stdout/stderr should be 'w'). I might
also I think the extra
do something more like:
#!/usr/bin/env python2.2
def daemonize():
"""Become a Linux/UNIX daemon"""
import os,sys
os.chdir('/')
if os.fork(): os._exit(0)
os.setsid()
sys.stdin = sys.__stdin__ = open('/dev/null','r')
sys.stdout = sys.__stdout__ = open('/dev/null','w')
sys.stdout = sys.__stderr__ = os.dup(sys.stdout.fileno())
def daemonize_log_demo():
"""Demonstrate deamonize() with trivial syslog output"""
import time,syslog,os
log = syslog.openlog("JimDaemon[%d]" % os.getpid())
t=0
while 1:
t+=1
syslog.syslog(syslog.LOG_INFO,"Interval %d reached" % (t) )
time.sleep(5)
if __name__=='__main__':
daemonize()
daemonize_log_demo()
Obviously you'd replace daemonize_log_demo with a function that
opened your db connection, periodically performed your queries
spawned off your child process and (preferably) read back the
child process exit results.
You might want to avoid os.system() and manage your own
child processes directly with os.fork() and os.execv() or
os.execl(). You can then perform your own process management,
spawning multiple concurrent processes when necessary, keeping
a list of outstanding/running processes, and reaping their results
with os.waitpid(). You might also want to use a signal handler
so that your child processes will notify you when they've exited
(SIGCHLD) and your process reaping can then be event driven.
Under those conditions you could also implement a timeout for
your children. If a process doesn't exit in a reasonable time
you can kill it and log the error (via syslog or some other
mechanism). Naturally you could report errors via e-mail or
some instant messaging mechanism if you wanted. Actually at
that point you can do pretty much anything that you could
concievably code up.
Perhaps I'll write the write of this (a PostgreSQL query/poll
and some process management and error handling functionality)
later. For now I have a pool party to get to.
More information about the Python-list
mailing list