-
Notifications
You must be signed in to change notification settings - Fork 677
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
154 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
//! Interfaces for Linux's PID file descriptors. | ||
use std::os::unix::io::RawFd; | ||
use crate::Result; | ||
use crate::errno::Errno; | ||
use crate::unistd::Pid; | ||
|
||
libc_bitflags!( | ||
/// Options that change the behavior of [`pidfd_open`]. | ||
pub struct PidFdOpenFlag: libc::c_uint { | ||
/// Return a nonblocking file descriptor. (since Linux 5.10) | ||
/// | ||
/// If the process referred to by the file descriptor has not yet terminated, | ||
/// then an attempt to wait on the file descriptor using [`waitid(2)`] will | ||
/// immediately return the error `EAGAIN` rather than blocking. | ||
/// | ||
/// [`waitid(2)`]: https://man7.org/linux/man-pages/man2/waitid.2.html | ||
PIDFD_NONBLOCK; | ||
} | ||
); | ||
|
||
/// Obtain a file descriptor that refers to a process. (since Linux 5.3) | ||
/// | ||
/// The `pidfd_open(2)` creates a file descriptor that refers to the process | ||
/// whose PID is specified in pid. The file descriptor is returned as the function | ||
/// result; the close-on-exec flag is set on the file descriptor. | ||
/// | ||
/// For more information, see [`pidfd_open(2)`]. | ||
/// | ||
/// [`pidfd_open(2)`]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html | ||
pub fn pidfd_open(pid: Pid, flags: PidFdOpenFlag) -> Result<RawFd> { | ||
let ret = unsafe { | ||
libc::syscall(libc::SYS_pidfd_open, pid, flags) | ||
}; | ||
Errno::result(ret).map(|r| r as RawFd) | ||
} | ||
|
||
/// Obtain a duplicate of another process's file descriptor. (since Linux 5.6) | ||
/// | ||
/// The `pidfd_getfd(2)` system call allocates a new file descriptor in the calling | ||
/// process. This new file descriptor is a duplicate of an existing file descriptor, | ||
/// `target_fd`, in the process referred to by the PID file descriptor pidfd. | ||
/// | ||
/// For more information, see [`pidfd_getfd(2)`]. | ||
/// | ||
/// [`pidfd_getfd(2)`]: https://man7.org/linux/man-pages/man2/pidfd_getfd.2.html | ||
pub fn pidfd_getfd(pidfd: RawFd, target_fd: RawFd) -> Result<RawFd> { | ||
let ret = unsafe { | ||
libc::syscall(libc::SYS_pidfd_getfd, pidfd, target_fd, 0) | ||
}; | ||
Errno::result(ret).map(|r| r as RawFd) | ||
} | ||
|
||
/// Send a signal to a process specified by a file descriptor. | ||
/// | ||
/// The `pidfd_send_signal(2)` system call sends the signal sig to the target process | ||
/// referred to by pidfd, a PID file descriptor that refers to a process. | ||
/// | ||
/// For more information, see [`pidfd_send_signal(2)`]. | ||
/// | ||
/// [`pidfd_send_signal(2)`]: https://man7.org/linux/man-pages/man2/pidfd_send_signal.2.html | ||
#[cfg(feature = "signal")] | ||
pub fn pidfd_send_signal<T: Into<Option<crate::sys::signal::Signal>>>(pidfd: RawFd, signal: T, sig_info: Option<&libc::siginfo_t>) -> Result<()> { | ||
let signal = match signal.into() { | ||
Some(signal) => signal as libc::c_int, | ||
_ => 0, | ||
}; | ||
let ret = unsafe { | ||
libc::syscall(libc::SYS_pidfd_send_signal, pidfd, signal, sig_info, 0) | ||
}; | ||
Errno::result(ret).map(drop) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use nix::poll::{poll, PollFd, PollFlags}; | ||
use nix::sys::pidfd::*; | ||
use nix::sys::signal::*; | ||
use nix::unistd::*; | ||
use std::io::{Read, Seek, SeekFrom, Write}; | ||
use std::os::unix::prelude::{AsRawFd, FromRawFd}; | ||
|
||
#[test] | ||
fn test_pidfd_open() { | ||
let pidfd = pidfd_open(getpid(), PidFdOpenFlag::empty()).unwrap(); | ||
close(pidfd).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn test_pidfd_getfd() { | ||
let pidfd = pidfd_open(getpid(), PidFdOpenFlag::empty()).unwrap(); | ||
|
||
let mut tempfile = tempfile::tempfile().unwrap(); | ||
tempfile.write_all(b"hello").unwrap(); | ||
tempfile.seek(SeekFrom::Start(0)).unwrap(); | ||
|
||
let tempfile2 = pidfd_getfd(pidfd, tempfile.as_raw_fd()).unwrap(); | ||
let mut tempfile2 = unsafe { std::fs::File::from_raw_fd(tempfile2) }; | ||
|
||
// Drop the original file. Since `tempfile2` should hold the same file, it would not be deleted. | ||
drop(tempfile); | ||
let mut buf = String::new(); | ||
tempfile2.read_to_string(&mut buf).unwrap(); | ||
assert_eq!(buf, "hello"); | ||
|
||
close(pidfd).unwrap(); | ||
} | ||
|
||
#[test] | ||
fn test_pidfd_poll_send_signal() { | ||
let me_pidfd = pidfd_open(getpid(), PidFdOpenFlag::empty()).unwrap(); | ||
|
||
let child = match unsafe { fork() }.expect("Error: Fork Failed") { | ||
ForkResult::Child => { | ||
sleep(1); | ||
unsafe { libc::_exit(42) } | ||
} | ||
ForkResult::Parent { child } => child, | ||
}; | ||
|
||
let child_pidfd = pidfd_open(child, PidFdOpenFlag::empty()).unwrap(); | ||
let mut poll_fds = [ | ||
PollFd::new(me_pidfd, PollFlags::POLLIN), | ||
PollFd::new(child_pidfd, PollFlags::POLLIN), | ||
]; | ||
|
||
// Timeout. | ||
assert_eq!(poll(&mut poll_fds, 100).unwrap(), 0); | ||
// Both parent and child are running. | ||
assert!(!poll_fds[0].revents().unwrap().contains(PollFlags::POLLIN)); | ||
assert!(!poll_fds[1].revents().unwrap().contains(PollFlags::POLLIN)); | ||
|
||
pidfd_send_signal(child_pidfd, Signal::SIGINT, None).unwrap(); | ||
|
||
// Child pidfd is ready. | ||
assert_eq!(poll(&mut poll_fds, 100).unwrap(), 1); | ||
// Parent is still running. | ||
assert!(!poll_fds[0].revents().unwrap().contains(PollFlags::POLLIN)); | ||
// Child is dead. | ||
assert!(poll_fds[1].revents().unwrap().contains(PollFlags::POLLIN)); | ||
|
||
close(me_pidfd).unwrap(); | ||
close(child_pidfd).unwrap(); | ||
} |