Example #1
0
def release_pidfile(path, fd):
    """Releases a locked PID file."""

    try:
        eintr_retry(os.unlink)(path)
    finally:
        eintr_retry(os.close)(fd)
Example #2
0
def daemonize(do_fork=True, skip_fds=[]):
    """Daemonizes current process."""

    if do_fork:
        if os.fork():
            os._exit(0)
        else:
            os.setsid()

            if os.fork():
                os._exit(0)

    os.chdir("/")
    os.umask(0)

    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    signal.siginterrupt(signal.SIGHUP, False)

    # Redirecting standard streams to /dev/null and closing original descriptors
    null_dev = eintr_retry(os.open)("/dev/null", os.O_RDWR)
    try:
        for fd in (sys.stdin.fileno(), sys.stdout.fileno(), sys.stderr.fileno()):
            if fd not in skip_fds:
                os.dup2(null_dev, fd)
    finally:
        eintr_retry(os.close)(null_dev)
    def __init__(self, io_loop):
        self.__client_id = 0

        path = constants.SERVER_SOCKET_PATH
        LOG.info("Listening to client connections at '%s'...", path)

        self.__delete_socket()
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

        try:
            sock.setblocking(False)

            try:
                sock.bind(path)
                sock.listen(128)
            except EnvironmentError as e:
                raise Error("Unable to create a UNIX socket '{0}': {1}.", path, e)

            super(Server, self).__init__(io_loop, sock, "Monitor's server socket")
        except:
            try:
                self.__delete_socket()
            except Exception as e:
                LOG.error(e)

            eintr_retry(sock.close())

            raise
Example #4
0
    def __wait_pid_thread(self, fork_lock, termination_fd):
        """Waits for the process termination."""

        try:
            # Wait for fork() process completion
            with fork_lock:
                pass

            # Wait only if we've successfully forked
            if self.__pid is None:
                return

            try:
                status = eintr_retry(os.waitpid)(self.__pid, 0)[1]
            except Exception as e:
                LOG.error("Failed to waitpid() process %s: %s.", self.__pid, psys.e(e))
                self.__status = 127
            else:
                if os.WIFEXITED(status):
                    self.__status = os.WEXITSTATUS(status)
                    LOG.debug("Command %s terminated with %s status code.", self, self.__status)
                elif os.WIFSIGNALED(status):
                    signum = os.WTERMSIG(status)
                    LOG.debug("Command %s terminated due to receipt of %s signal.", self, signum)
                    self.__status = 128 + signum
                else:
                    LOG.error("Command %s terminated due to unknown reason.", self)
                    self.__status = 127
        except Exception:
            LOG.exception("PID waiting thread crashed.")
        finally:
            try:
                eintr_retry(os.close)(termination_fd)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
Example #5
0
File: poll.py Project: wd5/psys
        def close(self):
            """Closes the object."""

            if self.__epoll is not None:
                try:
                    eintr_retry(self.__epoll.close)()
                except Exception as e:
                    LOG.error("Unable to close an epoll instance: %s.", psys.e(e))
                else:
                    self.__epoll = None
Example #6
0
    def __execute(self, stdout):
        """Executes the command."""

        # Configure the standard I/O file descriptors
        self.__configure_stdio(stdout)

        # Fork the process -->
        fork_lock = threading.Lock()

        with fork_lock:
            # Allocate all resources before fork() to guarantee that we will
            # be able to control the process execution.

            # Execution thread -->
            poll = psys.poll.Poll()

            try:
                self.__communication_thread = threading.Thread(
                    target=self.__communication_thread_func,
                    args=(fork_lock, poll))

                self.__communication_thread.daemon = True
                self.__communication_thread.start()
            except:
                poll.close()
                raise
            # Execution thread <--

            # Wait thread -->
            try:
                self.__termination_fd, termination_fd = os.pipe()
            except Exception as e:
                raise Error("Unable to create a pipe: {0}.", psys.e(e))

            try:
                self.__wait_thread = threading.Thread(
                    target=self.__wait_pid_thread,
                    args=[fork_lock, termination_fd])
                self.__wait_thread.daemon = True
                self.__wait_thread.start()
            except BaseException as error:
                try:
                    eintr_retry(os.close)(termination_fd)
                except Exception as e:
                    LOG.error("Unable to close a pipe: %s.", psys.e(e))

                raise error
            # Wait thread <--

            self.__pid = os.fork()

            if self.__pid:
                self.__state = _PROCESS_STATE_RUNNING
            else:
                self.__child()
