Writing a simple linux daemon type thing...

James T. Dennis jadestar at idiom.com
Mon Jun 17 02:21:17 CEST 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