diff --git a/daemon.py b/daemon.py index bed0d19..620c4f3 100644 --- a/daemon.py +++ b/daemon.py @@ -1,30 +1,36 @@ -""" -Generic linux daemon base class for python 3.x. +# Copyright (c) 2022 Johannes Findeisen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is furnished +# to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Original Source: -https://web.archive.org/web/20131017130434/http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ -https://web.archive.org/web/20131017130434/http://www.jejik.com/files/examples/daemon3x.py -See files/daemon3x.py in the uplink source tree. - -More Information: -https://franklingu.github.io/programming/2016/03/01/creating-daemon-process-python-example-explanation/ -https://dpbl.wordpress.com/2017/02/12/a-tutorial-on-python-daemon/ -""" +import atexit +import os +import signal +import sys +import time from logging import getLogger -import sys -import os -import time -import atexit -import signal logger = getLogger(__name__) class Daemon: """ - A generic daemon class. - Usage: subclass the daemon class and override the run() method. """ @@ -33,34 +39,35 @@ class Daemon: def daemonize(self): """ - Daemonize class. UNIX double fork mechanism. + Daemonize the class using the UNIX double fork mechanism. """ + # Do first fork try: pid = os.fork() if pid > 0: - # exit first parent + # Exit first parent sys.exit(0) except OSError as err: - logger.error('fork #1 failed: {0}\n'.format(err)) + logger.error(str("fork #1 failed: {0}".format(err))) sys.exit(1) - # decouple from parent environment + # Decouple from parent environment os.chdir('/') os.setsid() os.umask(0) - # do second fork + # Do second fork try: pid = os.fork() if pid > 0: - # exit from second parent + # Exit from second parent sys.exit(0) except OSError as err: - logger.error('fork #2 failed: {0}\n'.format(err)) + logger.error(str("fork #2 failed: {0}".format(err))) sys.exit(1) - # redirect standard file descriptors + # Redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(os.devnull, 'r') @@ -71,7 +78,7 @@ class Daemon: os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) - # write pidfile + # Write pidfile atexit.register(self.del_pid) pid = str(os.getpid()) @@ -94,8 +101,8 @@ class Daemon: pid = None if pid: - message = "pid_file {0} already exist. Daemon already running?" - logger.error(message.format(self.pid_file)) + message = "pid_file {0} already exist. daemon already running?" + logger.error(str(message.format(self.pid_file))) sys.exit(1) # Start the daemon @@ -107,7 +114,7 @@ class Daemon: Stop the daemon. """ - # Get the pid from the pidfile + # Get the pid from the pid_file try: with open(self.pid_file, 'r') as pf: pid = int(pf.read().strip()) @@ -115,10 +122,9 @@ class Daemon: pid = None if not pid: - message = "pid_file {0} does not exist. Daemon not running?" - logger.error(message.format(self.pid_file)) - # not an error in a restart - return + message = "pid_file {0} does not exist. daemon not running?" + logger.error(str(message.format(self.pid_file))) + return # not an error in a restart # Try killing the daemon process try: @@ -143,7 +149,8 @@ class Daemon: def run(self): """ - Override this method when you subclass Daemon. + You should override this method when you subclass Daemon. + It will be called after the process has been daemonized by start() or restart(). """ diff --git a/files/daemon3x.py b/files/daemon3x.py index cb2c6bd..624e3fe 100644 --- a/files/daemon3x.py +++ b/files/daemon3x.py @@ -1,4 +1,15 @@ -"""Generic linux daemon base class for python 3.x.""" +""" +Generic linux daemon base class for python 3.x. + +Original Source: +https://web.archive.org/web/20131017130434/http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ +https://web.archive.org/web/20131017130434/http://www.jejik.com/files/examples/daemon3x.py +See files/daemon3x.py in the uplink source tree. + +More Information: +https://franklingu.github.io/programming/2016/03/01/creating-daemon-process-python-example-explanation/ +https://dpbl.wordpress.com/2017/02/12/a-tutorial-on-python-daemon/ +""" import sys, os, time, atexit, signal diff --git a/uplink b/uplink index 8721257..c16cb30 100755 --- a/uplink +++ b/uplink @@ -31,13 +31,17 @@ import time from uplink import Uplink # Version format: MAJOR.FEATURE.FIXES -__version__ = "0.6.0" +__version__ = "0.6.1" # TODO: CHECK ALL ERROR HANDLING!!! # TODO: IDEA: Implement a small webserver inline to get statistics and graphs over the network? -# Or maybe better as a separate daemon +# Or maybe better as a separate daemon? # TODO: Make use of more args passed to the script # TODO: Implement a speedtest that will also run regularly but in an individual interval +# TODO: become more verbose in each log level and log only to error when it really is an error +# else log to info +# TODO: Maybe make path to pid_file an arg to use /var/run/$user/uplink.pid instead of +# /tmp/uplink.pid logger = logging.getLogger(__name__) @@ -139,7 +143,7 @@ def main(): config_data = configfile.read() config = json.loads(config_data) except Exception as err: - logger.error(str("uplink: configuration error!\n" + str(err))) + logger.error(str("uplink: configuration error! {0}".format(err))) sys.exit(1) try: @@ -175,7 +179,7 @@ def main(): t.start() try: time.sleep(config["interval"]) - except KeyboardInterrupt as err: + except KeyboardInterrupt: logger.info(str("uplink: program terminated by user!")) sys.exit(0) else: diff --git a/uplink.py b/uplink.py index e819d0a..736c481 100644 --- a/uplink.py +++ b/uplink.py @@ -30,7 +30,6 @@ from fritzconnection.lib.fritzstatus import FritzStatus from logging import getLogger from threading import Thread - logger = getLogger(__name__) @@ -38,7 +37,6 @@ class Uplink(Daemon): def __init__(self, pid_file, config): super().__init__(pid_file) - self.pid_file = pid_file self.config = config self.date = None self.time = None @@ -53,7 +51,7 @@ class Uplink(Daemon): password=config["database_password"], database=config["database"]) except Exception as err: - logger.error(str("uplink: Database connection failed: " + str(err))) + logger.error(str("uplink: Database connection failed: {0}".format(err))) sys.exit(1) timestamp = calendar.timegm(time.gmtime())