diff --git a/src/interfaces.ts b/src/interfaces.ts index 461b713e..01a88635 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -18,6 +18,11 @@ export interface ITerminal { */ pid: number; + /** + * Gets the foreground process group ID, if available. + */ + readonly foregroundPid?: number; + /** * Writes data to the socket. * @param data The data to write. diff --git a/src/native.d.ts b/src/native.d.ts index 03bfbeba..a813615c 100644 --- a/src/native.d.ts +++ b/src/native.d.ts @@ -14,6 +14,7 @@ interface IUnixNative { fork(file: string, args: string[], parsedEnv: string[], cwd: string, cols: number, rows: number, uid: number, gid: number, useUtf8: boolean, helperPath: string, onExitCallback: (code: number, signal: number) => void): IUnixProcess; open(cols: number, rows: number): IUnixOpenProcess; process(fd: number, pty?: string): string; + foregroundPid(fd: number): number | undefined; resize(fd: number, cols: number, rows: number, pixelWidth: number, pixelHeight: number): void; } diff --git a/src/unix/pty.cc b/src/unix/pty.cc index 88b1a4c9..ee2757c5 100644 --- a/src/unix/pty.cc +++ b/src/unix/pty.cc @@ -256,6 +256,7 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info); Napi::Value PtyOpen(const Napi::CallbackInfo& info); Napi::Value PtyResize(const Napi::CallbackInfo& info); Napi::Value PtyGetProc(const Napi::CallbackInfo& info); +Napi::Value PtyGetForegroundPid(const Napi::CallbackInfo& info); /** * Functions @@ -619,6 +620,30 @@ Napi::Value PtyGetProc(const Napi::CallbackInfo& info) { return name_; } +/** + * Foreground Process Group PID + * + * Returns the pid of the foreground process group attached to the pty + * referenced by `fd`, or undefined if the lookup fails. Cross-platform + * (Linux + macOS) — wraps tcgetpgrp(3), which is what `process` already + * calls internally to resolve the foreground process name. + */ +Napi::Value PtyGetForegroundPid(const Napi::CallbackInfo& info) { + Napi::Env env(info.Env()); + Napi::HandleScope scope(env); + + if (info.Length() != 1 || !info[0].IsNumber()) { + throw Napi::Error::New(env, "Usage: pty.foregroundPid(fd)"); + } + + int fd = info[0].As().Int32Value(); + pid_t pgrp = tcgetpgrp(fd); + if (pgrp == -1) { + return env.Undefined(); + } + return Napi::Number::New(env, static_cast(pgrp)); +} + /** * Nonblocking FD */ @@ -869,6 +894,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) { exports.Set("open", Napi::Function::New(env, PtyOpen)); exports.Set("resize", Napi::Function::New(env, PtyResize)); exports.Set("process", Napi::Function::New(env, PtyGetProc)); + exports.Set("foregroundPid", Napi::Function::New(env, PtyGetForegroundPid)); return exports; } diff --git a/src/unixTerminal.test.ts b/src/unixTerminal.test.ts index f13e66d4..ed9cab65 100644 --- a/src/unixTerminal.test.ts +++ b/src/unixTerminal.test.ts @@ -288,6 +288,18 @@ if (process.platform !== 'win32') { }, 1000); }); } + it('should return the foreground process group pid', (done) => { + const term = new UnixTerminal('node', ['-e', 'console.log("ready"); setTimeout(() => null, 200);']); + term.onData((data) => { + if (!data.includes('ready')) { + return; + } + assert.strictEqual(typeof term.foregroundPid, 'number'); + assert.strictEqual(term.foregroundPid, term.pid); + term.on('exit', () => done()); + term.kill(); + }); + }); if (process.platform === 'darwin') { it('should return the name of the process', (done) => { const term = new UnixTerminal('/bin/echo'); diff --git a/src/unixTerminal.ts b/src/unixTerminal.ts index 2776d501..2392d9ad 100644 --- a/src/unixTerminal.ts +++ b/src/unixTerminal.ts @@ -173,6 +173,11 @@ export class UnixTerminal extends Terminal { /* Accessors */ get fd(): number { return this._fd; } get ptsName(): string { return this._pty; } + /** + * Pid of the foreground process group attached to the pty, or undefined + * if the lookup fails (e.g. the pty has exited). Wraps tcgetpgrp(3). + */ + get foregroundPid(): number | undefined { return pty.foregroundPid(this._fd); } /** * openpty diff --git a/typings/node-pty.d.ts b/typings/node-pty.d.ts index a6fc5dd0..f6273ddc 100644 --- a/typings/node-pty.d.ts +++ b/typings/node-pty.d.ts @@ -120,6 +120,11 @@ declare module 'node-pty' { */ readonly pid: number; + /** + * The foreground process group ID, if available. + */ + readonly foregroundPid?: number; + /** * The column size in characters. */