diff --git a/src/interfaces.ts b/src/interfaces.ts index a269e77b..6a8c4ae8 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -115,6 +115,7 @@ interface IBasePtyForkOptions { export interface IPtyForkOptions extends IBasePtyForkOptions { uid?: number; gid?: number; + argv0?: string; } export interface IWindowsPtyForkOptions extends IBasePtyForkOptions { diff --git a/src/native.d.ts b/src/native.d.ts index c53e086b..ca866cc5 100644 --- a/src/native.d.ts +++ b/src/native.d.ts @@ -19,7 +19,7 @@ interface IWinptyNative { } 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; + 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, argv0: string): IUnixProcess; open(cols: number, rows: number): IUnixOpenProcess; process(fd: number, pty?: string): string; resize(fd: number, cols: number, rows: number): void; diff --git a/src/unix/pty.cc b/src/unix/pty.cc index 7b4b9e1f..41953fc7 100644 --- a/src/unix/pty.cc +++ b/src/unix/pty.cc @@ -262,7 +262,7 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { Napi::Env napiEnv(info.Env()); Napi::HandleScope scope(napiEnv); - if (info.Length() != 11 || + if (info.Length() != 12 || !info[0].IsString() || !info[1].IsArray() || !info[2].IsArray() || @@ -273,8 +273,9 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { !info[7].IsNumber() || !info[8].IsBoolean() || !info[9].IsString() || - !info[10].IsFunction()) { - throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)"); + !info[10].IsFunction() || + !info[11].IsString()) { + throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit, argv0)"); } // file @@ -351,20 +352,24 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { // helperPath std::string helper_path = info[9].As(); + // argv0 + std::string argv0 = info[11].As(); + pid_t pid; int master; #if defined(__APPLE__) int argc = argv_.Length(); - int argl = argc + 4; + int argl = argc + 5; std::unique_ptr argv_unique_ptr(new char *[argl], DelBuf(argl)); char **argv = argv_unique_ptr.get(); argv[0] = strdup(helper_path.c_str()); argv[1] = strdup(cwd_.c_str()); argv[2] = strdup(file.c_str()); + argv[3] = strdup(argv0.c_str()); argv[argl - 1] = NULL; for (int i = 0; i < argc; i++) { std::string arg = argv_.Get(i).As(); - argv[i + 3] = strdup(arg.c_str()); + argv[i + 4] = strdup(arg.c_str()); } int err = -1; @@ -380,7 +385,7 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { int argl = argc + 2; std::unique_ptr argv_unique_ptr(new char *[argl], DelBuf(argl)); char** argv = argv_unique_ptr.get(); - argv[0] = strdup(file.c_str()); + argv[0] = strdup(argv0.c_str()); argv[argl - 1] = NULL; for (int i = 0; i < argc; i++) { std::string arg = argv_.Get(i).As(); @@ -436,7 +441,8 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { { char **old = environ; environ = env; - execvp(argv[0], argv); + char* file_ = strdup(file.c_str()); + execvp(file_, argv); environ = old; perror("execvp(3) failed."); _exit(1); diff --git a/src/unix/spawn-helper.cc b/src/unix/spawn-helper.cc index 8066328f..1cb23148 100644 --- a/src/unix/spawn-helper.cc +++ b/src/unix/spawn-helper.cc @@ -12,7 +12,7 @@ int main (int argc, char** argv) { char *cwd = argv[1]; char *file = argv[2]; - argv = &argv[2]; + argv = &argv[3]; if (strlen(cwd) && chdir(cwd) == -1) { _exit(1); diff --git a/src/unixTerminal.test.ts b/src/unixTerminal.test.ts index 69647468..31e5d4c1 100644 --- a/src/unixTerminal.test.ts +++ b/src/unixTerminal.test.ts @@ -362,6 +362,31 @@ if (process.platform !== 'win32') { } }); }); + it('should default argv0 to file', (done) => { + const term = new UnixTerminal('/bin/sh', + [ '-c', 'echo $0' ]); + let argv0 = ''; + term.on('data', (data) => { + argv0 = argv0.concat(data); + }); + term.on('exit', () => { + assert.strictEqual(argv0.trim(), '/bin/sh'); + done(); + }); + }); + it('should allow an alternate argv0', (done) => { + const term = new UnixTerminal('/bin/sh', + [ '-c', 'echo $0' ], + { argv0: 'alternate' }); + let argv0 = ''; + term.on('data', (data) => { + argv0 = argv0.concat(data); + }); + term.on('exit', () => { + assert.strictEqual(argv0.trim(), 'alternate'); + done(); + }); + }); }); }); } diff --git a/src/unixTerminal.ts b/src/unixTerminal.ts index 98733dc0..fd215958 100644 --- a/src/unixTerminal.ts +++ b/src/unixTerminal.ts @@ -102,8 +102,10 @@ export class UnixTerminal extends Terminal { this.emit('exit', code, signal); }; + const argv0 = opt.argv0 ?? file; + // fork - const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), helperPath, onexit); + const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), helperPath, onexit, argv0); this._socket = new tty.ReadStream(term.fd); if (encoding !== null) { diff --git a/typings/node-pty.d.ts b/typings/node-pty.d.ts index 6f050ff1..2dd5db9d 100644 --- a/typings/node-pty.d.ts +++ b/typings/node-pty.d.ts @@ -83,6 +83,14 @@ declare module 'node-pty' { */ uid?: number; gid?: number; + + /** + * Alternate argv[0] to use instead of the file being launched. Can be used to launch a shell + * as a login shell, e.g. using `"-sh"` when launching /bin/sh. + * + * This setting does nothing on Windows. + */ + argv0?: string; } export interface IWindowsPtyForkOptions extends IBasePtyForkOptions {