Skip to content

Instantly share code, notes, and snippets.

@markhamilton1
Created March 23, 2014 23:26
Show Gist options
  • Save markhamilton1/9731428 to your computer and use it in GitHub Desktop.
Save markhamilton1/9731428 to your computer and use it in GitHub Desktop.
daemon is a Python module that helps in the development of daemon processes.
"""
The daemon module implements the support classes for creating daemon processes.
Classes:
Daemon - base class for constructing a daemon process
"""
import sys, os, time, atexit
from signal import SIGTERM
if hasattr(os, "devnull"): DEV_NULL = os.devnull
else: DEV_NULL = "/dev/null"
class Daemon:
"""
Base class for a daemon process.
Subclass the Daemon class and override the cleanup(), setup(), and run() methods as
necessary.
Derived from a class originally made available by Sander Marechal at
http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
Methods:
cleanup - override to perform cleanup on daemon stop
daemonize - should not be called directly
onexit - should not be called directly
restart - restart the daemon
run - override to perform main functionality of daemon
setup - override to perform setup on daemon start
start - start the daemon
status - get status for the daemon
stop - stop the daemon
"""
def __init__(self, pidfile, stdin=DEV_NULL, stdout=DEV_NULL, stderr=DEV_NULL):
"""
Construct and initialize an instance of Daemon.
In:
pidfile = path to location of pid file
stdin = path to use for standard in (default: DEV_NULL)
stdout = path to use for standard out (default: DEV_NULL)
stderr = path to use for standard error (default: DEV_NULL)
"""
self.pidfile = pidfile
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
self.sleep = 30
def cleanup(self):
"""
You should override this method when you subclass Daemon. It will
be called after the process has been terminated by stop().
"""
pass
def daemonize(self, run_as_daemon=True):
"""
Set the process up to run as a daemon. Should not be called directly.
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
In:
run_as_daemon = flag indicating whether to run as daemon or not (default: True)
"""
if run_as_daemon:
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())
else:
self.sleep = None
# write pidfile
atexit.register(self.onexit)
pid = str(os.getpid())
file(self.pidfile,'w+').write("%s\n" % pid)
def onexit(self):
"""
Method called on exit. Should not be called directly.
"""
self.cleanup()
os.remove(self.pidfile)
def restart(self, run_as_daemon=True):
"""
Restart the daemon.
In:
run_as_daemon = flag indicating whether to run as daemon or not (default: True)
"""
self.stop()
self.start(run_as_daemon)
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().
"""
pass
def setup(self):
"""
You can override this method when you subclass Daemon. It will
be called after calling start() or restart() but before the process is daemonized.
"""
pass
def start(self, run_as_daemon=True):
"""
Start the daemon.
In:
run_as_daemon = flag indicating whether to run as daemon or not (default: True)
"""
pid = self.status()
if pid:
message = "pidfile %s already exists. Daemon already running?\n" % self.pidfile
sys.stderr.write(message)
sys.exit(1)
# Start the daemon
self.setup()
self.daemonize(run_as_daemon)
self.run()
def status(self):
"""
Get status for the daemon.
Out:
the pid of the running daemon (None if not running)
"""
try:
pf = file(self.pidfile,'r')
pid = int(pf.read().strip())
pf.close()
except IOError:
pid = None
return pid
def stop(self):
"""
Stop the daemon.
"""
pid = self.status()
if not pid:
message = "pidfile %s does not exist. Daemon not running?\n" % self.pidfile
sys.stderr.write(message)
return # not an error in a restart
# Try killing the daemon process
try:
while True:
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment