diff --git a/src/interfaces.ts b/src/interfaces.ts index c05031f81..d50535149 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 2ba107699..8465646bd 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 309d38f68..d064ec37e 100644 --- a/src/unix/pty.cc +++ b/src/unix/pty.cc @@ -158,7 +158,7 @@ pty_posix_spawn(char** argv, char** env, NAN_METHOD(PtyFork) { Nan::HandleScope scope; - if (info.Length() != 11 || + if (info.Length() != 12 || !info[0]->IsString() || !info[1]->IsArray() || !info[2]->IsArray() || @@ -169,9 +169,10 @@ NAN_METHOD(PtyFork) { !info[7]->IsNumber() || !info[8]->IsBoolean() || !info[9]->IsString() || - !info[10]->IsFunction()) { + !info[10]->IsFunction() || + !info[11]->IsString()) { return Nan::ThrowError( - "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)"); + "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit, argv0)"); } // file @@ -247,19 +248,23 @@ NAN_METHOD(PtyFork) { // helperPath Nan::Utf8String helper_path(info[9]); + // argv0 + Nan::Utf8String argv0(info[11]); + pid_t pid; int master; #if defined(__APPLE__) int argc = argv_->Length(); - int argl = argc + 4; + int argl = argc + 5; char **argv = new char*[argl]; argv[0] = strdup(*helper_path); argv[1] = strdup(*cwd_); argv[2] = strdup(*file); + argv[3] = strdup(*argv0); argv[argl - 1] = NULL; for (int i = 0; i < argc; i++) { Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked()); - argv[i + 3] = strdup(*arg); + argv[i + 4] = strdup(*arg); } int err = -1; @@ -276,7 +281,7 @@ NAN_METHOD(PtyFork) { int argc = argv_->Length(); int argl = argc + 2; char **argv = new char*[argl]; - argv[0] = strdup(*file); + argv[0] = strdup(*argv0); argv[argl - 1] = NULL; for (int i = 0; i < argc; i++) { Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked()); @@ -342,7 +347,8 @@ NAN_METHOD(PtyFork) { { char **old = environ; environ = env; - execvp(argv[0], argv); + char* file_ = strdup(*file); + 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 8066328f5..1cb231489 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 651eb6516..cc83cf13f 100644 --- a/src/unixTerminal.test.ts +++ b/src/unixTerminal.test.ts @@ -365,6 +365,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 9f8207ef1..793d22312 100644 --- a/src/unixTerminal.ts +++ b/src/unixTerminal.ts @@ -110,8 +110,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 PipeSocket(term.fd); if (encoding !== null) { diff --git a/typings/node-pty.d.ts b/typings/node-pty.d.ts index 5fd7b237e..ccaf25119 100644 --- a/typings/node-pty.d.ts +++ b/typings/node-pty.d.ts @@ -83,6 +83,12 @@ 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. + */ + argv0?: string; } export interface IWindowsPtyForkOptions extends IBasePtyForkOptions {