Lot of refactoring in the daemon class and some more cleanups.

This commit is contained in:
Johannes Findeisen 2022-08-25 19:48:05 +02:00
commit d815508396
4 changed files with 63 additions and 43 deletions

View file

@ -1,30 +1,36 @@
"""
Generic linux daemon base class for python 3.x.
# Copyright (c) 2022 Johannes Findeisen <you@hanez.org>
#
# 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().
"""

View file

@ -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

12
uplink
View file

@ -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:

View file

@ -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())