summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--cli/BUILD.gn3
-rw-r--r--cli/Cargo.toml3
-rw-r--r--cli/errors.rs28
-rw-r--r--cli/main.rs3
-rw-r--r--cli/msg.fbs9
-rw-r--r--cli/ops.rs17
-rw-r--r--cli/signal.rs20
-rw-r--r--js/deno.ts9
-rw-r--r--js/process.ts89
-rw-r--r--js/process_test.ts68
11 files changed, 247 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 46754b8b5..9c73d7ad2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -225,6 +225,7 @@ dependencies = [
"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)",
"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)",
"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)",
diff --git a/cli/BUILD.gn b/cli/BUILD.gn
index a292e559f..7bf34dec3 100644
--- a/cli/BUILD.gn
+++ b/cli/BUILD.gn
@@ -42,6 +42,9 @@ main_extern = [
if (is_win) {
main_extern += [ "$rust_build:winapi" ]
}
+if (is_posix) {
+ main_extern += [ "$rust_build:nix" ]
+}
ts_sources = [
"../js/assets.ts",
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index a7a780618..ad921cbdd 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -50,3 +50,6 @@ url = "1.7.2"
[target.'cfg(windows)'.dependencies]
winapi = "0.3.7"
+
+[target.'cfg(unix)'.dependencies]
+nix = "0.11.0"
diff --git a/cli/errors.rs b/cli/errors.rs
index bd3e7ba73..424091584 100644
--- a/cli/errors.rs
+++ b/cli/errors.rs
@@ -4,6 +4,8 @@ pub use crate::msg::ErrorKind;
use crate::resolve_addr::ResolveAddrError;
use deno::JSError;
use hyper;
+#[cfg(unix)]
+use nix::{errno::Errno, Error as UnixError};
use std;
use std::fmt;
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 {
new(ErrorKind::BadResource, String::from("bad resource id"))
}
diff --git a/cli/main.rs b/cli/main.rs
index 4d12b6c89..41b04785c 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -9,6 +9,8 @@ extern crate futures;
extern crate serde_json;
extern crate clap;
extern crate deno;
+#[cfg(unix)]
+extern crate nix;
mod ansi;
pub mod compiler;
@@ -27,6 +29,7 @@ pub mod permissions;
mod repl;
pub mod resolve_addr;
pub mod resources;
+mod signal;
mod startup_data;
pub mod state;
mod tokio_util;
diff --git a/cli/msg.fbs b/cli/msg.fbs
index 4eca8dcf8..d217fc7ba 100644
--- a/cli/msg.fbs
+++ b/cli/msg.fbs
@@ -21,6 +21,7 @@ union Any {
GlobalTimerStop,
IsTTY,
IsTTYRes,
+ Kill,
Link,
Listen,
ListenRes,
@@ -129,7 +130,8 @@ enum ErrorKind: byte {
InvalidUri,
InvalidSeekMode,
OpNotAvaiable,
- WorkerInitFailed
+ WorkerInitFailed,
+ UnixError,
}
table Cwd {}
@@ -453,6 +455,11 @@ table Close {
rid: uint32;
}
+table Kill {
+ pid: int32;
+ signo: int32;
+}
+
table Shutdown {
rid: uint32;
how: uint;
diff --git a/cli/ops.rs b/cli/ops.rs
index 044e0fc20..bc06a2fb7 100644
--- a/cli/ops.rs
+++ b/cli/ops.rs
@@ -14,6 +14,7 @@ use crate::resolve_addr::resolve_addr;
use crate::resources;
use crate::resources::table_entries;
use crate::resources::Resource;
+use crate::signal::kill;
use crate::startup_data;
use crate::state::ThreadSafeState;
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::GlobalTimerStop => Some(op_global_timer_stop),
msg::Any::IsTTY => Some(op_is_tty),
+ msg::Any::Kill => Some(op_kill),
msg::Any::Link => Some(op_link),
msg::Any::Listen => Some(op_listen),
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(
_state: &ThreadSafeState,
base: &msg::Base<'_>,
diff --git a/cli/signal.rs b/cli/signal.rs
new file mode 100644
index 000000000..7d67ba743
--- /dev/null
+++ b/cli/signal.rs
@@ -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(())
+}
diff --git a/js/deno.ts b/js/deno.ts
index 51cc0791a..46f018afc 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -68,7 +68,14 @@ export { FileInfo } from "./file_info";
export { connect, dial, listen, Listener, Conn } from "./net";
export { metrics, Metrics } from "./metrics";
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 { build, platform, OperatingSystem, Arch } from "./build";
export { version } from "./version";
diff --git a/js/process.ts b/js/process.ts
index a0eef63dd..c0a66f311 100644
--- a/js/process.ts
+++ b/js/process.ts
@@ -7,6 +7,7 @@ import { File, close } from "./files";
import { ReadCloser, WriteCloser } from "./io";
import { readAll } from "./buffer";
import { assert, unreachable } from "./util";
+import { platform } from "./build";
/** 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 {
readonly rid: number;
readonly pid: number;
@@ -113,6 +124,10 @@ export class Process {
close(): void {
close(this.rid);
}
+
+ kill(signo: number): void {
+ kill(this.pid, signo);
+ }
}
export interface ProcessStatus {
@@ -179,3 +194,77 @@ export function run(opt: RunOptions): Process {
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;
diff --git a/js/process_test.ts b/js/process_test.ts
index c89e1cae7..8d266617a 100644
--- a/js/process_test.ts
+++ b/js/process_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test, testPerm, assert, assertEquals } from "./test_util.ts";
-const { run, DenoError, ErrorKind } = Deno;
+const { kill, run, DenoError, ErrorKind } = Deno;
test(function runPermissions(): void {
let caughtError = false;
@@ -223,3 +223,69 @@ testPerm({ run: true }, async function runEnv(): Promise<void> {
assertEquals(s, "01234567");
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();
+ });
+}