use libc::{gid_t, pid_t, uid_t};
#[cfg(not(target_os = "redox"))]
use nix::errno::Errno;
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 {
unsafe { libc::geteuid() }
}
pub fn getpgrp() -> pid_t {
unsafe { libc::getpgrp() }
}
pub fn getegid() -> gid_t {
unsafe { libc::getegid() }
}
pub fn getgid() -> gid_t {
unsafe { libc::getgid() }
}
pub fn getuid() -> uid_t {
unsafe { libc::getuid() }
}
pub fn getpid() -> pid_t {
unsafe { libc::getpid() }
}
#[cfg(not(target_os = "redox"))]
pub fn getsid(pid: i32) -> Result<pid_t, Errno> {
unsafe {
let result = libc::getsid(pid);
if Errno::last() == Errno::UnknownErrno {
Ok(result)
} else {
Err(Errno::last())
}
}
}
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<()> {
if unsafe { libc::kill(self.id() as pid_t, signal as i32) } == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
fn send_signal_group(&mut self, signal: usize) -> io::Result<()> {
if unsafe { libc::signal(signal as i32, libc::SIG_IGN) } == usize::MAX {
return Err(io::Error::last_os_error());
}
if unsafe { libc::kill(0, signal as i32) } == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
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());
}
}