Reference

pdwait(2)

System calls to manage process descriptors

Part of pddupfd(2).

NAME

pdfork, pdrfork, pdopenpid, pdgetpid, pdkill, pdwait, pddupfd – System calls to manage process descriptors

LIBRARY

Standard C Library (libc, -lc)

SYNOPSIS

#include <sys/procdesc.h>

pid_t

pdfork(int *fdp, int pdflags);

pid_t

pdrfork(int *fdp, int pdflags, int rfflags);

int

pdopenpid(pid_t pid, int pdflags);

int

pdgetpid(int fd, pid_t *pidp);

int

pdkill(int fd, int signum);

int

pdwait(int fd, int *status, int options, struct __wrusage *wrusage, struct __siginfo *info);

int

pddupfd(int fd, int remotefd, int flags);

int

pdptrace(int req, int fd, int lwpid, void *addr, int data);

DESCRIPTION

Process descriptors are special file descriptors that represent processes, and are created using pdfork(), a variant of fork(2), which, if successful, returns a process descriptor in the integer pointed to by fdp. Processes created via pdfork() will not cause SIGCHLD on termination. pdfork() can accept the pdflags:

PD_DAEMON

Instead of the default terminate-on-close behaviour, allow the process to live until it is explicitly killed with kill(2).

This option is not permitted in capsicum(4) capability mode (see cap_enter(2)).

Note: the option changes the behavior of close(2) for the process descriptor returned by the operation. If there is another process descriptor for the same process, created without specifying the PD_DAEMON flag, closing that descriptor kills the process.

PD_CLOEXEC

Set close-on-exec on process descriptor.

PD_NOWAITPID

The parent cannot obtain the child's status with waitpid(2).

A consequence of not setting the flag is that the child' zombie is not reaped until the waitpid(2) call is performed on the child' PID. Setting the kern.pdfork_implicit_nowaitpid sysctl MIB to true makes the system behave as if the flag is always passed.

PD_PTRACE_CAP

Enable pdptrace(2) requests on the resulting file descriptor. Otherwise the descriptor cannot be used to debug the child process. See rights(4) for the description of the CAP_PTRACE capability.

The pdrfork() system call is a variant of pdfork() that also takes the rfflags argument to control sharing of process resources between the caller and the new process. Like pdfork(), the function writes the process descriptor referencing the created process into the location pointed to by the fdp argument. See rfork(2) for a description of the possible rfflag flags. The pdrfork() system call requires that both the RFPROC and RFPROCDESC flags, or RFSPAWN flag are specified.

The pdopenpid() function opens the process descriptor for the process specified by the argument pid. It takes the same flags in the pdflags argument as pdfork(). The caller must have permission to debug the target process in order for pdopenpid() to succeed. Zombie processes cannot be opened.

There might be more that one file descriptor referencing the process. After the zombie is reaped, calls to pdwait() specifying any file descriptors for the same process fail with the ESRCH error.

The pdopenpid() system call is allowed in the capability mode (see capsicum(4)) when the target process is the child of the calling process, or when the calling process is the debugger of the target process. The debugger is attached to its target by ptrace(2), pdptrace(2), or by other means, e.g., by debugging the target process' parent with the follow-on-fork mode enabled.

pdgetpid() queries the process ID (PID) in the process descriptor fd.

pdkill() is functionally identical to kill(2), except that it accepts a process descriptor, fd, rather than a PID.

The pdwait() system call allows the calling thread to wait and retrieve the status information on the process referenced by the fd process descriptor. See the description of the wait6(2) system call for the behavior specification.

The pddupfd() function allows the caller to duplicate a file descriptor across the process boundaries. The function returns the new file descriptor that points to the same file as the file descriptor remotefd in the process specified by the fd process descriptor. The returned file descriptor has the O_CLOEXEC flag set. The flags argument is reserved and must be zero. Certain file descriptor types cannot be copied this way, namely kqueues.

The pdptrace() function enables execution of ptrace(2) requests on the process specified by the process descriptor fd.

In addition to the arguments taken by the ptrace(2), system call, the lwpid thread identifier can designate the thread on which the request must operate. The lwpid argument can be specified as -1 if the call is not thread-specific, or kernel is allowed to select some thread on its own.

Unlike the ptrace(2) implementation, pdptrace() does not clear the errno variable before executing the system call.

Interaction of Process Descriptors and Xr WAITPID 2

The pdwait() system call may be called on a process descriptor an unlimited number of times. In particular, it does not reap the target process, even if that process has exited. Each time, it returns the same status.

  • If the process was forked with pdfork(), and the PD_NOWAITPID flag was specified, then the process is automatically reaped after the last process descriptor referencing that process is closed. No waitpid(2) call (or a call from the same family of the wait functions which operate on PIDs) are needed to reap the zombie process.

  • If the process was created by pdfork(), and the PD_NOWAITPID flag was not specified, then after exiting, the process will not be reaped until the parent or reaper has called waitpid(2) and all process descriptors referencing the process are closed.

  • If the process was created by the fork(2) system call (which does not allocate a process descriptor for the child), and later the process was opened by pdopenpid(), then a waitpid(2) call from the parent is needed to reap the exited child.

