Module rsyscall.sys.signalfd

#include <sys/signalfd.h>

Expand source code Browse git
"`#include <sys/signalfd.h>`"
import typing as t
from rsyscall._raw import ffi, lib # type: ignore
from rsyscall.struct import Struct
from dataclasses import dataclass

from rsyscall.signal import SIG, Sigset
import enum

class SFD(enum.IntFlag):
    NONE = 0
    NONBLOCK = lib.SFD_NONBLOCK
    CLOEXEC = lib.SFD_CLOEXEC

@dataclass
class SignalfdSiginfo(Struct):
    # TODO fill in the rest of the data
    # (even though we don't use any of it ourselves)
    signo: SIG

    def to_bytes(self) -> bytes:
        struct = ffi.new('struct signalfd_siginfo*')
        struct.ssi_signo = self.signo
        return bytes(ffi.buffer(struct))

    T = t.TypeVar('T', bound='SignalfdSiginfo')
    @classmethod
    def from_bytes(cls: t.Type[T], data: bytes) -> T:
        struct = ffi.cast('struct signalfd_siginfo const*', ffi.from_buffer(data))
        return cls(
            signo=SIG(struct.ssi_signo),
        )

    @classmethod
    def sizeof(cls) -> int:
        return ffi.sizeof('struct signalfd_siginfo')

#### Classes ####
from rsyscall.handle.fd import BaseFileDescriptor, FileDescriptorTask
from rsyscall.handle.pointer import Pointer

T_fd = t.TypeVar('T_fd', bound='SignalFileDescriptor')
class SignalFileDescriptor(BaseFileDescriptor):
    async def signalfd(self, mask: Pointer[Sigset], flags: SFD) -> None:
        self._validate()
        with mask.borrow(self.task) as mask_n:
            await _signalfd(self.task.sysif, self.near, mask_n, mask.size(), flags)

class SignalfdTask(FileDescriptorTask[T_fd]):
    async def signalfd(self, mask: Pointer[Sigset], flags: SFD=SFD.NONE) -> T_fd:
        with mask.borrow(self) as mask_n:
            fd = await _signalfd(self.sysif, None, mask_n, mask.size(), flags|SFD.CLOEXEC)
            return self.make_fd_handle(fd)

#### Raw syscalls ####
import rsyscall.near.types as near
from rsyscall.near.sysif import SyscallInterface
from rsyscall.sys.syscall import SYS

async def _signalfd(sysif: SyscallInterface, fd: t.Optional[near.FileDescriptor],
                    mask: near.Address, sizemask: int, flags: SFD) -> near.FileDescriptor:
    if fd is None:
        fd = -1 # type: ignore
    return near.FileDescriptor(await sysif.syscall(SYS.signalfd4, fd, mask, sizemask, flags))

Classes

class SFD (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code Browse git
class SFD(enum.IntFlag):
    NONE = 0
    NONBLOCK = lib.SFD_NONBLOCK
    CLOEXEC = lib.SFD_CLOEXEC

Ancestors

  • enum.IntFlag
  • builtins.int
  • enum.Flag
  • enum.Enum

Class variables

var NONE
var NONBLOCK
var CLOEXEC
class SignalfdSiginfo (signo: SIG)

SignalfdSiginfo(signo: rsyscall.signal.SIG)

Expand source code Browse git
@dataclass
class SignalfdSiginfo(Struct):
    # TODO fill in the rest of the data
    # (even though we don't use any of it ourselves)
    signo: SIG

    def to_bytes(self) -> bytes:
        struct = ffi.new('struct signalfd_siginfo*')
        struct.ssi_signo = self.signo
        return bytes(ffi.buffer(struct))

    T = t.TypeVar('T', bound='SignalfdSiginfo')
    @classmethod
    def from_bytes(cls: t.Type[T], data: bytes) -> T:
        struct = ffi.cast('struct signalfd_siginfo const*', ffi.from_buffer(data))
        return cls(
            signo=SIG(struct.ssi_signo),
        )

    @classmethod
    def sizeof(cls) -> int:
        return ffi.sizeof('struct signalfd_siginfo')

Ancestors

Class variables

var signoSIG
var T

Inherited members

class SignalFileDescriptor (task: FileDescriptorTask, near: FileDescriptor, valid: bool)

A file descriptor accessed through some Task

This is an rsyscall-internal base class, which other FileDescriptor objects inherit from for core lifecycle methods. See FileDescriptor for more information.

Expand source code Browse git
class SignalFileDescriptor(BaseFileDescriptor):
    async def signalfd(self, mask: Pointer[Sigset], flags: SFD) -> None:
        self._validate()
        with mask.borrow(self.task) as mask_n:
            await _signalfd(self.task.sysif, self.near, mask_n, mask.size(), flags)

Ancestors

Subclasses

Methods

async def signalfd(self, mask: Pointer[Sigset], flags: SFD) ‑> NoneType
Expand source code Browse git
async def signalfd(self, mask: Pointer[Sigset], flags: SFD) -> None:
    self._validate()
    with mask.borrow(self.task) as mask_n:
        await _signalfd(self.task.sysif, self.near, mask_n, mask.size(), flags)

Inherited members

class SignalfdTask (sysif: SyscallInterface, near_process: Process, fd_table: FDTable, address_space: AddressSpace, pidns: PidNamespace)

A wrapper around SyscallInterface which tracks the namespaces of the underlying process

Note that this is a base class for the more fully featured Task.

We store namespace objects to represent the namespaces that we believe that underlying processes is in. Since we have complete control over the process, we can make sure this belief is accurate, by updating our stored namespaces when the process changes namespace. That isn't done here; it's done in handle.Task.

Currently, we store only one PidNamespace. But each process actually has two pid namespaces:

  • the process's own pid namespace, which determines the pids returned from getpid, clone, and other syscalls.
  • the pid namespace that new children will be in.

The two pid namespaces only differ if we call unshare(CLONE.NEWPID). Currently we don't do that because unshare(CLONE.NEWPID) makes monitoring children more complex, since they can be deleted without leaving a zombie at any time if the pid namespace shuts down. But if we did call unshare(CLONE.NEWPID), we'd need to handle this right.

In the analogy to near and far pointers, this is like a segment register, if a segment register was write-only. Then we'd need to maintain the knowledge of what the segment register was set to, outside the segment register itself. That's what we do here.

There actually were systems where segment registers were, if not quite write-only, at least expensive to set and expensive to read. For example, x86_64 - the FS and GS segment registers can only be set via syscall. If you wanted to use segmentation on such systems, you'd probably have a structure much like this one.

Expand source code Browse git
class SignalfdTask(FileDescriptorTask[T_fd]):
    async def signalfd(self, mask: Pointer[Sigset], flags: SFD=SFD.NONE) -> T_fd:
        with mask.borrow(self) as mask_n:
            fd = await _signalfd(self.sysif, None, mask_n, mask.size(), flags|SFD.CLOEXEC)
            return self.make_fd_handle(fd)

Ancestors

Subclasses

Class variables

var sysifSyscallInterface
var near_processProcess
var fd_tableFDTable
var address_spaceAddressSpace
var pidnsPidNamespace

Methods

async def signalfd(self, mask: Pointer[Sigset], flags: SFD = SFD.NONE) ‑> ~T_fd
Expand source code Browse git
async def signalfd(self, mask: Pointer[Sigset], flags: SFD=SFD.NONE) -> T_fd:
    with mask.borrow(self) as mask_n:
        fd = await _signalfd(self.sysif, None, mask_n, mask.size(), flags|SFD.CLOEXEC)
        return self.make_fd_handle(fd)

Inherited members