Ejemplo n.º 1
0
    def __init__(self, signals=None, setmask=False):
        """
        Initialize a new mask object.

        :param signals:
            List of signals to block
        :param setmask:
            A flag that controls if ``SIG_SETMASK`` should be used over
            ``SIG_BLOCK`` and ``SIG_UNBLOCK``.  For details see :meth:`block()`
            and :meth:`unblock()`.

        .. note::
            The masking function (``sigprocmask(2)`` or ``pthread_sigmask(3)``)
            is not called until :meth:`block()` is called.
        """
        if signals is None:
            self._signals = frozenset()
        else:
            self._signals = frozenset(signals)
        self._setmask = setmask
        self._mask = sigset_t()
        self._old_mask = None  # old mask is only used for SIG_SETMASK
        self._is_active = False
        sigemptyset(self._mask)
        for signal in self.signals:
            sigaddset(self._mask, signal)
Ejemplo n.º 2
0
    def get(cls):
        """
        Use ``pthread_sigmask(2)`` to obtain the mask of blocked signals

        :returns:
            A fresh :class:`pthread_sigmask` object.

        The returned object behaves as it was constructed with the list of
        currently blocked signals, ``setmask=False`` and as if the
        :meth:`block()` was immediately called.

        That is, calling :meth:`unblock()` will will cause those signals not to
        be blocked anymore while calling :meth:`block()` will re-block them (if
        they were unblocked after this method returns).
        """
        mask = sigset_t()
        sigemptyset(mask)
        _pthread_sigmask(0, None, mask)
        signals = []
        for sig_num in range(1, NSIG):
            if sigismember(mask, sig_num):
                signals.append(sig_num)
        self = cls(signals)
        self._is_active = True
        self._old_mask = mask
        return self
Ejemplo n.º 3
0
    def get(cls):
        """
        Use the masking function (``sigprocmask(2)`` or ``pthread_sigmask(3)``)
        to obtain the mask of blocked signals

        :returns:
            A fresh :class:`sigprocmask` object.

        The returned object behaves as it was constructed with the list of
        currently blocked signals, ``setmask=False`` and as if the
        :meth:`block()` was immediately called.

        That is, calling :meth:`unblock()` will will cause those signals not to
        be blocked anymore while calling :meth:`block()` will re-block them (if
        they were unblocked after this method returns).
        """
        mask = sigset_t()
        sigemptyset(mask)
        cls._do_mask(0, None, mask)
        signals = []
        for sig_num in range(1, NSIG):
            if sigismember(mask, sig_num):
                signals.append(sig_num)
        self = cls(signals)
        self._is_active = True
        self._old_mask = mask
        return self
Ejemplo n.º 4
0
 def signals(self, new_signals):
     # Convert signals to frozendict as we depend on that below
     new_signals = frozenset(new_signals)
     # Reset the mask to what signals describes
     sigemptyset(self._mask)
     for signal in new_signals:
         sigaddset(self._mask, signal)
     # If we're active, re-apply the changes
     if self.is_active:
         # In setmask mode we can just overwrite the old values directly
         if self._setmask:
             # NOTE: we're not updating self._old_mask here. This way
             # unblock will trully restore everything despite modifications
             # to signals that happened after the call to block()
             self._do_mask(SIG_SETMASK, self._mask, None)
         else:
             # in the non-setmask mode, let's just apply the delta
             delta_mask = sigset_t()
             # Let's start blocking the new signals first
             added_signals = new_signals - self._signals
             if added_signals:
                 sigemptyset(delta_mask)
                 for signal in added_signals:
                     sigaddset(delta_mask, signal)
                     self._do_mask(SIG_BLOCK, delta_mask, None)
             # Let's unblock signals next
             removed_signals = self._signals - new_signals
             if removed_signals:
                 sigemptyset(delta_mask)
                 for signal in removed_signals:
                     sigaddset(delta_mask, signal)
                     self._do_mask(SIG_UNBLOCK, delta_mask, None)
     # Reset signals to the new value
     self._signals = new_signals
