Ejemplo n.º 1
0
def check_process(pidfile):
    """ Read pid file and check process status.
        Return (running, pid).
    """
    # Check pid file
    try:
        handle = open(pidfile, 'r')
    except IOError as exc:
        if exc.errno == errno.ENOENT:
            # pid file disappeared
            return False, 0
        raise

    try:
        pid = int(handle.read().strip(), 10)
    except (TypeError, ValueError) as exc:
        raise EnvironmentError("Invalid PID file '%s' (%s), won't start!" %
                               (pidfile, exc))
    finally:
        handle.close()

    # Check process
    try:
        os.kill(pid, 0)
    except EnvironmentError as exc:
        return False, pid
    else:
        return True, pid
Ejemplo n.º 2
0
    def run(self):
        """ The main program skeleton.
        """
        log_total = True

        try:
            try:
                # Preparation steps
                self.get_options()

                # Template method with the tool's main loop
                self.mainloop()
            except error.LoggableError, exc:
                if self.options.debug:
                    raise

                # Log errors caused by invalid user input
                try:
                    msg = str(exc)
                except UnicodeError:
                    msg = unicode(exc, "UTF-8")
                self.LOG.error(msg)
                sys.exit(error.EX_SOFTWARE)
            except KeyboardInterrupt, exc:
                if self.options.debug:
                    raise

                sys.stderr.write("\n\nAborted by CTRL-C!\n")
                sys.stderr.flush()

                # See https://www.cons.org/cracauer/sigint.html
                signal.signal(signal.SIGINT, signal.SIG_DFL)
                os.kill(os.getpid(), signal.SIGINT)
                sys.exit(error.EX_TEMPFAIL)  # being paranoid
Ejemplo n.º 3
0
    except IOError, exc:
        if exc.errno == errno.ENOENT:
            # pid file disappeared
            return False, 0
        raise

    try:
        pid = int(handle.read().strip(), 10)
    except (TypeError, ValueError), exc:
        raise EnvironmentError("Invalid PID file '%s' (%s), won't start!" % (pidfile, exc))
    finally:
        handle.close()
    
    # Check process
    try:
        os.kill(pid, 0)
    except EnvironmentError, exc:
        return False, pid
    else:
        return True, pid


def guard(pidfile, guardfile=None):
    """ Raise an EnvironmentError when the "guardfile" doesn't exist, or
        the process with the ID found in "pidfile" is still active.
    """
    # Check guard
    if guardfile and not os.path.exists(guardfile):
        raise EnvironmentError("Guard file '%s' not found, won't start!" % guardfile)

    if os.path.exists(pidfile):
Ejemplo n.º 4
0
def daemonize(pidfile=None, logfile=None, sync=True):
    """ Fork the process into the background.

        @param pidfile: Optional PID file path.
        @param sync: Wait for parent process to disappear?
        @param logfile: Optional name of stdin/stderr log file or stream.
    """
    log = logging.getLogger("daemonize")
    ppid = os.getpid()

    try:
        pid = os.fork()
        if pid > 0:
            log.debug("Parent exiting (PID %d, CHILD %d)" % (ppid, pid))
            sys.exit(0)
    except OSError as exc:
        log.critical("fork #1 failed (PID %d): (%d) %s\n" %
                     (os.getpid(), exc.errno, exc.strerror))
        sys.exit(1)

    ##os.chdir("/")
    ##os.umask(0022)
    os.setsid()

    try:
        pid = os.fork()
        if pid > 0:
            log.debug("Session leader exiting (PID %d, PPID %d, DEMON %d)" %
                      (os.getpid(), ppid, pid))
            sys.exit(0)
    except OSError as exc:
        log.critical("fork #2 failed (PID %d): (%d) %s\n" %
                     (os.getpid(), exc.errno, exc.strerror))
        sys.exit(1)

    if pidfile:
        _write_pidfile(pidfile)

    def sig_term(*dummy):
        "Handler for SIGTERM."
        sys.exit(0)

    stdin = open("/dev/null", "r")
    os.dup2(stdin.fileno(), sys.stdin.fileno())
    signal.signal(signal.SIGTERM, sig_term)

    if logfile:
        try:
            logfile + ""
        except TypeError:
            if logfile.fileno() != sys.stdout.fileno():
                os.dup2(logfile.fileno(), sys.stdout.fileno())
            if logfile.fileno() != sys.stderr.fileno():
                os.dup2(logfile.fileno(), sys.stderr.fileno())
        else:
            log.debug("Redirecting stdout / stderr to %r" % logfile)
            loghandle = open(logfile, "a+")
            os.dup2(loghandle.fileno(), sys.stdout.fileno())
            os.dup2(loghandle.fileno(), sys.stderr.fileno())
            loghandle.close()

    if sync:
        # Wait for 5 seconds at most, in 10ms steps
        polling = 5, .01
        for _ in range(int(polling[0] * 1 / polling[1])):
            try:
                os.kill(ppid, 0)
            except OSError:
                break
            else:
                time.sleep(polling[1])

    log.debug("Process detached (PID %d)" % os.getpid())
