mirror of
https://github.com/denoland/deno.git
synced 2024-11-24 15:19:26 -05:00
fix(ext/node): support stdin child_process IPC & fd stdout/stderr (#24106)
Add supports for "ipc" and fd options in child_process spawn API. Internal changes: Adds a hidden rid and "ipc_for_internal_use" option to Deno.Command. Used by `node:child_process` Example: ```js const out = fs.openSync("./logfile.txt", 'a') const proc = spawn(process.execPath, ["./main.mjs", "child"], { stdio: ["ipc", out, "inherit"] }); ``` Ref #16753
This commit is contained in:
parent
ed20102713
commit
3735a1a542
5 changed files with 62 additions and 17 deletions
|
@ -362,17 +362,25 @@ export class ChildProcess extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
const supportedNodeStdioTypes: NodeStdio[] = ["pipe", "ignore", "inherit"];
|
||||
const supportedNodeStdioTypes: NodeStdio[] = [
|
||||
"pipe",
|
||||
"ignore",
|
||||
"inherit",
|
||||
"ipc",
|
||||
];
|
||||
function toDenoStdio(
|
||||
pipe: NodeStdio | number | Stream | null | undefined,
|
||||
): DenoStdio {
|
||||
if (pipe instanceof Stream) {
|
||||
return "inherit";
|
||||
}
|
||||
if (typeof pipe === "number") {
|
||||
/* Assume it's a rid returned by fs APIs */
|
||||
return pipe;
|
||||
}
|
||||
|
||||
if (
|
||||
!supportedNodeStdioTypes.includes(pipe as NodeStdio) ||
|
||||
typeof pipe === "number"
|
||||
!supportedNodeStdioTypes.includes(pipe as NodeStdio)
|
||||
) {
|
||||
notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`);
|
||||
}
|
||||
|
@ -385,6 +393,8 @@ function toDenoStdio(
|
|||
return "null";
|
||||
case "inherit":
|
||||
return "inherit";
|
||||
case "ipc":
|
||||
return "ipc_for_internal_use";
|
||||
default:
|
||||
notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`);
|
||||
}
|
||||
|
@ -1083,8 +1093,7 @@ function toDenoArgs(args: string[]): string[] {
|
|||
|
||||
if (useRunArgs) {
|
||||
// -A is not ideal, but needed to propagate permissions.
|
||||
// --unstable is needed for Node compat.
|
||||
denoArgs.unshift("run", "-A", "--unstable");
|
||||
denoArgs.unshift("run", "-A");
|
||||
}
|
||||
|
||||
return denoArgs;
|
||||
|
|
|
@ -37,11 +37,12 @@ use std::os::unix::process::CommandExt;
|
|||
pub const UNSTABLE_FEATURE_NAME: &str = "process";
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Stdio {
|
||||
Inherit,
|
||||
Piped,
|
||||
Null,
|
||||
IpcForInternalUse,
|
||||
}
|
||||
|
||||
impl Stdio {
|
||||
|
@ -50,6 +51,7 @@ impl Stdio {
|
|||
Stdio::Inherit => std::process::Stdio::inherit(),
|
||||
Stdio::Piped => std::process::Stdio::piped(),
|
||||
Stdio::Null => std::process::Stdio::null(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +74,9 @@ impl<'de> Deserialize<'de> for StdioOrRid {
|
|||
"inherit" => Ok(StdioOrRid::Stdio(Stdio::Inherit)),
|
||||
"piped" => Ok(StdioOrRid::Stdio(Stdio::Piped)),
|
||||
"null" => Ok(StdioOrRid::Stdio(Stdio::Null)),
|
||||
"ipc_for_internal_use" => {
|
||||
Ok(StdioOrRid::Stdio(Stdio::IpcForInternalUse))
|
||||
}
|
||||
val => Err(serde::de::Error::unknown_variant(
|
||||
val,
|
||||
&["inherit", "piped", "null"],
|
||||
|
@ -102,6 +107,10 @@ impl StdioOrRid {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ipc(&self) -> bool {
|
||||
matches!(self, StdioOrRid::Stdio(Stdio::IpcForInternalUse))
|
||||
}
|
||||
}
|
||||
|
||||
deno_core::extension!(
|
||||
|
@ -150,9 +159,9 @@ pub struct SpawnArgs {
|
|||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChildStdio {
|
||||
stdin: Stdio,
|
||||
stdout: Stdio,
|
||||
stderr: Stdio,
|
||||
stdin: StdioOrRid,
|
||||
stdout: StdioOrRid,
|
||||
stderr: StdioOrRid,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
@ -210,7 +219,7 @@ type CreateCommand = (std::process::Command, Option<ResourceId>);
|
|||
|
||||
fn create_command(
|
||||
state: &mut OpState,
|
||||
args: SpawnArgs,
|
||||
mut args: SpawnArgs,
|
||||
api_name: &str,
|
||||
) -> Result<CreateCommand, AnyError> {
|
||||
state
|
||||
|
@ -249,14 +258,19 @@ fn create_command(
|
|||
command.uid(uid);
|
||||
}
|
||||
|
||||
command.stdin(args.stdio.stdin.as_stdio());
|
||||
if args.stdio.stdin.is_ipc() {
|
||||
args.ipc = Some(0);
|
||||
} else {
|
||||
command.stdin(args.stdio.stdin.as_stdio(state)?);
|
||||
}
|
||||
|
||||
command.stdout(match args.stdio.stdout {
|
||||
Stdio::Inherit => StdioOrRid::Rid(1).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(1).as_stdio(state)?,
|
||||
value => value.as_stdio(state)?,
|
||||
});
|
||||
command.stderr(match args.stdio.stderr {
|
||||
Stdio::Inherit => StdioOrRid::Rid(2).as_stdio(state)?,
|
||||
value => value.as_stdio(),
|
||||
StdioOrRid::Stdio(Stdio::Inherit) => StdioOrRid::Rid(2).as_stdio(state)?,
|
||||
value => value.as_stdio(state)?,
|
||||
});
|
||||
|
||||
#[cfg(unix)]
|
||||
|
@ -608,8 +622,8 @@ fn op_spawn_sync(
|
|||
state: &mut OpState,
|
||||
#[serde] args: SpawnArgs,
|
||||
) -> Result<SpawnOutput, AnyError> {
|
||||
let stdout = matches!(args.stdio.stdout, Stdio::Piped);
|
||||
let stderr = matches!(args.stdio.stderr, Stdio::Piped);
|
||||
let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped));
|
||||
let (mut command, _) =
|
||||
create_command(state, args, "Deno.Command().outputSync()")?;
|
||||
let output = command.output().with_context(|| {
|
||||
|
|
5
tests/specs/node/stdio_ipc/__test__.jsonc
Normal file
5
tests/specs/node/stdio_ipc/__test__.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"args": "run -A main.mjs",
|
||||
"output": "main.out",
|
||||
"exitCode": 0
|
||||
}
|
16
tests/specs/node/stdio_ipc/main.mjs
Normal file
16
tests/specs/node/stdio_ipc/main.mjs
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { spawn } from "node:child_process";
|
||||
import process from "node:process";
|
||||
|
||||
if (process.argv[2] === "child") {
|
||||
process.send("hahah");
|
||||
} else {
|
||||
const proc = spawn(process.execPath, ["./main.mjs", "child"], {
|
||||
stdio: ["ipc", "inherit", "inherit"],
|
||||
});
|
||||
|
||||
proc.on("message", function (msg) {
|
||||
console.log(`msg: ${msg}`);
|
||||
proc.kill();
|
||||
Deno.exit(0);
|
||||
});
|
||||
}
|
1
tests/specs/node/stdio_ipc/main.out
Normal file
1
tests/specs/node/stdio_ipc/main.out
Normal file
|
@ -0,0 +1 @@
|
|||
msg: hahah
|
Loading…
Reference in a new issue