mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Add Deno.kill(pid, signo) and process.kill(signo) (Unix only) (#2177)
This commit is contained in:
parent
9dfebbc949
commit
1d4b92ac85
11 changed files with 247 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -225,6 +225,7 @@ dependencies = [
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -42,6 +42,9 @@ main_extern = [
|
||||||
if (is_win) {
|
if (is_win) {
|
||||||
main_extern += [ "$rust_build:winapi" ]
|
main_extern += [ "$rust_build:winapi" ]
|
||||||
}
|
}
|
||||||
|
if (is_posix) {
|
||||||
|
main_extern += [ "$rust_build:nix" ]
|
||||||
|
}
|
||||||
|
|
||||||
ts_sources = [
|
ts_sources = [
|
||||||
"../js/assets.ts",
|
"../js/assets.ts",
|
||||||
|
|
|
@ -50,3 +50,6 @@ url = "1.7.2"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.3.7"
|
winapi = "0.3.7"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
nix = "0.11.0"
|
||||||
|
|
|
@ -4,6 +4,8 @@ pub use crate::msg::ErrorKind;
|
||||||
use crate::resolve_addr::ResolveAddrError;
|
use crate::resolve_addr::ResolveAddrError;
|
||||||
use deno::JSError;
|
use deno::JSError;
|
||||||
use hyper;
|
use hyper;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use nix::{errno::Errno, Error as UnixError};
|
||||||
use std;
|
use std;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
@ -168,6 +170,32 @@ impl From<ResolveAddrError> for DenoError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl From<UnixError> for DenoError {
|
||||||
|
fn from(e: UnixError) -> Self {
|
||||||
|
match e {
|
||||||
|
UnixError::Sys(Errno::EPERM) => Self {
|
||||||
|
repr: Repr::Simple(
|
||||||
|
ErrorKind::PermissionDenied,
|
||||||
|
Errno::EPERM.desc().to_owned(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
UnixError::Sys(Errno::EINVAL) => Self {
|
||||||
|
repr: Repr::Simple(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
Errno::EINVAL.desc().to_owned(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
UnixError::Sys(err) => Self {
|
||||||
|
repr: Repr::Simple(ErrorKind::UnixError, err.desc().to_owned()),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
repr: Repr::Simple(ErrorKind::Other, format!("{}", e)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bad_resource() -> DenoError {
|
pub fn bad_resource() -> DenoError {
|
||||||
new(ErrorKind::BadResource, String::from("bad resource id"))
|
new(ErrorKind::BadResource, String::from("bad resource id"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ extern crate futures;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate deno;
|
extern crate deno;
|
||||||
|
#[cfg(unix)]
|
||||||
|
extern crate nix;
|
||||||
|
|
||||||
mod ansi;
|
mod ansi;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
|
@ -27,6 +29,7 @@ pub mod permissions;
|
||||||
mod repl;
|
mod repl;
|
||||||
pub mod resolve_addr;
|
pub mod resolve_addr;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
mod signal;
|
||||||
mod startup_data;
|
mod startup_data;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
mod tokio_util;
|
mod tokio_util;
|
||||||
|
|
|
@ -21,6 +21,7 @@ union Any {
|
||||||
GlobalTimerStop,
|
GlobalTimerStop,
|
||||||
IsTTY,
|
IsTTY,
|
||||||
IsTTYRes,
|
IsTTYRes,
|
||||||
|
Kill,
|
||||||
Link,
|
Link,
|
||||||
Listen,
|
Listen,
|
||||||
ListenRes,
|
ListenRes,
|
||||||
|
@ -129,7 +130,8 @@ enum ErrorKind: byte {
|
||||||
InvalidUri,
|
InvalidUri,
|
||||||
InvalidSeekMode,
|
InvalidSeekMode,
|
||||||
OpNotAvaiable,
|
OpNotAvaiable,
|
||||||
WorkerInitFailed
|
WorkerInitFailed,
|
||||||
|
UnixError,
|
||||||
}
|
}
|
||||||
|
|
||||||
table Cwd {}
|
table Cwd {}
|
||||||
|
@ -453,6 +455,11 @@ table Close {
|
||||||
rid: uint32;
|
rid: uint32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table Kill {
|
||||||
|
pid: int32;
|
||||||
|
signo: int32;
|
||||||
|
}
|
||||||
|
|
||||||
table Shutdown {
|
table Shutdown {
|
||||||
rid: uint32;
|
rid: uint32;
|
||||||
how: uint;
|
how: uint;
|
||||||
|
|
17
cli/ops.rs
17
cli/ops.rs
|
@ -14,6 +14,7 @@ use crate::resolve_addr::resolve_addr;
|
||||||
use crate::resources;
|
use crate::resources;
|
||||||
use crate::resources::table_entries;
|
use crate::resources::table_entries;
|
||||||
use crate::resources::Resource;
|
use crate::resources::Resource;
|
||||||
|
use crate::signal::kill;
|
||||||
use crate::startup_data;
|
use crate::startup_data;
|
||||||
use crate::state::ThreadSafeState;
|
use crate::state::ThreadSafeState;
|
||||||
use crate::tokio_util;
|
use crate::tokio_util;
|
||||||
|
@ -171,6 +172,7 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<OpCreator> {
|
||||||
msg::Any::GlobalTimer => Some(op_global_timer),
|
msg::Any::GlobalTimer => Some(op_global_timer),
|
||||||
msg::Any::GlobalTimerStop => Some(op_global_timer_stop),
|
msg::Any::GlobalTimerStop => Some(op_global_timer_stop),
|
||||||
msg::Any::IsTTY => Some(op_is_tty),
|
msg::Any::IsTTY => Some(op_is_tty),
|
||||||
|
msg::Any::Kill => Some(op_kill),
|
||||||
msg::Any::Link => Some(op_link),
|
msg::Any::Link => Some(op_link),
|
||||||
msg::Any::Listen => Some(op_listen),
|
msg::Any::Listen => Some(op_listen),
|
||||||
msg::Any::MakeTempDir => Some(op_make_temp_dir),
|
msg::Any::MakeTempDir => Some(op_make_temp_dir),
|
||||||
|
@ -906,6 +908,21 @@ fn op_close(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn op_kill(
|
||||||
|
_state: &ThreadSafeState,
|
||||||
|
base: &msg::Base<'_>,
|
||||||
|
data: deno_buf,
|
||||||
|
) -> Box<OpWithError> {
|
||||||
|
assert_eq!(data.len(), 0);
|
||||||
|
let inner = base.inner_as_kill().unwrap();
|
||||||
|
let pid = inner.pid();
|
||||||
|
let signo = inner.signo();
|
||||||
|
match kill(pid, signo) {
|
||||||
|
Ok(_) => ok_future(empty_buf()),
|
||||||
|
Err(e) => odd_future(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn op_shutdown(
|
fn op_shutdown(
|
||||||
_state: &ThreadSafeState,
|
_state: &ThreadSafeState,
|
||||||
base: &msg::Base<'_>,
|
base: &msg::Base<'_>,
|
||||||
|
|
20
cli/signal.rs
Normal file
20
cli/signal.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#[cfg(unix)]
|
||||||
|
use nix::sys::signal::{kill as unix_kill, Signal};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use nix::unistd::Pid;
|
||||||
|
|
||||||
|
use crate::errors::DenoResult;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn kill(pid: i32, signo: i32) -> DenoResult<()> {
|
||||||
|
use crate::errors::DenoError;
|
||||||
|
let sig = Signal::from_c_int(signo)?;
|
||||||
|
unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(DenoError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn kill(_pid: i32, _signal: i32) -> DenoResult<()> {
|
||||||
|
// NOOP
|
||||||
|
// TODO: implement this for windows
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -68,7 +68,14 @@ export { FileInfo } from "./file_info";
|
||||||
export { connect, dial, listen, Listener, Conn } from "./net";
|
export { connect, dial, listen, Listener, Conn } from "./net";
|
||||||
export { metrics, Metrics } from "./metrics";
|
export { metrics, Metrics } from "./metrics";
|
||||||
export { resources } from "./resources";
|
export { resources } from "./resources";
|
||||||
export { run, RunOptions, Process, ProcessStatus } from "./process";
|
export {
|
||||||
|
kill,
|
||||||
|
run,
|
||||||
|
RunOptions,
|
||||||
|
Process,
|
||||||
|
ProcessStatus,
|
||||||
|
Signal
|
||||||
|
} from "./process";
|
||||||
export { inspect } from "./console";
|
export { inspect } from "./console";
|
||||||
export { build, platform, OperatingSystem, Arch } from "./build";
|
export { build, platform, OperatingSystem, Arch } from "./build";
|
||||||
export { version } from "./version";
|
export { version } from "./version";
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { File, close } from "./files";
|
||||||
import { ReadCloser, WriteCloser } from "./io";
|
import { ReadCloser, WriteCloser } from "./io";
|
||||||
import { readAll } from "./buffer";
|
import { readAll } from "./buffer";
|
||||||
import { assert, unreachable } from "./util";
|
import { assert, unreachable } from "./util";
|
||||||
|
import { platform } from "./build";
|
||||||
|
|
||||||
/** How to handle subprocess stdio.
|
/** How to handle subprocess stdio.
|
||||||
*
|
*
|
||||||
|
@ -51,6 +52,16 @@ async function runStatus(rid: number): Promise<ProcessStatus> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Send a signal to process under given PID. Unix only at this moment.
|
||||||
|
* If pid is negative, the signal will be sent to the process group identified
|
||||||
|
* by -pid.
|
||||||
|
*/
|
||||||
|
export function kill(pid: number, signo: number): void {
|
||||||
|
const builder = flatbuffers.createBuilder();
|
||||||
|
const inner = msg.Kill.createKill(builder, pid, signo);
|
||||||
|
dispatch.sendSync(builder, msg.Any.Kill, inner);
|
||||||
|
}
|
||||||
|
|
||||||
export class Process {
|
export class Process {
|
||||||
readonly rid: number;
|
readonly rid: number;
|
||||||
readonly pid: number;
|
readonly pid: number;
|
||||||
|
@ -113,6 +124,10 @@ export class Process {
|
||||||
close(): void {
|
close(): void {
|
||||||
close(this.rid);
|
close(this.rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kill(signo: number): void {
|
||||||
|
kill(this.pid, signo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessStatus {
|
export interface ProcessStatus {
|
||||||
|
@ -179,3 +194,77 @@ export function run(opt: RunOptions): Process {
|
||||||
|
|
||||||
return new Process(res);
|
return new Process(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From `kill -l`
|
||||||
|
enum LinuxSignal {
|
||||||
|
SIGHUP = 1,
|
||||||
|
SIGINT = 2,
|
||||||
|
SIGQUIT = 3,
|
||||||
|
SIGILL = 4,
|
||||||
|
SIGTRAP = 5,
|
||||||
|
SIGABRT = 6,
|
||||||
|
SIGBUS = 7,
|
||||||
|
SIGFPE = 8,
|
||||||
|
SIGKILL = 9,
|
||||||
|
SIGUSR1 = 10,
|
||||||
|
SIGSEGV = 11,
|
||||||
|
SIGUSR2 = 12,
|
||||||
|
SIGPIPE = 13,
|
||||||
|
SIGALRM = 14,
|
||||||
|
SIGTERM = 15,
|
||||||
|
SIGSTKFLT = 16,
|
||||||
|
SIGCHLD = 17,
|
||||||
|
SIGCONT = 18,
|
||||||
|
SIGSTOP = 19,
|
||||||
|
SIGTSTP = 20,
|
||||||
|
SIGTTIN = 21,
|
||||||
|
SIGTTOU = 22,
|
||||||
|
SIGURG = 23,
|
||||||
|
SIGXCPU = 24,
|
||||||
|
SIGXFSZ = 25,
|
||||||
|
SIGVTALRM = 26,
|
||||||
|
SIGPROF = 27,
|
||||||
|
SIGWINCH = 28,
|
||||||
|
SIGIO = 29,
|
||||||
|
SIGPWR = 30,
|
||||||
|
SIGSYS = 31
|
||||||
|
}
|
||||||
|
|
||||||
|
// From `kill -l`
|
||||||
|
enum MacOSSignal {
|
||||||
|
SIGHUP = 1,
|
||||||
|
SIGINT = 2,
|
||||||
|
SIGQUIT = 3,
|
||||||
|
SIGILL = 4,
|
||||||
|
SIGTRAP = 5,
|
||||||
|
SIGABRT = 6,
|
||||||
|
SIGEMT = 7,
|
||||||
|
SIGFPE = 8,
|
||||||
|
SIGKILL = 9,
|
||||||
|
SIGBUS = 10,
|
||||||
|
SIGSEGV = 11,
|
||||||
|
SIGSYS = 12,
|
||||||
|
SIGPIPE = 13,
|
||||||
|
SIGALRM = 14,
|
||||||
|
SIGTERM = 15,
|
||||||
|
SIGURG = 16,
|
||||||
|
SIGSTOP = 17,
|
||||||
|
SIGTSTP = 18,
|
||||||
|
SIGCONT = 19,
|
||||||
|
SIGCHLD = 20,
|
||||||
|
SIGTTIN = 21,
|
||||||
|
SIGTTOU = 22,
|
||||||
|
SIGIO = 23,
|
||||||
|
SIGXCPU = 24,
|
||||||
|
SIGXFSZ = 25,
|
||||||
|
SIGVTALRM = 26,
|
||||||
|
SIGPROF = 27,
|
||||||
|
SIGWINCH = 28,
|
||||||
|
SIGINFO = 29,
|
||||||
|
SIGUSR1 = 30,
|
||||||
|
SIGUSR2 = 31
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Signals numbers. This is platform dependent.
|
||||||
|
*/
|
||||||
|
export const Signal = platform.os === "mac" ? MacOSSignal : LinuxSignal;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import { test, testPerm, assert, assertEquals } from "./test_util.ts";
|
import { test, testPerm, assert, assertEquals } from "./test_util.ts";
|
||||||
const { run, DenoError, ErrorKind } = Deno;
|
const { kill, run, DenoError, ErrorKind } = Deno;
|
||||||
|
|
||||||
test(function runPermissions(): void {
|
test(function runPermissions(): void {
|
||||||
let caughtError = false;
|
let caughtError = false;
|
||||||
|
@ -223,3 +223,69 @@ testPerm({ run: true }, async function runEnv(): Promise<void> {
|
||||||
assertEquals(s, "01234567");
|
assertEquals(s, "01234567");
|
||||||
p.close();
|
p.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testPerm({ run: true }, async function runClose(): Promise<void> {
|
||||||
|
const p = run({
|
||||||
|
args: [
|
||||||
|
"python",
|
||||||
|
"-c",
|
||||||
|
"from time import sleep; import sys; sleep(10000); sys.stderr.write('error')"
|
||||||
|
],
|
||||||
|
stderr: "piped"
|
||||||
|
});
|
||||||
|
assert(!p.stdin);
|
||||||
|
assert(!p.stdout);
|
||||||
|
|
||||||
|
p.close();
|
||||||
|
|
||||||
|
const data = new Uint8Array(10);
|
||||||
|
let r = await p.stderr.read(data);
|
||||||
|
assertEquals(r.nread, 0);
|
||||||
|
assertEquals(r.eof, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function signalNumbers(): void {
|
||||||
|
if (Deno.platform.os === "mac") {
|
||||||
|
assertEquals(Deno.Signal.SIGSTOP, 17);
|
||||||
|
} else if (Deno.platform.os === "linux") {
|
||||||
|
assertEquals(Deno.Signal.SIGSTOP, 19);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ignore signal tests on windows for now...
|
||||||
|
if (Deno.platform.os !== "win") {
|
||||||
|
testPerm({ run: true }, async function killSuccess(): Promise<void> {
|
||||||
|
const p = run({
|
||||||
|
args: ["python", "-c", "from time import sleep; sleep(10000)"]
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEquals(Deno.Signal.SIGINT, 2);
|
||||||
|
kill(p.pid, Deno.Signal.SIGINT);
|
||||||
|
const status = await p.status();
|
||||||
|
|
||||||
|
assertEquals(status.success, false);
|
||||||
|
assertEquals(status.code, undefined);
|
||||||
|
assertEquals(status.signal, Deno.Signal.SIGINT);
|
||||||
|
});
|
||||||
|
|
||||||
|
testPerm({ run: true }, async function killFailed(): Promise<void> {
|
||||||
|
const p = run({
|
||||||
|
args: ["python", "-c", "from time import sleep; sleep(10000)"]
|
||||||
|
});
|
||||||
|
assert(!p.stdin);
|
||||||
|
assert(!p.stdout);
|
||||||
|
|
||||||
|
let err;
|
||||||
|
try {
|
||||||
|
kill(p.pid, 12345);
|
||||||
|
} catch (e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!!err);
|
||||||
|
assertEquals(err.kind, Deno.ErrorKind.InvalidInput);
|
||||||
|
assertEquals(err.name, "InvalidInput");
|
||||||
|
|
||||||
|
p.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue