diff options
Diffstat (limited to 'runtime/ops/spawn.rs')
-rw-r--r-- | runtime/ops/spawn.rs | 289 |
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 - }, - }) -} |