Example #7
0
def write_pidfile(fd):
    """Write pid to pidfile previously allocated by acquire_pidfile()"""

    data = str(os.getpid())
    datalen = len(data)

    while data:
        size = eintr_retry(os.write)(fd, data)
        data = data[size:]

    eintr_retry(os.ftruncate)(fd, datalen)
Example #8
0
    def __init__(self, nonblock=False):
        self.read, self.write = os.pipe()

        try:
            if nonblock:
                for fd in self.read, self.write:
                    flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL)
                    eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
        except:
            self.close()
            raise
Example #9
0
    def __execute(self, stdout):
        """Executes the command."""

        # Configure the standard I/O file descriptors
        self.__configure_stdio(stdout)

        # Fork the process -->
        fork_lock = threading.Lock()

        with fork_lock:
            # Allocate all resources before fork() to guarantee that we will
            # be able to control the process execution.

            # Execution thread -->
            poll = psys.poll.Poll()

            try:
                self.__communication_thread = threading.Thread(
                    target=self.__communication_thread_func,
                    args=(fork_lock, poll))

                self.__communication_thread.daemon = True
                self.__communication_thread.start()
            except:
                poll.close()
                raise
            # Execution thread <--

            # Wait thread -->
            try:
                self.__termination_fd, termination_fd = os.pipe()
            except Exception as e:
                raise Error("Unable to create a pipe: {0}.", psys.e(e))

            try:
                self.__wait_thread = threading.Thread(
                    target=self.__wait_pid_thread, args=[fork_lock, termination_fd])
                self.__wait_thread.daemon = True
                self.__wait_thread.start()
            except BaseException as error:
                try:
                    eintr_retry(os.close)(termination_fd)
                except Exception as e:
                    LOG.error("Unable to close a pipe: %s.", psys.e(e))

                raise error
            # Wait thread <--

            self.__pid = os.fork()

            if self.__pid:
                self.__state = _PROCESS_STATE_RUNNING
            else:
                self.__child()
Example #10
0
        def close(self):
            """Closes the object."""

            if self.__epoll is not None:
                try:
                    eintr_retry(self.__epoll.close)()
                except Exception as e:
                    LOG.error("Unable to close an epoll instance: %s.",
                              psys.e(e))
                else:
                    self.__epoll = None
    def __poll_objects(self):
        """Polls the controlled objects."""

        timeout = -1
        if self.__deferred_calls:
            timeout = max(0, self.__deferred_calls[0].time - time.time())

        for fd, flags in eintr_retry(self.__epoll.poll)(timeout=timeout):
            try:
                obj = self.__objects[fd]
            except KeyError:
                continue

            try:
                if flags & EPOLLERR:
                    if not obj.closed():
                        obj.on_error(Error("Disconnected."))

                if flags & EPOLLHUP:
                    if not obj.closed():
                        obj.on_hang_up()

                if flags & EPOLLIN:
                    if not obj.closed():
                        obj.on_read()

                if flags & EPOLLOUT:
                    if not obj.closed():
                        obj.on_write()
            except Exception as e:
                if not isinstance(e, (EnvironmentError, EOFError)):
                    LOG.exception("%s handling crashed.", obj)

                obj.on_error(e)
Example #12
0
    def __close(self):
        """
        Frees all allocated resources unneeded after the process termination
        or its failed execution.
        """

        for pipe in self.__pipes:
            pipe.close()
        del self.__pipes[:]

        if self.__termination_fd is not None:
            try:
                eintr_retry(os.close)(self.__termination_fd)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.__termination_fd = None
Example #13
0
    def __close(self):
        """
        Frees all allocated resources unneeded after the process termination
        or its failed execution.
        """

        for pipe in self.__pipes:
            pipe.close()
        del self.__pipes[:]

        if self.__termination_fd is not None:
            try:
                eintr_retry(os.close)(self.__termination_fd)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.__termination_fd = None
Example #14
0
    def close(self, read=True, write=True):
        """Closes the pipe."""

        if read and self.read is not None:
            try:
                eintr_retry(os.close)(self.read)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.read = None

        if write and self.write is not None:
            try:
                eintr_retry(os.close)(self.write)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.write = None
