summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/js/40_process.js321
-rw-r--r--runtime/js/40_spawn.js326
-rw-r--r--runtime/js/90_deno_ns.js5
-rw-r--r--runtime/ops/mod.rs1
-rw-r--r--runtime/ops/process.rs672
-rw-r--r--runtime/ops/spawn.rs289
-rw-r--r--runtime/web_worker.rs3
-rw-r--r--runtime/worker.rs3
9 files changed, 793 insertions, 828 deletions
diff --git a/runtime/build.rs b/runtime/build.rs
index efcdb8f71..153a85628 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -207,7 +207,6 @@ mod startup_snapshot {
"40_http.js",
"40_process.js",
"40_signals.js",
- "40_spawn.js",
"40_tty.js",
"41_prompt.js",
"90_deno_ns.js",
diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js
index bb224ae9c..9599b7b74 100644
--- a/runtime/js/40_process.js
+++ b/runtime/js/40_process.js
@@ -2,10 +2,6 @@
const core = globalThis.Deno.core;
const ops = core.ops;
-import { FsFile } from "internal:runtime/30_fs.js";
-import { readAll } from "internal:deno_io/12_io.js";
-import { pathFromURL } from "internal:runtime/06_util.js";
-import { assert } from "internal:deno_web/00_infra.js";
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayPrototypeMap,
@@ -14,7 +10,25 @@ const {
ObjectEntries,
SafeArrayIterator,
String,
+ ObjectPrototypeIsPrototypeOf,
+ PromisePrototypeThen,
+ SafePromiseAll,
+ SymbolFor,
+ Symbol,
} = primordials;
+import { FsFile } from "internal:runtime/30_fs.js";
+import { readAll } from "internal:deno_io/12_io.js";
+import { pathFromURL } from "internal:runtime/06_util.js";
+import { assert } from "internal:deno_web/00_infra.js";
+import * as abortSignal from "internal:deno_web/03_abort_signal.js";
+import {
+ readableStreamCollectIntoUint8Array,
+ readableStreamForRidUnrefable,
+ readableStreamForRidUnrefableRef,
+ readableStreamForRidUnrefableUnref,
+ ReadableStreamPrototype,
+ writableStreamForRid,
+} from "internal:deno_web/06_streams.js";
function opKill(pid, signo, apiName) {
ops.op_kill(pid, signo, apiName);
@@ -130,4 +144,301 @@ function run({
return new Process(res);
}
-export { kill, Process, run };
+const illegalConstructorKey = Symbol("illegalConstructorKey");
+const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
+
+function spawnChildInner(opFn, command, apiName, {
+ args = [],
+ cwd = undefined,
+ clearEnv = false,
+ env = {},
+ uid = undefined,
+ gid = undefined,
+ stdin = "null",
+ stdout = "piped",
+ stderr = "piped",
+ signal = undefined,
+ windowsRawArguments = false,
+} = {}) {
+ const child = opFn({
+ cmd: pathFromURL(command),
+ args: ArrayPrototypeMap(args, String),
+ cwd: pathFromURL(cwd),
+ clearEnv,
+ env: ObjectEntries(env),
+ uid,
+ gid,
+ stdin,
+ stdout,
+ stderr,
+ windowsRawArguments,
+ }, apiName);
+ return new ChildProcess(illegalConstructorKey, {
+ ...child,
+ signal,
+ });
+}
+
+function spawnChild(command, options = {}) {
+ return spawnChildInner(
+ ops.op_spawn_child,
+ command,
+ "Deno.Command().spawn()",
+ options,
+ );
+}
+
+function collectOutput(readableStream) {
+ if (
+ !(ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, readableStream))
+ ) {
+ return null;
+ }
+
+ return readableStreamCollectIntoUint8Array(readableStream);
+}
+
+class ChildProcess {
+ #rid;
+ #waitPromiseId;
+ #unrefed = false;
+
+ #pid;
+ get pid() {
+ return this.#pid;
+ }
+
+ #stdin = null;
+ get stdin() {
+ if (this.#stdin == null) {
+ throw new TypeError("stdin is not piped");
+ }
+ return this.#stdin;
+ }
+
+ #stdoutRid;
+ #stdout = null;
+ get stdout() {
+ if (this.#stdout == null) {
+ throw new TypeError("stdout is not piped");
+ }
+ return this.#stdout;
+ }
+
+ #stderrRid;
+ #stderr = null;
+ get stderr() {
+ if (this.#stderr == null) {
+ throw new TypeError("stderr is not piped");
+ }
+ return this.#stderr;
+ }
+
+ constructor(key = null, {
+ signal,
+ rid,
+ pid,
+ stdinRid,
+ stdoutRid,
+ stderrRid,
+ } = null) {
+ if (key !== illegalConstructorKey) {
+ throw new TypeError("Illegal constructor.");
+ }
+
+ this.#rid = rid;
+ this.#pid = pid;
+
+ if (stdinRid !== null) {
+ this.#stdin = writableStreamForRid(stdinRid);
+ }
+
+ if (stdoutRid !== null) {
+ this.#stdoutRid = stdoutRid;
+ this.#stdout = readableStreamForRidUnrefable(stdoutRid);
+ }
+
+ if (stderrRid !== null) {
+ this.#stderrRid = stderrRid;
+ this.#stderr = readableStreamForRidUnrefable(stderrRid);
+ }
+
+ const onAbort = () => this.kill("SIGTERM");
+ signal?.[abortSignal.add](onAbort);
+
+ const waitPromise = core.opAsync("op_spawn_wait", this.#rid);
+ this.#waitPromiseId = waitPromise[promiseIdSymbol];
+ this.#status = PromisePrototypeThen(waitPromise, (res) => {
+ this.#rid = null;
+ signal?.[abortSignal.remove](onAbort);
+ return res;
+ });
+ }
+
+ #status;
+ get status() {
+ return this.#status;
+ }
+
+ async output() {
+ if (this.#stdout?.locked) {
+ throw new TypeError(
+ "Can't collect output because stdout is locked",
+ );
+ }
+ if (this.#stderr?.locked) {
+ throw new TypeError(
+ "Can't collect output because stderr is locked",
+ );
+ }
+
+ const { 0: status, 1: stdout, 2: stderr } = await SafePromiseAll([
+ this.#status,
+ collectOutput(this.#stdout),
+ collectOutput(this.#stderr),
+ ]);
+
+ return {
+ success: status.success,
+ code: status.code,
+ signal: status.signal,
+ get stdout() {
+ if (stdout == null) {
+ throw new TypeError("stdout is not piped");
+ }
+ return stdout;
+ },
+ get stderr() {
+ if (stderr == null) {
+ throw new TypeError("stderr is not piped");
+ }
+ return stderr;
+ },
+ };
+ }
+
+ kill(signo = "SIGTERM") {
+ if (this.#rid === null) {
+ throw new TypeError("Child process has already terminated.");
+ }
+ ops.op_kill(this.#pid, signo, "Deno.Child.kill()");
+ }
+
+ ref() {
+ this.#unrefed = false;
+ core.refOp(this.#waitPromiseId);
+ if (this.#stdout) readableStreamForRidUnrefableRef(this.#stdout);
+ if (this.#stderr) readableStreamForRidUnrefableRef(this.#stderr);
+ }
+
+ unref() {
+ this.#unrefed = true;
+ core.unrefOp(this.#waitPromiseId);
+ if (this.#stdout) readableStreamForRidUnrefableUnref(this.#stdout);
+ if (this.#stderr) readableStreamForRidUnrefableUnref(this.#stderr);
+ }
+}
+
+function spawn(command, options) {
+ if (options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
+ );
+ }
+ return spawnChildInner(
+ ops.op_spawn_child,
+ command,
+ "Deno.Command().output()",
+ options,
+ )
+ .output();
+}
+
+function spawnSync(command, {
+ args = [],
+ cwd = undefined,
+ clearEnv = false,
+ env = {},
+ uid = undefined,
+ gid = undefined,
+ stdin = "null",
+ stdout = "piped",
+ stderr = "piped",
+ windowsRawArguments = false,
+} = {}) {
+ if (stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
+ );
+ }
+ const result = ops.op_spawn_sync({
+ cmd: pathFromURL(command),
+ args: ArrayPrototypeMap(args, String),
+ cwd: pathFromURL(cwd),
+ clearEnv,
+ env: ObjectEntries(env),
+ uid,
+ gid,
+ stdin,
+ stdout,
+ stderr,
+ windowsRawArguments,
+ });
+ return {
+ success: result.status.success,
+ code: result.status.code,
+ signal: result.status.signal,
+ get stdout() {
+ if (result.stdout == null) {
+ throw new TypeError("stdout is not piped");
+ }
+ return result.stdout;
+ },
+ get stderr() {
+ if (result.stderr == null) {
+ throw new TypeError("stderr is not piped");
+ }
+ return result.stderr;
+ },
+ };
+}
+
+class Command {
+ #command;
+ #options;
+
+ constructor(command, options) {
+ this.#command = command;
+ this.#options = options;
+ }
+
+ output() {
+ if (this.#options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
+ );
+ }
+ return spawn(this.#command, this.#options);
+ }
+
+ outputSync() {
+ if (this.#options?.stdin === "piped") {
+ throw new TypeError(
+ "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
+ );
+ }
+ return spawnSync(this.#command, this.#options);
+ }
+
+ spawn() {
+ const options = {
+ ...(this.#options ?? {}),
+ stdout: this.#options?.stdout ?? "inherit",
+ stderr: this.#options?.stderr ?? "inherit",
+ stdin: this.#options?.stdin ?? "inherit",
+ };
+ return spawnChild(this.#command, options);
+ }
+}
+
+export { ChildProcess, Command, kill, Process, run };
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js
deleted file mode 100644
index 173f596cc..000000000
--- a/runtime/js/40_spawn.js
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-const core = globalThis.Deno.core;
-const ops = core.ops;
-const primordials = globalThis.__bootstrap.primordials;
-import { pathFromURL } from "internal:runtime/06_util.js";
-import { add, remove } from "internal:deno_web/03_abort_signal.js";
-const {
- ArrayPrototypeMap,
- ObjectEntries,
- ObjectPrototypeIsPrototypeOf,
- String,
- TypeError,
- PromisePrototypeThen,
- SafePromiseAll,
- SymbolFor,
- Symbol,
-} = primordials;
-import {
- readableStreamCollectIntoUint8Array,
- readableStreamForRidUnrefable,
- readableStreamForRidUnrefableRef,
- readableStreamForRidUnrefableUnref,
- ReadableStreamPrototype,
- writableStreamForRid,
-} from "internal:deno_web/06_streams.js";
-
-const illegalConstructorKey = Symbol("illegalConstructorKey");
-
-const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
-
-function spawnChildInner(opFn, command, apiName, {
- args = [],
- cwd = undefined,
- clearEnv = false,
- env = {},
- uid = undefined,
- gid = undefined,
- stdin = "null",
- stdout = "piped",
- stderr = "piped",
- signal = undefined,
- windowsRawArguments = false,
-} = {}) {
- const child = opFn({
- cmd: pathFromURL(command),
- args: ArrayPrototypeMap(args, String),
- cwd: pathFromURL(cwd),
- clearEnv,
- env: ObjectEntries(env),
- uid,
- gid,
- stdin,
- stdout,
- stderr,
- windowsRawArguments,
- }, apiName);
- return new ChildProcess(illegalConstructorKey, {
- ...child,
- signal,
- });
-}
-
-function spawnChild(command, options = {}) {
- return spawnChildInner(
- ops.op_spawn_child,
- command,
- "Deno.Command().spawn()",
- options,
- );
-}
-
-function collectOutput(readableStream) {
- if (
- !(ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, readableStream))
- ) {
- return null;
- }
-
- return readableStreamCollectIntoUint8Array(readableStream);
-}
-
-class ChildProcess {
- #rid;
- #waitPromiseId;
- #unrefed = false;
-
- #pid;
- get pid() {
- return this.#pid;
- }
-
- #stdin = null;
- get stdin() {
- if (this.#stdin == null) {
- throw new TypeError("stdin is not piped");
- }
- return this.#stdin;
- }
-
- #stdoutRid;
- #stdout = null;
- get stdout() {
- if (this.#stdout == null) {
- throw new TypeError("stdout is not piped");
- }
- return this.#stdout;
- }
-
- #stderrRid;
- #stderr = null;
- get stderr() {
- if (this.#stderr == null) {
- throw new TypeError("stderr is not piped");
- }
- return this.#stderr;
- }
-
- constructor(key = null, {
- signal,
- rid,
- pid,
- stdinRid,
- stdoutRid,
- stderrRid,
- } = null) {
- if (key !== illegalConstructorKey) {
- throw new TypeError("Illegal constructor.");
- }
-
- this.#rid = rid;
- this.#pid = pid;
-
- if (stdinRid !== null) {
- this.#stdin = writableStreamForRid(stdinRid);
- }
-
- if (stdoutRid !== null) {
- this.#stdoutRid = stdoutRid;
- this.#stdout = readableStreamForRidUnrefable(stdoutRid);
- }
-
- if (stderrRid !== null) {
- this.#stderrRid = stderrRid;
- this.#stderr = readableStreamForRidUnrefable(stderrRid);
- }
-
- const onAbort = () => this.kill("SIGTERM");
- signal?.[add](onAbort);
-
- const waitPromise = core.opAsync("op_spawn_wait", this.#rid);
- this.#waitPromiseId = waitPromise[promiseIdSymbol];
- this.#status = PromisePrototypeThen(waitPromise, (res) => {
- this.#rid = null;
- signal?.[remove](onAbort);
- return res;
- });
- }
-
- #status;
- get status() {
- return this.#status;
- }
-
- async output() {
- if (this.#stdout?.locked) {
- throw new TypeError(
- "Can't collect output because stdout is locked",
- );
- }
- if (this.#stderr?.locked) {
- throw new TypeError(
- "Can't collect output because stderr is locked",
- );
- }
-
- const { 0: status, 1: stdout, 2: stderr } = await SafePromiseAll([
- this.#status,
- collectOutput(this.#stdout),
- collectOutput(this.#stderr),
- ]);
-
- return {
- success: status.success,
- code: status.code,
- signal: status.signal,
- get stdout() {
- if (stdout == null) {
- throw new TypeError("stdout is not piped");
- }
- return stdout;
- },
- get stderr() {
- if (stderr == null) {
- throw new TypeError("stderr is not piped");
- }
- return stderr;
- },
- };
- }
-
- kill(signo = "SIGTERM") {
- if (this.#rid === null) {
- throw new TypeError("Child process has already terminated.");
- }
- ops.op_kill(this.#pid, signo, "Deno.Child.kill()");
- }
-
- ref() {
- this.#unrefed = false;
- core.refOp(this.#waitPromiseId);
- if (this.#stdout) readableStreamForRidUnrefableRef(this.#stdout);
- if (this.#stderr) readableStreamForRidUnrefableRef(this.#stderr);
- }
-
- unref() {
- this.#unrefed = true;
- core.unrefOp(this.#waitPromiseId);
- if (this.#stdout) readableStreamForRidUnrefableUnref(this.#stdout);
- if (this.#stderr) readableStreamForRidUnrefableUnref(this.#stderr);
- }
-}
-
-function spawn(command, options) {
- if (options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
- );
- }
- return spawnChildInner(
- ops.op_spawn_child,
- command,
- "Deno.Command().output()",
- options,
- )
- .output();
-}
-
-function spawnSync(command, {
- args = [],
- cwd = undefined,
- clearEnv = false,
- env = {},
- uid = undefined,
- gid = undefined,
- stdin = "null",
- stdout = "piped",
- stderr = "piped",
- windowsRawArguments = false,
-} = {}) {
- if (stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command().spawn()' instead",
- );
- }
- const result = ops.op_spawn_sync({
- cmd: pathFromURL(command),
- args: ArrayPrototypeMap(args, String),
- cwd: pathFromURL(cwd),
- clearEnv,
- env: ObjectEntries(env),
- uid,
- gid,
- stdin,
- stdout,
- stderr,
- windowsRawArguments,
- });
- return {
- success: result.status.success,
- code: result.status.code,
- signal: result.status.signal,
- get stdout() {
- if (result.stdout == null) {
- throw new TypeError("stdout is not piped");
- }
- return result.stdout;
- },
- get stderr() {
- if (result.stderr == null) {
- throw new TypeError("stderr is not piped");
- }
- return result.stderr;
- },
- };
-}
-
-class Command {
- #command;
- #options;
-
- constructor(command, options) {
- this.#command = command;
- this.#options = options;
- }
-
- output() {
- if (this.#options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
- );
- }
- return spawn(this.#command, this.#options);
- }
-
- outputSync() {
- if (this.#options?.stdin === "piped") {
- throw new TypeError(
- "Piped stdin is not supported for this function, use 'Deno.Command.spawn()' instead",
- );
- }
- return spawnSync(this.#command, this.#options);
- }
-
- spawn() {
- const options = {
- ...(this.#options ?? {}),
- stdout: this.#options?.stdout ?? "inherit",
- stderr: this.#options?.stderr ?? "inherit",
- stdin: this.#options?.stdin ?? "inherit",
- };
- return spawnChild(this.#command, options);
- }
-}
-
-export { ChildProcess, Command };
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 39a1def90..f83695952 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -22,7 +22,6 @@ import * as fsEvents from "internal:runtime/40_fs_events.js";
import * as process from "internal:runtime/40_process.js";
import * as signals from "internal:runtime/40_signals.js";
import * as tty from "internal:runtime/40_tty.js";
-import * as spawn from "internal:runtime/40_spawn.js";
// TODO(bartlomieju): this is funky we have two `http` imports
import * as httpRuntime from "internal:runtime/40_http.js";
@@ -148,9 +147,9 @@ const denoNs = {
consoleSize: tty.consoleSize,
gid: os.gid,
uid: os.uid,
- Command: spawn.Command,
+ Command: process.Command,
// TODO(bartlomieju): why is this exported?
- ChildProcess: spawn.ChildProcess,
+ ChildProcess: process.ChildProcess,
};
const denoNsUnstable = {
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index 0564474b1..48c22ca92 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -8,7 +8,6 @@ pub mod permissions;
pub mod process;
pub mod runtime;
pub mod signal;
-pub mod spawn;
pub mod tty;
mod utils;
pub mod web_worker;
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index ca37c08b7..ad14ef2e4 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -1,13 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use super::check_unstable;
+use super::signal;
use crate::permissions::PermissionsContainer;
use deno_core::error::AnyError;
use deno_core::op;
-use deno_io::ChildStderrResource;
-use deno_io::ChildStdinResource;
-use deno_io::ChildStdoutResource;
-use deno_io::StdFileResource;
-
use deno_core::serde_json;
use deno_core::AsyncMutFuture;
use deno_core::AsyncRefCell;
@@ -16,21 +13,26 @@ use deno_core::OpState;
use deno_core::RcRef;
use deno_core::Resource;
use deno_core::ResourceId;
+use deno_core::ZeroCopyBuf;
+use deno_io::ChildStderrResource;
+use deno_io::ChildStdinResource;
+use deno_io::ChildStdoutResource;
+use deno_io::StdFileResource;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
+use std::process::ExitStatus;
use std::rc::Rc;
use tokio::process::Command;
-#[cfg(unix)]
-use std::os::unix::process::ExitStatusExt;
+#[cfg(windows)]
+use std::os::windows::process::CommandExt;
-pub fn init() -> Extension {
- Extension::builder("deno_process")
- .ops(vec![op_run::decl(), op_run_status::decl(), op_kill::decl()])
- .build()
-}
+#[cfg(unix)]
+use std::os::unix::prelude::ExitStatusExt;
+#[cfg(unix)]
+use std::os::unix::process::CommandExt;
#[derive(Copy, Clone, Eq, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -98,10 +100,32 @@ impl StdioOrRid {
}
}
+pub fn init_ops() -> Extension {
+ Extension::builder("deno_process")
+ .ops(vec![
+ op_spawn_child::decl(),
+ op_spawn_wait::decl(),
+ op_spawn_sync::decl(),
+ deprecated::op_run::decl(),
+ deprecated::op_run_status::decl(),
+ deprecated::op_kill::decl(),
+ ])
+ .build()
+}
+
+struct ChildResource(tokio::process::Child);
+
+impl Resource for ChildResource {
+ fn name(&self) -> Cow<str> {
+ "child".into()
+ }
+}
+
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
-pub struct RunArgs {
- cmd: Vec<String>,
+pub struct SpawnArgs {
+ cmd: String,
+ args: Vec<String>,
cwd: Option<String>,
clear_env: bool,
env: Vec<(String, String)>,
@@ -109,142 +133,176 @@ pub struct RunArgs {
gid: Option<u32>,
#[cfg(unix)]
uid: Option<u32>,
- stdin: StdioOrRid,
- stdout: StdioOrRid,
- stderr: StdioOrRid,
+ #[cfg(windows)]
+ windows_raw_arguments: bool,
+
+ #[serde(flatten)]
+ stdio: ChildStdio,
}
-struct ChildResource {
- child: AsyncRefCell<tokio::process::Child>,
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ChildStdio {
+ stdin: Stdio,
+ stdout: Stdio,
+ stderr: Stdio,
}
-impl Resource for ChildResource {
- fn name(&self) -> Cow<str> {
- "child".into()
- }
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ChildStatus {
+ success: bool,
+ code: i32,
+ signal: Option<String>,
}
-impl ChildResource {
- fn borrow_mut(self: Rc<Self>) -> AsyncMutFuture<tokio::process::Child> {
- RcRef::map(self, |r| &r.child).borrow_mut()
+impl TryFrom<ExitStatus> for ChildStatus {
+ type Error = AnyError;
+
+ fn try_from(status: ExitStatus) -> Result<Self, Self::Error> {
+ let code = status.code();
+ #[cfg(unix)]
+ let signal = status.signal();
+ #[cfg(not(unix))]
+ let signal: Option<i32> = None;
+
+ let status = if let Some(signal) = signal {
+ ChildStatus {
+ success: false,
+ code: 128 + signal,
+ #[cfg(unix)]
+ signal: Some(
+ crate::ops::signal::signal_int_to_str(signal)?.to_string(),
+ ),
+ #[cfg(not(unix))]
+ signal: None,
+ }
+ } else {
+ let code = code.expect("Should have either an exit code or a signal.");
+
+ ChildStatus {
+ success: code == 0,
+ code,
+ signal: None,
+ }
+ };
+
+ Ok(status)
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
-// TODO(@AaronO): maybe find a more descriptive name or a convention for return structs
-struct RunInfo {
- rid: ResourceId,
- pid: Option<u32>,
- stdin_rid: Option<ResourceId>,
- stdout_rid: Option<ResourceId>,
- stderr_rid: Option<ResourceId>,
+pub struct SpawnOutput {
+ status: ChildStatus,
+ stdout: Option<ZeroCopyBuf>,
+ stderr: Option<ZeroCopyBuf>,
}
-#[op]
-fn op_run(state: &mut OpState, run_args: RunArgs) -> Result<RunInfo, AnyError> {
- let args = run_args.cmd;
+fn create_command(
+ state: &mut OpState,
+ args: SpawnArgs,
+ api_name: &str,
+) -> Result<std::process::Command, AnyError> {
state
.borrow_mut::<PermissionsContainer>()
- .check_run(&args[0], "Deno.run()")?;
- let env = run_args.env;
- let cwd = run_args.cwd;
-
- let mut c = Command::new(args.get(0).unwrap());
- (1..args.len()).for_each(|i| {
- let arg = args.get(i).unwrap();
- c.arg(arg);
- });
- cwd.map(|d| c.current_dir(d));
+ .check_run(&args.cmd, api_name)?;
+
+ let mut command = std::process::Command::new(args.cmd);
+
+ #[cfg(windows)]
+ if args.windows_raw_arguments {
+ for arg in args.args.iter() {
+ command.raw_arg(arg);
+ }
+ } else {
+ command.args(args.args);
+ }
- if run_args.clear_env {
- super::check_unstable(state, "Deno.run.clearEnv");
- c.env_clear();
+ #[cfg(not(windows))]
+ command.args(args.args);
+
+ if let Some(cwd) = args.cwd {
+ command.current_dir(cwd);
}
- for (key, value) in &env {
- c.env(key, value);
+
+ if args.clear_env {
+ command.env_clear();
}
+ command.envs(args.env);
#[cfg(unix)]
- if let Some(gid) = run_args.gid {
- super::check_unstable(state, "Deno.run.gid");
- c.gid(gid);
+ if let Some(gid) = args.gid {
+ command.gid(gid);
}
#[cfg(unix)]
- if let Some(uid) = run_args.uid {
- super::check_unstable(state, "Deno.run.uid");
- c.uid(uid);
+ if let Some(uid) = args.uid {
+ command.uid(uid);
}
#[cfg(unix)]
// TODO(bartlomieju):
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
- c.pre_exec(|| {
+ command.pre_exec(|| {
libc::setgroups(0, std::ptr::null());
Ok(())
});
}
- // TODO: make this work with other resources, eg. sockets
- c.stdin(run_args.stdin.as_stdio(state)?);
- c.stdout(
- match run_args.stdout {
- StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(1),
- value => value,
- }
- .as_stdio(state)?,
- );
- c.stderr(
- match run_args.stderr {
- StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(2),
- value => value,
- }
- .as_stdio(state)?,
- );
+ command.stdin(args.stdio.stdin.as_stdio());
+ command.stdout(match args.stdio.stdout {
+ Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?,
+ value => value.as_stdio(),
+ });
+ command.stderr(match args.stdio.stderr {
+ Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?,
+ value => value.as_stdio(),
+ });
+
+ Ok(command)
+}
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct Child {
+ rid: ResourceId,
+ pid: u32,
+ stdin_rid: Option<ResourceId>,
+ stdout_rid: Option<ResourceId>,
+ stderr_rid: Option<ResourceId>,
+}
+
+fn spawn_child(
+ state: &mut OpState,
+ command: std::process::Command,
+) -> Result<Child, AnyError> {
+ let mut command = tokio::process::Command::from(command);
+ // TODO(@crowlkats): allow detaching processes.
+ // currently deno will orphan a process when exiting with an error or Deno.exit()
// We want to kill child when it's closed
- c.kill_on_drop(true);
-
- // Spawn the command.
- let mut child = c.spawn()?;
- let pid = child.id();
-
- let stdin_rid = match child.stdin.take() {
- Some(child_stdin) => {
- let rid = state
- .resource_table
- .add(ChildStdinResource::from(child_stdin));
- Some(rid)
- }
- None => None,
- };
-
- let stdout_rid = match child.stdout.take() {
- Some(child_stdout) => {
- let rid = state
- .resource_table
- .add(ChildStdoutResource::from(child_stdout));
- Some(rid)
- }
- None => None,
- };
-
- let stderr_rid = match child.stderr.take() {
- Some(child_stderr) => {
- let rid = state
- .resource_table
- .add(ChildStderrResource::from(child_stderr));
- Some(rid)
- }
- None => None,
- };
+ command.kill_on_drop(true);
+
+ let mut child = command.spawn()?;
+ let pid = child.id().expect("Process ID should be set.");
+
+ let stdin_rid = child
+ .stdin
+ .take()
+ .map(|stdin| state.resource_table.add(ChildStdinResource::from(stdin)));
- let child_resource = ChildResource {
- child: AsyncRefCell::new(child),
- };
- let child_rid = state.resource_table.add(child_resource);
+ let stdout_rid = child
+ .stdout
+ .take()
+ .map(|stdout| state.resource_table.add(ChildStdoutResource::from(stdout)));
- Ok(RunInfo {
+ let stderr_rid = child
+ .stderr
+ .take()
+ .map(|stderr| state.resource_table.add(ChildStderrResource::from(stderr)));
+
+ let child_rid = state.resource_table.add(ChildResource(child));
+
+ Ok(Child {
rid: child_rid,
pid,
stdin_rid,
@@ -253,109 +311,325 @@ fn op_run(state: &mut OpState, run_args: RunArgs) -> Result<RunInfo, AnyError> {
})
}
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-struct ProcessStatus {
- got_signal: bool,
- exit_code: i32,
- exit_signal: i32,
+#[op]
+fn op_spawn_child(
+ state: &mut OpState,
+ args: SpawnArgs,
+ api_name: String,
+) -> Result<Child, AnyError> {
+ let command = create_command(state, args, &api_name)?;
+ spawn_child(state, command)
}
#[op]
-async fn op_run_status(
+async fn op_spawn_wait(
state: Rc<RefCell<OpState>>,
rid: ResourceId,
-) -> Result<ProcessStatus, AnyError> {
+) -> Result<ChildStatus, AnyError> {
let resource = state
.borrow_mut()
.resource_table
- .get::<ChildResource>(rid)?;
- let mut child = resource.borrow_mut().await;
- let run_status = child.wait().await?;
- 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();
+ .take::<ChildResource>(rid)?;
+ Rc::try_unwrap(resource)
+ .ok()
+ .unwrap()
+ .0
+ .wait()
+ .await?
+ .try_into()
+}
- Ok(ProcessStatus {
- got_signal,
- exit_code: code.unwrap_or(-1),
- exit_signal: signal.unwrap_or(-1),
+#[op]
+fn op_spawn_sync(
+ state: &mut OpState,
+ args: SpawnArgs,
+) -> Result<SpawnOutput, AnyError> {
+ let stdout = matches!(args.stdio.stdout, Stdio::Piped);
+ let stderr = matches!(args.stdio.stderr, Stdio::Piped);
+ let output =
+ create_command(state, args, "Deno.Command().outputSync()")?.output()?;
+
+ Ok(SpawnOutput {
+ status: output.status.try_into()?,
+ stdout: if stdout {
+ Some(output.stdout.into())
+ } else {
+ None
+ },
+ stderr: if stderr {
+ Some(output.stderr.into())
+ } else {
+ None
+ },
})
}
-#[cfg(unix)]
-pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
- let signo = super::signal::signal_str_to_int(signal)?;
- use nix::sys::signal::kill as unix_kill;
- use nix::sys::signal::Signal;
- use nix::unistd::Pid;
- let sig = Signal::try_from(signo)?;
- unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(AnyError::from)
-}
+mod deprecated {
+ use super::*;
+
+ #[derive(Deserialize)]
+ #[serde(rename_all = "camelCase")]
+ pub struct RunArgs {
+ cmd: Vec<String>,
+ cwd: Option<String>,
+ clear_env: bool,
+ env: Vec<(String, String)>,
+ #[cfg(unix)]
+ gid: Option<u32>,
+ #[cfg(unix)]
+ uid: Option<u32>,
+ stdin: StdioOrRid,
+ stdout: StdioOrRid,
+ stderr: StdioOrRid,
+ }
-#[cfg(not(unix))]
-pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
- use deno_core::error::type_error;
- use std::io::Error;
- use std::io::ErrorKind::NotFound;
- use winapi::shared::minwindef::DWORD;
- use winapi::shared::minwindef::FALSE;
- use winapi::shared::minwindef::TRUE;
- use winapi::shared::winerror::ERROR_INVALID_PARAMETER;
- use winapi::um::errhandlingapi::GetLastError;
- use winapi::um::handleapi::CloseHandle;
- use winapi::um::processthreadsapi::OpenProcess;
- use winapi::um::processthreadsapi::TerminateProcess;
- use winapi::um::winnt::PROCESS_TERMINATE;
-
- if !matches!(signal, "SIGKILL" | "SIGTERM") {
- Err(type_error(format!("Invalid signal: {signal}")))
- } else if pid <= 0 {
- Err(type_error("Invalid pid"))
- } else {
- // SAFETY: winapi call
- let handle = unsafe { OpenProcess(PROCESS_TERMINATE, FALSE, pid as DWORD) };
+ struct ChildResource {
+ child: AsyncRefCell<tokio::process::Child>,
+ }
- if handle.is_null() {
- // SAFETY: winapi call
- let err = match unsafe { GetLastError() } {
- ERROR_INVALID_PARAMETER => Error::from(NotFound), // Invalid `pid`.
- errno => Error::from_raw_os_error(errno as i32),
- };
- Err(err.into())
+ 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()
+ }
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename_all = "camelCase")]
+ // TODO(@AaronO): maybe find a more descriptive name or a convention for return structs
+ struct RunInfo {
+ rid: ResourceId,
+ pid: Option<u32>,
+ stdin_rid: Option<ResourceId>,
+ stdout_rid: Option<ResourceId>,
+ stderr_rid: Option<ResourceId>,
+ }
+
+ #[op]
+ fn op_run(
+ state: &mut OpState,
+ run_args: RunArgs,
+ ) -> Result<RunInfo, AnyError> {
+ let args = run_args.cmd;
+ state
+ .borrow_mut::<PermissionsContainer>()
+ .check_run(&args[0], "Deno.run()")?;
+ let env = run_args.env;
+ let cwd = run_args.cwd;
+
+ let mut c = Command::new(args.get(0).unwrap());
+ (1..args.len()).for_each(|i| {
+ let arg = args.get(i).unwrap();
+ c.arg(arg);
+ });
+ cwd.map(|d| c.current_dir(d));
+
+ if run_args.clear_env {
+ super::check_unstable(state, "Deno.run.clearEnv");
+ c.env_clear();
+ }
+ for (key, value) in &env {
+ c.env(key, value);
+ }
+
+ #[cfg(unix)]
+ if let Some(gid) = run_args.gid {
+ super::check_unstable(state, "Deno.run.gid");
+ c.gid(gid);
+ }
+ #[cfg(unix)]
+ if let Some(uid) = run_args.uid {
+ super::check_unstable(state, "Deno.run.uid");
+ c.uid(uid);
+ }
+ #[cfg(unix)]
+ // TODO(bartlomieju):
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ unsafe {
+ c.pre_exec(|| {
+ libc::setgroups(0, std::ptr::null());
+ Ok(())
+ });
+ }
+
+ // TODO: make this work with other resources, eg. sockets
+ c.stdin(run_args.stdin.as_stdio(state)?);
+ c.stdout(
+ match run_args.stdout {
+ StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(1),
+ value => value,
+ }
+ .as_stdio(state)?,
+ );
+ c.stderr(
+ match run_args.stderr {
+ StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(2),
+ value => value,
+ }
+ .as_stdio(state)?,
+ );
+
+ // We want to kill child when it's closed
+ c.kill_on_drop(true);
+
+ // Spawn the command.
+ let mut child = c.spawn()?;
+ let pid = child.id();
+
+ let stdin_rid = match child.stdin.take() {
+ Some(child_stdin) => {
+ let rid = state
+ .resource_table
+ .add(ChildStdinResource::from(child_stdin));
+ Some(rid)
+ }
+ None => None,
+ };
+
+ let stdout_rid = match child.stdout.take() {
+ Some(child_stdout) => {
+ let rid = state
+ .resource_table
+ .add(ChildStdoutResource::from(child_stdout));
+ Some(rid)
+ }
+ None => None,
+ };
+
+ let stderr_rid = match child.stderr.take() {
+ Some(child_stderr) => {
+ let rid = state
+ .resource_table
+ .add(ChildStderrResource::from(child_stderr));
+ Some(rid)
+ }
+ None => None,
+ };
+
+ let child_resource = ChildResource {
+ child: AsyncRefCell::new(child),
+ };
+ let child_rid = state.resource_table.add(child_resource);
+
+ Ok(RunInfo {
+ rid: child_rid,
+ pid,
+ stdin_rid,
+ stdout_rid,
+ stderr_rid,
+ })
+ }
+
+ #[derive(Serialize)]
+ #[serde(rename_all = "camelCase")]
+ struct ProcessStatus {
+ got_signal: bool,
+ exit_code: i32,
+ exit_signal: i32,
+ }
+
+ #[op]
+ async fn op_run_status(
+ state: Rc<RefCell<OpState>>,
+ rid: ResourceId,
+ ) -> Result<ProcessStatus, AnyError> {
+ let resource = state
+ .borrow_mut()
+ .resource_table
+ .get::<ChildResource>(rid)?;
+ let mut child = resource.borrow_mut().await;
+ let run_status = child.wait().await?;
+ 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(ProcessStatus {
+ got_signal,
+ exit_code: code.unwrap_or(-1),
+ exit_signal: signal.unwrap_or(-1),
+ })
+ }
+
+ #[cfg(unix)]
+ pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
+ let signo = super::signal::signal_str_to_int(signal)?;
+ use nix::sys::signal::kill as unix_kill;
+ use nix::sys::signal::Signal;
+ use nix::unistd::Pid;
+ 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: &str) -> Result<(), AnyError> {
+ use deno_core::error::type_error;
+ use std::io::Error;
+ use std::io::ErrorKind::NotFound;
+ use winapi::shared::minwindef::DWORD;
+ use winapi::shared::minwindef::FALSE;
+ use winapi::shared::minwindef::TRUE;
+ use winapi::shared::winerror::ERROR_INVALID_PARAMETER;
+ use winapi::um::errhandlingapi::GetLastError;
+ use winapi::um::handleapi::CloseHandle;
+ use winapi::um::processthreadsapi::OpenProcess;
+ use winapi::um::processthreadsapi::TerminateProcess;
+ use winapi::um::winnt::PROCESS_TERMINATE;
+
+ if !matches!(signal, "SIGKILL" | "SIGTERM") {
+ Err(type_error(format!("Invalid signal: {signal}")))
+ } else if pid <= 0 {
+ Err(type_error("Invalid pid"))
} else {
- // SAFETY: winapi calls
- unsafe {
- let is_terminated = TerminateProcess(handle, 1);
- CloseHandle(handle);
- match is_terminated {
- FALSE => Err(Error::last_os_error().into()),
- TRUE => Ok(()),
- _ => unreachable!(),
+ // SAFETY: winapi call
+ let handle =
+ unsafe { OpenProcess(PROCESS_TERMINATE, FALSE, pid as DWORD) };
+
+ if handle.is_null() {
+ // SAFETY: winapi call
+ let err = match unsafe { GetLastError() } {
+ ERROR_INVALID_PARAMETER => Error::from(NotFound), // Invalid `pid`.
+ errno => Error::from_raw_os_error(errno as i32),
+ };
+ Err(err.into())
+ } else {
+ // SAFETY: winapi calls
+ unsafe {
+ let is_terminated = TerminateProcess(handle, 1);
+ CloseHandle(handle);
+ match is_terminated {
+ FALSE => Err(Error::last_os_error().into()),
+ TRUE => Ok(()),
+ _ => unreachable!(),
+ }
}
}
}
}
-}
-#[op]
-fn op_kill(
- state: &mut OpState,
- pid: i32,
- signal: String,
- api_name: String,
-) -> Result<(), AnyError> {
- state
- .borrow_mut::<PermissionsContainer>()
- .check_run_all(&api_name)?;
- kill(pid, &signal)?;
- Ok(())
+ #[op]
+ fn op_kill(
+ state: &mut OpState,
+ pid: i32,
+ signal: String,
+ api_name: String,
+ ) -> Result<(), AnyError> {
+ state
+ .borrow_mut::<PermissionsContainer>()
+ .check_run_all(&api_name)?;
+ kill(pid, &signal)?;
+ Ok(())
+ }
}
diff --git a/runtime/ops/spawn.rs b/runtime/ops/spawn.rs
deleted file mode 100644
index 884c46229..000000000
--- a/runtime/ops/spawn.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use super::process::Stdio;
-use super::process::StdioOrRid;
-use crate::permissions::PermissionsContainer;
-use deno_core::error::AnyError;
-use deno_core::op;
-use deno_core::Extension;
-use deno_core::OpState;
-use deno_core::Resource;
-use deno_core::ResourceId;
-use deno_core::ZeroCopyBuf;
-use deno_io::ChildStderrResource;
-use deno_io::ChildStdinResource;
-use deno_io::ChildStdoutResource;
-use serde::Deserialize;
-use serde::Serialize;
-use std::borrow::Cow;
-use std::cell::RefCell;
-#[cfg(windows)]
-use std::os::windows::process::CommandExt;
-use std::process::ExitStatus;
-use std::rc::Rc;
-
-#[cfg(unix)]
-use std::os::unix::prelude::ExitStatusExt;
-#[cfg(unix)]
-use std::os::unix::process::CommandExt;
-
-pub fn init() -> Extension {
- Extension::builder("deno_spawn")
- .ops(vec![
- op_spawn_child::decl(),
- op_spawn_wait::decl(),
- op_spawn_sync::decl(),
- ])
- .build()
-}
-
-struct ChildResource(tokio::process::Child);
-
-impl Resource for ChildResource {
- fn name(&self) -> Cow<str> {
- "child".into()
- }
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SpawnArgs {
- cmd: String,
- args: Vec<String>,
- cwd: Option<String>,
- clear_env: bool,
- env: Vec<(String, String)>,
- #[cfg(unix)]
- gid: Option<u32>,
- #[cfg(unix)]
- uid: Option<u32>,
- #[cfg(windows)]
- windows_raw_arguments: bool,
-
- #[serde(flatten)]
- stdio: ChildStdio,
-}
-
-#[derive(Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ChildStdio {
- stdin: Stdio,
- stdout: Stdio,
- stderr: Stdio,
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ChildStatus {
- success: bool,
- code: i32,
- signal: Option<String>,
-}
-
-impl TryFrom<ExitStatus> for ChildStatus {
- type Error = AnyError;
-
- fn try_from(status: ExitStatus) -> Result<Self, Self::Error> {
- let code = status.code();
- #[cfg(unix)]
- let signal = status.signal();
- #[cfg(not(unix))]
- let signal: Option<i32> = None;
-
- let status = if let Some(signal) = signal {
- ChildStatus {
- success: false,
- code: 128 + signal,
- #[cfg(unix)]
- signal: Some(
- crate::ops::signal::signal_int_to_str(signal)?.to_string(),
- ),
- #[cfg(not(unix))]
- signal: None,
- }
- } else {
- let code = code.expect("Should have either an exit code or a signal.");
-
- ChildStatus {
- success: code == 0,
- code,
- signal: None,
- }
- };
-
- Ok(status)
- }
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SpawnOutput {
- status: ChildStatus,
- stdout: Option<ZeroCopyBuf>,
- stderr: Option<ZeroCopyBuf>,
-}
-
-fn create_command(
- state: &mut OpState,
- args: SpawnArgs,
- api_name: &str,
-) -> Result<std::process::Command, AnyError> {
- state
- .borrow_mut::<PermissionsContainer>()
- .check_run(&args.cmd, api_name)?;
-
- let mut command = std::process::Command::new(args.cmd);
-
- #[cfg(windows)]
- if args.windows_raw_arguments {
- for arg in args.args.iter() {
- command.raw_arg(arg);
- }
- } else {
- command.args(args.args);
- }
-
- #[cfg(not(windows))]
- command.args(args.args);
-
- if let Some(cwd) = args.cwd {
- command.current_dir(cwd);
- }
-
- if args.clear_env {
- command.env_clear();
- }
- command.envs(args.env);
-
- #[cfg(unix)]
- if let Some(gid) = args.gid {
- command.gid(gid);
- }
- #[cfg(unix)]
- if let Some(uid) = args.uid {
- command.uid(uid);
- }
- #[cfg(unix)]
- // TODO(bartlomieju):
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- command.pre_exec(|| {
- libc::setgroups(0, std::ptr::null());
- Ok(())
- });
- }
-
- command.stdin(args.stdio.stdin.as_stdio());
- command.stdout(match args.stdio.stdout {
- Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?,
- value => value.as_stdio(),
- });
- command.stderr(match args.stdio.stderr {
- Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?,
- value => value.as_stdio(),
- });
-
- Ok(command)
-}
-
-#[derive(Serialize)]
-#[serde(rename_all = "camelCase")]
-struct Child {
- rid: ResourceId,
- pid: u32,
- stdin_rid: Option<ResourceId>,
- stdout_rid: Option<ResourceId>,
- stderr_rid: Option<ResourceId>,
-}
-
-fn spawn_child(
- state: &mut OpState,
- command: std::process::Command,
-) -> Result<Child, AnyError> {
- let mut command = tokio::process::Command::from(command);
- // TODO(@crowlkats): allow detaching processes.
- // currently deno will orphan a process when exiting with an error or Deno.exit()
- // We want to kill child when it's closed
- command.kill_on_drop(true);
-
- let mut child = command.spawn()?;
- let pid = child.id().expect("Process ID should be set.");
-
- let stdin_rid = child
- .stdin
- .take()
- .map(|stdin| state.resource_table.add(ChildStdinResource::from(stdin)));
-
- let stdout_rid = child
- .stdout
- .take()
- .map(|stdout| state.resource_table.add(ChildStdoutResource::from(stdout)));
-
- let stderr_rid = child
- .stderr
- .take()
- .map(|stderr| state.resource_table.add(ChildStderrResource::from(stderr)));
-
- let child_rid = state.resource_table.add(ChildResource(child));
-
- Ok(Child {
- rid: child_rid,
- pid,
- stdin_rid,
- stdout_rid,
- stderr_rid,
- })
-}
-
-#[op]
-fn op_spawn_child(
- state: &mut OpState,
- args: SpawnArgs,
- api_name: String,
-) -> Result<Child, AnyError> {
- let command = create_command(state, args, &api_name)?;
- spawn_child(state, command)
-}
-
-#[op]
-async fn op_spawn_wait(
- state: Rc<RefCell<OpState>>,
- rid: ResourceId,
-) -> Result<ChildStatus, AnyError> {
- let resource = state
- .borrow_mut()
- .resource_table
- .take::<ChildResource>(rid)?;
- Rc::try_unwrap(resource)
- .ok()
- .unwrap()
- .0
- .wait()
- .await?
- .try_into()
-}
-
-#[op]
-fn op_spawn_sync(
- state: &mut OpState,
- args: SpawnArgs,
-) -> Result<SpawnOutput, AnyError> {
- let stdout = matches!(args.stdio.stdout, Stdio::Piped);
- let stderr = matches!(args.stdio.stderr, Stdio::Piped);
- let output =
- create_command(state, args, "Deno.Command().outputSync()")?.output()?;
-
- Ok(SpawnOutput {
- status: output.status.try_into()?,
- stdout: if stdout {
- Some(output.stdout.into())
- } else {
- None
- },
- stderr: if stderr {
- Some(output.stderr.into())
- } else {
- None
- },
- })
-}
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index fbf10905f..ffafbc1d3 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -438,8 +438,7 @@ impl WebWorker {
deno_node::init::<PermissionsContainer>(options.npm_resolver),
ops::os::init_for_worker(),
ops::permissions::init(),
- ops::process::init(),
- ops::spawn::init(),
+ ops::process::init_ops(),
ops::signal::init(),
ops::tty::init(),
deno_http::init(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 1f567837a..8fa17a7f5 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -254,7 +254,6 @@ impl MainWorker {
options.web_worker_pre_execute_module_cb.clone(),
options.format_js_error_fn.clone(),
),
- ops::spawn::init(),
ops::fs_events::init(),
ops::fs::init::<PermissionsContainer>(),
deno_io::init(options.stdio),
@@ -269,7 +268,7 @@ impl MainWorker {
deno_node::init::<PermissionsContainer>(options.npm_resolver),
ops::os::init(exit_code.clone()),
ops::permissions::init(),
- ops::process::init(),
+ ops::process::init_ops(),
ops::signal::init(),
ops::tty::init(),
deno_http::init(),