1use std::ffi::OsString;
2use std::io::{self, IoSlice, IoSliceMut};
3use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
4use std::os::unix::net::UnixStream;
5use std::os::unix::process::CommandExt;
6use std::process::{Command, ExitStatus, Output};
7use std::time::Duration;
8
9use nix::sys::signal::{Signal, kill};
10use nix::sys::socket::{ControlMessage, ControlMessageOwned, MsgFlags, recvmsg, sendmsg};
11use nix::unistd::Pid;
12
13pub fn args<S: AsRef<str>>(items: &[S]) -> Vec<OsString> {
15 items.iter().map(|s| OsString::from(s.as_ref())).collect()
16}
17
18pub fn exec_replace(bin: &str, args: &[OsString]) -> anyhow::Error {
23 let mut cmd = Command::new(bin);
24 cmd.args(args);
25 let err = cmd.exec();
26 anyhow::Error::from(err).context(format!("failed to exec {}", bin))
27}
28
29pub fn run_piped(bin: &str, args: &[OsString]) -> anyhow::Result<Output> {
31 let output = Command::new(bin)
32 .args(args)
33 .stdout(std::process::Stdio::piped())
34 .stderr(std::process::Stdio::piped())
35 .spawn()?
36 .wait_with_output()?;
37 Ok(output)
38}
39
40pub fn spawn_interactive(bin: &str, args: &[OsString]) -> anyhow::Result<ExitStatus> {
42 let status = Command::new(bin).args(args).status()?;
43 Ok(status)
44}
45
46pub fn run_piped_timeout(
50 bin: &str,
51 args: &[OsString],
52 timeout: Duration,
53) -> anyhow::Result<Output> {
54 let child = Command::new(bin)
55 .args(args)
56 .stdout(std::process::Stdio::piped())
57 .stderr(std::process::Stdio::piped())
58 .spawn()?;
59 wait_child_timeout(child, timeout)
60}
61
62pub fn spawn_interactive_timeout(
68 bin: &str,
69 args: &[OsString],
70 timeout: Duration,
71) -> anyhow::Result<ExitStatus> {
72 let mut child = Command::new(bin).args(args).spawn()?;
73 let pid = Pid::from_raw(child.id().cast_signed());
74 let (tx, rx) = std::sync::mpsc::channel();
75 std::thread::spawn(move || {
76 if rx.recv_timeout(timeout).is_err() {
77 let _ = kill(pid, Signal::SIGTERM);
78 std::thread::sleep(Duration::from_secs(5));
79 let _ = kill(pid, Signal::SIGKILL);
80 }
81 });
82 let status = child.wait()?;
83 let _ = tx.send(());
84 Ok(status)
85}
86
87pub fn wait_child_timeout(child: std::process::Child, timeout: Duration) -> anyhow::Result<Output> {
89 let pid = Pid::from_raw(child.id().cast_signed());
90 let (tx, rx) = std::sync::mpsc::channel();
91 std::thread::spawn(move || {
92 if rx.recv_timeout(timeout).is_err() {
93 let _ = kill(pid, Signal::SIGKILL);
94 }
95 });
96 let output = child.wait_with_output()?;
97 let _ = tx.send(());
98 Ok(output)
99}
100
101pub fn open_pidfd(pid: i32) -> io::Result<OwnedFd> {
105 let ret = unsafe { nix::libc::syscall(nix::libc::SYS_pidfd_open, pid, 0) };
106 if ret < 0 {
107 Err(io::Error::last_os_error())
108 } else {
109 let fd = i32::try_from(ret).map_err(|_| {
110 io::Error::new(
111 io::ErrorKind::InvalidInput,
112 "pidfd_open returned invalid fd",
113 )
114 })?;
115 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
117 }
118}
119
120pub fn send_fd(stream: &UnixStream, fd: RawFd) -> io::Result<()> {
125 let raw_fd = stream.as_raw_fd();
126 let cmsg = ControlMessage::ScmRights(&[fd]);
127 let iov = [IoSlice::new(&[0u8])];
128 loop {
129 match sendmsg::<()>(raw_fd, &iov, &[cmsg], MsgFlags::empty(), None) {
130 Ok(_) => return Ok(()),
131 Err(nix::errno::Errno::EINTR) => {}
132 Err(e) => return Err(io::Error::from(e)),
133 }
134 }
135}
136
137pub fn recv_fd(stream: &UnixStream) -> io::Result<Option<RawFd>> {
142 let raw_fd = stream.as_raw_fd();
143 let mut buf = [0u8; 1];
144 let mut iov = [IoSliceMut::new(&mut buf)];
145 let mut cmsg_buf = vec![0u8; 256];
146 let msg = loop {
147 match recvmsg::<()>(raw_fd, &mut iov, Some(&mut cmsg_buf), MsgFlags::empty()) {
148 Ok(m) => break m,
149 Err(nix::errno::Errno::EINTR) => {}
150 Err(e) => return Err(io::Error::from(e)),
151 }
152 };
153
154 if msg.bytes == 0 {
155 return Ok(None);
156 }
157
158 if let Ok(cmsgs) = msg.cmsgs() {
159 for cmsg in cmsgs {
160 if let ControlMessageOwned::ScmRights(fds) = cmsg {
161 if let Some(&fd) = fds.first() {
162 return Ok(Some(fd));
163 }
164 }
165 }
166 }
167 Ok(None)
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn args_builds_osstring_vec() {
176 let v = args(&["foo", "bar", "baz"]);
177 assert_eq!(v.len(), 3);
178 assert_eq!(v[0], "foo");
179 assert_eq!(v[1], "bar");
180 assert_eq!(v[2], "baz");
181 }
182
183 #[test]
184 fn args_accepts_mixed_types() {
185 let s = String::from("hello");
186 let v = args(&["a", &s, "c"]);
187 assert_eq!(v[1], "hello");
188 }
189}