Source code for freezer_dr.common.daemon

#!/usr/bin/env python
"""Generic deamon."""
# (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import print_function

import atexit
import logging as log
import os
import sys
import time

from signal import SIGTERM


[docs]class Daemon(object): """A generic daemon class. Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): """Instantiantion.""" self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile
[docs] 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 """ 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)) log.error(e) sys.exit(1) # decouple from parent environment os.chdir("/") os.setsid() os.umask(0) # 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)) log.error(e) sys.exit(1) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(self.stdin, 'r') so = open(self.stdout, 'a+') se = open(self.stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile atexit.register(self.delpid) pid = str(os.getpid()) f = open(self.pidfile, 'w+') f.write("%s\n" % pid) f.close()
[docs] def delpid(self): """Delete PID file.""" os.remove(self.pidfile)
[docs] def start(self): """Start the daemon.""" log.error("Test") # 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 if pid: message = "pidfile %s already exist. Daemon" \ " already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) # Start the daemon self.daemonize() self.run()
# @todo needs some enhancement like check /proc/%pid/status if it's # really running or not ! may be it's killed by external process # the PID won't be updated !
[docs] def status(self): """Check daemon status.""" try: pf = open(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid: message = "pidfile %s already exist. Daemon already " \ "running. PID: %d \n" sys.stdout.write(message % (self.pidfile, pid)) sys.exit(0) else: message = "Service not running!\n" sys.stdout.write(message) sys.exit(0)
[docs] def stop(self): """Stop the daemon.""" # Get the pid from the pidfile try: pf = open(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if not pid: message = "pidfile %s does not exist." \ " Daemon not running?\n" sys.stderr.write(message % self.pidfile) return # not an error in a restart # Try killing the daemon process try: while 1: os.kill(pid, SIGTERM) time.sleep(0.1) except OSError as err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print(str(err)) sys.exit(1)
[docs] def restart(self): """Restart the daemon.""" self.stop() self.start()
[docs] 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(). """