Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/uu/pidwait/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ clap = { workspace = true }
regex = { workspace = true }
uu_pgrep = { path = "../pgrep" }

[target.'cfg(unix)'.dependencies]
rustix = { version = "1", default-features = false, features = ["event", "process", "std"] }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Threading"] }

[lib]
path = "src/pidwait.rs"

Expand Down
51 changes: 51 additions & 0 deletions src/uu/pidwait/src/imp/bsd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// This file is part of the uutils procps package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// Reference: pidwait-any crate.
// Thanks to @oxalica's implementation.

// FIXME: Test this implementation

use rustix::event::kqueue::{kevent, kqueue, Event, EventFilter, EventFlags, ProcessEvents};
use rustix::process::Pid;
use std::io::{Error, ErrorKind, Result};
use std::mem::MaybeUninit;
use std::time::Duration;
use uu_pgrep::process::ProcessInformation;

pub fn wait(procs: &[ProcessInformation], timeout: Option<Duration>) -> Result<Option<()>> {
let mut events = Vec::with_capacity(procs.len());
let kqueue = kqueue()?;
for proc in procs {
let pid = Pid::from_raw(proc.pid as i32).ok_or_else(|| {
Error::new(
ErrorKind::InvalidInput,
format!("Invalid PID: {}", proc.pid),
)
})?;
let event = Event::new(
EventFilter::Proc {
pid,
flags: ProcessEvents::EXIT,
},
EventFlags::ADD,
std::ptr::null_mut(),
);
events.push(event);
}
let ret = unsafe { kevent::<_, &mut [Event; 0]>(&kqueue, &events, &mut [], None)? };
debug_assert_eq!(ret, 0);
let mut buf = [MaybeUninit::uninit()];
let (events, _rest_buf) = unsafe { kevent(&kqueue, &[], &mut buf, timeout)? };
if events.is_empty() {
return Ok(None);
};
debug_assert!(matches!(
events[0].filter(),
EventFilter::Proc { flags, .. }
if flags.contains(ProcessEvents::EXIT)
));
Ok(Some(()))
}
45 changes: 45 additions & 0 deletions src/uu/pidwait/src/imp/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This file is part of the uutils procps package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// Reference: pidwait-any crate.
// Thanks to @oxalica's implementation.

use std::io::{Error, ErrorKind};
use std::os::fd::OwnedFd;

use rustix::event::{poll, PollFd, PollFlags};
use rustix::io::Errno;
use rustix::process::{pidfd_open, Pid, PidfdFlags};
use std::io::Result;
use std::time::Duration;
use uu_pgrep::process::ProcessInformation;

pub fn wait(procs: &[ProcessInformation], timeout: Option<Duration>) -> Result<Option<()>> {
let mut pidfds: Vec<OwnedFd> = Vec::with_capacity(procs.len());
for proc in procs {
let pid = Pid::from_raw(proc.pid as i32).ok_or_else(|| {
Error::new(
ErrorKind::InvalidInput,
format!("Invalid PID: {}", proc.pid),
)
})?;
let pidfd = pidfd_open(pid, PidfdFlags::empty())?;
pidfds.push(pidfd);
}
let timespec = match timeout {
Some(timeout) => Some(timeout.try_into().map_err(|_| Errno::INVAL)?),
None => None,
};
let mut fds: Vec<PollFd> = Vec::with_capacity(pidfds.len());
for pidfd in &pidfds {
fds.push(PollFd::new(pidfd, PollFlags::IN));
}
let ret = poll(&mut fds, timespec.as_ref())?;
if ret == 0 {
return Ok(None);
}
debug_assert!(fds[0].revents().contains(PollFlags::IN));
Ok(Some(()))
}
68 changes: 68 additions & 0 deletions src/uu/pidwait/src/imp/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// This file is part of the uutils procps package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// Reference: pidwait-any crate.
// Thanks to @oxalica's implementation.

use std::time::Duration;
use uu_pgrep::process::ProcessInformation;

use std::ffi::c_void;
use std::io::{Error, Result};
use std::ptr::NonNull;

use windows_sys::Win32::Foundation::{CloseHandle, WAIT_OBJECT_0, WAIT_TIMEOUT};
use windows_sys::Win32::System::Threading::{
OpenProcess, WaitForMultipleObjects, INFINITE, PROCESS_SYNCHRONIZE,
};

struct HandleWrapper(NonNull<c_void>);
unsafe impl Send for HandleWrapper {}
impl Drop for HandleWrapper {
fn drop(&mut self) {
unsafe {
CloseHandle(self.0.as_ptr());
};
}
}

