Skip to content

Commit e2d5ceb

Browse files
committed
auto merge of #5761 : Dretch/rust/murder-death-kill, r=thestinger
As proposed in issue #5632. I added some new stuff to libc - hopefully correctly. I only added a single signal constant (SIGKILL) because adding more seems complicated by differences between platforms - and since it is not required for issue #5632 then I figure that I can use a further pull request to flesh out the SIG* constants more.
2 parents d57aaae + 995d444 commit e2d5ceb

File tree

2 files changed

+136
-15
lines changed

2 files changed

+136
-15
lines changed

src/libcore/libc.rs

+19
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,8 @@ pub mod consts {
863863
pub static F_TEST : int = 3;
864864
pub static F_TLOCK : int = 2;
865865
pub static F_ULOCK : int = 0;
866+
pub static SIGKILL : int = 9;
867+
pub static SIGTERM : int = 15;
866868
}
867869
pub mod posix01 {
868870
}
@@ -930,6 +932,8 @@ pub mod consts {
930932
pub static F_TEST : int = 3;
931933
pub static F_TLOCK : int = 2;
932934
pub static F_ULOCK : int = 0;
935+
pub static SIGKILL : int = 9;
936+
pub static SIGTERM : int = 15;
933937
}
934938
pub mod posix01 {
935939
}
@@ -998,6 +1002,8 @@ pub mod consts {
9981002
pub static F_TEST : int = 3;
9991003
pub static F_TLOCK : int = 2;
10001004
pub static F_ULOCK : int = 0;
1005+
pub static SIGKILL : int = 9;
1006+
pub static SIGTERM : int = 15;
10011007
}
10021008
pub mod posix01 {
10031009
}
@@ -1482,6 +1488,17 @@ pub mod funcs {
14821488
-> ssize_t;
14831489
}
14841490
}
1491+
1492+
#[nolink]
1493+
#[abi = "cdecl"]
1494+
pub mod signal {
1495+
use libc::types::os::arch::c95::{c_int};
1496+
use libc::types::os::arch::posix88::{pid_t};
1497+
1498+
pub extern {
1499+
unsafe fn kill(pid: pid_t, sig: c_int) -> c_int;
1500+
}
1501+
}
14851502
}
14861503

14871504
#[cfg(target_os = "linux")]
@@ -1623,6 +1640,7 @@ pub mod funcs {
16231640
pub mod extra {
16241641

16251642
pub mod kernel32 {
1643+
use libc::types::os::arch::c95::{c_uint};
16261644
use libc::types::os::arch::extra::{BOOL, DWORD, HMODULE};
16271645
use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPTCH};
16281646
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES};
@@ -1663,6 +1681,7 @@ pub mod funcs {
16631681
findFileData: HANDLE)
16641682
-> BOOL;
16651683
unsafe fn FindClose(findFile: HANDLE) -> BOOL;
1684+
unsafe fn TerminateProcess(hProcess: HANDLE, uExitCode: c_uint) -> BOOL;
16661685
}
16671686
}
16681687

src/libcore/run.rs

+117-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -44,13 +44,13 @@ pub trait Program {
4444
/// Returns the process id of the program
4545
fn get_id(&mut self) -> pid_t;
4646

47-
/// Returns an io::writer that can be used to write to stdin
47+
/// Returns an io::Writer that can be used to write to stdin
4848
fn input(&mut self) -> @io::Writer;
4949

50-
/// Returns an io::reader that can be used to read from stdout
50+
/// Returns an io::Reader that can be used to read from stdout
5151
fn output(&mut self) -> @io::Reader;
5252

53-
/// Returns an io::reader that can be used to read from stderr
53+
/// Returns an io::Reader that can be used to read from stderr
5454
fn err(&mut self) -> @io::Reader;
5555

5656
/// Closes the handle to the child processes standard input
@@ -62,8 +62,23 @@ pub trait Program {
6262
*/
6363
fn finish(&mut self) -> int;
6464

65-
/// Closes open handles
65+
/**
66+
* Terminate the program, giving it a chance to clean itself up if
67+
* this is supported by the operating system.
68+
*
69+
* On Posix OSs SIGTERM will be sent to the process. On Win32
70+
* TerminateProcess(..) will be called.
71+
*/
6672
fn destroy(&mut self);
73+
74+
/**
75+
* Terminate the program as soon as possible without giving it a
76+
* chance to clean itself up.
77+
*
78+
* On Posix OSs SIGKILL will be sent to the process. On Win32
79+
* TerminateProcess(..) will be called.
80+
*/
81+
fn force_destroy(&mut self);
6782
}
6883

6984

@@ -172,6 +187,14 @@ fn with_dirp<T>(d: &Option<~str>,
172187
}
173188
}
174189

