2021-01-10 21:59:07 -05:00
|
|
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
2020-09-05 20:34:02 -04:00
|
|
|
|
2020-12-16 11:14:12 -05:00
|
|
|
use super::io::{std_file_resource, StreamResource};
|
2020-09-19 19:17:35 -04:00
|
|
|
use crate::permissions::Permissions;
|
2020-09-14 12:48:57 -04:00
|
|
|
use deno_core::error::bad_resource_id;
|
|
|
|
use deno_core::error::type_error;
|
|
|
|
use deno_core::error::AnyError;
|
2020-09-21 12:36:37 -04:00
|
|
|
use deno_core::serde_json;
|
|
|
|
use deno_core::serde_json::json;
|
|
|
|
use deno_core::serde_json::Value;
|
2020-12-16 11:14:12 -05:00
|
|
|
use deno_core::AsyncMutFuture;
|
|
|
|
use deno_core::AsyncRefCell;
|
2020-08-28 11:08:24 -04:00
|
|
|
use deno_core::BufVec;
|
2020-09-10 09:57:45 -04:00
|
|
|
use deno_core::OpState;
|
2020-12-16 11:14:12 -05:00
|
|
|
use deno_core::RcRef;
|
|
|
|
use deno_core::Resource;
|
2020-04-23 05:51:07 -04:00
|
|
|
use deno_core::ZeroCopyBuf;
|
2020-09-16 12:43:08 -04:00
|
|
|
use serde::Deserialize;
|
2020-12-16 11:14:12 -05:00
|
|
|
use std::borrow::Cow;
|
2020-09-10 09:57:45 -04:00
|
|
|
use std::cell::RefCell;
|
2020-08-18 12:30:13 -04:00
|
|
|
use std::rc::Rc;
|
2019-12-30 08:57:17 -05:00
|
|
|
use tokio::process::Command;
|
2019-08-14 11:03:02 -04:00
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
use std::os::unix::process::ExitStatusExt;
|
|
|
|
|
2020-09-10 09:57:45 -04:00
|
|
|
pub fn init(rt: &mut deno_core::JsRuntime) {
|
|
|
|
super::reg_json_sync(rt, "op_run", op_run);
|
|
|
|
super::reg_json_async(rt, "op_run_status", op_run_status);
|
|
|
|
super::reg_json_sync(rt, "op_kill", op_kill);
|
2019-10-11 14:41:54 -04:00
|
|
|
}
|
|
|
|
|
2020-09-14 12:48:57 -04:00
|
|
|
fn clone_file(
|
|
|
|
state: &mut OpState,
|
|
|
|
rid: u32,
|
|
|
|
) -> Result<std::fs::File, AnyError> {
|
2020-09-05 20:34:02 -04:00
|
|
|
std_file_resource(state, rid, move |r| match r {
|
2020-09-14 12:48:57 -04:00
|
|
|
Ok(std_file) => std_file.try_clone().map_err(AnyError::from),
|
|
|
|
Err(_) => Err(bad_resource_id()),
|
2020-04-16 10:29:28 -04:00
|
|
|
})
|
2019-11-14 12:10:25 -05:00
|
|
|
}
|
|
|
|
|
2020-09-14 12:48:57 -04:00
|
|
|
fn subprocess_stdio_map(s: &str) -> Result<std::process::Stdio, AnyError> {
|
2019-08-26 08:50:21 -04:00
|
|
|
match s {
|
2020-06-25 12:38:19 -04:00
|
|
|
"inherit" => Ok(std::process::Stdio::inherit()),
|
|
|
|
"piped" => Ok(std::process::Stdio::piped()),
|
|
|
|
"null" => Ok(std::process::Stdio::null()),
|
2020-09-14 12:48:57 -04:00
|
|
|
_ => Err(type_error("Invalid resource for stdio")),
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-26 08:50:21 -04:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
struct RunArgs {
|
2020-03-21 17:44:18 -04:00
|
|
|
cmd: Vec<String>,
|
2019-08-26 08:50:21 -04:00
|
|
|
cwd: Option<String>,
|
|
|
|
env: Vec<(String, String)>,
|
|
|
|
stdin: String,
|
|
|
|
stdout: String,
|
|
|
|
stderr: String,
|
|
|
|
stdin_rid: u32,
|
|
|
|
stdout_rid: u32,
|
|
|
|
stderr_rid: u32,
|
|
|
|
}
|
|
|
|
|
2019-11-07 18:52:21 -05:00
|
|
|
struct ChildResource {
|
2020-12-16 11:14:12 -05:00
|
|
|
child: AsyncRefCell<tokio::process::Child>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Resource for ChildResource {
|
|
|
|
fn name(&self) -> Cow<str> {
|
|
|
|
"child".into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChildResource {
|
|
|
|
fn borrow_mut(self: Rc<Self>) -> AsyncMutFuture<tokio::process::Child> {
|
|
|
|
RcRef::map(self, |r| &r.child).borrow_mut()
|
|
|
|
}
|
2019-11-07 18:52:21 -05:00
|
|
|
}
|
|
|
|
|
2019-10-11 14:41:54 -04:00
|
|
|
fn op_run(
|
2020-09-10 09:57:45 -04:00
|
|
|
state: &mut OpState,
|
2019-08-26 08:50:21 -04:00
|
|
|
args: Value,
|
2020-06-01 14:20:47 -04:00
|
|
|
_zero_copy: &mut [ZeroCopyBuf],
|
2020-09-14 12:48:57 -04:00
|
|
|
) -> Result<Value, AnyError> {
|
2019-08-26 08:50:21 -04:00
|
|
|
let run_args: RunArgs = serde_json::from_value(args)?;
|
2020-09-19 19:17:35 -04:00
|
|
|
state.borrow::<Permissions>().check_run()?;
|
2019-08-14 11:03:02 -04:00
|
|
|
|
2020-03-21 17:44:18 -04:00
|
|
|
let args = run_args.cmd;
|
2019-08-26 08:50:21 -04:00
|
|
|
let env = run_args.env;
|
|
|
|
let cwd = run_args.cwd;
|
2019-08-14 11:03:02 -04:00
|
|
|
|
2019-08-26 08:50:21 -04:00
|
|
|
let mut c = Command::new(args.get(0).unwrap());
|
2019-08-14 11:03:02 -04:00
|
|
|
(1..args.len()).for_each(|i| {
|
2019-08-26 08:50:21 -04:00
|
|
|
let arg = args.get(i).unwrap();
|
2019-08-14 11:03:02 -04:00
|
|
|
c.arg(arg);
|
|
|
|
});
|
|
|
|
cwd.map(|d| c.current_dir(d));
|
2019-08-26 08:50:21 -04:00
|
|
|
for (key, value) in &env {
|
|
|
|
c.env(key, value);
|
|
|
|
}
|
2019-08-14 11:03:02 -04:00
|
|
|
|
|
|
|
// TODO: make this work with other resources, eg. sockets
|
2020-11-27 14:47:35 -05:00
|
|
|
if !run_args.stdin.is_empty() {
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stdin(subprocess_stdio_map(run_args.stdin.as_ref())?);
|
2019-08-14 11:03:02 -04:00
|
|
|
} else {
|
2020-09-05 20:34:02 -04:00
|
|
|
let file = clone_file(state, run_args.stdin_rid)?;
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stdin(file);
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
|
2020-11-27 14:47:35 -05:00
|
|
|
if !run_args.stdout.is_empty() {
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stdout(subprocess_stdio_map(run_args.stdout.as_ref())?);
|
2019-08-14 11:03:02 -04:00
|
|
|
} else {
|
2020-09-05 20:34:02 -04:00
|
|
|
let file = clone_file(state, run_args.stdout_rid)?;
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stdout(file);
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
|
2020-11-27 14:47:35 -05:00
|
|
|
if !run_args.stderr.is_empty() {
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stderr(subprocess_stdio_map(run_args.stderr.as_ref())?);
|
2019-08-14 11:03:02 -04:00
|
|
|
} else {
|
2020-09-05 20:34:02 -04:00
|
|
|
let file = clone_file(state, run_args.stderr_rid)?;
|
2020-06-25 12:38:19 -04:00
|
|
|
c.stderr(file);
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
|
2019-12-30 08:57:17 -05:00
|
|
|
// We want to kill child when it's closed
|
|
|
|
c.kill_on_drop(true);
|
|
|
|
|
2019-08-14 11:03:02 -04:00
|
|
|
// Spawn the command.
|
2019-12-30 08:57:17 -05:00
|
|
|
let mut child = c.spawn()?;
|
2019-08-14 11:03:02 -04:00
|
|
|
let pid = child.id();
|
2019-11-07 18:52:21 -05:00
|
|
|
|
2020-01-12 04:21:53 -05:00
|
|
|
let stdin_rid = match child.stdin.take() {
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(child_stdin) => {
|
2020-12-16 11:14:12 -05:00
|
|
|
let rid = state
|
|
|
|
.resource_table
|
|
|
|
.add(StreamResource::child_stdin(child_stdin));
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(rid)
|
|
|
|
}
|
|
|
|
None => None,
|
2019-11-07 18:52:21 -05:00
|
|
|
};
|
|
|
|
|
2020-01-12 04:21:53 -05:00
|
|
|
let stdout_rid = match child.stdout.take() {
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(child_stdout) => {
|
2020-12-16 11:14:12 -05:00
|
|
|
let rid = state
|
|
|
|
.resource_table
|
|
|
|
.add(StreamResource::child_stdout(child_stdout));
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(rid)
|
|
|
|
}
|
|
|
|
None => None,
|
2019-11-07 18:52:21 -05:00
|
|
|
};
|
|
|
|
|
2020-01-12 04:21:53 -05:00
|
|
|
let stderr_rid = match child.stderr.take() {
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(child_stderr) => {
|
2020-12-16 11:14:12 -05:00
|
|
|
let rid = state
|
|
|
|
.resource_table
|
|
|
|
.add(StreamResource::child_stderr(child_stderr));
|
2019-11-14 12:10:25 -05:00
|
|
|
Some(rid)
|
|
|
|
}
|
|
|
|
None => None,
|
2019-11-07 18:52:21 -05:00
|
|
|
};
|
|
|
|
|
2020-12-16 11:14:12 -05:00
|
|
|
let child_resource = ChildResource {
|
|
|
|
child: AsyncRefCell::new(child),
|
|
|
|
};
|
|
|
|
let child_rid = state.resource_table.add(child_resource);
|
2019-08-14 11:03:02 -04:00
|
|
|
|
2020-08-28 11:08:24 -04:00
|
|
|
Ok(json!({
|
2019-11-07 18:52:21 -05:00
|
|
|
"rid": child_rid,
|
2019-08-26 08:50:21 -04:00
|
|
|
"pid": pid,
|
2019-11-07 18:52:21 -05:00
|
|
|
"stdinRid": stdin_rid,
|
|
|
|
"stdoutRid": stdout_rid,
|
|
|
|
"stderrRid": stderr_rid,
|
2020-08-28 11:08:24 -04:00
|
|
|
}))
|
2019-08-26 08:50:21 -04:00
|
|
|
}
|
2019-08-24 16:20:48 -04:00
|
|
|
|
2019-08-26 08:50:21 -04:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
struct RunStatusArgs {
|
|
|
|
rid: i32,
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
|
2020-08-28 11:08:24 -04:00
|
|
|
async fn op_run_status(
|
2020-09-10 09:57:45 -04:00
|
|
|
state: Rc<RefCell<OpState>>,
|
2019-08-26 08:50:21 -04:00
|
|
|
args: Value,
|
2020-08-28 11:08:24 -04:00
|
|
|
_zero_copy: BufVec,
|
2020-09-14 12:48:57 -04:00
|
|
|
) -> Result<Value, AnyError> {
|
2019-08-26 08:50:21 -04:00
|
|
|
let args: RunStatusArgs = serde_json::from_value(args)?;
|
|
|
|
let rid = args.rid as u32;
|
2019-08-14 11:03:02 -04:00
|
|
|
|
2020-09-19 19:17:35 -04:00
|
|
|
{
|
|
|
|
let s = state.borrow();
|
|
|
|
s.borrow::<Permissions>().check_run()?;
|
|
|
|
}
|
2019-08-26 08:50:21 -04:00
|
|
|
|
2020-12-16 11:14:12 -05:00
|
|
|
let resource = state
|
|
|
|
.borrow_mut()
|
|
|
|
.resource_table
|
|
|
|
.get::<ChildResource>(rid)
|
|
|
|
.ok_or_else(bad_resource_id)?;
|
|
|
|
let mut child = resource.borrow_mut().await;
|
2021-01-12 02:50:02 -05:00
|
|
|
let run_status = child.wait().await?;
|
2020-08-28 11:08:24 -04:00
|
|
|
let code = run_status.code();
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
let signal = run_status.signal();
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
let signal = None;
|
|
|
|
|
|
|
|
code
|
|
|
|
.or(signal)
|
|
|
|
.expect("Should have either an exit code or a signal.");
|
|
|
|
let got_signal = signal.is_some();
|
|
|
|
|
|
|
|
Ok(json!({
|
|
|
|
"gotSignal": got_signal,
|
|
|
|
"exitCode": code.unwrap_or(-1),
|
|
|
|
"exitSignal": signal.unwrap_or(-1),
|
|
|
|
}))
|
2019-08-26 08:50:21 -04:00
|
|
|
}
|
|
|
|
|
2020-12-13 13:45:53 -05:00
|
|
|
#[cfg(not(unix))]
|
|
|
|
const SIGINT: i32 = 2;
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
const SIGKILL: i32 = 9;
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
const SIGTERM: i32 = 15;
|
|
|
|
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
use winapi::{
|
|
|
|
shared::minwindef::DWORD,
|
|
|
|
um::{
|
|
|
|
handleapi::CloseHandle,
|
|
|
|
processthreadsapi::{OpenProcess, TerminateProcess},
|
|
|
|
winnt::PROCESS_TERMINATE,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
pub fn kill(pid: i32, signo: i32) -> Result<(), AnyError> {
|
|
|
|
use nix::sys::signal::{kill as unix_kill, Signal};
|
|
|
|
use nix::unistd::Pid;
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
let sig = Signal::try_from(signo)?;
|
|
|
|
unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(AnyError::from)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
pub fn kill(pid: i32, signal: i32) -> Result<(), AnyError> {
|
|
|
|
use std::io::Error;
|
|
|
|
match signal {
|
|
|
|
SIGINT | SIGKILL | SIGTERM => {
|
|
|
|
if pid <= 0 {
|
|
|
|
return Err(type_error("unsupported pid"));
|
|
|
|
}
|
|
|
|
unsafe {
|
|
|
|
let handle = OpenProcess(PROCESS_TERMINATE, 0, pid as DWORD);
|
|
|
|
if handle.is_null() {
|
|
|
|
return Err(Error::last_os_error().into());
|
|
|
|
}
|
|
|
|
if TerminateProcess(handle, 1) == 0 {
|
|
|
|
CloseHandle(handle);
|
|
|
|
return Err(Error::last_os_error().into());
|
|
|
|
}
|
|
|
|
if CloseHandle(handle) == 0 {
|
|
|
|
return Err(Error::last_os_error().into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(type_error("unsupported signal"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-08-26 08:50:21 -04:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct KillArgs {
|
|
|
|
pid: i32,
|
|
|
|
signo: i32,
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|
|
|
|
|
2019-10-11 14:41:54 -04:00
|
|
|
fn op_kill(
|
2020-09-10 09:57:45 -04:00
|
|
|
state: &mut OpState,
|
2019-08-26 08:50:21 -04:00
|
|
|
args: Value,
|
2020-06-01 14:20:47 -04:00
|
|
|
_zero_copy: &mut [ZeroCopyBuf],
|
2020-09-14 12:48:57 -04:00
|
|
|
) -> Result<Value, AnyError> {
|
2020-09-26 14:26:51 -04:00
|
|
|
super::check_unstable(state, "Deno.kill");
|
2020-09-19 19:17:35 -04:00
|
|
|
state.borrow::<Permissions>().check_run()?;
|
2019-08-14 11:03:02 -04:00
|
|
|
|
2019-08-26 08:50:21 -04:00
|
|
|
let args: KillArgs = serde_json::from_value(args)?;
|
|
|
|
kill(args.pid, args.signo)?;
|
2020-08-28 11:08:24 -04:00
|
|
|
Ok(json!({}))
|
2019-08-14 11:03:02 -04:00
|
|
|
}
|