1use libc::_exit; 2use nix::errno::Errno; 3use nix::sys::signal::*; 4use nix::sys::wait::*; 5use nix::unistd::ForkResult::*; 6use nix::unistd::*; 7 8#[test] 9#[cfg(not(any(target_os = "redox", target_os = "haiku")))] 10fn test_wait_signal() { 11 let _m = crate::FORK_MTX.lock(); 12 13 // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. 14 match unsafe { fork() }.expect("Error: Fork Failed") { 15 Child => { 16 pause(); 17 unsafe { _exit(123) } 18 } 19 Parent { child } => { 20 kill(child, Some(SIGKILL)).expect("Error: Kill Failed"); 21 assert_eq!( 22 waitpid(child, None), 23 Ok(WaitStatus::Signaled(child, SIGKILL, false)) 24 ); 25 } 26 } 27} 28 29#[test] 30#[cfg(any( 31 target_os = "android", 32 target_os = "freebsd", 33 //target_os = "haiku", 34 all(target_os = "linux", not(target_env = "uclibc")), 35))] 36#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] 37fn test_waitid_signal() { 38 let _m = crate::FORK_MTX.lock(); 39 40 // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe. 41 match unsafe { fork() }.expect("Error: Fork Failed") { 42 Child => { 43 pause(); 44 unsafe { _exit(123) } 45 } 46 Parent { child } => { 47 kill(child, Some(SIGKILL)).expect("Error: Kill Failed"); 48 assert_eq!( 49 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 50 Ok(WaitStatus::Signaled(child, SIGKILL, false)), 51 ); 52 } 53 } 54} 55 56#[test] 57fn test_wait_exit() { 58 let _m = crate::FORK_MTX.lock(); 59 60 // Safe: Child only calls `_exit`, which is async-signal-safe. 61 match unsafe { fork() }.expect("Error: Fork Failed") { 62 Child => unsafe { 63 _exit(12); 64 }, 65 Parent { child } => { 66 assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12))); 67 } 68 } 69} 70 71#[cfg(not(target_os = "haiku"))] 72#[test] 73#[cfg(any( 74 target_os = "android", 75 target_os = "freebsd", 76 target_os = "haiku", 77 all(target_os = "linux", not(target_env = "uclibc")), 78))] 79#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] 80fn test_waitid_exit() { 81 let _m = crate::FORK_MTX.lock(); 82 83 // Safe: Child only calls `_exit`, which is async-signal-safe. 84 match unsafe { fork() }.expect("Error: Fork Failed") { 85 Child => unsafe { 86 _exit(12); 87 }, 88 Parent { child } => { 89 assert_eq!( 90 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 91 Ok(WaitStatus::Exited(child, 12)), 92 ); 93 } 94 } 95} 96 97#[test] 98fn test_waitstatus_from_raw() { 99 let pid = Pid::from_raw(1); 100 assert_eq!( 101 WaitStatus::from_raw(pid, 0x0002), 102 Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)) 103 ); 104 assert_eq!( 105 WaitStatus::from_raw(pid, 0x0200), 106 Ok(WaitStatus::Exited(pid, 2)) 107 ); 108 assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL)); 109} 110 111#[test] 112fn test_waitstatus_pid() { 113 let _m = crate::FORK_MTX.lock(); 114 115 match unsafe { fork() }.unwrap() { 116 Child => unsafe { _exit(0) }, 117 Parent { child } => { 118 let status = waitpid(child, None).unwrap(); 119 assert_eq!(status.pid(), Some(child)); 120 } 121 } 122} 123 124#[test] 125#[cfg(any( 126 target_os = "android", 127 target_os = "freebsd", 128 target_os = "haiku", 129 all(target_os = "linux", not(target_env = "uclibc")), 130))] 131fn test_waitid_pid() { 132 let _m = crate::FORK_MTX.lock(); 133 134 match unsafe { fork() }.unwrap() { 135 Child => unsafe { _exit(0) }, 136 Parent { child } => { 137 let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).unwrap(); 138 assert_eq!(status.pid(), Some(child)); 139 } 140 } 141} 142 143#[cfg(any(target_os = "linux", target_os = "android"))] 144// FIXME: qemu-user doesn't implement ptrace on most arches 145#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 146mod ptrace { 147 use crate::*; 148 use libc::_exit; 149 use nix::sys::ptrace::{self, Event, Options}; 150 use nix::sys::signal::*; 151 use nix::sys::wait::*; 152 use nix::unistd::ForkResult::*; 153 use nix::unistd::*; 154 155 fn ptrace_child() -> ! { 156 ptrace::traceme().unwrap(); 157 // As recommended by ptrace(2), raise SIGTRAP to pause the child 158 // until the parent is ready to continue 159 raise(SIGTRAP).unwrap(); 160 unsafe { _exit(0) } 161 } 162 163 fn ptrace_wait_parent(child: Pid) { 164 // Wait for the raised SIGTRAP 165 assert_eq!( 166 waitpid(child, None), 167 Ok(WaitStatus::Stopped(child, SIGTRAP)) 168 ); 169 // We want to test a syscall stop and a PTRACE_EVENT stop 170 ptrace::setoptions( 171 child, 172 Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT, 173 ) 174 .expect("setoptions failed"); 175 176 // First, stop on the next system call, which will be exit() 177 ptrace::syscall(child, None).expect("syscall failed"); 178 assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); 179 // Then get the ptrace event for the process exiting 180 ptrace::cont(child, None).expect("cont failed"); 181 assert_eq!( 182 waitpid(child, None), 183 Ok(WaitStatus::PtraceEvent( 184 child, 185 SIGTRAP, 186 Event::PTRACE_EVENT_EXIT as i32 187 )) 188 ); 189 // Finally get the normal wait() result, now that the process has exited 190 ptrace::cont(child, None).expect("cont failed"); 191 assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); 192 } 193 194 #[cfg(not(target_env = "uclibc"))] 195 fn ptrace_waitid_parent(child: Pid) { 196 // Wait for the raised SIGTRAP 197 // 198 // Unlike waitpid(), waitid() can distinguish trap events from regular 199 // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here 200 assert_eq!( 201 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 202 Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)), 203 ); 204 // We want to test a syscall stop and a PTRACE_EVENT stop 205 ptrace::setoptions( 206 child, 207 Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT, 208 ) 209 .expect("setopts failed"); 210 211 // First, stop on the next system call, which will be exit() 212 ptrace::syscall(child, None).expect("syscall failed"); 213 assert_eq!( 214 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 215 Ok(WaitStatus::PtraceSyscall(child)), 216 ); 217 // Then get the ptrace event for the process exiting 218 ptrace::cont(child, None).expect("cont failed"); 219 assert_eq!( 220 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 221 Ok(WaitStatus::PtraceEvent( 222 child, 223 SIGTRAP, 224 Event::PTRACE_EVENT_EXIT as i32 225 )), 226 ); 227 // Finally get the normal wait() result, now that the process has exited 228 ptrace::cont(child, None).expect("cont failed"); 229 assert_eq!( 230 waitid(Id::Pid(child), WaitPidFlag::WEXITED), 231 Ok(WaitStatus::Exited(child, 0)), 232 ); 233 } 234 235 #[test] 236 fn test_wait_ptrace() { 237 require_capability!("test_wait_ptrace", CAP_SYS_PTRACE); 238 let _m = crate::FORK_MTX.lock(); 239 240 match unsafe { fork() }.expect("Error: Fork Failed") { 241 Child => ptrace_child(), 242 Parent { child } => ptrace_wait_parent(child), 243 } 244 } 245 246 #[test] 247 #[cfg(not(target_env = "uclibc"))] 248 fn test_waitid_ptrace() { 249 require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE); 250 let _m = crate::FORK_MTX.lock(); 251 252 match unsafe { fork() }.expect("Error: Fork Failed") { 253 Child => ptrace_child(), 254 Parent { child } => ptrace_waitid_parent(child), 255 } 256 } 257} 258