use libc::{gid_t, pid_t, uid_t};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
use nix::sys::signal::{self as nix_signal, SigHandler, Signal};
use nix::unistd::Pid;
use std::io;
use std::process::Child;
use std::process::ExitStatus;
use std::sync::atomic;
use std::sync::atomic::AtomicBool;
use std::thread;
use std::time::{Duration, Instant};
pub fn geteuid() -> uid_t {
nix::unistd::geteuid().as_raw()
}
pub fn getpgrp() -> pid_t {
nix::unistd::getpgrp().as_raw()
}
pub fn getegid() -> gid_t {
nix::unistd::getegid().as_raw()
}
pub fn getgid() -> gid_t {
nix::unistd::getgid().as_raw()
}
pub fn getuid() -> uid_t {
nix::unistd::getuid().as_raw()
}
pub fn getpid() -> pid_t {
nix::unistd::getpid().as_raw()
}
#[cfg(not(target_os = "redox"))]
pub fn getsid(pid: i32) -> Result<pid_t, Errno> {
let pid = if pid == 0 {
None
} else {
Some(Pid::from_raw(pid))
};
nix::unistd::getsid(pid).map(Pid::as_raw)
}
pub trait ChildExt {
fn send_signal(&mut self, signal: usize) -> io::Result<()>;
fn send_signal_group(&mut self, signal: usize) -> io::Result<()>;
fn wait_or_timeout(
&mut self,
timeout: Duration,
signaled: Option<&AtomicBool>,
) -> io::Result<Option<ExitStatus>>;
}
impl ChildExt for Child {
fn send_signal(&mut self, signal: usize) -> io::Result<()> {
let pid = Pid::from_raw(self.id() as pid_t);
let result = if signal == 0 {
nix_signal::kill(pid, None)
} else {
let signal = Signal::try_from(signal as i32)
.map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
nix_signal::kill(pid, Some(signal))
};
result.map_err(|e| io::Error::from_raw_os_error(e as i32))
}
fn send_signal_group(&mut self, signal: usize) -> io::Result<()> {
if signal == 0 {
return nix_signal::kill(Pid::from_raw(0), None)
.map_err(|e| io::Error::from_raw_os_error(e as i32));
}
let signal = Signal::try_from(signal as i32)
.map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
let old_handler = unsafe { nix_signal::signal(signal, SigHandler::SigIgn) }
.map_err(|e| io::Error::from_raw_os_error(e as i32))?;
let result = nix_signal::kill(Pid::from_raw(0), Some(signal));
let _ = unsafe { nix_signal::signal(signal, old_handler) };
result.map_err(|e| io::Error::from_raw_os_error(e as i32))
}
fn wait_or_timeout(
&mut self,
timeout: Duration,
signaled: Option<&AtomicBool>,
) -> io::Result<Option<ExitStatus>> {
if timeout == Duration::from_micros(0) {
return self.wait().map(Some);
}
drop(self.stdin.take());
let start = Instant::now();
loop {
if let Some(status) = self.try_wait()? {
return Ok(Some(status));
}
if start.elapsed() >= timeout
|| signaled.is_some_and(|signaled| signaled.load(atomic::Ordering::Relaxed))
{
break;
}
thread::sleep(Duration::from_millis(100));
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(not(target_os = "redox"))]
fn test_getsid() {
assert_eq!(
getsid(getpid()).expect("getsid(getpid)"),
getsid(0).expect("getsid(0)")
);
assert!(getsid(getpid()).expect("getsid(getpid)") > 0);
assert!(getsid(999_999).is_err());
}
}