2014-08-29 18:05:56 +00:00
|
|
|
"""Generic linux daemon base class for python 3.x."""
|
|
|
|
|
2015-01-20 12:24:00 +00:00
|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import atexit
|
|
|
|
import signal
|
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
|
|
|
|
class daemon:
|
2015-01-20 12:24:00 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
"""A generic daemon class.
|
|
|
|
|
|
|
|
Usage: subclass the daemon class and override the run() method."""
|
|
|
|
|
2014-09-08 15:35:22 +00:00
|
|
|
def __init__(self, pidfile, options):
|
2014-08-29 18:05:56 +00:00
|
|
|
self.pidfile = pidfile
|
|
|
|
self.options = options
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
def daemonize(self):
|
|
|
|
"""Deamonize class. UNIX double fork mechanism."""
|
|
|
|
|
2014-09-08 15:35:22 +00:00
|
|
|
try:
|
|
|
|
pid = os.fork()
|
2014-08-29 18:05:56 +00:00
|
|
|
if pid > 0:
|
|
|
|
# exit first parent
|
2014-09-08 15:35:22 +00:00
|
|
|
sys.exit(0)
|
|
|
|
except OSError as err:
|
2014-08-29 18:05:56 +00:00
|
|
|
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
|
|
|
|
sys.exit(1)
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
# decouple from parent environment
|
2014-09-08 15:35:22 +00:00
|
|
|
os.chdir('/')
|
|
|
|
os.setsid()
|
|
|
|
os.umask(0)
|
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
# do second fork
|
2014-09-08 15:35:22 +00:00
|
|
|
try:
|
|
|
|
pid = os.fork()
|
2014-08-29 18:05:56 +00:00
|
|
|
if pid > 0:
|
|
|
|
|
|
|
|
# exit from second parent
|
2014-09-08 15:35:22 +00:00
|
|
|
sys.exit(0)
|
|
|
|
except OSError as err:
|
2014-08-29 18:05:56 +00:00
|
|
|
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
|
2014-09-08 15:35:22 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
# redirect standard file descriptors
|
|
|
|
sys.stdout.flush()
|
|
|
|
sys.stderr.flush()
|
|
|
|
si = open(os.devnull, 'r')
|
|
|
|
so = open(os.devnull, 'a+')
|
|
|
|
se = open(os.devnull, 'a+')
|
|
|
|
|
|
|
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
|
|
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
|
|
|
os.dup2(se.fileno(), sys.stderr.fileno())
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
# write pidfile
|
|
|
|
atexit.register(self.delpid)
|
|
|
|
|
|
|
|
pid = str(os.getpid())
|
2015-01-20 12:24:00 +00:00
|
|
|
with open(self.pidfile, 'w+') as f:
|
2014-08-29 18:05:56 +00:00
|
|
|
f.write(pid + '\n')
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
def delpid(self):
|
|
|
|
os.remove(self.pidfile)
|
|
|
|
|
2014-09-08 15:35:22 +00:00
|
|
|
def check_pid(self, pid):
|
|
|
|
""" Check For the existence of a unix pid. """
|
|
|
|
try:
|
|
|
|
os.kill(pid, 0)
|
|
|
|
except OSError:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
def start(self):
|
|
|
|
"""Start the daemon."""
|
|
|
|
|
|
|
|
# Check for a pidfile to see if the daemon already runs
|
|
|
|
try:
|
2015-01-20 12:24:00 +00:00
|
|
|
with open(self.pidfile, 'r') as pf:
|
2014-08-29 18:05:56 +00:00
|
|
|
|
|
|
|
pid = int(pf.read().strip())
|
|
|
|
except IOError:
|
|
|
|
pid = None
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
if pid:
|
2014-09-08 15:35:22 +00:00
|
|
|
pid_exist = self.check_pid(pid)
|
|
|
|
|
|
|
|
if pid_exist:
|
|
|
|
message = "Already running: %s\n" % (pid)
|
|
|
|
sys.stderr.write(message)
|
|
|
|
sys.exit(1)
|
|
|
|
else:
|
|
|
|
message = "pidfile {0} already exist. " + \
|
|
|
|
"but process is dead\n"
|
2014-08-29 18:05:56 +00:00
|
|
|
sys.stderr.write(message.format(self.pidfile))
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
# Start the daemon
|
|
|
|
self.daemonize()
|
|
|
|
self.run()
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
"""Stop the daemon."""
|
|
|
|
|
|
|
|
# Get the pid from the pidfile
|
|
|
|
try:
|
2015-01-20 12:24:00 +00:00
|
|
|
with open(self.pidfile, 'r') as pf:
|
2014-08-29 18:05:56 +00:00
|
|
|
pid = int(pf.read().strip())
|
|
|
|
except IOError:
|
|
|
|
pid = None
|
2014-09-08 15:35:22 +00:00
|
|
|
|
2014-08-29 18:05:56 +00:00
|
|
|
if not pid:
|
|
|
|
message = "pidfile {0} does not exist. " + \
|
2015-01-20 12:24:00 +00:00
|
|
|
"Daemon not running?\n"
|
2014-08-29 18:05:56 +00:00
|
|
|
sys.stderr.write(message.format(self.pidfile))
|
2015-01-20 12:24:00 +00:00
|
|
|
return # not an error in a restart
|
2014-08-29 18:05:56 +00:00
|
|
|
|
2014-09-08 15:35:22 +00:00
|
|
|
# Try killing the daemon process
|
2014-08-29 18:05:56 +00:00
|
|
|
try:
|
2015-01-20 12:24:00 +00:00
|
|
|
while True:
|
2014-08-29 18:05:56 +00:00
|
|
|
os.kill(pid, signal.SIGTERM)
|
|
|
|
time.sleep(0.1)
|
|
|
|
except OSError as err:
|
|
|
|
e = str(err.args)
|
|
|
|
if e.find("No such process") > 0:
|
|
|
|
if os.path.exists(self.pidfile):
|
|
|
|
os.remove(self.pidfile)
|
|
|
|
else:
|
2015-01-20 12:24:00 +00:00
|
|
|
print(str(err.args))
|
2014-08-29 18:05:56 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
def restart(self):
|
|
|
|
"""Restart the daemon."""
|
|
|
|
self.stop()
|
|
|
|
self.start()
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
"""You should override this method when you subclass Daemon.
|
2014-09-08 15:35:22 +00:00
|
|
|
|
|
|
|
It will be called after the process has been daemonized by
|
2014-08-29 18:05:56 +00:00
|
|
|
start() or restart()."""
|