Example #1
0
    async def clone(self, flags: CLONE = CLONE.NONE) -> ChildThread:
        """Create a new child thread

        manpage: clone(2)
        """
        child_process, task = await clone_child_task(
            self.task, self.ram, self.connection, self.loader, self.monitor,
            flags,
            lambda sock: Trampoline(self.loader.server_func, [sock, sock]))
        ram = RAM(
            task,
            # We don't inherit the transport because it leads to a deadlock:
            # If when a child task calls transport.read, it performs a syscall in the child task,
            # then the parent task will need to call waitid to monitor the child task during the syscall,
            # which will in turn need to also call transport.read.
            # But the child is already using the transport and holding the lock,
            # so the parent will block forever on taking the lock,
            # and child's read syscall will never complete.
            self.ram.transport,
            self.ram.allocator.inherit(task),
        )
        if flags & CLONE.NEWPID:
            # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
            # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
            # in both cases we just fall back to making a new ChildProcessMonitor for the child.
            epoller = await Epoller.make_root(ram, task)
            # this signal is already blocked, we inherited the block, um... I guess...
            # TODO handle this more formally
            signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
            monitor = await ChildProcessMonitor.make(ram,
                                                     task,
                                                     epoller,
                                                     signal_block=signal_block)
        else:
            epoller = self.epoller.inherit(ram)
            monitor = self.monitor.inherit_to_child(task)
        thread = ChildThread(
            Thread(
                task,
                ram,
                self.connection.inherit(task, ram),
                self.loader,
                epoller,
                monitor,
                self.environ.inherit(task, ram),
                stdin=self.stdin.inherit(task),
                stdout=self.stdout.inherit(task),
                stderr=self.stderr.inherit(task),
            ), child_process)
        if flags & CLONE.NEWUSER:
            # hack, we should really track the [ug]id ahead of this so we don't have to get it
            # we have to get the [ug]id from the parent because it will fail in the child
            uid = await self.task.getuid()
            gid = await self.task.getgid()
            await write_user_mappings(thread, uid, gid)
        return thread
Example #2
0
async def clone_persistent(
    parent: Thread,
    path: t.Union[str, os.PathLike],
) -> PersistentThread:
    """Create a new not-yet-persistent thread and return the thread and its tracking object

    To make the thread actually persistent, you must call PersistentServer.make_persistent().

    The point of this hoop-jumping is just to prevent unnecessary resource leakage, so you
    can set up things in a persistent thread and only make it persistent when you're
    actually ready.

    A persistent thread is essentially the same as a normal thread, just running a
    different function. As such, it starts off sharing its file descriptor table and
    everything else with its parent thread. It's only when we disconnect and reconnect
    that it changes behavior.

    """
    listening_sock = await parent.task.socket(AF.UNIX, SOCK.STREAM)
    await listening_sock.bind(await parent.ram.ptr(await SockaddrUn.from_path(
        parent, path)))
    await listening_sock.listen(1)
    child_process, task = await clone_child_task(
        parent.task, parent.ram, parent.connection, parent.loader,
        parent.monitor, CLONE.FILES | CLONE.FS | CLONE.SIGHAND,
        lambda sock: Trampoline(parent.loader.persistent_server_func,
                                [sock, sock, listening_sock]))
    listening_sock_handle = listening_sock.move(task)
    ram = RAM(task, parent.ram.transport, parent.ram.allocator.inherit(task))

    ## create the new persistent task
    epoller = await Epoller.make_root(ram, task)
    signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
    # TODO use an inherited signalfd instead
    child_monitor = await ChildProcessMonitor.make(ram,
                                                   task,
                                                   epoller,
                                                   signal_block=signal_block)
    return PersistentThread(Thread(
        task,
        ram,
        parent.connection.inherit(task, ram),
        parent.loader,
        epoller,
        child_monitor,
        parent.environ.inherit(task, ram),
        stdin=parent.stdin.for_task(task),
        stdout=parent.stdout.for_task(task),
        stderr=parent.stderr.for_task(task),
    ),
                            persistent_path=path,
                            persistent_sock=listening_sock_handle)
Example #3
0
    async def clone(
            self,
            flags: CLONE = CLONE.NONE,
            automatically_write_user_mappings: bool = True) -> ChildProcess:
        """Create a new child process

        manpage: clone(2)
        """
        child_pid, task = await clone_child_task(
            self.task, self.connection, self.loader, self.monitor, flags,
            lambda sock: Trampoline(self.loader.server_func, [sock, sock]))
        if flags & CLONE.NEWPID:
            # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
            # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
            # in both cases we just fall back to making a new ChildPidMonitor for the child.
            epoller = await Epoller.make_root(task)
            # this signal is already blocked, we inherited the block, um... I guess...
            # TODO handle this more formally
            signal_block = SignalBlock(task, await
                                       task.ptr(Sigset({SIG.CHLD})))
            monitor = await ChildPidMonitor.make(task,
                                                 epoller,
                                                 signal_block=signal_block)
        else:
            epoller = self.epoller.inherit(task)
            monitor = self.monitor.inherit_to_child(task)
        process = ChildProcess(
            Process(
                task,
                self.connection.inherit(task),
                self.loader,
                epoller,
                monitor,
                self.environ.inherit(task),
                stdin=self.stdin.inherit(task),
                stdout=self.stdout.inherit(task),
                stderr=self.stderr.inherit(task),
            ), child_pid)
        if flags & CLONE.NEWUSER and automatically_write_user_mappings:
            # hack, we should really track the [ug]id ahead of this so we don't have to get it
            # we have to get the [ug]id from the parent because it will fail in the child
            uid = await self.task.getuid()
            gid = await self.task.getgid()
            await write_user_mappings(process, uid, gid)
        return process
Example #4
0
 async def fork(self, flags: CLONE = CLONE.SIGHAND) -> ChildUnixThread:
     "Create a new child thread"
     child_process, task = await self._fork_task(flags)
     ram = RAM(
         task,
         # We don't inherit the transport because it leads to a deadlock:
         # If when a child task calls transport.read, it performs a syscall in the child task,
         # then the parent task will need to call waitid to monitor the child task during the syscall,
         # which will in turn need to also call transport.read.
         # But the child is already using the transport and holding the lock,
         # so the parent will block forever on taking the lock,
         # and child's read syscall will never complete.
         self.ram.transport,
         self.ram.allocator.inherit(task),
     )
     if flags & CLONE.NEWPID:
         # if the new process is pid 1, then CLONE_PARENT isn't allowed so we can't use inherit_to_child.
         # if we are a reaper, than we don't want our child CLONE_PARENTing to us, so we can't use inherit_to_child.
         # in both cases we just fall back to making a new ChildProcessMonitor for the child.
         epoller = await Epoller.make_root(ram, task)
         # this signal is already blocked, we inherited the block, um... I guess...
         # TODO handle this more formally
         signal_block = SignalBlock(task, await ram.ptr(Sigset({SIG.CHLD})))
         monitor = await ChildProcessMonitor.make(ram,
                                                  task,
                                                  epoller,
                                                  signal_block=signal_block)
     else:
         epoller = self.epoller.inherit(ram)
         monitor = self.monitor.inherit_to_child(ram, task)
     return ChildUnixThread(UnixThread(
         task,
         ram,
         self.connection.for_task(task, ram),
         self.loader,
         epoller,
         monitor,
         self.environ.inherit(task, ram),
         stdin=self.stdin.for_task(task),
         stdout=self.stdout.for_task(task),
         stderr=self.stderr.for_task(task),
     ),
                            process=child_process)