Ejemplo n.º 5
0
    def __init__(self, signals=None, setmask=False):
        """
        Initialize a new pthread_sigmask object.

        :param signals:
            List of signals to block
        :param setmask:
            A flag that controls if ``SIG_SETMASK`` should be used over
            ``SIG_BLOCK`` and ``SIG_UNBLOCK``.  For details see :meth:`block()`
            and :meth:`unblock()`.

        .. note::
            ``pthread_sigmask(2)`` is not called until :meth:`block()` is
            called.
        """
        if signals is None:
            self._signals = frozenset()
        else:
            self._signals = frozenset(signals)
        self._setmask = setmask
        self._mask = sigset_t()
        self._old_mask = None  # old mask is only used for SIG_SETMASK
        self._is_active = False
        sigemptyset(self._mask)
        for signal in self.signals:
            sigaddset(self._mask, signal)
Ejemplo n.º 6
0
 def signals(self, new_signals):
     # Convert signals to frozendict as we depend on that below
     new_signals = frozenset(new_signals)
     # Reset the mask to what signals describes
     sigemptyset(self._mask)
     for signal in new_signals:
         sigaddset(self._mask, signal)
     # If we're active, re-apply the changes
     if self.is_active:
         # In setmask mode we can just overwrite the old values directly
         if self._setmask:
             # NOTE: we're not updating self._old_mask here. This way
             # unblock will trully restore everything despite modifications
             # to signals that happened after the call to block()
             _pthread_sigmask(SIG_SETMASK, self._mask, None)
         else:
             # in the non-setmask mode, let's just apply the delta
             delta_mask = sigset_t()
             # Let's start blocking the new signals first
             added_signals = new_signals - self._signals
             if added_signals:
                 sigemptyset(delta_mask)
                 for signal in added_signals:
                     sigaddset(delta_mask, signal)
                     _pthread_sigmask(SIG_BLOCK, delta_mask, None)
             # Let's unblock signals next
             removed_signals = self._signals - new_signals
             if removed_signals:
                 sigemptyset(delta_mask)
                 for signal in removed_signals:
                     sigaddset(delta_mask, signal)
                     _pthread_sigmask(SIG_UNBLOCK, delta_mask, None)
     # Reset signals to the new value
     self._signals = new_signals
Ejemplo n.º 7
0
    def create_signal_mask():
        """Create signal mask for blocking/unblocking signals.

        :return:

        """
        mask = sigset_t()
        sigemptyset(mask)
        for sig in [SIGINT, SIGQUIT, SIGTERM]:
            sigaddset(mask, sig)
        return mask
Ejemplo n.º 8
0
    def update(self, signals):
        """
        Update the mask of signals this signalfd reacts to

        :param signals:
            A replacement set of signal numbers to monitor
        :raises ValueError:
            If :meth:`closed()` is True
        """
        if self._sfd < 0:
            _err_closed()
        mask = sigset_t()
        sigemptyset(mask)
        if signals is not None:
            for signal in signals:
                sigaddset(mask, signal)
        # flags are ignored when sfd is not -1
        _signalfd(self._sfd, mask, 0)
        self._signals = frozenset(signals)
Ejemplo n.º 9
0
    def update(self, signals):
        """
        Update the mask of signals this signalfd reacts to

        :param signals:
            A replacement set of signal numbers to monitor
        :raises ValueError:
            If :meth:`closed()` is True
        """
        if self._sfd < 0:
            _err_closed()
        mask = sigset_t()
        sigemptyset(mask)
        if signals is not None:
            for signal in signals:
                sigaddset(mask, signal)
        # flags are ignored when sfd is not -1
        _signalfd(self._sfd, mask, 0)
        self._signals = frozenset(signals)
Ejemplo n.º 10
0
    def block(self):
        """
        Use ``pthread_sigmask(2)`` to block signals.

        This method uses either ``SIG_SETMASK`` or ``SIG_BLOCK``, depending on
        how the object was constructed. After this method is called, the
        subsequent call to :meth:`unblock()` will undo its effects.

        .. note::
            This method is a no-op if signal blocking is currently active (as
            determined by :meth:`is_active` returning True).
        """
        if self._is_active:
            return
        if self._setmask:
            self._old_mask = sigset_t()
            sigemptyset(self._old_mask)
            _pthread_sigmask(SIG_SETMASK, self._mask, self._old_mask)
        else:
            _pthread_sigmask(SIG_BLOCK, self._mask, None)
        self._is_active = True
