summaryrefslogtreecommitdiff
path: root/daemon.py
diff options
context:
space:
mode:
authorZHENG Yanqin <[email protected]>2023-05-25 07:37:53 +0000
committerZHENG Yanqin <[email protected]>2023-05-25 07:37:53 +0000
commite9896bd62bb29da00ec00a121374167ad91bfe47 (patch)
treed94845574c8ef7473d0204d28b4efd4038035463 /daemon.py
parentfad9aa875c84b38cbb5a6010e104922b1eea7291 (diff)
parent4c5734c624705449c6b21c4b2bc5554e7259fdba (diff)
Merge branch 'master' into 'main'HEADmain
readme See merge request zyq/time_series_anomaly_detection!1
Diffstat (limited to 'daemon.py')
-rw-r--r--daemon.py250
1 files changed, 250 insertions, 0 deletions
diff --git a/daemon.py b/daemon.py
new file mode 100644
index 0000000..4b3cf0b
--- /dev/null
+++ b/daemon.py
@@ -0,0 +1,250 @@
+'''
+***
+Modified generic daemon class
+***
+
+Author: http://www.jejik.com/articles/2007/02/
+ a_simple_unix_linux_daemon_in_python/www.boxedice.com
+
+License: http://creativecommons.org/licenses/by-sa/3.0/
+
+Changes: 23rd Jan 2009 (David Mytton <[email protected]>)
+ - Replaced hard coded '/dev/null in __init__ with os.devnull
+ - Added OS check to conditionally remove code that doesn't
+ work on OS X
+ - Added output to console on completion
+ - Tidied up formatting
+ 11th Mar 2009 (David Mytton <[email protected]>)
+ - Fixed problem with daemon exiting on Python 2.4
+ (before SystemExit was part of the Exception base)
+ 13th Aug 2010 (David Mytton <[email protected]>
+ - Fixed unhandled exception if PID file is empty
+'''
+
+# Core modules
+from __future__ import print_function
+import atexit
+import errno
+import os
+import sys
+import time
+import signal
+
+
+class Daemon(object):
+ """
+ A generic daemon class.
+
+ Usage: subclass the Daemon class and override the run() method
+ """
+ def __init__(self, pidfile, stdin=os.devnull,
+ stdout=os.devnull, stderr=os.devnull,
+ home_dir='.', umask=0o22, verbose=1,
+ use_gevent=False, use_eventlet=False):
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ self.pidfile = pidfile
+ self.home_dir = home_dir
+ self.verbose = verbose
+ self.umask = umask
+ self.daemon_alive = True
+ self.use_gevent = use_gevent
+ self.use_eventlet = use_eventlet
+
+ def log(self, *args):
+ if self.verbose >= 1:
+ print(*args)
+
+ def daemonize(self):
+ """
+ Do the UNIX double-fork magic, see Stevens' "Advanced
+ Programming in the UNIX Environment" for details (ISBN 0201563177)
+ http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
+ """
+ if self.use_eventlet:
+ import eventlet.tpool
+ eventlet.tpool.killall()
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # Exit first parent
+ sys.exit(0)
+ except OSError as e:
+ sys.stderr.write(
+ "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # Decouple from parent environment
+ os.chdir(self.home_dir)
+ os.setsid()
+ os.umask(self.umask)
+
+ # Do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # Exit from second parent
+ sys.exit(0)
+ except OSError as e:
+ sys.stderr.write(
+ "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ if sys.platform != 'darwin': # This block breaks on OS X
+ # Redirect standard file descriptors
+ sys.stdout.flush()
+ sys.stderr.flush()
+ si = open(self.stdin, 'r')
+ so = open(self.stdout, 'a+')
+ if self.stderr:
+ try:
+ se = open(self.stderr, 'a+', 0)
+ except ValueError:
+ # Python 3 can't have unbuffered text I/O
+ se = open(self.stderr, 'a+', 1)
+ else:
+ se = so
+ os.dup2(si.fileno(), sys.stdin.fileno())
+ os.dup2(so.fileno(), sys.stdout.fileno())
+ os.dup2(se.fileno(), sys.stderr.fileno())
+
+ def sigtermhandler(signum, frame):
+ self.daemon_alive = False
+ sys.exit()
+
+ if self.use_gevent:
+ import gevent
+ gevent.reinit()
+ gevent.signal(signal.SIGTERM, sigtermhandler, signal.SIGTERM, None)
+ gevent.signal(signal.SIGINT, sigtermhandler, signal.SIGINT, None)
+ else:
+ signal.signal(signal.SIGTERM, sigtermhandler)
+ signal.signal(signal.SIGINT, sigtermhandler)
+
+ self.log("Started")
+
+ # Write pidfile
+ atexit.register(
+ self.delpid) # Make sure pid file is removed if we quit
+ pid = str(os.getpid())
+ open(self.pidfile, 'w+').write("%s\n" % pid)
+
+ def delpid(self):
+ try:
+ # the process may fork itself again
+ pid = int(open(self.pidfile, 'r').read().strip())
+ if pid == os.getpid():
+ os.remove(self.pidfile)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+ def start(self, *args, **kwargs):
+ """
+ Start the daemon
+ """
+
+ self.log("Starting...")
+
+ # Check for a pidfile to see if the daemon already runs
+ try:
+ pf = open(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+ except SystemExit:
+ pid = None
+
+ if pid:
+ message = "pidfile %s already exists. Is it already running?\n"
+ sys.stderr.write(message % self.pidfile)
+ sys.exit(1)
+
+ # Start the daemon
+ self.daemonize()
+ self.run(*args, **kwargs)
+
+ def stop(self):
+ """
+ Stop the daemon
+ """
+
+ if self.verbose >= 1:
+ self.log("Stopping...")
+
+ # Get the pid from the pidfile
+ pid = self.get_pid()
+
+ if not pid:
+ message = "pidfile %s does not exist. Not running?\n"
+ sys.stderr.write(message % self.pidfile)
+
+ # Just to be sure. A ValueError might occur if the PID file is
+ # empty but does actually exist
+ if os.path.exists(self.pidfile):
+ os.remove(self.pidfile)
+
+ sys.exit(0) # Not an error in a restart
+
+ # Try killing the daemon process
+ try:
+ i = 0
+ while 1:
+ os.kill(pid, signal.SIGTERM)
+ time.sleep(0.1)
+ i = i + 1
+ if i % 10 == 0:
+ os.kill(pid, signal.SIGHUP)
+ except OSError as err:
+ if err.errno == errno.ESRCH:
+ if os.path.exists(self.pidfile):
+ os.remove(self.pidfile)
+ else:
+ print(str(err))
+ sys.exit(1)
+
+ self.log("Stopped")
+
+
+ def restart(self):
+ """
+ Restart the daemon
+ """
+ self.stop()
+ self.start()
+
+ def get_pid(self):
+ try:
+ pf = open(self.pidfile, 'r')
+ pid = int(pf.read().strip())
+ pf.close()
+ except IOError:
+ pid = None
+ except SystemExit:
+ pid = None
+ return pid
+
+ def is_running(self):
+ pid = self.get_pid()
+
+ if pid is None:
+ self.log('Process is stopped')
+ return False
+ elif os.path.exists('/proc/%d' % pid):
+ self.log('Process (pid %d) is running...' % pid)
+ return True
+ else:
+ self.log('Process (pid %d) is killed' % pid)
+ return False
+
+ def run(self):
+ """
+ You should override this method when you subclass Daemon.
+ It will be called after the process has been
+ daemonized by start() or restart().
+ """
+ raise NotImplementedError