pub fn wait(procs: &[ProcessInformation], timeout: Option<Duration>) -> Result<Option<()>> {
let hprocess = unsafe {
let mut result = Vec::with_capacity(procs.len());
for proc in procs {
let handle = OpenProcess(PROCESS_SYNCHRONIZE, 0, proc.pid as u32);
result.push(HandleWrapper(
NonNull::new(handle).ok_or_else(Error::last_os_error)?,
));
}
result
};
const _: [(); 1] = [(); (INFINITE == u32::MAX) as usize];
let timeout = match timeout {
Some(timeout) => timeout
.as_millis()
.try_into()
.unwrap_or(INFINITE - 1)
.min(INFINITE - 1),
None => INFINITE,
};
let ret = unsafe {
WaitForMultipleObjects(
hprocess.len() as u32,
hprocess
.into_iter()
.map(|proc| proc.0.as_ptr())
.collect::<Vec<_>>()
.as_ptr(),
1,
timeout,
)
};
match ret {
WAIT_OBJECT_0 => Ok(Some(())),
WAIT_TIMEOUT => Ok(None),
_ => Err(Error::last_os_error()),
}
}
5 changes: 4 additions & 1 deletion src/uu/pidwait/src/pidwait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;

let settings = process_matcher::get_match_settings(&matches)?;

// FIXME: process_matcher::find_matching_pids() is not working on Windows
let mut proc_infos = process_matcher::find_matching_pids(&settings);

// For empty result
Expand All @@ -42,7 +44,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}
}

wait(&proc_infos);
// It should be fine to reserve a `timeout` parameter for future use.
wait(&proc_infos, None)?;

Ok(())
}
Expand Down
104 changes: 65 additions & 39 deletions src/uu/pidwait/src/wait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,77 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use std::io::Result;
use std::time::Duration;
use uu_pgrep::process::ProcessInformation;

// Reference: pidwait-any crate.
// Thanks to @oxalica's implementation.

#[cfg(target_os = "linux")]
#[path = "./imp/linux.rs"]
mod imp;

#[cfg(windows)]
#[path = "./imp/windows.rs"]
mod imp;

#[cfg(any(
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[path = "./imp/bsd.rs"]
mod imp;

pub(crate) fn wait(procs: &[ProcessInformation], timeout: Option<Duration>) -> Result<Option<()>> {
imp::wait(procs, timeout)
}

// Dirty, but it works.
// TODO: Use better implementation instead
#[cfg(target_os = "linux")]
pub(crate) fn wait(procs: &[ProcessInformation]) {
use std::{thread::sleep, time::Duration};
// #[cfg(target_os = "linux")]
// pub(crate) fn wait(procs: &[ProcessInformation]) {
// use std::{thread::sleep, time::Duration};

let mut list = procs.to_vec();
// let mut list = procs.to_vec();

loop {
for proc in &list.clone() {
// Check is running
if !is_running(proc.pid) {
list.retain(|it| it.pid != proc.pid);
}
}
// loop {
// for proc in &list.clone() {
// // Check is running
// if !is_running(proc.pid) {
// list.retain(|it| it.pid != proc.pid);
// }
// }

if list.is_empty() {
return;
}
// if list.is_empty() {
// return;
// }

sleep(Duration::from_millis(50));
}
}
#[cfg(target_os = "linux")]
fn is_running(pid: usize) -> bool {
use std::{path::PathBuf, str::FromStr};
use uu_pgrep::process::RunState;

let proc = PathBuf::from_str(&format!("/proc/{}", pid)).unwrap();

if !proc.exists() {
return false;
}

match ProcessInformation::try_new(proc) {
Ok(mut proc) => proc
.run_state()
.map(|it| it != RunState::Stopped)
.unwrap_or(false),
Err(_) => false,
}
}
// sleep(Duration::from_millis(50));
// }
// }
// #[cfg(target_os = "linux")]
// fn is_running(pid: usize) -> bool {
// use std::{path::PathBuf, str::FromStr};
// use uu_pgrep::process::RunState;

// let proc = PathBuf::from_str(&format!("/proc/{}", pid)).unwrap();

// if !proc.exists() {
// return false;
// }

// match ProcessInformation::try_new(proc) {
// Ok(mut proc) => proc
// .run_state()
// .map(|it| it != RunState::Stopped)
// .unwrap_or(false),
// Err(_) => false,
// }
// }

// Just for passing compile on other system.
#[cfg(not(target_os = "linux"))]
pub(crate) fn wait(_procs: &[ProcessInformation]) {}
// // Just for passing compile on other system.
// #[cfg(not(target_os = "linux"))]
// pub(crate) fn wait(_procs: &[ProcessInformation]) {}
Loading