Ejemplo n.º 5
0
    def mainloop(self):
        """ The main loop.
        """
        self._validate_config()
        config.engine.load_config()

        # Defaults for process control paths
        if not self.options.no_fork and not self.options.guard_file:
            self.options.guard_file = os.path.join(config.config_dir,
                                                   "run/pyrotorque")
        if not self.options.pid_file:
            self.options.pid_file = os.path.join(config.config_dir,
                                                 "run/pyrotorque.pid")

        # Process control
        if self.options.status or self.options.stop or self.options.restart:
            if self.options.pid_file and os.path.exists(self.options.pid_file):
                running, pid = osmagic.check_process(self.options.pid_file)
            else:
                running, pid = False, 0

            if self.options.stop or self.options.restart:
                if running:
                    os.kill(pid, signal.SIGTERM)

                    # Wait for termination (max. 10 secs)
                    for _ in range(100):
                        running, _ = osmagic.check_process(
                            self.options.pid_file)
                        if not running:
                            break
                        time.sleep(.1)

                    self.LOG.info("Process #%d stopped." % (pid))
                elif pid:
                    self.LOG.info("Process #%d NOT running anymore." % (pid))
                else:
                    self.LOG.info("No pid file '%s'" %
                                  (self.options.pid_file or "<N/A>"))
            else:
                self.LOG.info("Process #%d %s running." %
                              (pid, "UP and" if running else "NOT"))

            if self.options.restart:
                if self.options.pid_file:
                    running, pid = osmagic.check_process(self.options.pid_file)
                    if running:
                        self.return_code = error.EX_TEMPFAIL
                        return
            else:
                self.return_code = error.EX_OK if running else error.EX_UNAVAILABLE
                return

        # Check for guard file and running daemon, abort if not OK
        try:
            osmagic.guard(self.options.pid_file, self.options.guard_file)
        except EnvironmentError as exc:
            self.LOG.debug(str(exc))
            self.return_code = error.EX_TEMPFAIL
            return

        # Detach, if not disabled via option
        if not self.options.no_fork:  # or getattr(sys.stdin, "isatty", lambda: False)():
            osmagic.daemonize(pidfile=self.options.pid_file,
                              logfile=logutil.get_logfile())
            time.sleep(.05)  # let things settle a little
        signal.signal(signal.SIGTERM, _raise_interrupt)

        # Set up services
        from apscheduler.scheduler import Scheduler
        self.sched = Scheduler(config.torque)

        self._init_wsgi_server()

        # Run services
        self.sched.start()
        try:
            self._add_jobs()
            # TODO: daemonize here, or before the scheduler starts?
            self._run_forever()
        finally:
            self.sched.shutdown()
            if self.wsgi_server:
                self.wsgi_server.task_dispatcher.shutdown()
                self.wsgi_server = None

            if self.options.pid_file:
                try:
                    os.remove(self.options.pid_file)
                except EnvironmentError as exc:
                    self.LOG.warn("Failed to remove pid file '%s' (%s)" %
                                  (self.options.pid_file, exc))
                    self.return_code = error.EX_IOERR
Ejemplo n.º 6
0
        if exc.errno == errno.ENOENT:
            # pid file disappeared
            return False, 0
        raise

    try:
        pid = int(handle.read().strip(), 10)
    except (TypeError, ValueError), exc:
        raise EnvironmentError("Invalid PID file '%s' (%s), won't start!" %
                               (pidfile, exc))
    finally:
        handle.close()

    # Check process
    try:
        os.kill(pid, 0)
    except EnvironmentError, exc:
        return False, pid
    else:
        return True, pid


def guard(pidfile, guardfile=None):
    """ Raise an EnvironmentError when the "guardfile" doesn't exist, or
        the process with the ID found in "pidfile" is still active.
    """
    # Check guard
    if guardfile and not os.path.exists(guardfile):
        raise EnvironmentError("Guard file '%s' not found, won't start!" %
                               guardfile)