summaryrefslogtreecommitdiff
path: root/runtime/ops/process.rs
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops/process.rs')
-rw-r--r--runtime/ops/process.rs147
1 files changed, 134 insertions, 13 deletions
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index 6f89e5529..e8dc9c690 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -141,7 +141,6 @@ pub struct SpawnArgs {
uid: Option<u32>,
#[cfg(windows)]
windows_raw_arguments: bool,
- #[cfg(unix)]
ipc: Option<i32>,
#[serde(flatten)]
@@ -207,12 +206,7 @@ pub struct SpawnOutput {
stderr: Option<ToJsBuffer>,
}
-type CreateCommand = (
- std::process::Command,
- // TODO(@littledivy): Ideally this would return Option<ResourceId> but we are dealing with file descriptors
- // all the way until setupChannel which makes it easier to share code between parent and child fork.
- Option<i32>,
-);
+type CreateCommand = (std::process::Command, Option<ResourceId>);
fn create_command(
state: &mut OpState,
@@ -337,17 +331,144 @@ fn create_command(
});
/* One end returned to parent process (this) */
- let pipe_fd = Some(fd1);
+ let pipe_rid = Some(
+ state
+ .resource_table
+ .add(deno_node::IpcJsonStreamResource::new(fd1 as _)?),
+ );
/* The other end passed to child process via DENO_CHANNEL_FD */
command.env("DENO_CHANNEL_FD", format!("{}", ipc));
- return Ok((command, pipe_fd));
+ return Ok((command, pipe_rid));
}
Ok((command, None))
}
+ #[cfg(windows)]
+ // Safety: We setup a windows named pipe and pass one end to the child process.
+ unsafe {
+ use windows_sys::Win32::Foundation::CloseHandle;
+ use windows_sys::Win32::Foundation::DuplicateHandle;
+ use windows_sys::Win32::Foundation::DUPLICATE_SAME_ACCESS;
+ use windows_sys::Win32::Foundation::ERROR_ACCESS_DENIED;
+ use windows_sys::Win32::Foundation::ERROR_PIPE_CONNECTED;
+ use windows_sys::Win32::Foundation::GENERIC_READ;
+ use windows_sys::Win32::Foundation::GENERIC_WRITE;
+ use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
+ use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
+ use windows_sys::Win32::Storage::FileSystem::CreateFileW;
+ use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_FIRST_PIPE_INSTANCE;
+ use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED;
+ use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING;
+ use windows_sys::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX;
+ use windows_sys::Win32::System::Pipes::ConnectNamedPipe;
+ use windows_sys::Win32::System::Pipes::CreateNamedPipeW;
+ use windows_sys::Win32::System::Pipes::PIPE_READMODE_BYTE;
+ use windows_sys::Win32::System::Pipes::PIPE_TYPE_BYTE;
+ use windows_sys::Win32::System::Threading::GetCurrentProcess;
+
+ use std::io;
+ use std::os::windows::ffi::OsStrExt;
+ use std::path::Path;
+ use std::ptr;
+
+ if let Some(ipc) = args.ipc {
+ if ipc < 0 {
+ return Ok((command, None));
+ }
+
+ let (path, hd1) = loop {
+ let name = format!("\\\\.\\pipe\\{}", uuid::Uuid::new_v4());
+ let mut path = Path::new(&name)
+ .as_os_str()
+ .encode_wide()
+ .collect::<Vec<_>>();
+ path.push(0);
+
+ let hd1 = CreateNamedPipeW(
+ path.as_ptr(),
+ PIPE_ACCESS_DUPLEX
+ | FILE_FLAG_FIRST_PIPE_INSTANCE
+ | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ 65536,
+ 65536,
+ 0,
+ std::ptr::null_mut(),
+ );
+
+ if hd1 == INVALID_HANDLE_VALUE {
+ let err = io::Error::last_os_error();
+ /* If the pipe name is already in use, try again. */
+ if err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32) {
+ continue;
+ }
+
+ return Err(err.into());
+ }
+
+ break (path, hd1);
+ };
+
+ /* Create child pipe handle. */
+ let s = SECURITY_ATTRIBUTES {
+ nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
+ lpSecurityDescriptor: ptr::null_mut(),
+ bInheritHandle: 1,
+ };
+ let mut hd2 = CreateFileW(
+ path.as_ptr(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ &s,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED,
+ 0,
+ );
+ if hd2 == INVALID_HANDLE_VALUE {
+ return Err(io::Error::last_os_error().into());
+ }
+
+ // Will not block because we have create the pair.
+ if ConnectNamedPipe(hd1, ptr::null_mut()) == 0 {
+ let err = std::io::Error::last_os_error();
+ if err.raw_os_error() != Some(ERROR_PIPE_CONNECTED as i32) {
+ CloseHandle(hd2);
+ return Err(err.into());
+ }
+ }
+
+ // Duplicating the handle to allow the child process to use it.
+ if DuplicateHandle(
+ GetCurrentProcess(),
+ hd2,
+ GetCurrentProcess(),
+ &mut hd2,
+ 0,
+ 1,
+ DUPLICATE_SAME_ACCESS,
+ ) == 0
+ {
+ return Err(std::io::Error::last_os_error().into());
+ }
+
+ /* One end returned to parent process (this) */
+ let pipe_fd = Some(
+ state
+ .resource_table
+ .add(deno_node::IpcJsonStreamResource::new(hd1 as i64)?),
+ );
+
+ /* The other end passed to child process via DENO_CHANNEL_FD */
+ command.env("DENO_CHANNEL_FD", format!("{}", hd2 as i64));
+
+ return Ok((command, pipe_fd));
+ }
+ }
+
#[cfg(not(unix))]
return Ok((command, None));
}
@@ -360,13 +481,13 @@ struct Child {
stdin_rid: Option<ResourceId>,
stdout_rid: Option<ResourceId>,
stderr_rid: Option<ResourceId>,
- pipe_fd: Option<i32>,
+ pipe_fd: Option<ResourceId>,
}
fn spawn_child(
state: &mut OpState,
command: std::process::Command,
- pipe_fd: Option<i32>,
+ pipe_fd: Option<ResourceId>,
) -> Result<Child, AnyError> {
let mut command = tokio::process::Command::from(command);
// TODO(@crowlkats): allow detaching processes.
@@ -459,8 +580,8 @@ fn op_spawn_child(
#[serde] args: SpawnArgs,
#[string] api_name: String,
) -> Result<Child, AnyError> {
- let (command, pipe_fd) = create_command(state, args, &api_name)?;
- spawn_child(state, command, pipe_fd)
+ let (command, pipe_rid) = create_command(state, args, &api_name)?;
+ spawn_child(state, command, pipe_rid)
}
#[op2(async)]