Example #15
0
    def close(self, read=True, write=True):
        """Closes the pipe."""

        if read and self.read is not None:
            try:
                eintr_retry(os.close)(self.read)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.read = None

        if write and self.write is not None:
            try:
                eintr_retry(os.close)(self.write)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
            else:
                self.write = None
    def close(self):
        """Closes the object."""

        if self.__closed:
            return

        self.__closed = True

        # Detach all objects
        for obj in list(self.__objects.values()):
            obj.close()

        # Break possible cycle references
        del self.__deferred_calls[:]

        try:
            eintr_retry(self.__epoll.close)()
        except Exception as e:
            LOG.error("Failed to close a epoll object: %s.", e)
    def on_read(self):
        """Called when we have data to read."""

        try:
            connection = eintr_retry(self._file.accept)()[0]
        except EnvironmentError as e:
            if e.errno != errno.ECONNABORTED:
                LOG.error("Unable to accept a connection: %s.", e)
        else:
            connection_name = "Client connection #{0}".format(self.__client_id)
            self.__client_id += 1

            LOG.debug("Accepting a new %s...", connection_name)

            try:
                _Client(self._weak_io_loop(), connection, connection_name)
            except Exception as e:
                LOG.error("Failed to accept %s: %s.", connection_name, e)
                eintr_retry(connection.close())
