starry_api/imp/task/
wait.rs

1use alloc::{sync::Arc, vec::Vec};
2use axerrno::{LinuxError, LinuxResult};
3use axprocess::{Pid, Process};
4use axtask::{TaskExtRef, current};
5use bitflags::bitflags;
6use linux_raw_sys::general::{
7    __WALL, __WCLONE, __WNOTHREAD, WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WUNTRACED,
8};
9use starry_core::task::ProcessData;
10
11use crate::ptr::{UserPtr, nullable};
12
13bitflags! {
14    #[derive(Debug)]
15    struct WaitOptions: u32 {
16        /// Do not block when there are no processes wishing to report status.
17        const WNOHANG = WNOHANG;
18        /// Report the status of selected processes which are stopped due to a
19        /// `SIGTTIN`, `SIGTTOU`, `SIGTSTP`, or `SIGSTOP` signal.
20        const WUNTRACED = WUNTRACED;
21        /// Report the status of selected processes which have terminated.
22        const WEXITED = WEXITED;
23        /// Report the status of selected processes that have continued from a
24        /// job control stop by receiving a `SIGCONT` signal.
25        const WCONTINUED = WCONTINUED;
26        /// Don't reap, just poll status.
27        const WNOWAIT = WNOWAIT;
28
29        /// Don't wait on children of other threads in this group
30        const WNOTHREAD = __WNOTHREAD;
31        /// Wait on all children, regardless of type
32        const WALL = __WALL;
33        /// Wait for "clone" children only.
34        const WCLONE = __WCLONE;
35    }
36}
37
38#[derive(Debug, Clone, Copy)]
39enum WaitPid {
40    /// Wait for any child process
41    Any,
42    /// Wait for the child whose process ID is equal to the value.
43    Pid(Pid),
44    /// Wait for any child process whose process group ID is equal to the value.
45    Pgid(Pid),
46}
47
48impl WaitPid {
49    fn apply(&self, child: &Arc<Process>) -> bool {
50        match self {
51            WaitPid::Any => true,
52            WaitPid::Pid(pid) => child.pid() == *pid,
53            WaitPid::Pgid(pgid) => child.group().pgid() == *pgid,
54        }
55    }
56}
57
58pub fn sys_waitpid(pid: i32, exit_code_ptr: UserPtr<i32>, options: u32) -> LinuxResult<isize> {
59    let options = WaitOptions::from_bits_truncate(options);
60    info!("sys_waitpid <= pid: {:?}, options: {:?}", pid, options);
61
62    let curr = current();
63    let proc_data = curr.task_ext().process_data();
64    let process = curr.task_ext().thread.process();
65
66    let pid = if pid == -1 {
67        WaitPid::Any
68    } else if pid == 0 {
69        WaitPid::Pgid(process.group().pgid())
70    } else if pid > 0 {
71        WaitPid::Pid(pid as _)
72    } else {
73        WaitPid::Pgid(-pid as _)
74    };
75
76    let children = process
77        .children()
78        .into_iter()
79        .filter(|child| pid.apply(child))
80        .filter(|child| {
81            options.contains(WaitOptions::WALL)
82                || (options.contains(WaitOptions::WCLONE)
83                    == child.data::<ProcessData>().unwrap().is_clone_child())
84        })
85        .collect::<Vec<_>>();
86    if children.is_empty() {
87        return Err(LinuxError::ECHILD);
88    }
89
90    let exit_code = nullable!(exit_code_ptr.get_as_mut())?;
91    loop {
92        if let Some(child) = children.iter().find(|child| child.is_zombie()) {
93            if !options.contains(WaitOptions::WNOWAIT) {
94                child.free();
95            }
96            if let Some(exit_code) = exit_code {
97                *exit_code = child.exit_code();
98            }
99            return Ok(child.pid() as _);
100        } else if options.contains(WaitOptions::WNOHANG) {
101            return Ok(0);
102        } else {
103            proc_data.child_exit_wq.wait();
104        }
105    }
106}