node-pty 0.11.0-beta3 → 0.11.0-beta30

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.
Files changed (42) hide show
  1. package/README.md +7 -3
  2. package/binding.gyp +69 -29
  3. package/lib/conpty_console_list_agent.js.map +1 -1
  4. package/lib/terminal.js +10 -4
  5. package/lib/terminal.js.map +1 -1
  6. package/lib/unixTerminal.js +26 -4
  7. package/lib/unixTerminal.js.map +1 -1
  8. package/lib/unixTerminal.test.js +127 -18
  9. package/lib/unixTerminal.test.js.map +1 -1
  10. package/lib/windowsConoutConnection.js +2 -1
  11. package/lib/windowsConoutConnection.js.map +1 -1
  12. package/lib/windowsPtyAgent.js +3 -0
  13. package/lib/windowsPtyAgent.js.map +1 -1
  14. package/lib/windowsTerminal.js +15 -3
  15. package/lib/windowsTerminal.js.map +1 -1
  16. package/lib/windowsTerminal.test.js +4 -5
  17. package/lib/windowsTerminal.test.js.map +1 -1
  18. package/lib/worker/conoutSocketWorker.js +3 -0
  19. package/lib/worker/conoutSocketWorker.js.map +1 -1
  20. package/package.json +2 -3
  21. package/scripts/post-install.js +1 -0
  22. package/src/conpty_console_list_agent.ts +1 -1
  23. package/src/interfaces.ts +3 -2
  24. package/src/native.d.ts +1 -1
  25. package/src/terminal.ts +20 -20
  26. package/src/tsconfig.json +1 -3
  27. package/src/unix/comms.h +5 -0
  28. package/src/unix/pty.cc +146 -172
  29. package/src/unix/spawn-helper.cc +70 -0
  30. package/src/unixTerminal.test.ts +145 -25
  31. package/src/unixTerminal.ts +24 -11
  32. package/src/win/conpty.cc +34 -22
  33. package/src/win/path_util.cc +1 -1
  34. package/src/windowsConoutConnection.ts +3 -2
  35. package/src/windowsPtyAgent.ts +5 -5
  36. package/src/windowsTerminal.test.ts +5 -6
  37. package/src/windowsTerminal.ts +17 -4
  38. package/src/worker/conoutSocketWorker.ts +4 -0
  39. package/typings/node-pty.d.ts +9 -3
  40. package/deps/winpty/.gitignore +0 -15
  41. package/deps/winpty/misc/.gitignore +0 -2
  42. package/scripts/install.js +0 -18
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,31 @@ 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, utf8, closeFDs, onexit, helperPath)");
150
155
  }
151
156
 
152
157
  // file
153
158
  Nan::Utf8String file(info[0]);
154
159
 
155
160
  // args
156
- int i = 0;
157
161
  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
162
 
168
163
  // env
169
- i = 0;
170
164
  v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
171
165
  int envc = env_->Length();
172
166
  char **env = new char*[envc+1];
173
167
  env[envc] = NULL;
174
- for (; i < envc; i++) {
168
+ for (int i = 0; i < envc; i++) {
175
169
  Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
176
170
  env[i] = strdup(*pair);
177
171
  }
178
172
 
179
173
  // cwd
180
174
  Nan::Utf8String cwd_(info[3]);
181
- char *cwd = strdup(*cwd_);
182
175
 
183
176
  // size
184
177
  struct winsize winp;
@@ -187,6 +180,10 @@ NAN_METHOD(PtyFork) {
187
180
  winp.ws_xpixel = 0;
188
181
  winp.ws_ypixel = 0;
189
182
 
183
+ // uid / gid
184
+ int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
185
+ int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
186
+
190
187
  // termios
191
188
  struct termios t = termios();
192
189
  struct termios *term = &t;
@@ -222,18 +219,35 @@ NAN_METHOD(PtyFork) {
222
219
  term->c_cc[VSTATUS] = 20;
223
220
  #endif
224
221
 
225
- cfsetispeed(term, B38400);
226
- cfsetospeed(term, B38400);
222
+ // closeFDs
223
+ bool closeFDs = Nan::To<bool>(info[9]).FromJust();
224
+ bool explicitlyCloseFDs = closeFDs && !HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT;
227
225
 
228
- // uid / gid
229
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
230
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
226
+ // helperPath
227
+ Nan::Utf8String helper_path_(info[11]);
228
+ char *helper_path = strdup(*helper_path_);
229
+
230
+ const int EXTRA_ARGS = 6;
231
+ int argc = argv_->Length();
232
+ int argl = argc + EXTRA_ARGS + 1;
233
+ char **argv = new char*[argl];
234
+ argv[0] = strdup(helper_path);
235
+ argv[1] = strdup(*cwd_);
236
+ argv[2] = strdup(std::to_string(uid).c_str());
237
+ argv[3] = strdup(std::to_string(gid).c_str());
238
+ argv[4] = strdup(explicitlyCloseFDs ? "1": "0");
239
+ argv[5] = strdup(*file);
240
+ argv[argl - 1] = NULL;
241
+ for (int i = 0; i < argc; i++) {
242
+ Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
243
+ argv[i + EXTRA_ARGS] = strdup(*arg);
244
+ }
231
245
 
232
- // fork the pty
233
- int master = -1;
246
+ cfsetispeed(term, B38400);
247
+ cfsetospeed(term, B38400);
234
248
 
235
249
  sigset_t newmask, oldmask;
236
- struct sigaction sig_action;
250
+ int flags = POSIX_SPAWN_USEVFORK;
237
251
 
238
252
  // temporarily block all signals
239
253
  // this is needed due to a race condition in openpty
@@ -242,85 +256,109 @@ NAN_METHOD(PtyFork) {
242
256
  sigfillset(&newmask);
243
257
  pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
244
258
 
245
- pid_t pid = pty_forkpty(&master, nullptr, term, &winp);
259
+ int master, slave;
260
+ int ret = pty_openpty(&master, &slave, nullptr, term, &winp);
261
+ if (ret == -1) {
262
+ perror("openpty failed");
263
+ Nan::ThrowError("openpty failed.");
264
+ goto done;
265
+ }
246
266
 
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
- }
267
+ int comms_pipe[2];
268
+ if (pipe(comms_pipe)) {
269
+ perror("pipe() failed");
270
+ Nan::ThrowError("pipe() failed.");
271
+ goto done;
255
272
  }
256
- // reenable signals
257
- pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
258
273
 
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);
274
+ posix_spawn_file_actions_t acts;
275
+ posix_spawn_file_actions_init(&acts);
276
+ posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO);
277
+ posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO);
278
+ posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO);
279
+ posix_spawn_file_actions_adddup2(&acts, comms_pipe[1], COMM_PIPE_FD);
280
+ posix_spawn_file_actions_addclose(&acts, comms_pipe[1]);
281
+
282
+ posix_spawnattr_t attrs;
283
+ posix_spawnattr_init(&attrs);
284
+ if (closeFDs) {
285
+ flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
265
286
  }