Ejemplo n.º 11
0
    def block(self):
        """
        Use the masking function (``sigprocmask(2)`` or ``pthread_sigmask(3)``)
        to block signals.

        This method uses either ``SIG_SETMASK`` or ``SIG_BLOCK``, depending on
        how the object was constructed. After this method is called, the
        subsequent call to :meth:`unblock()` will undo its effects.

        .. note::
            This method is a no-op if signal blocking is currently active (as
            determined by :meth:`is_active` returning True).
        """
        if self._is_active:
            return
        if self._setmask:
            self._old_mask = sigset_t()
            sigemptyset(self._old_mask)
            self._do_mask(SIG_SETMASK, self._mask, self._old_mask)
        else:
            self._do_mask(SIG_BLOCK, self._mask, None)
        self._is_active = True
Ejemplo n.º 12
0
def main():
    # Block signals so that they aren't handled
    # according to their default dispositions
    mask = sigset_t()
    fdsi = signalfd_siginfo()
    sigemptyset(mask)
    sigaddset(mask, SIGINT)
    sigaddset(mask, SIGQUIT)
    sigprocmask(SIG_BLOCK, mask, None)
    # Get a signalfd descriptor
    sfd = signalfd(-1, mask, SFD_CLOEXEC)
    # Get a epoll descriptor
    epollfd = epoll_create1(EPOLL_CLOEXEC)
    ev = epoll_event()
    ev.events = EPOLLIN
    ev.data.fd = sfd
    epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, byref(ev))
    MAX_EVENTS = 10
    events = (epoll_event * MAX_EVENTS)()
    with fdopen(sfd, 'rb', 0) as sfd_stream:
        while True:
            nfds = epoll_wait(
                epollfd, cast(byref(events), POINTER(epoll_event)),
                MAX_EVENTS, -1)
            for event_id in range(nfds):
                if events[event_id].data.fd == sfd:
                    # Read the next delivered signal
                    sfd_stream.readinto(fdsi)
                    if fdsi.ssi_signo == SIGINT:
                        print("Got SIGINT")
                    elif fdsi.ssi_signo == SIGQUIT:
                        print("Got SIGQUIT")
                        return
                    else:
                        print("Read unexpected signal")
                else:
                    raise Exception("unexpected fd?")
    close(sfd)
    close(epollfd)
Ejemplo n.º 13
0
def main():
    # Block signals so that they aren't handled
    # according to their default dispositions
    mask = sigset_t()
    fdsi = signalfd_siginfo()
    sigemptyset(mask)
    sigaddset(mask, SIGINT)
    sigaddset(mask, SIGQUIT)
    sigprocmask(SIG_BLOCK, mask, None)
    # Get a signalfd descriptor
    sfd = signalfd(-1, mask, 0)
    with fdopen(sfd, 'rb', 0) as sfd_stream:
        while True:
            # Read the next delivered signal
            sfd_stream.readinto(fdsi)
            if fdsi.ssi_signo == SIGINT:
                print("Got SIGINT")
            elif fdsi.ssi_signo == SIGQUIT:
                print("Got SIGQUIT")
                return
            else:
                print("Read unexpected signal")
Ejemplo n.º 14
0
    def __init__(self, signals=None, flags=0):
        """
        Create a signalfd() descriptor reacting to a set of signals.

        :param signals:
            A set of signal numbers to include in the mask of signals passed to
            signalfd(2).
        :param flags:
            A bit-mask of flags to pass to signalfd(2). You should pass
            SFD_CLOEXEC here, to ensure that a other threads don't inadvertedly
            fork and leak this descriptor. You can also pass SFD_NONBLOCK to
            make reads from the signalfd object non-blocking.
        """
        self._sfd = -1
        self._signals = frozenset()
        mask = sigset_t()
        sigemptyset(mask)
        if signals is None:
            signals = ()
        for signal in signals:
            sigaddset(mask, signal)
        self._sfd = _signalfd(-1, mask, flags)
        self._signals = frozenset(signals)
Ejemplo n.º 15
0
    def __init__(self, signals=None, flags=0):
        """
        Create a signalfd() descriptor reacting to a set of signals.

        :param signals:
            A set of signal numbers to include in the mask of signals passed to
            signalfd(2).
        :param flags:
            A bit-mask of flags to pass to signalfd(2). You should pass
            SFD_CLOEXEC here, to ensure that a other threads don't inadvertedly
            fork and leak this descriptor. You can also pass SFD_NONBLOCK to
            make reads from the signalfd object non-blocking.
        """
        self._sfd = -1
        self._signals = frozenset()
        mask = sigset_t()
        sigemptyset(mask)
        if signals is None:
            signals = ()
        for signal in signals:
            sigaddset(mask, signal)
        self._sfd = _signalfd(-1, mask, flags)
        self._signals = frozenset(signals)