Example #18
0
    def __init__(self, process, raw, delimiter):
        # The process
        self.__process = process

        # Raw or unicode string iteration
        self.__raw = raw

        # Block delimiter
        self.__delimiter = delimiter

        # Output pipe
        self.__pipe = None

        # Polling object
        self.__poll = None

        # Output data buffer. None indicates EOF.
        self.__data = b""

        # Is the iterator closed
        self.__closed = False

        if delimiter:
            self.__iter = self.__iter_with_delimiter
        else:
            if raw:
                self.__iter = self.__iter_without_delimiter
            else:
                raise InvalidOperation(
                    "Can't iterate over unicode data without delimiter")

        try:
            self.__pipe = Pipe(psys.STDOUT_FILENO)
            flags = eintr_retry(fcntl.fcntl)(self.__pipe.read, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(self.__pipe.read, fcntl.F_SETFL,
                                     flags | os.O_NONBLOCK)

            self.__poll = psys.poll.Poll()
            self.__poll.register(self.__pipe.read, self.__poll.POLLIN)
        except:
            self.close()
            raise
Example #19
0
    def __backup_file(self, path):
        """Backups the specified file."""

        try:
            try:
                fd = eintr_retry(os.open)(path, self.__open_flags)
            except EnvironmentError as e:
                # If O_NOATIME flag was specified, but the effective user ID
                # of the caller did not match the owner of the file and the
                # caller was not privileged (CAP_FOWNER), the EPERM will be
                # returned.
                if (
                    hasattr(os, "O_NOATIME") and e.errno == errno.EPERM and
                    self.__open_flags & os.O_NOATIME
                ):
                    # Just disable this flag on a first EPERM error
                    LOG.debug("Got EPERM error. Disabling O_NOATIME for file opening operations...")
                    self.__open_flags &= ~os.O_NOATIME
                    fd = eintr_retry(os.open)(path, self.__open_flags)
                else:
                    raise
        except EnvironmentError as e:
            # When O_NOFOLLOW is specified, indicates that this is a
            # symbolic link.
            if e.errno == errno.ELOOP:
                raise FileTypeChangedError()
            else:
                raise

        try:
            file_obj = os.fdopen(fd, "rb")
        except:
            try:
                eintr_retry(os.close)(fd)
            except Exception as e:
                LOG.error("Unable to close a file: %s.", psys.e(e))

            raise

        with file_obj:
            stat_info = os.fstat(file_obj.fileno())
            self.__backup.add_file(path, stat_info, file_obj = file_obj)
Example #20
0
    def __init__(self, process, raw, delimiter):
        # The process
        self.__process = process

        # Raw or unicode string iteration
        self.__raw = raw

        # Block delimiter
        self.__delimiter = delimiter

        # Output pipe
        self.__pipe = None

        # Polling object
        self.__poll = None

        # Output data buffer. None indicates EOF.
        self.__data = b""

        # Is the iterator closed
        self.__closed = False

        if delimiter:
            self.__iter = self.__iter_with_delimiter
        else:
            if raw:
                self.__iter = self.__iter_without_delimiter
            else:
                raise InvalidOperation("Can't iterate over unicode data without delimiter")

        try:
            self.__pipe = Pipe(psys.STDOUT_FILENO)
            flags = eintr_retry(fcntl.fcntl)(self.__pipe.read, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(self.__pipe.read, fcntl.F_SETFL, flags | os.O_NONBLOCK)

            self.__poll = psys.poll.Poll()
            self.__poll.register(self.__pipe.read, self.__poll.POLLIN)
        except:
            self.close()
            raise
Example #21
0
    def __wait_pid_thread(self, fork_lock, termination_fd):
        """Waits for the process termination."""

        try:
            # Wait for fork() process completion
            with fork_lock:
                pass

            # Wait only if we've successfully forked
            if self.__pid is None:
                return

            try:
                status = eintr_retry(os.waitpid)(self.__pid, 0)[1]
            except Exception as e:
                LOG.error("Failed to waitpid() process %s: %s.", self.__pid,
                          psys.e(e))
                self.__status = 127
            else:
                if os.WIFEXITED(status):
                    self.__status = os.WEXITSTATUS(status)
                    LOG.debug("Command %s terminated with %s status code.",
                              self, self.__status)
                elif os.WIFSIGNALED(status):
                    signum = os.WTERMSIG(status)
                    LOG.debug(
                        "Command %s terminated due to receipt of %s signal.",
                        self, signum)
                    self.__status = 128 + signum
                else:
                    LOG.error("Command %s terminated due to unknown reason.",
                              self)
                    self.__status = 127
        except Exception:
            LOG.exception("PID waiting thread crashed.")
        finally:
            try:
                eintr_retry(os.close)(termination_fd)
            except Exception as e:
                LOG.error("Unable to close a pipe: %s.", psys.e(e))
def main():
    """The daemon's main function."""

    parser = argparse.ArgumentParser(description="XBee monitor")
    parser.add_argument("-d", "--debug", action="store_true",
        help="print debug messages")

    args = parser.parse_args()

    try:
        monitor.config.load()
        common.log.setup("xbee-monitor", debug_mode=args.debug)
    except Exception as e:
        sys.exit("Unable to start the daemon: {0}".format(e))

    LOG.info("Starting the daemon...")

    try:
        with _MainLoop() as io_loop:
            signals = (signal.SIGINT, signal.SIGTERM, signal.SIGQUIT)
            read_fd, write_fd = os.pipe()

            try:
                try:
                    _configure_termination_signals(
                        io_loop, signals, read_fd, write_fd)
                except:
                    eintr_retry(os.close)(read_fd)
                    raise

                io_loop.start()
            finally:
                for sig in signals:
                    signal.signal(sig, signal.SIG_IGN)

                eintr_retry(os.close)(write_fd)
    except Exception as e:
        LOG.error("The daemon has crashed: %s", e)
Example #23
0
def main():
    try:
        _configure_signal_handling()

        args = _parse_args()
        backup_dir = args.backup_dir

        pcli.log.setup(
            name="git-backup", debug_mode=args.debug,
            level=logging.WARNING if not args.debug and args.cron else None)

        _check_backup_dir(backup_dir)

        lock_file_path = os.path.expanduser(os.path.join(backup_dir, _LOCK_FILE_NAME))

        try:
            lock_file_fd = psys.daemon.acquire_pidfile(lock_file_path)
        except psys.daemon.PidFileLockedError as e:
            if args.cron:
                log.debug("Exiting: %s", e)
            else:
                raise Error("{}", e)
        except psys.daemon.PidFileLockError as e:
            raise Error("{}", e)
        else:
            try:
                _backup(args.user, backup_dir)
            finally:
                try:
                    os.unlink(lock_file_path)
                except EnvironmentError as e:
                    log.error("Failed to delete lock file '%s': %s.", lock_file_path, e)
                finally:
                    eintr_retry(os.close)(lock_file_fd)
    except Error as e:
        sys.exit("Error: {}".format(e))
    def close(self):
        """Closes the object."""

        if not self.closed():
            LOG.debug("Close %s.", self)

            io_loop = self._weak_io_loop()
            if io_loop is not None:
                io_loop.remove_object(self)

            try:
                eintr_retry(self._file.close)()
            except Exception as e:
                LOG.error("Failed to close %s: %s.", self, e)

            self._file = None

            for handler in self.__on_close_handlers:
                try:
                    handler()
                except:
                    LOG.exception("A close handler for %s crashed.", self)

        self.__on_close_handlers = []
Example #25
0
    def __iter_without_delimiter(self):
        """Iterates over the data."""

        try:
            self.__poll.poll()
            data = eintr_retry(os.read)(self.__pipe.read, psys.BUFSIZE)
        except:
            self.__finalize()
            raise
        else:
            if not data:
                self.__data = None
                self.__finalize()
                raise StopIteration()

            return data
Example #26
0
    def __iter_without_delimiter(self):
        """Iterates over the data."""

        try:
            self.__poll.poll()
            data = eintr_retry(os.read)(self.__pipe.read, psys.BUFSIZE)
        except:
            self.__finalize()
            raise
        else:
            if not data:
                self.__data = None
                self.__finalize()
                raise StopIteration()

            return data
Example #27
0
def acquire_pidfile(path):
    """Creates and locks a PID file."""

    fd = -1

    try:
        fd = eintr_retry(os.open)(path, os.O_RDWR | os.O_CREAT, 0o600)

        if fd <= sys.stderr.fileno():
            eintr_retry(os.dup2)(fd, sys.stderr.fileno() + 1)
            eintr_retry(os.close)(fd)
            fd = sys.stderr.fileno() + 1

        try:
            fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except EnvironmentError as e:
            if e.errno == errno.EWOULDBLOCK:
                raise PidFileLockedError(path)
            else:
                raise e

        fd_stat = os.fstat(fd)

        try:
            file_stat = os.stat(path)
        except EnvironmentError as e:
            if e.errno == errno.ENOENT:
                raise PidFileLockedError(path)
            else:
                raise e

        if(
            ( fd_stat[stat.ST_DEV],   fd_stat[stat.ST_INO]   ) !=
            ( file_stat[stat.ST_DEV], file_stat[stat.ST_INO] )
        ):
            raise PidFileLockedError(path)

        return fd
    except Exception as e:
        if fd != -1:
            eintr_retry(os.close)(fd)

        if isinstance(e, PidFileLockedError):
            raise e

        raise PidFileLockError(path,
            "Failed to lock PID file '{0}': {1}.", path, e)
    def _read(self, size):
        """
        Reads data from the file to the read buffer and returns True only when
        the specified size will be read.
        """

        if len(self._read_buffer) < size:
            try:
                data = eintr_retry(os.read)(self.fileno(), size - len(self._read_buffer))
            except EnvironmentError as e:
                if e.errno != errno.EWOULDBLOCK:
                    raise
            else:
                if not data:
                    raise EOFError("End of file has been reached.")

                self._read_buffer.extend(data)

        return len(self._read_buffer) >= size
Example #29
0
                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(os.open)(
                                path, os.O_WRONLY | os.O_CREAT | (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                            fd_name[fd] if write else "'" + path + "'",
                            "'" + path + "'" if write else fd_name[fd], psys.e(e))
    def _write(self, data=None):
        """
        Writes the data from the write buffer + the specified data and returns
        True only when all the data will be written.
        """

        if data is not None:
            self._write_buffer.extend(data)

        if self._write_buffer:
            try:
                size = eintr_retry(os.write)(self.fileno(), self._write_buffer)
            except EnvironmentError as e:
                if e.errno == errno.EWOULDBLOCK:
                    pass
                else:
                    raise
            else:
                if size:
                    del self._write_buffer[:size + 1]

        return not self._write_buffer
Example #31
0
                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(
                                os.open)(path, os.O_WRONLY | os.O_CREAT |
                                         (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                                    fd_name[fd] if write else "'" + path + "'",
                                    "'" + path + "'" if write else fd_name[fd],
                                    psys.e(e))
Example #32
0
    def __iter_with_delimiter(self):
        """Iterates over the data splitting it with the delimiter."""

        try:
            pos = self.__data.index(self.__delimiter)
        except ValueError:
            while True:
                try:
                    self.__poll.poll()
                    data = eintr_retry(os.read)(self.__pipe.read, psys.BUFSIZE)
                except:
                    self.__finalize(check_status=False)
                    raise

                if data:
                    try:
                        pos = data.index(self.__delimiter)
                    except ValueError:
                        self.__data += data
                    else:
                        block = self.__data + data[:pos + 1]
                        self.__data = data[pos + 1:]
                        return self.__transform_block(block)
                else:
                    block = self.__data
                    self.__data = None
                    self.__finalize()

                    if not block:
                        raise StopIteration()

                    return self.__transform_block(block)
        else:
            block = self.__data[:pos + 1]
            self.__data = self.__data[pos + 1:]
            return self.__transform_block(block)
Example #33
0
    def __iter_with_delimiter(self):
        """Iterates over the data splitting it with the delimiter."""

        try:
            pos = self.__data.index(self.__delimiter)
        except ValueError:
            while True:
                try:
                    self.__poll.poll()
                    data = eintr_retry(os.read)(self.__pipe.read, psys.BUFSIZE)
                except:
                    self.__finalize(check_status=False)
                    raise

                if data:
                    try:
                        pos = data.index(self.__delimiter)
                    except ValueError:
                        self.__data += data
                    else:
                        block = self.__data + data[:pos + 1]
                        self.__data = data[pos + 1:]
                        return self.__transform_block(block)
                else:
                    block = self.__data
                    self.__data = None
                    self.__finalize()

                    if not block:
                        raise StopIteration()

                    return self.__transform_block(block)
        else:
            block = self.__data[:pos + 1]
            self.__data = self.__data[pos + 1:]
            return self.__transform_block(block)
Example #34
0
    def __communicate(self, poll):
        """Communicates with the process and waits for its termination."""

        pipe_map = {}
        poll_objects = 0

        # Configure the poll object and pipes -->
        poll.register(self.__termination_fd, poll.POLLIN)
        poll_objects += 1

        for pipe in self.__pipes:
            fd = pipe.read if pipe.output else pipe.write
            if fd is None:
                continue

            pipe_map[fd] = pipe

            flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

            poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT)
            pipe.close(read=not pipe.output, write=pipe.output)
            poll_objects += 1
        # Configure the poll object and pipes <--

        # Communicate with the process -->
        stdin = None

        while poll_objects:
            events = poll.poll()

            for fd, flags in events:
                # Process termination
                if fd == self.__termination_fd:
                    if self.__wait_for_output:
                        poll.unregister(self.__termination_fd)
                        poll_objects -= 1
                        continue
                    else:
                        poll_objects = 0
                        break

                pipe = pipe_map[fd]

                # stdin
                if pipe.source == psys.STDIN_FILENO:
                    if stdin is None:
                        try:
                            stdin = next(self.__stdin_generator)

                            try:
                                if type(stdin) not in (bytes, str):
                                    raise TypeError("must be a string")

                                stdin = psys.b(stdin)
                            except Exception as e:
                                raise InvalidArgument(
                                    "Invalid stdin data: {0}", e)
                        except StopIteration:
                            pass
                        except Exception as e:
                            self.__error = e
                            stdin = None

                    if stdin is None:
                        poll.unregister(fd)
                        poll_objects -= 1
                        pipe.close()
                    else:
                        try:
                            size = eintr_retry(os.write)(fd, stdin)
                        except EnvironmentError as e:
                            # The process closed its stdin
                            if e.errno == errno.EPIPE:
                                poll.unregister(fd)
                                poll_objects -= 1
                                pipe.close()
                            else:
                                raise e
                        else:
                            if size == len(stdin):
                                stdin = None
                            else:
                                stdin = stdin[size:]
                # stdout/stderr
                elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO):
                    data = eintr_retry(os.read)(fd, psys.BUFSIZE)

                    if data:
                        self.__on_output(pipe.source, data)
                    else:
                        poll.unregister(fd)
                        poll_objects -= 1
                else:
                    raise LogicalError()
        # Communicate with the process <--

        if not self.__wait_for_output:
            # The process has terminated, but we should continue communication
            # to get output that we haven't got from it yet. But we must do it
            # wisely, because the process might fork() itself, so we'll read
            # its child's output forever.

            # Maximum output size after process termination (bigger than any
            # pipe buffer size).
            max_data_size = 1024 * 1024

            for pipe in self.__pipes:
                if (pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO)
                        and pipe.read is not None):
                    size = 0

                    while size < max_data_size:
                        try:
                            data = eintr_retry(os.read)(
                                pipe.read,
                                min(psys.BUFSIZE, max_data_size - size))
                        except EnvironmentError as e:
                            if e.errno != errno.EAGAIN:
                                raise e

                            if not self.__truncate_output:
                                self.__error = ProcessOutputWasTruncated(
                                    str(self), self.__status,
                                    self.__stdout.getvalue(),
                                    self.__stderr.getvalue())

                            break
                        else:
                            if data:
                                size += len(data)
                                self.__on_output(pipe.source, data)
                            else:
                                break
Example #35
0
    def __communicate(self, poll):
        """Communicates with the process and waits for its termination."""

        pipe_map = {}
        poll_objects = 0

        # Configure the poll object and pipes -->
        poll.register(self.__termination_fd, poll.POLLIN)
        poll_objects += 1

        for pipe in self.__pipes:
            fd = pipe.read if pipe.output else pipe.write
            if fd is None:
                continue

            pipe_map[fd] = pipe

            flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

            poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT)
            pipe.close(read=not pipe.output, write=pipe.output)
            poll_objects += 1
        # Configure the poll object and pipes <--

        # Communicate with the process -->
        stdin = None

        while poll_objects:
            events = poll.poll()

            for fd, flags in events:
                # Process termination
                if fd == self.__termination_fd:
                    if self.__wait_for_output:
                        poll.unregister(self.__termination_fd)
                        poll_objects -= 1
                        continue
                    else:
                        poll_objects = 0
                        break

                pipe = pipe_map[fd]

                # stdin
                if pipe.source == psys.STDIN_FILENO:
                    if stdin is None:
                        try:
                            stdin = next(self.__stdin_generator)

                            try:
                                if type(stdin) not in (bytes, str):
                                    raise TypeError("must be a string")

                                stdin = psys.b(stdin)
                            except Exception as e:
                                raise InvalidArgument("Invalid stdin data: {0}", e)
                        except StopIteration:
                            pass
                        except Exception as e:
                            self.__error = e
                            stdin = None

                    if stdin is None:
                        poll.unregister(fd)
                        poll_objects -= 1
                        pipe.close()
                    else:
                        try:
                            size = eintr_retry(os.write)(fd, stdin)
                        except EnvironmentError as e:
                            # The process closed its stdin
                            if e.errno == errno.EPIPE:
                                poll.unregister(fd)
                                poll_objects -= 1
                                pipe.close()
                            else:
                                raise e
                        else:
                            if size == len(stdin):
                                stdin = None
                            else:
                                stdin = stdin[size:]
                # stdout/stderr
                elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO):
                    data = eintr_retry(os.read)(fd, psys.BUFSIZE)

                    if data:
                        self.__on_output(pipe.source, data)
                    else:
                        poll.unregister(fd)
                        poll_objects -= 1
                else:
                    raise LogicalError()
        # Communicate with the process <--

        if not self.__wait_for_output:
            # The process has terminated, but we should continue communication
            # to get output that we haven't got from it yet. But we must do it
            # wisely, because the process might fork() itself, so we'll read
            # its child's output forever.

            # Maximum output size after process termination (bigger than any
            # pipe buffer size).
            max_data_size = 1024 * 1024

            for pipe in self.__pipes:
                if (
                    pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO) and
                    pipe.read is not None
                ):
                    size = 0

                    while size < max_data_size:
                        try:
                            data = eintr_retry(os.read)(
                                pipe.read, min(psys.BUFSIZE, max_data_size - size))
                        except EnvironmentError as e:
                            if e.errno != errno.EAGAIN:
                                raise e

                            if not self.__truncate_output:
                                self.__error = ProcessOutputWasTruncated(
                                    str(self), self.__status,
                                    self.__stdout.getvalue(), self.__stderr.getvalue())

                            break
                        else:
                            if data:
                                size += len(data)
                                self.__on_output(pipe.source, data)
                            else:
                                break
 def on_terminate(signum, stack):
     try:
         eintr_retry(os.write)(write_fd, b"\0")
     except EnvironmentError as e:
         if e.errno not in (errno.EPIPE, errno.EWOULDBLOCK):
             LOG.error("Failed to send termination signal to I/O loop: %s.", e)
