@@ -25,6 +25,9 @@ pub fn setup() -> Option<Setup> {
2525
2626#[ cfg( unix) ]
2727mod imp {
28+ use std:: env;
29+ use libc;
30+
2831 pub type Setup = ( ) ;
2932
3033 pub unsafe fn setup ( ) -> Option < ( ) > {
@@ -36,19 +39,23 @@ mod imp {
3639mod imp {
3740 extern crate winapi;
3841
39- use std:: ffi:: OsString ;
4042 use std:: io;
4143 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 ;
4552
4653 pub struct Setup {
4754 job : Handle ,
4855 }
4956
5057 pub struct Handle {
51- inner : ntdef :: HANDLE ,
58+ inner : HANDLE ,
5259 }
5360
5461 fn last_err ( ) -> io:: Error {
@@ -65,67 +72,53 @@ mod imp {
6572 // use job objects, so we instead just ignore errors and assume that
6673 // we're otherwise part of someone else's job object in this case.
6774
68- let job = jobapi2 :: CreateJobObjectW ( 0 as * mut _ , 0 as * const _ ) ;
75+ let job = CreateJobObjectW ( ptr :: null_mut ( ) , ptr :: null ( ) ) ;
6976 if job. is_null ( ) {
7077 return None ;
7178 }
7279 let job = Handle { inner : job } ;
7380
7481 // Indicate that when all handles to the job object are gone that all
7582 // 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
7784 // 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 ;
7986 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 (
8289 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 ,
8693 ) ;
8794 if r == 0 {
8895 return None ;
8996 }
9097
9198 // Assign our process to this job object, meaning that our children will
9299 // 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) ;
95102 if r == 0 {
96103 return None ;
97104 }
98105
99- Some ( Setup { job : job } )
106+ Some ( Setup { job } )
100107 }
101108
102109 impl Drop for Setup {
103110 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.
117114 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 ;
123116 info = mem:: zeroed ( ) ;
124- let r = jobapi2 :: SetInformationJobObject (
117+ let r = SetInformationJobObject (
125118 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 ,
129122 ) ;
130123 if r == 0 {
131124 info ! ( "failed to configure job object to defaults: {}" , last_err( ) ) ;
@@ -134,126 +127,10 @@ mod imp {
134127 }
135128 }
136129
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-
253130 impl Drop for Handle {
254131 fn drop ( & mut self ) {
255132 unsafe {
256- handleapi :: CloseHandle ( self . inner ) ;
133+ CloseHandle ( self . inner ) ;
257134 }
258135 }
259136 }
0 commit comments