Ejemplo n.º 16
0
def main():
    # Block signals so that they aren't handled
    # according to their default dispositions
    mask = sigset_t()
    fdsi = signalfd_siginfo()
    sigemptyset(mask)
    sigaddset(mask, SIGINT)
    sigaddset(mask, SIGQUIT)
    sigaddset(mask, SIGCHLD)
    sigaddset(mask, SIGPIPE)
    print("Blocking signals")
    sigprocmask(SIG_BLOCK, mask, None)
    # Get a signalfd descriptor
    sfd = signalfd(-1, mask, SFD_CLOEXEC | SFD_NONBLOCK)
    print("Got signalfd", sfd)
    # Get a epoll descriptor
    ep = epoll()
    print("Got epollfd", ep.fileno())
    print("Adding signalfd fd {} to epoll".format(sfd))
    ep.register(sfd, EPOLLIN)
    # Get two pair of pipes, one for stdout and one for stderr
    stdout_pair = (c_int * 2)()
    pipe2(byref(stdout_pair), O_CLOEXEC | O_NONBLOCK)
    print("Got stdout pipe pair", stdout_pair[0], stdout_pair[1])
    print("Adding pipe fd {} to epoll".format(stdout_pair[0]))
    ep.register(stdout_pair[0], EPOLLIN)
    stderr_pair = (c_int * 2)()
    pipe2(byref(stderr_pair), O_CLOEXEC | O_NONBLOCK)
    print("Got stderr pipe pair", stderr_pair[0], stderr_pair[1])
    print("Adding pipe fd {} to epoll".format(stdout_pair[0]))
    ep.register(stderr_pair[0], EPOLLIN)
    prog = argv[1:]
    if not prog:
        prog = ['echo', 'usage: demo.py PROG [ARGS]']
    print("Going to start program:", prog)
    # Fork :-)
    pid = fork()
    if pid == 0:  # Child.
        # NOTE: we are not closing any of the pipe ends. Why? Because they are
        # all O_CLOEXEC and will thus not live across the execlp call down
        # below.
        dup3(stdout_pair[1], 1, 0)
        dup3(stderr_pair[1], 2, 0)
        execlp(prog[0], *prog)
        return -1
    else:
        close(stdout_pair[1])
        close(stderr_pair[1])
    with fdopen(sfd, 'rb', 0) as sfd_stream:
        waiting_for = set(['stdout', 'stderr', 'proc'])
        while waiting_for:
            print("Waiting for events...", ' '.join(waiting_for), flush=True)
            event_list = ep.poll(maxevents=10)
            print("epoll_wait() read {} events".format(len(event_list)))
            for fd, events in event_list:
                print("[event]")
                event_bits = []
                if events & EPOLLIN == EPOLLIN:
                    event_bits.append('EPOLLIN')
                if events & EPOLLOUT == EPOLLOUT:
                    event_bits.append('EPOLLOUT')
                if events & EPOLLRDHUP == EPOLLRDHUP:
                    event_bits.append('EPOLLRDHUP')
                if events & EPOLLPRI == EPOLLPRI:
                    event_bits.append('EPOLLPRI')
                if events & EPOLLERR == EPOLLERR:
                    event_bits.append('EPOLLERR')
                if events & EPOLLHUP == EPOLLHUP:
                    event_bits.append('EPOLLHUP')
                if fd == sfd:
                    fd_name = 'signalfd()'
                elif fd == stdout_pair[0]:
                    fd_name = 'stdout pipe2()'
                elif fd == stderr_pair[0]:
                    fd_name = 'stderr pipe2()'
                else:
                    fd_name = "???"
                print(" events: {} ({})".format(
                    events, ' | '.join(event_bits)))
                print(" fd: {} ({})".format(fd, fd_name))
                if fd == sfd:
                    print("signalfd() descriptor ready")
                    if events & EPOLLIN:
                        print("Reading data from signalfd()...")
                        # Read the next delivered signal
                        sfd_stream.readinto(fdsi)
                        if fdsi.ssi_signo == SIGINT:
                            print("Got SIGINT")
                        elif fdsi.ssi_signo == SIGQUIT:
                            print("Got SIGQUIT")
                            raise SystemExit("exiting prematurly")
                        elif fdsi.ssi_signo == SIGCHLD:
                            print("Got SIGCHLD")
                            waitid_result = waitid(
                                P_PID, pid,
                                WNOHANG |
                                WEXITED | WSTOPPED | WCONTINUED |
                                WUNTRACED)
                            if waitid_result is None:
                                print("child not ready")
                            else:
                                print("child event")
                                print("si_pid:", waitid_result.si_pid)
                                print("si_uid:", waitid_result.si_uid)
                                print("si_signo:", waitid_result.si_signo)
                                assert waitid_result.si_signo == SIGCHLD
                                print("si_status:", waitid_result.si_status)
                                print("si_code:", waitid_result.si_code)
                                if waitid_result.si_code == CLD_EXITED:
                                    # assert WIFEXITED(waitid_result.si_status)
                                    print("child exited normally")
                                    print("exit code:",
                                          WEXITSTATUS(waitid_result.si_status))
                                    waiting_for.remove('proc')
                                    if 'stdout' in waiting_for:
                                        ep.unregister(stdout_pair[0])
                                        close(stdout_pair[0])
                                        waiting_for.remove('stdout')
                                    if 'stderr' in waiting_for:
                                        ep.unregister(stderr_pair[0])
                                        close(stderr_pair[0])
                                        waiting_for.remove('stderr')
                                elif waitid_result.si_code == CLD_KILLED:
                                    assert WIFSIGNALED(waitid_result.si_status)
                                    print("child was killed by signal")
                                    print("death signal:",
                                          waitid_result.si_status)
                                    waiting_for.remove('proc')
                                elif waitid_result.si_code == CLD_DUMPED:
                                    assert WIFSIGNALED(waitid_result.si_status)
                                    print("core:",
                                          WCOREDUMP(waitid_result.si_status))
                                elif waitid_result.si_code == CLD_STOPPED:
                                    print("child was stopped")
                                    print("stop signal:",
                                          waitid_result.si_status)
                                elif waitid_result.si_code == CLD_TRAPPED:
                                    print("child was trapped")
                                    # TODO: we could explore trap stuff here
                                elif waitid_result.si_code == CLD_CONTINUED:
                                    print("child was continued")
                                else:
                                    raise SystemExit(
                                        "Unknown CLD_ code: {}".format(
                                            waitid_result.si_code))
                        elif fdsi.ssi_signo == SIGPIPE:
                            print("Got SIGPIPE")
                        else:
                            print("Read unexpected signal: {}".format(
                                fdsi.ssi_signo))
                elif fd == stdout_pair[0]:
                    print("pipe() (stdout) descriptor ready")
                    if events & EPOLLIN:
                        print("Reading data from stdout...")
                        data = read(stdout_pair[0], PIPE_BUF)
                        print("Read {} bytes from stdout".format(len(data)))
                        print(data)
                    if events & EPOLLHUP:
                        print("Removing stdout pipe from epoll")
                        ep.unregister(stdout_pair[0])
                        print("Closing stdout pipe")
                        close(stdout_pair[0])
                        waiting_for.remove('stdout')
                elif fd == stderr_pair[0]:
                    print("pipe() (stderr) descriptor ready")
                    if events & EPOLLIN:
                        print("Reading data from stdout...")
                        data = read(stderr_pair[0], PIPE_BUF)
                        print("Read {} bytes from stderr".format(len(data)))
                        print(data)
                    if events & EPOLLHUP:
                        print("Removing stderr pipe from epoll")
                        ep.unregister(stderr_pair[0])
                        print("Closing stderr pipe")
                        close(stderr_pair[0])
                        waiting_for.remove('stderr')
                else:
                    # FIXME: we are still getting weird activation events on fd
                    # 0 (stdin) with events == 0 (nothing). I cannot explain
                    # this yet.
                    print("Unexpected descriptor ready:", fd)
    assert not waiting_for
    print("Closing", ep)
    ep.close()
    print("Unblocking signals")
    sigprocmask(SIG_UNBLOCK, mask, None)
    print("Exiting normally")