Example #37
0
    def __child(self):
        """Handles child process execution."""

        exit_code = 127

        try:
            exec_error = False

            try:
                fd_name = {
                    0: "stdin",
                    1: "stdout",
                    2: "stderr",
                }

                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(
                                os.open)(path, os.O_WRONLY | os.O_CREAT |
                                         (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                                    fd_name[fd] if write else "'" + path + "'",
                                    "'" + path + "'" if write else fd_name[fd],
                                    psys.e(e))

                # Connect all pipes
                for pipe in self.__pipes:
                    try:
                        eintr_retry(
                            os.dup2)(pipe.write if pipe.output else pipe.read,
                                     pipe.source)
                    except Exception as e:
                        raise Error("Unable to connect a pipe to {0}: {1}",
                                    fd_name[pipe.source], psys.e(e))

                    pipe.close()

                # Close all file descriptors
                psys.close_all_fds()

                # Configure stdin
                if isinstance(self.__stdin_source, File):
                    redirect_fd(self.__stdin_source.path,
                                psys.STDIN_FILENO,
                                write=False)

                # Configure stdout
                if self.__stdout_target is STDERR:
                    try:
                        eintr_retry(os.dup2)(psys.STDERR_FILENO,
                                             psys.STDOUT_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stdout to stderr: {0}",
                                    psys.e(e))
                elif isinstance(self.__stdout_target, File):
                    redirect_fd(self.__stdout_target.path,
                                psys.STDOUT_FILENO,
                                append=self.__stdout_target.append)

                # Configure stderr
                if self.__stderr_target is STDOUT:
                    try:
                        eintr_retry(os.dup2)(psys.STDOUT_FILENO,
                                             psys.STDERR_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}",
                                    psys.e(e))
                elif isinstance(self.__stderr_target, File):
                    redirect_fd(self.__stderr_target.path,
                                psys.STDERR_FILENO,
                                append=self.__stderr_target.append)

                # Required when we have C locale
                command = [psys.b(arg) for arg in self.__command]

                exec_error = True

                if self.__env is None:
                    os.execvp(self.__program, command)
                else:
                    os.execvpe(self.__program, command, self.__env)
            except Exception as e:
                if exec_error and isinstance(
                        e, EnvironmentError) and e.errno == errno.EACCES:
                    exit_code = 126

                print("Failed to execute '{program}': {error}.".format(
                    program=self.__program, error=psys.e(e)),
                      file=sys.stderr)
        finally:
            os._exit(exit_code)
