Skip to content

Instantly share code, notes, and snippets.

@slor
Created July 8, 2013 05:04
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save slor/5946334 to your computer and use it in GitHub Desktop.
Save slor/5946334 to your computer and use it in GitHub Desktop.
An example of a Linux daemon written in Python. Based on http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python
""" An example of a Linux daemon written in Python.
Based on http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
The changes are:
1 - Uses file open context managers instead of calls to file().
2 - Forces stdin to /dev/null. stdout and stderr go to log files.
3 - Uses print instead of sys.stdout.write prior to pointing stdout to the log file.
4 - Omits try/excepts if they only wrap one error message w/ another.
i - http://stackoverflow.com/questions/3263672/python-the-difference-between-sys-stdout-write-and-print
"""
import atexit
import datetime
import os
import signal
import sys
import time
class Daemon(object):
""" Linux Daemon boilerplate. """
def __init__(self, pid_file,
stdout='/var/log/daemon_example_out.log',
stderr='/var/log/daemon_example_err.log'):
self.stdout = stdout
self.stderr = stderr
self.pid_file = pid_file
def del_pid(self):
""" Delete the pid file. """
os.remove(self.pid_file)
def daemonize(self):
""" There shined a shiny daemon, In the middle, Of the road... """
# fork 1 to spin off the child that will spawn the deamon.
if os.fork():
sys.exit()
# This is the child.
# 1. cd to root for a guarenteed working dir.
# 2. clear the session id to clear the controlling TTY.
# 3. set the umask so we have access to all files created by the daemon.
os.chdir("/")
os.setsid()
os.umask(0)
# fork 2 ensures we can't get a controlling ttd.
if os.fork():
sys.exit()
# This is a child that can't ever have a controlling TTY.
# Now we shut down stdin and point stdout/stderr at log files.
# stdin
with open('/dev/null', 'r') as dev_null:
os.dup2(dev_null.fileno(), sys.stdin.fileno())
# stderr - do this before stdout so that errors about setting stdout write to the log file.
#
# Exceptions raised after this point will be written to the log file.
sys.stderr.flush()
with open(self.stderr, 'a+', 0) as stderr:
os.dup2(stderr.fileno(), sys.stderr.fileno())
# stdout
#
# Print statements after this step will not work. Use sys.stdout
# instead.
sys.stdout.flush()
with open(self.stdout, 'a+', 0) as stdout:
os.dup2(stdout.fileno(), sys.stdout.fileno())
# Write pid file
# Before file creation, make sure we'll delete the pid file on exit!
atexit.register(self.del_pid)
pid = str(os.getpid())
with open(self.pid_file, 'w+') as pid_file:
pid_file.write('{0}'.format(pid))
def get_pid_by_file(self):
""" Return the pid read from the pid file. """
try:
with open(self.pid_file, 'r') as pid_file:
pid = int(pid_file.read().strip())
return pid
except IOError:
return
def start(self):
""" Start the daemon. """
print "Starting..."
if self.get_pid_by_file():
print 'PID file {0} exists. Is the deamon already running?'.format(self.pid_file)
sys.exit(1)
self.daemonize()
self.run()
def stop(self):
""" Stop the daemon. """
print "Stopping..."
pid = self.get_pid_by_file()
if not pid:
print "PID file {0} doesn't exist. Is the daemon not running?".format(self.pid_file)
return
# Time to kill.
try:
while 1:
os.kill(pid, signal.SIGTERM)
time.sleep(0.1)
except OSError as err:
if 'No such process' in err.strerror and os.path.exists(self.pid_file):
os.remove(self.pid_file)
else:
print err
sys.exit(1)
def restart(self):
""" Restart the deamon. """
self.stop()
self.start()
def run(self):
""" The main loop of the daemon. """
while 1:
with open(self.stdout, 'w') as stdout:
stdout.write(datetime.datetime.now().isoformat() + '\n')
time.sleep(1)
if __name__ == '__main__':
if len(sys.argv) < 2:
print "Usage: {0} start|stop|restart".format(sys.argv[0])
sys.exit(2)
daemon = Daemon('/tmp/daemon_example.pid')
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 '{0}'".format(sys.argv[1])
sys.exit(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment