Created
April 14, 2018 11:11
-
-
Save costastf/497d22b1729cd6cee01991d1fa48b268 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2.7 | |
# -*- coding: UTF-8 -*- | |
# File: daemonlib.py | |
"""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 abc | |
import os | |
import signal | |
import sys | |
import time | |
# import logging | |
__author__ = '''Costas Tyfoxylos <costas.tyf@gmail.com>''' | |
__docformat__ = 'plaintext' | |
__date__ = '''2015-04-03''' | |
# This is the main prefix used for logging | |
# LOGGER_BASENAME = '''daemonlib''' | |
# LOGGER = logging.getLogger(LOGGER_BASENAME) | |
# LOGGER.setLevel(logging.DEBUG) | |
# LOGGER.addHandler(logging.NullHandler()) | |
class Daemon(object): | |
__metaclass__ = abc.ABCMeta | |
"""Instantiating the daemon""" | |
def __init__(self, pid_file=None, stdout=None, stderr=None): | |
self.stdout = stdout or './daemon_out.log' | |
self.stderr = stderr or './daemon_err.log' | |
self.pid_file = pid_file or './daemon.pid' | |
def _remove_pid(self): | |
"""Delete the pid file.""" | |
os.remove(self.pid_file) | |
def _daemonize(self): | |
"""Double forking of the process""" | |
# fork 1 to spin off the child that will spawn the deamon. | |
if os.fork(): | |
sys.exit() | |
# This is the child. | |
# 1. clear the session id to clear the controlling TTY. | |
# 2. set the umask so we have access to all files created by the daemon. | |
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._remove_pid) | |
pid = str(os.getpid()) | |
with open(self.pid_file, 'w+') as pid_file: | |
pid_file.write('{0}'.format(pid)) | |
@property | |
def pid(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.pid: | |
print(('PID file {0} exists. ' | |
'Is the deamon already running?').format(self.pid_file)) | |
sys.exit(1) | |
self._daemonize() | |
self.execute() | |
def stop(self): | |
"""Stop the daemon.""" | |
print('Stopping...') | |
if not self.pid: | |
print(("PID file {0} doesn't exist. " | |
"Is the daemon not running?").format(self.pid_file)) | |
return | |
try: | |
while 1: | |
os.kill(self.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() | |
@abc.abstractmethod | |
def execute(self): | |
"""The main loop of the daemon.""" | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment