node-pty 0.11.0-beta1 → 0.11.0-beta13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/unix/pty.cc CHANGED
@@ -29,6 +29,9 @@
29
29
  #include <sys/wait.h>
30
30
  #include <fcntl.h>
31
31
  #include <signal.h>
32
+ #include <spawn.h>
33
+
34
+ #include "comms.h"
32
35
 
33
36
  /* forkpty */
34
37
  /* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
@@ -54,15 +57,6 @@
54
57
  #define VDISCARD VDISCRD
55
58
  #endif
56
59
 
57
- /* environ for execvpe */
58
- /* node/src/node_child_process.cc */
59
- #if defined(__APPLE__) && !TARGET_OS_IPHONE
60
- #include <crt_externs.h>
61
- #define environ (*_NSGetEnviron())
62
- #else
63
- extern char **environ;
64
- #endif
65
-
66
60
  /* for pty_getproc */
67
61
  #if defined(__linux__)
68
62
  #include <stdio.h>
@@ -77,6 +71,17 @@ extern char **environ;
77
71
  #define NSIG 32
78
72
  #endif
79
73
 
74
+ #ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
75
+ #define HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT 1
76
+ #else
77
+ #define HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT 0
78
+ #define POSIX_SPAWN_CLOEXEC_DEFAULT 0
79
+ #endif
80
+
81
+ #ifndef POSIX_SPAWN_USEVFORK
82
+ #define POSIX_SPAWN_USEVFORK 0
83
+ #endif
84
+
80
85
  /**
81
86
  * Structs
82
87
  */
@@ -103,9 +108,6 @@ NAN_METHOD(PtyGetProc);
103
108
  * Functions
104
109
  */
105
110
 
106
- static int
107
- pty_execvpe(const char *, char **, char **);
108
-
109
111
  static int
110
112
  pty_nonblock(int);
111
113
 
@@ -117,11 +119,6 @@ pty_openpty(int *, int *, char *,
117
119
  const struct termios *,
118
120
  const struct winsize *);
119
121
 
120
- static pid_t
121
- pty_forkpty(int *, char *,
122
- const struct termios *,
123
- const struct winsize *);
124
-
125
122
  static void
126
123
  pty_waitpid(void *);
127
124
 
@@ -131,10 +128,16 @@ pty_after_waitpid(uv_async_t *);
131
128
  static void
132
129
  pty_after_close(uv_handle_t *);
133
130
 