190+
/// helper function that closes non-NULL files and then makes them NULL
191+
priv unsafe fn fclose_and_null(f: &mut *libc::FILE) {
192+
if *f != 0 as *libc::FILE {
193+
libc::fclose(*f);
194+
*f = 0 as *libc::FILE;
195+
}
196+
}
197+
175198
/**
176199
* Spawns a process and waits for it to terminate
177200
*
@@ -192,9 +215,9 @@ pub fn run_program(prog: &str, args: &[~str]) -> int {
192215
}
193216

194217
/**
195-
* Spawns a process and returns a program
218+
* Spawns a process and returns a Program
196219
*
197-
* The returned value is a boxed class containing a <program> object that can
220+
* The returned value is a boxed class containing a <Program> object that can
198221
* be used for sending and receiving data over the standard file descriptors.
199222
* The class will ensure that file descriptors are closed properly.
200223
*
@@ -240,28 +263,59 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
240263
r.in_fd = invalid_fd;
241264
}
242265
}
266+
267+
fn close_repr_outputs(r: &mut ProgRepr) {
268+
unsafe {
269+
fclose_and_null(&mut r.out_file);
270+
fclose_and_null(&mut r.err_file);
271+
}
272+
}
273+
243274
fn finish_repr(r: &mut ProgRepr) -> int {
244275
if r.finished { return 0; }
245276
r.finished = true;
246277
close_repr_input(&mut *r);
247278
return waitpid(r.pid);
248279
}
249-
fn destroy_repr(r: &mut ProgRepr) {
250-
unsafe {
251-
finish_repr(&mut *r);
252-
libc::fclose(r.out_file);
253-
libc::fclose(r.err_file);
280+
281+
fn destroy_repr(r: &mut ProgRepr, force: bool) {
282+
killpid(r.pid, force);
283+
finish_repr(&mut *r);
284+
close_repr_outputs(&mut *r);
285+
286+
#[cfg(windows)]
287+
fn killpid(pid: pid_t, _force: bool) {
288+
unsafe {
289+
libc::funcs::extra::kernel32::TerminateProcess(
290+
cast::transmute(pid), 1);
291+
}
292+
}
293+
294+
#[cfg(unix)]
295+
fn killpid(pid: pid_t, force: bool) {
296+
297+
let signal = if force {
298+
libc::consts::os::posix88::SIGKILL
299+
} else {
300+
libc::consts::os::posix88::SIGTERM
301+
};
302+
303+
unsafe {
304+
libc::funcs::posix88::signal::kill(pid, signal as c_int);
305+
}
254306
}
255307
}
308+
256309
struct ProgRes {
257310
r: ProgRepr,
258311
}
259312

260313
impl Drop for ProgRes {
261314
fn finalize(&self) {
262315
unsafe {
263-
// FIXME #4943: This is bad.
264-
destroy_repr(cast::transmute(&self.r));
316+
// FIXME #4943: transmute is bad.
317+
finish_repr(cast::transmute(&self.r));
318+
close_repr_outputs(cast::transmute(&self.r));
265319
}
266320
}
267321
}
@@ -285,8 +339,10 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
285339
}
286340
fn close_input(&mut self) { close_repr_input(&mut self.r); }
287341
fn finish(&mut self) -> int { finish_repr(&mut self.r) }
288-
fn destroy(&mut self) { destroy_repr(&mut self.r); }
342+
fn destroy(&mut self) { destroy_repr(&mut self.r, false); }
343+
fn force_destroy(&mut self) { destroy_repr(&mut self.r, true); }
289344
}
345+
290346
let mut repr = ProgRepr {
291347
pid: pid,
292348
in_fd: pipe_input.out,
@@ -458,8 +514,10 @@ pub fn waitpid(pid: pid_t) -> int {
458514

459515
#[cfg(test)]
460516
mod tests {
517+
use libc;
461518
use option::None;
462519
use os;
520+
use path::Path;
463521
use run::{readclose, writeclose};
464522
use run;
465523

@@ -507,6 +565,50 @@ mod tests {
507565
assert!(status == 1);
508566
}
509567

568+
#[test]
569+
pub fn test_destroy_once() {
570+
let mut p = run::start_program("echo", []);
571+
p.destroy(); // this shouldn't crash (and nor should the destructor)
572+
}
573+
574+
#[test]
575+
pub fn test_destroy_twice() {
576+
let mut p = run::start_program("echo", []);
577+
p.destroy(); // this shouldnt crash...
578+
p.destroy(); // ...and nor should this (and nor should the destructor)
579+
}
580+
581+
#[cfg(unix)] // there is no way to sleep on windows from inside libcore...
582+
pub fn test_destroy_actually_kills(force: bool) {
583+
let path = Path(fmt!("test/core-run-test-destroy-actually-kills-%?.tmp", force));
584+
585+
os::remove_file(&path);
586+
587+
let cmd = fmt!("sleep 5 && echo MurderDeathKill > %s", path.to_str());
588+
let mut p = run::start_program("sh", [~"-c", cmd]);
589+
590+
p.destroy(); // destroy the program before it has a chance to echo its message
591+
592+
unsafe {
593+
// wait to ensure the program is really destroyed and not just waiting itself
594+
libc::sleep(10);
595+
}
596+
597+
// the program should not have had chance to echo its message
598+
assert!(!path.exists());
599+
}
600+
601+
#[test]
602+
#[cfg(unix)]
603+
pub fn test_unforced_destroy_actually_kills() {
604+
test_destroy_actually_kills(false);
605+
}
606+
607+
#[test]
608+
#[cfg(unix)]
609+
pub fn test_forced_destroy_actually_kills() {
610+
test_destroy_actually_kills(true);
611+
}
510612
}
511613

512614
// Local Variables:

0 commit comments

Comments
 (0)