Пример #1
0
 def __init__(self, daemonize, pidpath, logpath, confpath, workpath):
     self.daemonize = daemonize
     self.workpath = os.path.abspath(workpath)
     self.pidpath = self.resolved(pidpath)
     self.logpath = self.resolved(logpath)
     self.confpaths = [
         os.path.join(
             appdirs.user_config_dir("fortunebot"), "fortunebot.conf"),
         self.resolved("fortunebot.conf"),
         self.resolved(confpath),
     ]
     try:
         self.bot = Fortunebot()
     except Exception as ex:
         die("Died when constructing bot: {}".format(ex))
Пример #2
0
class FortunebotRunner(object):

    def __init__(self, daemonize, pidpath, logpath, confpath, workpath):
        self.daemonize = daemonize
        self.workpath = os.path.abspath(workpath)
        self.pidpath = self.resolved(pidpath)
        self.logpath = self.resolved(logpath)
        self.confpaths = [
            os.path.join(
                appdirs.user_config_dir("fortunebot"), "fortunebot.conf"),
            self.resolved("fortunebot.conf"),
            self.resolved(confpath),
        ]
        try:
            self.bot = Fortunebot()
        except Exception as ex:
            die("Died when constructing bot: {}".format(ex))

    def resolved(self, path):
        return os.path.abspath(os.path.join(self.workpath, path))

    def start(self):
        if self.status():
            die("Bot already running!")

        self.setup_signals()

        if self.daemonize:
            self.send_background()
            self.redirect_IO()
            self.setup_logging()
            self.writepid()

        try:
            os.chdir(self.workpath)
        except Exception as ex:
            die("Died while changing to workpath: {}".format(ex))

        logger.info("Starting bot")
        try:
            self.bot.load_config(self.confpaths)
            self.bot.start()
        except Exception:
            logger.exception("Bot died:")
        finally:
            self.clean()
            os._exit(1)

    def setup_signals(self):
        """
        Register signal handlers
        """
        signal.signal(signal.SIGTERM, self.sigterm_handler)
        signal.signal(signal.SIGINT, self.sigterm_handler)
        signal.signal(signal.SIGHUP, self.sighup_handler)
        signal.siginterrupt(signal.SIGHUP, False)
        logger.info("Set up signal handlers")

    def setup_logging(self):
        """
        Setup logfile and formatting
        """
        try:
            logger.handlers = []
            hdlr = logging.FileHandler(self.logpath)
            fmtr = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
            hdlr.setFormatter(fmtr)
            logger.addHandler(hdlr)
        except IOError as e:
            die("Unable to set up logging at {}. {}".format(self.logpath, e.strerror))
        logger.info("Set up log file")

    def send_background(self):
        """
        Send to background with double-fork
        """
        try:
            pid = os.fork()
            if pid:
                os._exit(0)
            os.setsid()
            pid = os.fork()
            if pid:
                os._exit(0)
            os.chdir("/")
            os.umask(0)
        except OSError as e:
            die("Unable to fork into background. {}".format(e.strerror))
        logger.info("Forked into background")

    def redirect_IO(self):
        """
        Redirect IO to /dev/null
        """
        try:
            """
            Close all open file descriptors, including standard IO
            See code.activestate.com/recipes/278731/
            """
            maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
            if maxfd == resource.RLIM_INFINITY:
                maxfd = 1024
            for fd in reversed(six.moves.xrange(maxfd)):
                try:
                    os.close(fd)
                except OSError:
                    pass
            """
            Open /dev/null as fd 0, 1, and 2
            """
            open(os.devnull, "r")
            open(os.devnull, "w")
            open(os.devnull, "w")
        except IOError as e:
            die("Unable to redirect IO. {}".format(e.strerror))
        logger.info("Redirected IO to /dev/null")

    def writepid(self):
        """
        Writing pidfile
        """
        try:
            pidfile = open(self.pidpath, "w")
            pidfile.write(str(os.getpid()))
            pidfile.close()
        except IOError as e:
            die("Unable to write pidfile at {}: {}".format(self.pidpath, e.strerror))
        logger.info("Wrote pidfile")

    def getpid(self):
        try:
            pidfile = open(self.pidpath, "r")
            pid = int(pidfile.read())
            pidfile.close()
            return pid
        except IOError:
            return None

    def status(self):
        pid = self.getpid()
        if not pid:
            return False
        return os.path.exists("/proc/{}".format(pid))

    def clean(self):
        if self.daemonize:
            logger.info("Removing pid file")
            try:
                if os.path.exists(self.pidpath):
                    os.remove(self.pidpath)
            except OSError as e:
                logger.warning("Problem encountered deleting pidfile: {}".format(e.strerror))

    def sigterm_handler(self, signum, frame):
        logger.info("Disconnecting bot...")
        self.bot.disconnect("Farewell comrades!")
        self.bot.clean()
        self.clean()
        os._exit(0)

    def sighup_handler(self, signum, frame):
        logger.info("Reloading bot configs")
        self.bot.load_config(self.confpaths)