Example #38
0
    def __child(self):
        """Handles child process execution."""

        exit_code = 127

        try:
            exec_error = False

            try:
                fd_name = {
                    0: "stdin",
                    1: "stdout",
                    2: "stderr",
                }

                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(os.open)(
                                path, os.O_WRONLY | os.O_CREAT | (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                            fd_name[fd] if write else "'" + path + "'",
                            "'" + path + "'" if write else fd_name[fd], psys.e(e))

                # Connect all pipes
                for pipe in self.__pipes:
                    try:
                        eintr_retry(os.dup2)(pipe.write if pipe.output else pipe.read, pipe.source)
                    except Exception as e:
                        raise Error("Unable to connect a pipe to {0}: {1}",
                            fd_name[pipe.source], psys.e(e))

                    pipe.close()

                # Close all file descriptors
                psys.close_all_fds()

                # Configure stdin
                if isinstance(self.__stdin_source, File):
                    redirect_fd(self.__stdin_source.path, psys.STDIN_FILENO, write=False)

                # Configure stdout
                if self.__stdout_target is STDERR:
                    try:
                        eintr_retry(os.dup2)(psys.STDERR_FILENO, psys.STDOUT_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}", psys.e(e))
                elif isinstance(self.__stdout_target, File):
                    redirect_fd(self.__stdout_target.path, psys.STDOUT_FILENO,
                        append=self.__stdout_target.append)

                # Configure stderr
                if self.__stderr_target is STDOUT:
                    try:
                        eintr_retry(os.dup2)(psys.STDOUT_FILENO, psys.STDERR_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}", psys.e(e))
                elif isinstance(self.__stderr_target, File):
                    redirect_fd(self.__stderr_target.path, psys.STDERR_FILENO,
                        append=self.__stderr_target.append)

                # Required when we have C locale
                command = [psys.b(arg) for arg in self.__command]

                exec_error = True

                if self.__env is None:
                    os.execvp(self.__program, command)
                else:
                    os.execvpe(self.__program, command, self.__env)
            except Exception as e:
                if exec_error and isinstance(e, EnvironmentError) and e.errno == errno.EACCES:
                    exit_code = 126

                print("Failed to execute '{program}': {error}.".format(
                    program=self.__program, error=psys.e(e)), file=sys.stderr)
        finally:
            os._exit(exit_code)