In any case, the PID of the process is not reused until its zombie is reaped, and all its process descriptors are closed.

A debugger attached by ptrace(2) can execute the waitpid() calls against the alive target regardless of the way the target process was forked.

Interaction of Process Descriptors with other System Calls

The following system calls also have effects specific to process descriptors:

fstat(2) queries status of a process descriptor; currently only the st_mode, st_birthtime, st_atime, st_ctime and st_mtime fields are defined. If the owner read, write, and execute bits are set then the process represented by the process descriptor is still alive.

poll(2) and select(2) allow waiting for process state transitions; currently only POLLHUP is defined, and will be raised when the process dies. Process state transitions can also be monitored using kqueue(2) filter EVFILT_PROCDESC; currently only NOTE_EXIT is implemented.

close(2) will close the process descriptor unless PD_DAEMON is set; if the process is still alive and this is the last reference to the process descriptor, the process will be terminated with the signal SIGKILL. The PID of the referenced process is not reused until the process descriptor is closed, whether or not the zombie process is reaped by pdwait(), wait6, or similar system calls.

RETURN VALUES

pdfork() and pdrfork() return a PID, 0 or -1, as fork(2) does.

pdopenpid(), pdgetpid(), pdkill(), pdwait(), and pddupfd() return 0 on success and -1 on failure.

ERRORS

These functions may return the same error numbers as their PID-based equivalents (e.g. pdfork() may return the same error numbers as fork(2)), with the following additions:

[EFAULT]

The copyout of the resulting file descriptor value to the memory pointed to by fdp failed.

Note that the child process was already created when this condition is detected, and the child continues execution, same as the parent. If this error must be handled, it is advisable to memoize the getpid() result before the call to pdfork() or pdrfork(), and compare it to the value returned by getpid() after, to see if code is executing in parent or child.

[EINVAL]

The signal number given to pdkill() is invalid.

[ENOTCAPABLE]

The process descriptor being operated on has insufficient rights (e.g. CAP_PDKILL for pdkill()).

[EINVAL]

The pdwait() function is called with reserved bits set in options.

The pdopenpid() might return the same errors as open(2), related to the file descriptor allocation problems, as well as the following specific errors:

[EINVAL]

The flags argument has reserved bits set.

[ECAPMODE]

pdopenpid() is called by the process in capability mode.

[EBUSY]

The process specified by the pid argument already terminated.

[ESRCH]

The process specified by the pid argument does not exist, or the caller does not have enough privileges to open the process.

[EMFILE]

The calling process already reached its limit for open file descriptors.

Current implementation installs the opened process descriptor into the calling process's file descriptors table. If the descriptor cannot be installed, the process descriptor is closed, which executes all actions performed by close(2) on it.

The pddupfd() returns the following errors:

[EINVAL]

The flags argument is not zero.

[EINVAL]

The file descriptor fd is not a process file descriptor.

[ESRCH]

The process specified by the file descriptor fd exited.

[EBADF]

The file descriptor remotefd is not a valid file descriptor in the specified process.

[ENOENT]

The specified process does not have a file descriptor table.

[EOPNOTSUPP]

remotefd refers to a file that cannot be duplicated across a process boundary, such as a kqueue.

The pdptrace() system call returns the same errors as ptrace(2), as well as the following specific errors:

[ECAPMODE]

The process issuing the pdptrace() call is in capability mode, and the security.bsd.allow_ptrace_in_cap_mode tunable is set to false.

[ENOTCAPABLE]

The process called pdptrace() on the process descriptor that does not have the CAP_PTRACE capability enabled.

SEE ALSO

close(2), fork(2), fstat(2), kill(2), kqueue(2), poll(2), wait4(2), capsicum(4), procdesc(4)

HISTORY

The pdfork(), pdgetpid(), and pdkill() system calls first appeared in FreeBSD 9.0. The pdrfork() and pdwait() system calls first appeared in FreeBSD 15.1. The pdopenpid() and pddupfd() system calls first appeared in FreeBSD 16.0.

Support for process descriptors mode was developed as part of the TrustedBSD Project.

AUTHORS

These functions and the capability facility were created by Robert N. M. Watson ⟨[email protected]⟩ and Jonathan Anderson ⟨[email protected]⟩ at the University of Cambridge Computer Laboratory with support from a grant from Google, Inc. The pdrfork() and pdwait() functions were developed by Konstantin Belousov ⟨[email protected]⟩ with input from Alan Somers ⟨[email protected]⟩. The pdopenpid(), pddupfd(), and pdptrace() functions were developed by Konstantin Belousov ⟨[email protected]⟩.

History