131
+ static void throw_for_errno(const char* message, int _errno) {
132
+ Nan::ThrowError((
133
+ message + std::string(strerror(_errno))
134
+ ).c_str());
135
+ }
136
+
134
137
  NAN_METHOD(PtyFork) {
135
138
  Nan::HandleScope scope;
136
139
 
137
- if (info.Length() != 10 ||
140
+ if (info.Length() != 12 ||
138
141
  !info[0]->IsString() ||
139
142
  !info[1]->IsArray() ||
140
143
  !info[2]->IsArray() ||
@@ -144,41 +147,54 @@ NAN_METHOD(PtyFork) {
144
147
  !info[6]->IsNumber() ||
145
148
  !info[7]->IsNumber() ||
146
149
  !info[8]->IsBoolean() ||
147
- !info[9]->IsFunction()) {
150
+ !info[9]->IsBoolean() ||
151
+ !info[10]->IsFunction() ||
152
+ !info[11]->IsString()) {
148
153
  return Nan::ThrowError(
149
- "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, onexit)");
154
+ "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, closeFDs, utf8, onexit, helperPath)");
150
155
  }
151
156
 
152
157
  // file
153
158
  Nan::Utf8String file(info[0]);
154
159
 
155
- // args
156
- int i = 0;
157
- v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
158
- int argc = argv_->Length();
159
- int argl = argc + 1 + 1;
160
- char **argv = new char*[argl];
161
- argv[0] = strdup(*file);
162
- argv[argl-1] = NULL;
163
- for (; i < argc; i++) {
164
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
165
- argv[i+1] = strdup(*arg);
166
- }
167
-
168
160
  // env
169
- i = 0;
170
161
  v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
171
162
  int envc = env_->Length();
172
163
  char **env = new char*[envc+1];
173
164
  env[envc] = NULL;
174
- for (; i < envc; i++) {
165
+ for (int i = 0; i < envc; i++) {
175
166
  Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
176
167
  env[i] = strdup(*pair);
177
168
  }
178
169
 
179
170
  // cwd
180
171
  Nan::Utf8String cwd_(info[3]);
181
- char *cwd = strdup(*cwd_);
172
+
173
+ // uid / gid
174
+ int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
175
+ int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
176
+
177
+ // closeFDs
178
+ bool closeFDs = Nan::To<bool>(info[8]).FromJust();
179
+ bool explicitlyCloseFDs = closeFDs && !HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT;
180
+
181
+ // args
182
+ v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
183
+
184
+ const int EXTRA_ARGS = 5;
185
+ int argc = argv_->Length();
186
+ int argl = argc + EXTRA_ARGS + 1;
187
+ char **argv = new char*[argl];
188
+ argv[0] = strdup(*cwd_);
189
+ argv[1] = strdup(std::to_string(uid).c_str());
190
+ argv[2] = strdup(std::to_string(gid).c_str());
191
+ argv[3] = strdup(explicitlyCloseFDs ? "1": "0");
192
+ argv[4] = strdup(*file);
193
+ argv[argl - 1] = NULL;
194
+ for (int i = 0; i < argc; i++) {
195
+ Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
196
+ argv[i + EXTRA_ARGS] = strdup(*arg);
197
+ }
182
198
 
183
199
  // size
184
200
  struct winsize winp;
@@ -191,7 +207,7 @@ NAN_METHOD(PtyFork) {
191
207
  struct termios t = termios();
192
208
  struct termios *term = &t;
193
209
  term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
194
- if (Nan::To<bool>(info[8]).FromJust()) {
210
+ if (Nan::To<bool>(info[9]).FromJust()) {
195
211
  #if defined(IUTF8)
196
212
  term->c_iflag |= IUTF8;
197
213
  #endif
@@ -225,15 +241,12 @@ NAN_METHOD(PtyFork) {
225
241
  cfsetispeed(term, B38400);
226
242
  cfsetospeed(term, B38400);
227
243
 
228
- // uid / gid
229
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
230
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
231
-
232
- // fork the pty
233
- int master = -1;
244
+ // helperPath
245
+ Nan::Utf8String helper_path_(info[11]);
246
+ char *helper_path = strdup(*helper_path_);
234
247
 
235
248
  sigset_t newmask, oldmask;
236
- struct sigaction sig_action;
249
+ int flags = POSIX_SPAWN_USEVFORK;
237
250
 
238
251
  // temporarily block all signals
239
252
  // this is needed due to a race condition in openpty
@@ -242,85 +255,109 @@ NAN_METHOD(PtyFork) {
242
255
  sigfillset(&newmask);
243
256
  pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
244
257
 
245
- pid_t pid = pty_forkpty(&master, nullptr, term, &winp);
258
+ int master, slave;
259
+ int ret = pty_openpty(&master, &slave, nullptr, term, &winp);
260
+ if (ret == -1) {
261
+ perror("openpty failed");
262
+ Nan::ThrowError("openpty failed.");
263
+ goto done;
264
+ }
246
265
 
247
- if (!pid) {
248
- // remove all signal handler from child
249
- sig_action.sa_handler = SIG_DFL;
250
- sig_action.sa_flags = 0;
251
- sigemptyset(&sig_action.sa_mask);
252
- for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
253
- sigaction(i, &sig_action, NULL);
254
- }
266
+ int comms_pipe[2];
267
+ if (pipe(comms_pipe)) {
268
+ perror("pipe() failed");
269
+ Nan::ThrowError("pipe() failed.");
270
+ goto done;
255
271
  }
256
- // reenable signals
257
- pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
258
272
 
259
- if (pid) {
260
- for (i = 0; i < argl; i++) free(argv[i]);
261
- delete[] argv;
262
- for (i = 0; i < envc; i++) free(env[i]);
263
- delete[] env;
264
- free(cwd);
273
+ posix_spawn_file_actions_t acts;
274
+ posix_spawn_file_actions_init(&acts);
275
+ posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO);
276
+ posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO);
277
+ posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO);
278
+ posix_spawn_file_actions_adddup2(&acts, comms_pipe[1], COMM_PIPE_FD);
279
+ posix_spawn_file_actions_addclose(&acts, comms_pipe[1]);
280
+
281
+ posix_spawnattr_t attrs;
282
+ posix_spawnattr_init(&attrs);
283
+ if (closeFDs) {
284
+ flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
265
285
  }
286
+ posix_spawnattr_setflags(&attrs, flags);
266
287
 
267
- switch (pid) {
268
- case -1:
269
- return Nan::ThrowError("forkpty(3) failed.");
270
- case 0:
271
- if (strlen(cwd)) {
272
- if (chdir(cwd) == -1) {
273
- perror("chdir(2) failed.");
274
- _exit(1);
275
- }
276
- }
288
+ { // suppresses "jump bypasses variable initialization" errors
289
+ pid_t pid;
290
+ auto error = posix_spawn(&pid, helper_path, &acts, &attrs, argv, env);
277
291
 
278
- if (uid != -1 && gid != -1) {
279
- if (setgid(gid) == -1) {
280
- perror("setgid(2) failed.");
281
- _exit(1);
282
- }
283
- if (setuid(uid) == -1) {
284
- perror("setuid(2) failed.");
285
- _exit(1);
286
- }
287
- }
292
+ close(comms_pipe[1]);
288
293
 
289
- pty_execvpe(argv[0], argv, env);
294
+ // reenable signals
295
+ pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
290
296
 
291
- perror("execvp(3) failed.");
292
- _exit(1);
293
- default:
294
- if (pty_nonblock(master) == -1) {
295
- return Nan::ThrowError("Could not set master fd to nonblocking.");
297
+ if (error) {
298
+ throw_for_errno("posix_spawn failed: ", error);
299
+ goto done;
300
+ }
301
+
302
+ int helper_error[2];
303
+ auto bytes_read = read(comms_pipe[0], &helper_error, sizeof(helper_error));
304
+ close(comms_pipe[0]);
305
+
306
+ if (bytes_read == sizeof(helper_error)) {
307
+ if (helper_error[0] == COMM_ERR_EXEC) {
308
+ throw_for_errno("exec() failed: ", helper_error[1]);
309
+ } else if (helper_error[0] == COMM_ERR_CHDIR) {
310
+ throw_for_errno("chdir() failed: ", helper_error[1]);
311
+ } else if (helper_error[0] == COMM_ERR_SETUID) {
312
+ throw_for_errno("setuid() failed: ", helper_error[1]);
313
+ } else if (helper_error[0] == COMM_ERR_SETGID) {
314
+ throw_for_errno("setgid() failed: ", helper_error[1]);
296
315
  }
316
+ goto done;
317
+ }
297
318
 
298
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
299
- Nan::Set(obj,
300
- Nan::New<v8::String>("fd").ToLocalChecked(),
301
- Nan::New<v8::Number>(master));
302
- Nan::Set(obj,
303
- Nan::New<v8::String>("pid").ToLocalChecked(),
304
- Nan::New<v8::Number>(pid));
305
- Nan::Set(obj,
306
- Nan::New<v8::String>("pty").ToLocalChecked(),
307
- Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
308
-
309
- pty_baton *baton = new pty_baton();
310
- baton->exit_code = 0;
311
- baton->signal_code = 0;
312
- baton->cb.Reset(v8::Local<v8::Function>::Cast(info[9]));
313
- baton->pid = pid;
314
- baton->async.data = baton;
315
-
316
- uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
317
-
318
- uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
319
-
320
- return info.GetReturnValue().Set(obj);
319
+ if (pty_nonblock(master) == -1) {
320
+ Nan::ThrowError("Could not set master fd to nonblocking.");
321
+ goto done;
322
+ }
323
+
324
+ v8::Local<v8::Object> obj = Nan::New<v8::Object>();
325
+ Nan::Set(obj,
326
+ Nan::New<v8::String>("fd").ToLocalChecked(),
327
+ Nan::New<v8::Number>(master));
328
+ Nan::Set(obj,
329
+ Nan::New<v8::String>("pid").ToLocalChecked(),
330
+ Nan::New<v8::Number>(pid));
331
+ Nan::Set(obj,
332
+ Nan::New<v8::String>("pty").ToLocalChecked(),
333
+ Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
334
+
335
+ pty_baton *baton = new pty_baton();
336
+ baton->exit_code = 0;
337
+ baton->signal_code = 0;
338
+ baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
339
+ baton->pid = pid;
340
+ baton->async.data = baton;
341
+
342
+ uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
343
+
344
+ uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
345
+
346
+ info.GetReturnValue().Set(obj);
321
347
  }
348
+ done:
349
+ posix_spawn_file_actions_destroy(&acts);
350
+ posix_spawnattr_destroy(&attrs);
322
351
 
323
- return info.GetReturnValue().SetUndefined();
352
+ if (argv) {
353
+ for (int i = 0; i < argl; i++) free(argv[i]);
354
+ delete[] argv;
355
+ }
356
+ if (env) {
357
+ for (int i = 0; i < envc; i++) free(env[i]);
358
+ delete[] env;
359
+ }
360
+ free(helper_path);
324
361
  }
325
362
 
326
363
  NAN_METHOD(PtyOpen) {
@@ -428,21 +465,6 @@ NAN_METHOD(PtyGetProc) {
428
465
  return info.GetReturnValue().Set(name_);
429
466
  }
430
467
 
431
- /**
432
- * execvpe
433
- */
434
-
435
- // execvpe(3) is not portable.
436
- // http://www.gnu.org/software/gnulib/manual/html_node/execvpe.html
437
- static int
438
- pty_execvpe(const char *file, char **argv, char **envp) {
439
- char **old = environ;
440
- environ = envp;
441
- int ret = execvp(file, argv);
442
- environ = old;
443
- return ret;
444
- }
445
-
446
468
  /**
447
469
  * Nonblocking FD
448
470
  */
@@ -670,55 +692,6 @@ err:
670
692
  #endif
671
693
  }
672
694
 
673
- static pid_t
674
- pty_forkpty(int *amaster,
675
- char *name,
676
- const struct termios *termp,
677
- const struct winsize *winp) {
678
- #if defined(__sun)
679
- int master, slave;
680
-
681
- int ret = pty_openpty(&master, &slave, name, termp, winp);
682
- if (ret == -1) return -1;
683
- if (amaster) *amaster = master;
684
-
685
- pid_t pid = fork();
686
-
687
- switch (pid) {
688
- case -1: // error in fork, we are still in parent
689
- close(master);
690
- close(slave);
691
- return -1;
692
- case 0: // we are in the child process
693
- close(master);
694
- setsid();
695
-
696
- #if defined(TIOCSCTTY)
697
- // glibc does this
698
- if (ioctl(slave, TIOCSCTTY, NULL) == -1) {
699
- _exit(1);
700
- }
701
- #endif
702
-
703
- dup2(slave, 0);
704
- dup2(slave, 1);
705
- dup2(slave, 2);
706
-
707
- if (slave > 2) close(slave);
708
-
709
- return 0;
710
- default: // we are in the parent process
711
- close(slave);
712
- return pid;
713
- }
714
-
715
- return -1;
716
- #else
717
- return forkpty(amaster, name, (termios *)termp, (winsize *)winp);
718
- #endif
719
- }
720
-
721
-
722
695
  /**
723
696
  * Init
724
697
  */
@@ -0,0 +1,70 @@
1
+ #include <errno.h>
2
+ #include <fcntl.h>
3
+ #include <signal.h>
4
+ #include <string>
5
+ #include <sys/ioctl.h>
6
+ #include <sys/resource.h>
7
+ #include <termios.h>
8
+ #include <unistd.h>
9
+ #include <string.h>
10
+
11
+ #include "comms.h"
12
+
13
+ void bail (int type, int code) {
14
+ int buf[2] = { type, code };
15
+ (void)! write(COMM_PIPE_FD, &buf, sizeof(buf));
16
+ _exit(1);
17
+ }
18
+
19
+ int main (int argc, char** argv) {
20
+ sigset_t empty_set;
21
+ sigemptyset(&empty_set);
22
+ pthread_sigmask(SIG_SETMASK, &empty_set, nullptr);
23
+
24
+ setsid();
25
+
26
+ #if defined(TIOCSCTTY)
27
+ // glibc does this
28
+ if (ioctl(STDIN_FILENO, TIOCSCTTY, NULL) == -1) {
29
+ _exit(1);
30
+ }
31
+ #else
32
+ char *slave_path = ttyname(STDIN_FILENO);
33
+ // open implicit attaches a process to a terminal device if:
34
+ // - process has no controlling terminal yet
35
+ // - O_NOCTTY is not set
36
+ close(open(slave_path, O_RDWR));
37
+ #endif
38
+
39
+ char *cwd = argv[0];
40
+ int uid = std::stoi(argv[1]);
41
+ int gid = std::stoi(argv[2]);
42
+ bool closeFDs = std::stoi(argv[3]);
43
+ char *file = argv[4];
44
+ argv = &argv[4];
45
+
46
+ fcntl(COMM_PIPE_FD, F_SETFD, FD_CLOEXEC);
47
+
48
+ if (strlen(cwd) && chdir(cwd) == -1) {
49
+ bail(COMM_ERR_CHDIR, errno);
50
+ }
51
+ if (uid != -1 && setuid(uid) == -1) {
52
+ bail(COMM_ERR_SETUID, errno);
53
+ }
54
+ if (gid != -1 && setgid(gid) == -1) {
55
+ bail(COMM_ERR_SETGID, errno);
56
+ }
57
+ if (closeFDs) {
58
+ struct rlimit rlim_ofile;
59
+ getrlimit(RLIMIT_NOFILE, &rlim_ofile);
60
+ for (rlim_t fd = STDERR_FILENO + 1; fd < rlim_ofile.rlim_cur; fd++) {
61
+ if (fd != COMM_PIPE_FD) {
62
+ close(fd);
63
+ }
64
+ }
65
+ }
66
+
67
+ execvp(file, argv);
68
+ bail(COMM_ERR_EXEC, errno);
69
+ return 1;
70
+ }
@@ -239,5 +239,33 @@ if (process.platform !== 'win32') {
239
239
  });
240
240
  });
241
241
  });
242
+ describe('spawn', () => {
243
+ it('should handle exec() errors', (done) => {
244
+ try {
245
+ new UnixTerminal('/bin/bogus.exe', []);
246
+ done(new Error('should have failed'));
247
+ } catch {
248
+ done();
249
+ }
250
+ });
251
+ it('should handle chdir() errors', (done) => {
252
+ try {
253
+ new UnixTerminal('/bin/echo', [], { cwd: '/nowhere' });
254
+ done(new Error('should have failed'));
255
+ } catch (e) {
256
+ assert.equal(e.toString(), 'Error: chdir() failed: No such file or directory');
257
+ done();
258
+ }
259
+ });
260
+ it('should handle setuid() errors', (done) => {
261
+ try {
262
+ new UnixTerminal('/bin/echo', [], { uid: 999999 });
263
+ done(new Error('should have failed'));
264
+ } catch (e) {
265
+ assert.equal(e.toString(), 'Error: setuid() failed: Operation not permitted');
266
+ done();
267
+ }
268
+ });
269
+ });
242
270
  });
243
271
  }
@@ -4,17 +4,21 @@
4
4
  * Copyright (c) 2018, Microsoft Corporation (MIT License).
5
5
  */
6
6
  import * as net from 'net';
7
+ import * as path from 'path';
7
8
  import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal';
8
9
  import { IProcessEnv, IPtyForkOptions, IPtyOpenOptions } from './interfaces';
9
10
  import { ArgvOrCommandLine } from './types';
10
11
  import { assign } from './utils';
11
12
 
12
13
  let pty: IUnixNative;
14
+ let helperPath: string;
13
15
  try {
14
16
  pty = require('../build/Release/pty.node');
17
+ helperPath = '../build/Release/spawn-helper';
15
18
  } catch (outerError) {
16
19
  try {
17
20
  pty = require('../build/Debug/pty.node');
21
+ helperPath = '../build/Debug/spawn-helper';
18
22
  } catch (innerError) {
19
23
  console.error('innerError', innerError);
20
24
  // Re-throw the exception from the Release require if the Debug require fails as well
@@ -22,6 +26,10 @@ try {
22
26
  }
23
27
  }
24
28
 
29
+ helperPath = path.resolve(__dirname, helperPath);
30
+ helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
31
+ helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
32
+
25
33
  const DEFAULT_FILE = 'sh';
26
34
  const DEFAULT_NAME = 'xterm';
27
35
  const DESTROY_SOCKET_TIMEOUT_MS = 200;
@@ -61,7 +69,8 @@ export class UnixTerminal extends Terminal {
61
69
  this._rows = opt.rows || DEFAULT_ROWS;
62
70
  const uid = opt.uid || -1;
63
71
  const gid = opt.gid || -1;
64
- const env = assign({}, opt.env);
72
+ const closeFDs = opt.closeFDs || false;
73
+ const env: IProcessEnv = assign({}, opt.env);
65
74
 
66
75
  if (opt.env === process.env) {
67
76
  this._sanitizeEnv(env);
@@ -103,7 +112,7 @@ export class UnixTerminal extends Terminal {
103
112
  };
104
113
 
105
114
  // fork
106
- const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), onexit);
115
+ const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), closeFDs, onexit, helperPath);
107
116
 
