diff options
| author | ZHENG Yanqin <[email protected]> | 2023-05-25 07:37:53 +0000 |
|---|---|---|
| committer | ZHENG Yanqin <[email protected]> | 2023-05-25 07:37:53 +0000 |
| commit | e9896bd62bb29da00ec00a121374167ad91bfe47 (patch) | |
| tree | d94845574c8ef7473d0204d28b4efd4038035463 /daemon.py | |
| parent | fad9aa875c84b38cbb5a6010e104922b1eea7291 (diff) | |
| parent | 4c5734c624705449c6b21c4b2bc5554e7259fdba (diff) | |
readme
See merge request zyq/time_series_anomaly_detection!1
Diffstat (limited to 'daemon.py')
| -rw-r--r-- | daemon.py | 250 |
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 |
