summaryrefslogtreecommitdiff
path: root/runtime/ops/spawn.rs
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops/spawn.rs')
-rw-r--r--runtime/ops/spawn.rs289
1 files changed, 0 insertions, 289 deletions
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
- },
- })
-}