287
+ posix_spawnattr_setflags(&attrs, flags);
266
288
 
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
- }
289
+ { // suppresses "jump bypasses variable initialization" errors
290
+ pid_t pid;
291
+ auto error = posix_spawn(&pid, argv[0], &acts, &attrs, argv, env);
277
292
 
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
- }
293
+ close(comms_pipe[1]);
288
294
 
289
- pty_execvpe(argv[0], argv, env);
295
+ // reenable signals
296
+ pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
297
+
298
+ if (error) {
299
+ throw_for_errno("posix_spawn failed: ", error);
300
+ goto done;
301
+ }
290
302
 
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.");
303
+ int helper_error[2];
304
+ auto bytes_read = read(comms_pipe[0], &helper_error, sizeof(helper_error));
305
+ close(comms_pipe[0]);
306
+
307
+ if (bytes_read == sizeof(helper_error)) {
308
+ if (helper_error[0] == COMM_ERR_EXEC) {
309
+ throw_for_errno("exec() failed: ", helper_error[1]);
310
+ } else if (helper_error[0] == COMM_ERR_CHDIR) {
311
+ throw_for_errno("chdir() failed: ", helper_error[1]);
312
+ } else if (helper_error[0] == COMM_ERR_SETUID) {
313
+ throw_for_errno("setuid() failed: ", helper_error[1]);
314
+ } else if (helper_error[0] == COMM_ERR_SETGID) {
315
+ throw_for_errno("setgid() failed: ", helper_error[1]);
296
316
  }
317
+ goto done;
318
+ }
319
+
320
+ if (pty_nonblock(master) == -1) {
321
+ Nan::ThrowError("Could not set master fd to nonblocking.");
322
+ goto done;
323
+ }
297
324
 
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);
325
+ v8::Local<v8::Object> obj = Nan::New<v8::Object>();
326
+ Nan::Set(obj,
327
+ Nan::New<v8::String>("fd").ToLocalChecked(),
328
+ Nan::New<v8::Number>(master));
329
+ Nan::Set(obj,
330
+ Nan::New<v8::String>("pid").ToLocalChecked(),
331
+ Nan::New<v8::Number>(pid));
332
+ Nan::Set(obj,
333
+ Nan::New<v8::String>("pty").ToLocalChecked(),
334
+ Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
335
+
336
+ pty_baton *baton = new pty_baton();
337
+ baton->exit_code = 0;
338
+ baton->signal_code = 0;
339
+ baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
340
+ baton->pid = pid;
341
+ baton->async.data = baton;
342
+
343
+ uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
344
+
345
+ uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
346
+
347
+ info.GetReturnValue().Set(obj);
321
348
  }
349
+ done:
350
+ posix_spawn_file_actions_destroy(&acts);
351
+ posix_spawnattr_destroy(&attrs);
322
352
 
323
- return info.GetReturnValue().SetUndefined();
353
+ if (argv) {
354
+ for (int i = 0; i < argl; i++) free(argv[i]);
355
+ delete[] argv;
356
+ }
357
+ if (env) {
358
+ for (int i = 0; i < envc; i++) free(env[i]);
359
+ delete[] env;
360
+ }
361
+ free(helper_path);
324
362
  }
325
363
 
326
364
  NAN_METHOD(PtyOpen) {
@@ -428,21 +466,6 @@ NAN_METHOD(PtyGetProc) {
428
466
  return info.GetReturnValue().Set(name_);
429
467
  }
430
468
 
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
469
  /**
447
470
  * Nonblocking FD
448
471
  */
@@ -670,55 +693,6 @@ err:
670
693
  #endif
671
694
  }
672
695
 
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
696
  /**
723
697
  * Init
724
698
  */
@@ -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[1];
40
+ int uid = std::stoi(argv[2]);
41
+ int gid = std::stoi(argv[3]);
42
+ bool closeFDs = std::stoi(argv[4]);
43
+ char *file = argv[5];
44
+ argv = &argv[5];
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
+ }