108
117
  this._socket = new PipeSocket(term.fd);
109
118
  if (encoding !== null) {
package/src/win/conpty.cc CHANGED
@@ -289,6 +289,13 @@ static NAN_METHOD(PtyConnect) {
289
289
  const v8::Local<v8::Array> envValues = info[3].As<v8::Array>();
290
290
  const v8::Local<v8::Function> exitCallback = v8::Local<v8::Function>::Cast(info[4]);
291
291
 
292
+ // Fetch pty handle from ID and start process
293
+ pty_baton* handle = get_pty_baton(id);
294
+ if (!handle) {
295
+ Nan::ThrowError("Invalid pty handle");
296
+ return;
297
+ }
298
+
292
299
  // Prepare command line
293
300
  std::unique_ptr<wchar_t[]> mutableCommandline = std::make_unique<wchar_t[]>(cmdline.length() + 1);
294
301
  HRESULT hr = StringCchCopyW(mutableCommandline.get(), cmdline.length() + 1, cmdline.c_str());
@@ -311,11 +318,8 @@ static NAN_METHOD(PtyConnect) {
311
318
  auto envV = vectorFromString(env);
312
319
  LPWSTR envArg = envV.empty() ? nullptr : envV.data();
313
320
 
314
- // Fetch pty handle from ID and start process
315
- pty_baton* handle = get_pty_baton(id);
316
-
317
- BOOL success = ConnectNamedPipe(handle->hIn, nullptr);
318
- success = ConnectNamedPipe(handle->hOut, nullptr);
321
+ ConnectNamedPipe(handle->hIn, nullptr);
322
+ ConnectNamedPipe(handle->hOut, nullptr);
319
323
 
320
324
  // Attach the pseudoconsole to the client application we're creating
321
325
  STARTUPINFOEXW siEx{0};
@@ -396,15 +400,17 @@ static NAN_METHOD(PtyResize) {
396
400
 
397
401
  const pty_baton* handle = get_pty_baton(id);
398
402
 
399
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
400
- bool fLoadedDll = hLibrary != nullptr;
401
- if (fLoadedDll)
402
- {
403
- PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ResizePseudoConsole");
404
- if (pfnResizePseudoConsole)
403
+ if (handle != nullptr) {
404
+ HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
405
+ bool fLoadedDll = hLibrary != nullptr;
406
+ if (fLoadedDll)
405
407
  {
406
- COORD size = {cols, rows};
407
- pfnResizePseudoConsole(handle->hpc, size);
408
+ PFNRESIZEPSEUDOCONSOLE const pfnResizePseudoConsole = (PFNRESIZEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ResizePseudoConsole");
409
+ if (pfnResizePseudoConsole)
410
+ {
411
+ COORD size = {cols, rows};
412
+ pfnResizePseudoConsole(handle->hpc, size);
413
+ }
408
414
  }
409
415
  }
410
416
 
@@ -424,18 +430,24 @@ static NAN_METHOD(PtyKill) {
424
430
 
425
431
  const pty_baton* handle = get_pty_baton(id);
426
432
 
427
- HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
428
- bool fLoadedDll = hLibrary != nullptr;
429
- if (fLoadedDll)
430
- {
431
- PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClosePseudoConsole");
432
- if (pfnClosePseudoConsole)
433
+ if (handle != nullptr) {
434
+ HANDLE hLibrary = LoadLibraryExW(L"kernel32.dll", 0, 0);
435
+ bool fLoadedDll = hLibrary != nullptr;
436
+ if (fLoadedDll)
433
437
  {
434
- pfnClosePseudoConsole(handle->hpc);
438
+ PFNCLOSEPSEUDOCONSOLE const pfnClosePseudoConsole = (PFNCLOSEPSEUDOCONSOLE)GetProcAddress((HMODULE)hLibrary, "ClosePseudoConsole");
439
+ if (pfnClosePseudoConsole)
440
+ {
441
+ pfnClosePseudoConsole(handle->hpc);
442
+ }
435
443
  }
436
- }
437
444
 
438
- CloseHandle(handle->hShell);
445
+ DisconnectNamedPipe(handle->hIn);
446
+ DisconnectNamedPipe(handle->hOut);
447
+ CloseHandle(handle->hIn);
448
+ CloseHandle(handle->hOut);
449
+ CloseHandle(handle->hShell);
450
+ }
439
451
 
440
452
  return info.GetReturnValue().SetUndefined();
441
453
  }
@@ -52,7 +52,7 @@ std::wstring get_shell_path(std::wstring filename) {
52
52
 
53
53
  const wchar_t *filename_ = filename.c_str();
54
54
 
55
- for (int i = 0; i < paths.size(); ++i) {
55
+ for (size_t i = 0; i < paths.size(); ++i) {
56
56
  std::wstring path = paths[i];
57
57
  wchar_t searchPath[MAX_PATH];
58
58
  ::PathCombineW(searchPath, const_cast<wchar_t*>(path.c_str()), filename_);