@@ -23,6 +23,35 @@ fn test_wait_signal() {
23
23
}
24
24
}
25
25
26
+ #[ test]
27
+ #[ cfg( any(
28
+ target_os = "android" ,
29
+ target_os = "freebsd" ,
30
+ target_os = "haiku" ,
31
+ target_os = "ios" ,
32
+ target_os = "linux" ,
33
+ target_os = "macos" ,
34
+ target_os = "netbsd"
35
+ ) ) ]
36
+ fn test_waitid_signal ( ) {
37
+ let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
38
+
39
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
40
+ match unsafe { fork ( ) } . expect ( "Error: Fork Failed" ) {
41
+ Child => {
42
+ pause ( ) ;
43
+ unsafe { _exit ( 123 ) }
44
+ } ,
45
+ Parent { child } => {
46
+ kill ( child, Some ( SIGKILL ) ) . expect ( "Error: Kill Failed" ) ;
47
+ assert_eq ! (
48
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
49
+ Ok ( WaitStatus :: Signaled ( child, SIGKILL , false ) ) ,
50
+ ) ;
51
+ } ,
52
+ }
53
+ }
54
+
26
55
#[ test]
27
56
fn test_wait_exit ( ) {
28
57
let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
@@ -35,6 +64,21 @@ fn test_wait_exit() {
35
64
} ,
36
65
}
37
66
}
67
+ #[ test]
68
+ fn test_waitid_exit ( ) {
69
+ let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
70
+
71
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
72
+ match unsafe { fork ( ) } . expect ( "Error: Fork Failed" ) {
73
+ Child => unsafe { _exit ( 12 ) ; } ,
74
+ Parent { child } => {
75
+ assert_eq ! (
76
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
77
+ Ok ( WaitStatus :: Exited ( child, 12 ) ) ,
78
+ ) ;
79
+ }
80
+ }
81
+ }
38
82
39
83
#[ test]
40
84
fn test_waitstatus_from_raw ( ) {
@@ -57,6 +101,19 @@ fn test_waitstatus_pid() {
57
101
}
58
102
}
59
103
104
+ #[ test]
105
+ fn test_waitid_pid ( ) {
106
+ let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
107
+
108
+ match unsafe { fork ( ) } . unwrap ( ) {
109
+ Child => unsafe { _exit ( 0 ) } ,
110
+ Parent { child } => {
111
+ let status = waitid ( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) . unwrap ( ) ;
112
+ assert_eq ! ( status. pid( ) , Some ( child) ) ;
113
+ }
114
+ }
115
+ }
116
+
60
117
#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
61
118
// FIXME: qemu-user doesn't implement ptrace on most arches
62
119
#[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
@@ -77,7 +134,7 @@ mod ptrace {
77
134
unsafe { _exit ( 0 ) }
78
135
}
79
136
80
- fn ptrace_parent ( child : Pid ) {
137
+ fn ptrace_wait_parent ( child : Pid ) {
81
138
// Wait for the raised SIGTRAP
82
139
assert_eq ! ( waitpid( child, None ) , Ok ( WaitStatus :: Stopped ( child, SIGTRAP ) ) ) ;
83
140
// We want to test a syscall stop and a PTRACE_EVENT stop
@@ -94,14 +151,57 @@ mod ptrace {
94
151
assert_eq ! ( waitpid( child, None ) , Ok ( WaitStatus :: Exited ( child, 0 ) ) ) ;
95
152
}
96
153
154
+ fn ptrace_waitid_parent ( child : Pid ) {
155
+ // Wait for the raised SIGTRAP
156
+ //
157
+ // Unlike waitpid(), waitid() can distinguish trap events from regular
158
+ // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
159
+ assert_eq ! (
160
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
161
+ Ok ( WaitStatus :: PtraceEvent ( child, SIGTRAP , 0 ) ) ,
162
+ ) ;
163
+ // We want to test a syscall stop and a PTRACE_EVENT stop
164
+ assert ! ( ptrace:: setoptions( child, Options :: PTRACE_O_TRACESYSGOOD | Options :: PTRACE_O_TRACEEXIT ) . is_ok( ) ) ;
165
+
166
+ // First, stop on the next system call, which will be exit()
167
+ assert ! ( ptrace:: syscall( child, None ) . is_ok( ) ) ;
168
+ assert_eq ! (
169
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
170
+ Ok ( WaitStatus :: PtraceSyscall ( child) ) ,
171
+ ) ;
172
+ // Then get the ptrace event for the process exiting
173
+ assert ! ( ptrace:: cont( child, None ) . is_ok( ) ) ;
174
+ assert_eq ! (
175
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
176
+ Ok ( WaitStatus :: PtraceEvent ( child, SIGTRAP , Event :: PTRACE_EVENT_EXIT as i32 ) ) ,
177
+ ) ;
178
+ // Finally get the normal wait() result, now that the process has exited
179
+ assert ! ( ptrace:: cont( child, None ) . is_ok( ) ) ;
180
+ assert_eq ! (
181
+ waitid( Id :: Pid ( child) , WaitPidFlag :: WEXITED ) ,
182
+ Ok ( WaitStatus :: Exited ( child, 0 ) ) ,
183
+ ) ;
184
+ }
185
+
97
186
#[ test]
98
187
fn test_wait_ptrace ( ) {
99
188
require_capability ! ( "test_wait_ptrace" , CAP_SYS_PTRACE ) ;
100
189
let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
101
190
102
191
match unsafe { fork ( ) } . expect ( "Error: Fork Failed" ) {
103
192
Child => ptrace_child ( ) ,
104
- Parent { child } => ptrace_parent ( child) ,
193
+ Parent { child } => ptrace_wait_parent ( child) ,
194
+ }
195
+ }
196
+
197
+ #[ test]
198
+ fn test_waitid_ptrace ( ) {
199
+ require_capability ! ( "test_waitid_ptrace" , CAP_SYS_PTRACE ) ;
200
+ let _m = crate :: FORK_MTX . lock ( ) . expect ( "Mutex got poisoned by another test" ) ;
201
+
202
+ match unsafe { fork ( ) } . expect ( "Error: Fork Failed" ) {
203
+ Child => ptrace_child ( ) ,
204
+ Parent { child } => ptrace_waitid_parent ( child) ,
105
205
}
106
206
}
107
207
}
0 commit comments