From a2bf61d1ae3ba2ff746a98ad2f0a96b6fc7782d0 Mon Sep 17 00:00:00 2001 From: uki00a Date: Wed, 8 Jul 2020 23:35:45 +0900 Subject: [PATCH] feat(unstable): Deno.ppid (#6539) --- cli/Cargo.toml | 2 +- cli/js/lib.deno.unstable.d.ts | 5 +++ cli/js/ops/runtime.ts | 1 + cli/js/runtime_main.ts | 3 +- cli/ops/runtime.rs | 60 +++++++++++++++++++++++++++++++++++ cli/tests/unit/os_test.ts | 22 +++++++++++++ 6 files changed, 91 insertions(+), 2 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 11665023d3..64688d5d0c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,7 +64,7 @@ swc_ecma_visit = "0.7.0" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.8", features = ["knownfolders", "objbase", "shlobj", -"winbase", "winerror"] } +"winbase", "winerror", "tlhelp32"] } fwdansi = "1.1.0" [target.'cfg(unix)'.dependencies] diff --git a/cli/js/lib.deno.unstable.d.ts b/cli/js/lib.deno.unstable.d.ts index 20096cb287..cec4f7f466 100644 --- a/cli/js/lib.deno.unstable.d.ts +++ b/cli/js/lib.deno.unstable.d.ts @@ -1194,4 +1194,9 @@ declare namespace Deno { * ``` */ export function fstat(rid: number): Promise; + + /** **UNSTABLE**: New API, yet to be vetted. + * The pid of the current process's parent. + */ + export const ppid: number; } diff --git a/cli/js/ops/runtime.ts b/cli/js/ops/runtime.ts index 70addf4691..09208df6de 100644 --- a/cli/js/ops/runtime.ts +++ b/cli/js/ops/runtime.ts @@ -9,6 +9,7 @@ export interface Start { denoVersion: string; noColor: boolean; pid: number; + ppid: number; repl: boolean; target: string; tsVersion: string; diff --git a/cli/js/runtime_main.ts b/cli/js/runtime_main.ts index 25a6b0f932..0c579626b1 100644 --- a/cli/js/runtime_main.ts +++ b/cli/js/runtime_main.ts @@ -96,10 +96,11 @@ export function bootstrapMainRuntime(): void { } }); - const { args, cwd, noColor, pid, repl, unstableFlag } = runtime.start(); + const { args, cwd, noColor, pid, ppid, repl, unstableFlag } = runtime.start(); Object.defineProperties(denoNs, { pid: readOnly(pid), + ppid: readOnly(ppid), noColor: readOnly(noColor), args: readOnly(Object.freeze(args)), }); diff --git a/cli/ops/runtime.rs b/cli/ops/runtime.rs index e47b40792d..1e426ad8ae 100644 --- a/cli/ops/runtime.rs +++ b/cli/ops/runtime.rs @@ -32,6 +32,7 @@ fn op_start( "denoVersion": version::DENO, "noColor": !colors::use_color(), "pid": std::process::id(), + "ppid": ppid(), "repl": gs.flags.subcommand == DenoSubcommand::Repl, "target": env!("TARGET"), "tsVersion": version::TYPESCRIPT, @@ -78,3 +79,62 @@ fn op_metrics( "bytesReceived": m.bytes_received }))) } + +fn ppid() -> Value { + #[cfg(windows)] + { + // Adopted from rustup: + // https://github.com/rust-lang/rustup/blob/1.21.1/src/cli/self_update.rs#L1036 + // Copyright Diggory Blake, the Mozilla Corporation, and rustup contributors. + // Licensed under either of + // - Apache License, Version 2.0 + // - MIT license + use std::mem; + use winapi::shared::minwindef::DWORD; + use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; + use winapi::um::processthreadsapi::GetCurrentProcessId; + use winapi::um::tlhelp32::{ + CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, + TH32CS_SNAPPROCESS, + }; + unsafe { + // Take a snapshot of system processes, one of which is ours + // and contains our parent's pid + let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if snapshot == INVALID_HANDLE_VALUE { + return serde_json::to_value(-1).unwrap(); + } + + let mut entry: PROCESSENTRY32 = mem::zeroed(); + entry.dwSize = mem::size_of::() as DWORD; + + // Iterate over system processes looking for ours + let success = Process32First(snapshot, &mut entry); + if success == 0 { + CloseHandle(snapshot); + return serde_json::to_value(-1).unwrap(); + } + + let this_pid = GetCurrentProcessId(); + while entry.th32ProcessID != this_pid { + let success = Process32Next(snapshot, &mut entry); + if success == 0 { + CloseHandle(snapshot); + return serde_json::to_value(-1).unwrap(); + } + } + CloseHandle(snapshot); + + // FIXME: Using the process ID exposes a race condition + // wherein the parent process already exited and the OS + // reassigned its ID. + let parent_id = entry.th32ParentProcessID; + serde_json::to_value(parent_id).unwrap() + } + } + #[cfg(not(windows))] + { + use std::os::unix::process::parent_id; + serde_json::to_value(parent_id()).unwrap() + } +} diff --git a/cli/tests/unit/os_test.ts b/cli/tests/unit/os_test.ts index b737d939a9..e969464e51 100644 --- a/cli/tests/unit/os_test.ts +++ b/cli/tests/unit/os_test.ts @@ -111,6 +111,28 @@ unitTest(function osPid(): void { assert(Deno.pid > 0); }); +unitTest(function osPpid(): void { + assert(Deno.ppid > 0); +}); + +unitTest( + { perms: { run: true, read: true } }, + async function osPpidIsEqualToPidOfParentProcess(): Promise { + const decoder = new TextDecoder(); + const process = Deno.run({ + cmd: [Deno.execPath(), "eval", "-p", "--unstable", "Deno.ppid"], + stdout: "piped", + env: { NO_COLOR: "true" }, + }); + const output = await process.output(); + process.close(); + + const expected = Deno.pid; + const actual = parseInt(decoder.decode(output)); + assertEquals(actual, expected); + } +); + unitTest({ perms: { read: true } }, function execPath(): void { assertNotEquals(Deno.execPath(), ""); });