@@ -25,6 +25,9 @@ pub fn setup() -> Option<Setup> {
25
25
26
26
#[ cfg( unix) ]
27
27
mod imp {
28
+ use std:: env;
29
+ use libc;
30
+
28
31
pub type Setup = ( ) ;
29
32
30
33
pub unsafe fn setup ( ) -> Option < ( ) > {
@@ -36,19 +39,23 @@ mod imp {
36
39
mod imp {
37
40
extern crate winapi;
38
41
39
- use std:: ffi:: OsString ;
40
42
use std:: io;
41
43
use std:: mem;
42
- use std:: os:: windows:: prelude:: * ;
43
- use winapi:: shared:: * ;
44
- use winapi:: um:: * ;
44
+ use std:: ptr;
45
+
46
+ use self :: winapi:: shared:: minwindef:: * ;
47
+ use self :: winapi:: um:: handleapi:: * ;
48
+ use self :: winapi:: um:: jobapi2:: * ;
49
+ use self :: winapi:: um:: processthreadsapi:: * ;
50
+ use self :: winapi:: um:: winnt:: * ;
51
+ use self :: winapi:: um:: winnt:: HANDLE ;
45
52
46
53
pub struct Setup {
47
54
job : Handle ,
48
55
}
49
56
50
57
pub struct Handle {
51
- inner : ntdef :: HANDLE ,
58
+ inner : HANDLE ,
52
59
}
53
60
54
61
fn last_err ( ) -> io:: Error {
@@ -65,67 +72,53 @@ mod imp {
65
72
// use job objects, so we instead just ignore errors and assume that
66
73
// we're otherwise part of someone else's job object in this case.
67
74
68
- let job = jobapi2 :: CreateJobObjectW ( 0 as * mut _ , 0 as * const _ ) ;
75
+ let job = CreateJobObjectW ( ptr :: null_mut ( ) , ptr :: null ( ) ) ;
69
76
if job. is_null ( ) {
70
77
return None ;
71
78
}
72
79
let job = Handle { inner : job } ;
73
80
74
81
// Indicate that when all handles to the job object are gone that all
75
82
// process in the object should be killed. Note that this includes our
76
- // entire process tree by default because we've added ourselves and and
83
+ // entire process tree by default because we've added ourselves and
77
84
// our children will reside in the job once we spawn a process.
78
- let mut info: winnt :: JOBOBJECT_EXTENDED_LIMIT_INFORMATION ;
85
+ let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION ;
79
86
info = mem:: zeroed ( ) ;
80
- info. BasicLimitInformation . LimitFlags = winnt :: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ;
81
- let r = jobapi2 :: SetInformationJobObject (
87
+ info. BasicLimitInformation . LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ;
88
+ let r = SetInformationJobObject (
82
89
job. inner ,
83
- winnt :: JobObjectExtendedLimitInformation ,
84
- & mut info as * mut _ as minwindef :: LPVOID ,
85
- mem:: size_of_val ( & info) as minwindef :: DWORD ,
90
+ JobObjectExtendedLimitInformation ,
91
+ & mut info as * mut _ as LPVOID ,
92
+ mem:: size_of_val ( & info) as DWORD ,
86
93
) ;
87
94
if r == 0 {
88
95
return None ;
89
96
}
90
97
91
98
// Assign our process to this job object, meaning that our children will
92
99
// now live or die based on our existence.
93
- let me = processthreadsapi :: GetCurrentProcess ( ) ;
94
- let r = jobapi2 :: AssignProcessToJobObject ( job. inner , me) ;
100
+ let me = GetCurrentProcess ( ) ;
101
+ let r = AssignProcessToJobObject ( job. inner , me) ;
95
102
if r == 0 {
96
103
return None ;
97
104
}
98
105
99
- Some ( Setup { job : job } )
106
+ Some ( Setup { job } )
100
107
}
101
108
102
109
impl Drop for Setup {
103
110
fn drop ( & mut self ) {
104
- // This is a litte subtle. By default if we are terminated then all
105
- // processes in our job object are terminated as well, but we
106
- // intentionally want to whitelist some processes to outlive our job
107
- // object (see below).
108
- //
109
- // To allow for this, we manually kill processes instead of letting
110
- // the job object kill them for us. We do this in a loop to handle
111
- // processes spawning other processes.
112
- //
113
- // Finally once this is all done we know that the only remaining
114
- // ones are ourselves and the whitelisted processes. The destructor
115
- // here then configures our job object to *not* kill everything on
116
- // close, then closes the job object.
111
+ // On normal exits (not ctrl-c), we don't want to kill any child
112
+ // processes. The destructor here configures our job object to
113
+ // *not* kill everything on close, then closes the job object.
117
114
unsafe {
118
- while self . kill_remaining ( ) {
119
- info ! ( "killed some, going for more" ) ;
120
- }
121
-
122
- let mut info: winnt:: JOBOBJECT_EXTENDED_LIMIT_INFORMATION ;
115
+ let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION ;
123
116
info = mem:: zeroed ( ) ;
124
- let r = jobapi2 :: SetInformationJobObject (
117
+ let r = SetInformationJobObject (
125
118
self . job . inner ,
126
- winnt :: JobObjectExtendedLimitInformation ,
127
- & mut info as * mut _ as minwindef :: LPVOID ,
128
- mem:: size_of_val ( & info) as minwindef :: DWORD ,
119
+ JobObjectExtendedLimitInformation ,
120
+ & mut info as * mut _ as LPVOID ,
121
+ mem:: size_of_val ( & info) as DWORD ,
129
122
) ;
130
123
if r == 0 {
131
124
info ! ( "failed to configure job object to defaults: {}" , last_err( ) ) ;
@@ -134,126 +127,10 @@ mod imp {
134
127
}
135
128
}
136
129
137
- impl Setup {
138
- unsafe fn kill_remaining ( & mut self ) -> bool {
139
- #[ repr( C ) ]
140
- struct Jobs {
141
- header : winnt:: JOBOBJECT_BASIC_PROCESS_ID_LIST ,
142
- list : [ basetsd:: ULONG_PTR ; 1024 ] ,
143
- }
144
-
145
- let mut jobs: Jobs = mem:: zeroed ( ) ;
146
- let r = jobapi2:: QueryInformationJobObject (
147
- self . job . inner ,
148
- winnt:: JobObjectBasicProcessIdList ,
149
- & mut jobs as * mut _ as minwindef:: LPVOID ,
150
- mem:: size_of_val ( & jobs) as minwindef:: DWORD ,
151
- 0 as * mut _ ,
152
- ) ;
153
- if r == 0 {
154
- info ! ( "failed to query job object: {}" , last_err( ) ) ;
155
- return false ;
156
- }
157
-
158
- let mut killed = false ;
159
- let list = & jobs. list [ ..jobs. header . NumberOfProcessIdsInList as usize ] ;
160
- assert ! ( list. len( ) > 0 ) ;
161
-
162
- let list = list. iter ( )
163
- . filter ( |& & id| {
164
- // let's not kill ourselves
165
- id as minwindef:: DWORD != processthreadsapi:: GetCurrentProcessId ( )
166
- } )
167
- . filter_map ( |& id| {
168
- // Open the process with the necessary rights, and if this
169
- // fails then we probably raced with the process exiting so we
170
- // ignore the problem.
171
- let flags = winnt:: PROCESS_QUERY_INFORMATION | winnt:: PROCESS_TERMINATE
172
- | winnt:: SYNCHRONIZE ;
173
- let p = processthreadsapi:: OpenProcess (
174
- flags,
175
- minwindef:: FALSE ,
176
- id as minwindef:: DWORD ,
177
- ) ;
178
- if p. is_null ( ) {
179
- None
180
- } else {
181
- Some ( Handle { inner : p } )
182
- }
183
- } )
184
- . filter ( |p| {
185
- // Test if this process was actually in the job object or not.
186
- // If it's not then we likely raced with something else
187
- // recycling this PID, so we just skip this step.
188
- let mut res = 0 ;
189
- let r = jobapi:: IsProcessInJob ( p. inner , self . job . inner , & mut res) ;
190
- if r == 0 {
191
- info ! ( "failed to test is process in job: {}" , last_err( ) ) ;
192
- return false ;
193
- }
194
- res == minwindef:: TRUE
195
- } ) ;
196
-
197
- for p in list {
198
- // Load the file which this process was spawned from. We then
199
- // later use this for identification purposes.
200
- let mut buf = [ 0 ; 1024 ] ;
201
- let r = psapi:: GetProcessImageFileNameW (
202
- p. inner ,
203
- buf. as_mut_ptr ( ) ,
204
- buf. len ( ) as minwindef:: DWORD ,
205
- ) ;
206
- if r == 0 {
207
- info ! ( "failed to get image name: {}" , last_err( ) ) ;
208
- continue ;
209
- }
210
- let s = OsString :: from_wide ( & buf[ ..r as usize ] ) ;
211
- info ! ( "found remaining: {:?}" , s) ;
212
-
213
- // And here's where we find the whole purpose for this
214
- // function! Currently, our only whitelisted process is
215
- // `mspdbsrv.exe`, and more details about that can be found
216
- // here:
217
- //
218
- // https://github.com/rust-lang/rust/issues/33145
219
- //
220
- // The gist of it is that all builds on one machine use the
221
- // same `mspdbsrv.exe` instance. If we were to kill this
222
- // instance then we could erroneously cause other builds to
223
- // fail.
224
- if let Some ( s) = s. to_str ( ) {
225
- if s. contains ( "mspdbsrv" ) {
226
- info ! ( "\t oops, this is mspdbsrv" ) ;
227
- continue ;
228
- }
229
- }
230
-
231
- // Ok, this isn't mspdbsrv, let's kill the process. After we
232
- // kill it we wait on it to ensure that the next time around in
233
- // this function we're not going to see it again.
234
- let r = processthreadsapi:: TerminateProcess ( p. inner , 1 ) ;
235
- if r == 0 {
236
- info ! ( "\t failed to kill subprocess: {}" , last_err( ) ) ;
237
- info ! ( "\t assuming subprocess is dead..." ) ;
238
- } else {
239
- info ! ( "\t terminated subprocess" ) ;
240
- }
241
- let r = synchapi:: WaitForSingleObject ( p. inner , winbase:: INFINITE ) ;
242
- if r != 0 {
243
- info ! ( "failed to wait for process to die: {}" , last_err( ) ) ;
244
- return false ;
245
- }
246
- killed = true ;
247
- }
248
-
249
- return killed;
250
- }
251
- }
252
-
253
130
impl Drop for Handle {
254
131
fn drop ( & mut self ) {
255
132
unsafe {
256
- handleapi :: CloseHandle ( self . inner ) ;
133
+ CloseHandle ( self . inner ) ;
257
134
}
258
135
}
259
136
}
0 commit comments