node-pty 0.11.0-beta3 → 0.11.0-beta31

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 (38) hide show
  1. package/README.md +7 -3
  2. package/binding.gyp +54 -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 +17 -3
  7. package/lib/unixTerminal.js.map +1 -1
  8. package/lib/unixTerminal.test.js +161 -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/src/conpty_console_list_agent.ts +1 -1
  22. package/src/interfaces.ts +2 -2
  23. package/src/terminal.ts +20 -20
  24. package/src/tsconfig.json +1 -3
  25. package/src/unix/pty.cc +344 -181
  26. package/src/unixTerminal.test.ts +128 -25
  27. package/src/unixTerminal.ts +15 -10
  28. package/src/win/conpty.cc +34 -22
  29. package/src/win/path_util.cc +1 -1
  30. package/src/windowsConoutConnection.ts +3 -2
  31. package/src/windowsPtyAgent.ts +5 -5
  32. package/src/windowsTerminal.test.ts +5 -6
  33. package/src/windowsTerminal.ts +17 -4
  34. package/src/worker/conoutSocketWorker.ts +4 -0
  35. package/typings/node-pty.d.ts +3 -3
  36. package/deps/winpty/.gitignore +0 -15
  37. package/deps/winpty/misc/.gitignore +0 -2
  38. package/scripts/install.js +0 -18
package/src/unix/pty.cc CHANGED
@@ -32,20 +32,12 @@
32
32
 
33
33
  /* forkpty */
34
34
  /* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */
35
- #if defined(__GLIBC__) || defined(__CYGWIN__)
35
+ #if defined(__linux__)
36
36
  #include <pty.h>
37
- #elif defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
37
+ #elif defined(__APPLE__)
38
38
  #include <util.h>
39
- #elif defined(__FreeBSD__)
40
- #include <libutil.h>
41
- #elif defined(__sun)
42
- #include <stropts.h> /* for I_PUSH */
43
- #else
44
- #include <pty.h>
45
39
  #endif
46
40
 
47
- #include <termios.h> /* tcgetattr, tty_ioctl */
48
-
49
41
  /* Some platforms name VWERASE and VDISCARD differently */
50
42
  #if !defined(VWERASE) && defined(VWERSE)
51
43
  #define VWERASE VWERSE
@@ -54,22 +46,18 @@
54
46
  #define VDISCARD VDISCRD
55
47
  #endif
56
48
 
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
49
  /* for pty_getproc */
67
50
  #if defined(__linux__)
68
51
  #include <stdio.h>
69
52
  #include <stdint.h>
70
53
  #elif defined(__APPLE__)
71
- #include <sys/sysctl.h>
72
54
  #include <libproc.h>
55
+ #include <os/availability.h>
56
+ #include <paths.h>
57
+ #include <spawn.h>
58
+ #include <sys/event.h>
59
+ #include <sys/sysctl.h>
60
+ #include <termios.h>
73
61
  #endif
74
62
 
75
63
  /* NSIG - macro for highest signal + 1, should be defined */
@@ -77,6 +65,38 @@ extern char **environ;
77
65
  #define NSIG 32
78
66
  #endif
79
67
 
68
+ /* macOS 10.14 back does not define this constant */
69
+ #ifndef POSIX_SPAWN_SETSID
70
+ #define POSIX_SPAWN_SETSID 1024
71
+ #endif
72
+
73
+ /* environ for execvpe */
74
+ /* node/src/node_child_process.cc */
75
+ #if !defined(__APPLE__)
76
+ extern char **environ;
77
+ #endif
78
+
79
+ #if defined(__APPLE__)
80
+ extern "C" {
81
+ // Changes the current thread's directory to a path or directory file
82
+ // descriptor. libpthread only exposes a syscall wrapper starting in
83
+ // macOS 10.12, but the system call dates back to macOS 10.5. On older OSes,
84
+ // the syscall is issued directly.
85
+ int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12));
86
+ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
87
+ }
88
+
89
+ #define HANDLE_EINTR(x) ({ \
90
+ int eintr_wrapper_counter = 0; \
91
+ decltype(x) eintr_wrapper_result; \
92
+ do { \
93
+ eintr_wrapper_result = (x); \
94
+ } while (eintr_wrapper_result == -1 && errno == EINTR && \
95
+ eintr_wrapper_counter++ < 100); \
96
+ eintr_wrapper_result; \
97
+ })
98
+ #endif
99
+
80
100
  /**
81
101
  * Structs
82
102
  */
@@ -103,25 +123,12 @@ NAN_METHOD(PtyGetProc);
103
123
  * Functions
104
124
  */
105
125
 
106
- static int
107
- pty_execvpe(const char *, char **, char **);
108
-
109
126
  static int
110
127
  pty_nonblock(int);
111
128
 
112
129
  static char *
113
130
  pty_getproc(int, char *);
114
131
 
115
- static int
116
- pty_openpty(int *, int *, char *,
117
- const struct termios *,
118
- const struct winsize *);
119
-
120
- static pid_t
121
- pty_forkpty(int *, char *,
122
- const struct termios *,
123
- const struct winsize *);
124
-
125
132
  static void
126
133
  pty_waitpid(void *);
127
134
 
@@ -131,6 +138,17 @@ pty_after_waitpid(uv_async_t *);
131
138
  static void
132
139
  pty_after_close(uv_handle_t *);
133
140
 
141
+ #if defined(__APPLE__) || defined(__OpenBSD__)
142
+ static void
143
+ pty_posix_spawn(char** argv, char** env,
144
+ char* cwd,
145
+ const struct termios *termp,
146
+ const struct winsize *winp,
147
+ int* master,
148
+ pid_t* pid,
149
+ int* err);
150
+ #endif
151
+
134
152
  NAN_METHOD(PtyFork) {
135
153
  Nan::HandleScope scope;
136
154
 
@@ -153,32 +171,21 @@ NAN_METHOD(PtyFork) {
153
171
  Nan::Utf8String file(info[0]);
154
172
 
155
173
  // args
156
- int i = 0;
157
174
  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
175
 
168
176
  // env
169
- i = 0;
170
177
  v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
171
178
  int envc = env_->Length();
172
179
  char **env = new char*[envc+1];
173
180
  env[envc] = NULL;
174
- for (; i < envc; i++) {
181
+ for (int i = 0; i < envc; i++) {
175
182
  Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
176
183
  env[i] = strdup(*pair);
177
184
  }
178
185
 
179
186
  // cwd
180
187
  Nan::Utf8String cwd_(info[3]);
181
- char *cwd = strdup(*cwd_);
188
+ char* cwd = strdup(*cwd_);
182
189
 
183
190
  // size
184
191
  struct winsize winp;
@@ -187,6 +194,12 @@ NAN_METHOD(PtyFork) {
187
194
  winp.ws_xpixel = 0;
188
195
  winp.ws_ypixel = 0;
189
196
 
197
+ #if !defined(__APPLE__)
198
+ // uid / gid
199
+ int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
200
+ int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
201
+ #endif
202
+
190
203
  // termios
191
204
  struct termios t = termios();
192
205
  struct termios *term = &t;
@@ -225,16 +238,32 @@ NAN_METHOD(PtyFork) {
225
238
  cfsetispeed(term, B38400);
226
239
  cfsetospeed(term, B38400);
227
240
 
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;
241
+ int argc = argv_->Length();
242
+ int argl = argc + 2;
243
+ char **argv = new char*[argl];
244
+ argv[0] = strdup(*file);
245
+ argv[argl - 1] = NULL;
246
+ for (int i = 0; i < argc; i++) {
247
+ Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
248
+ argv[i + 1] = strdup(*arg);
249
+ }
234
250
 
251
+ pid_t pid;
252
+ int master;
253
+ #if defined(__APPLE__)
254
+ int err = 0;
255
+ pty_posix_spawn(argv, env, cwd, term, &winp, &master, &pid, &err);
256
+ if (err != 0) {
257
+ Nan::ThrowError("posix_spawnp failed.");
258
+ goto done;
259
+ }
260
+ if (pty_nonblock(master) == -1) {
261
+ Nan::ThrowError("Could not set master fd to nonblocking.");
262
+ goto done;
263
+ }
264
+ #else
235
265
  sigset_t newmask, oldmask;
236
266
  struct sigaction sig_action;
237
-
238
267
  // temporarily block all signals
239
268
  // this is needed due to a race condition in openpty
240
269
  // and to avoid running signal handlers in the child
@@ -242,7 +271,7 @@ NAN_METHOD(PtyFork) {
242
271
  sigfillset(&newmask);
243
272
  pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
244
273
 
245
- pid_t pid = pty_forkpty(&master, nullptr, term, &winp);
274
+ pid = forkpty(&master, nullptr, static_cast<termios*>(term), static_cast<winsize*>(&winp));
246
275
 
247
276
  if (!pid) {
248
277
  // remove all signal handler from child
@@ -252,21 +281,23 @@ NAN_METHOD(PtyFork) {
252
281
  for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
253
282
  sigaction(i, &sig_action, NULL);
254
283
  }
255
- }
256
- // reenable signals
257
- pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
258
-
259
- if (pid) {
260
- for (i = 0; i < argl; i++) free(argv[i]);
284
+ } else {
285
+ for (int i = 0; i < argl; i++) free(argv[i]);
261
286
  delete[] argv;
262
- for (i = 0; i < envc; i++) free(env[i]);
287
+
288
+ for (int i = 0; i < envc; i++) free(env[i]);
263
289
  delete[] env;
290
+
264
291
  free(cwd);
265
292
  }
266
293
 
294
+ // reenable signals
295
+ pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
296
+
267
297
  switch (pid) {
268
298
  case -1:
269
- return Nan::ThrowError("forkpty(3) failed.");
299
+ Nan::ThrowError("forkpty(3) failed.");
300
+ goto done;
270
301
  case 0:
271
302
  if (strlen(cwd)) {
272
303
  if (chdir(cwd) == -1) {
@@ -286,40 +317,58 @@ NAN_METHOD(PtyFork) {
286
317
  }
287
318
  }
288
319
 
289
- pty_execvpe(argv[0], argv, env);
290
-
291
- perror("execvp(3) failed.");
292
- _exit(1);
320
+ {
321
+ char **old = environ;
322
+ environ = env;
323
+ execvp(argv[0], argv);
324
+ environ = old;
325
+ perror("execvp(3) failed.");
326
+ _exit(1);
327
+ }
293
328
  default:
294
329
  if (pty_nonblock(master) == -1) {
295
- return Nan::ThrowError("Could not set master fd to nonblocking.");
330
+ Nan::ThrowError("Could not set master fd to nonblocking.");
331
+ goto done;
296
332
  }
333
+ }
334
+ #endif
297
335
 
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);
336
+ {
337
+ v8::Local<v8::Object> obj = Nan::New<v8::Object>();
338
+ Nan::Set(obj,
339
+ Nan::New<v8::String>("fd").ToLocalChecked(),
340
+ Nan::New<v8::Number>(master));
341
+ Nan::Set(obj,
342
+ Nan::New<v8::String>("pid").ToLocalChecked(),
343
+ Nan::New<v8::Number>(pid));
344
+ Nan::Set(obj,
345
+ Nan::New<v8::String>("pty").ToLocalChecked(),
346
+ Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
347
+
348
+ pty_baton *baton = new pty_baton();
349
+ baton->exit_code = 0;
350
+ baton->signal_code = 0;
351
+ baton->cb.Reset(v8::Local<v8::Function>::Cast(info[9]));
352
+ baton->pid = pid;
353
+ baton->async.data = baton;
354
+
355
+ uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
356
+
357
+ uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
358
+
359
+ return info.GetReturnValue().Set(obj);
360
+ }
317
361
 
318
- uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
362
+ done:
363
+ #if defined(__APPLE__)
364
+ for (int i = 0; i < argl; i++) free(argv[i]);
365
+ delete[] argv;
319
366
 
320
- return info.GetReturnValue().Set(obj);
321
- }
367
+ for (int i = 0; i < envc; i++) free(env[i]);
368
+ delete[] env;
322
369
 
370
+ free(cwd);
371
+ #endif
323
372
  return info.GetReturnValue().SetUndefined();
324
373
  }
325
374
 
@@ -341,7 +390,7 @@ NAN_METHOD(PtyOpen) {
341
390
 
342
391
  // pty
343
392
  int master, slave;
344
- int ret = pty_openpty(&master, &slave, nullptr, NULL, &winp);
393
+ int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
345
394
 
346
395
  if (ret == -1) {
347
396
  return Nan::ThrowError("openpty(3) failed.");
@@ -428,21 +477,6 @@ NAN_METHOD(PtyGetProc) {
428
477
  return info.GetReturnValue().Set(name_);
429
478
  }
430
479
 
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
480
  /**
447
481
  * Nonblocking FD
448
482
  */
@@ -463,11 +497,44 @@ static void
463
497
  pty_waitpid(void *data) {
464
498
  int ret;
465
499
  int stat_loc;
466
-
467
500
  pty_baton *baton = static_cast<pty_baton*>(data);
468
-
469
501
  errno = 0;
470
-
502
+ #if defined(__APPLE__)
503
+ // Based on
504
+ // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
505
+ int kq = HANDLE_EINTR(kqueue());
506
+ struct kevent change = {0};
507
+ EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
508
+ ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
509
+ if (ret == -1) {
510
+ if (errno == ESRCH) {
511
+ // At this point, one of the following has occurred:
512
+ // 1. The process has died but has not yet been reaped.
513
+ // 2. The process has died and has already been reaped.
514
+ // 3. The process is in the process of dying. It's no longer
515
+ // kqueueable, but it may not be waitable yet either. Mark calls
516
+ // this case the "zombie death race".
517
+ ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG));
518
+ if (ret == 0) {
519
+ ret = kill(baton->pid, SIGKILL);
520
+ if (ret != -1) {
521
+ HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
522
+ }
523
+ }
524
+ }
525
+ } else {
526
+ struct kevent event = {0};
527
+ ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
528
+ if (ret == 1) {
529
+ if ((event.fflags & NOTE_EXIT) &&
530
+ (event.ident == static_cast<uintptr_t>(baton->pid))) {
531
+ // The process is dead or dying. This won't block for long, if at
532
+ // all.
533
+ HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
534
+ }
535
+ }
536
+ }
537
+ #else
471
538
  if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
472
539
  if (ret == -1 && errno == EINTR) {
473
540
  return pty_waitpid(baton);
@@ -480,6 +547,7 @@ pty_waitpid(void *data) {
480
547
  assert(false);
481
548
  }
482
549
  }
550
+ #endif
483
551
 
484
552
  if (WIFEXITED(stat_loc)) {
485
553
  baton->exit_code = WEXITSTATUS(stat_loc); // errno?
@@ -625,99 +693,194 @@ pty_getproc(int fd, char *tty) {
625
693
 
626
694
  #endif
627
695
 
628
- /**
629
- * openpty(3) / forkpty(3)
630
- */
696
+ #if defined(__APPLE__)
697
+ static void
698
+ pty_posix_spawn(char** argv, char** env,
699
+ char* cwd,
700
+ const struct termios *termp,
701
+ const struct winsize *winp,
702
+ int* master,
703
+ pid_t* pid,
704
+ int* err) {
705
+ int low_fds[3];
706
+ size_t count = 0;
707
+ const char *p;
708
+ const char *z;
709
+ size_t l;
710
+ size_t k;
711
+ int seen_eacces;
712
+ const char *path;
713
+ char** env_iterator;
714
+ const char path_var[] = "PATH=";
715
+
716
+ for (; count < 3; count++) {
717
+ low_fds[count] = posix_openpt(O_RDWR);
718
+ if (low_fds[count] >= STDERR_FILENO)
719
+ break;
720
+ }
631
721
 
632
- static int
633
- pty_openpty(int *amaster,
634
- int *aslave,
635
- char *name,
636
- const struct termios *termp,
637
- const struct winsize *winp) {
638
- #if defined(__sun)
639
- char *slave_name;
722
+ int flags = POSIX_SPAWN_CLOEXEC_DEFAULT |
723
+ POSIX_SPAWN_SETSIGDEF |
724
+ POSIX_SPAWN_SETSIGMASK |
725
+ POSIX_SPAWN_SETSID;
726
+ *master = posix_openpt(O_RDWR);
727
+ if (*master == -1) {
728
+ return;
729
+ }
730
+
731
+ int res = grantpt(*master) || unlockpt(*master);
732
+ if (res == -1) {
733
+ return;
734
+ }
735
+
736
+ // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
640
737
  int slave;
641
- int master = open("/dev/ptmx", O_RDWR | O_NOCTTY);
642
- if (master == -1) return -1;
643
- if (amaster) *amaster = master;
738
+ char slave_pty_name[128];
739
+ res = ioctl(*master, TIOCPTYGNAME, slave_pty_name);
740
+ if (res == -1) {
741
+ return;
742
+ }
644
743
 
645
- if (grantpt(master) == -1) goto err;
646
- if (unlockpt(master) == -1) goto err;
744
+ slave = open(slave_pty_name, O_RDWR | O_NOCTTY);
745
+ if (slave == -1) {
746
+ return;
747
+ }
647
748
 
648
- slave_name = ptsname(master);
649
- if (slave_name == NULL) goto err;
650
- if (name) strcpy(name, slave_name);
749
+ if (termp) {
750
+ res = tcsetattr(slave, TCSANOW, termp);
751
+ if (res == -1) {
752
+ return;
753
+ };
754
+ }
651
755
 
652
- slave = open(slave_name, O_RDWR | O_NOCTTY);
653
- if (slave == -1) goto err;
654
- if (aslave) *aslave = slave;
756
+ if (winp) {
757
+ res = ioctl(slave, TIOCSWINSZ, winp);
758
+ if (res == -1) {
759
+ return;
760
+ }
761
+ }
655
762
 
656
- ioctl(slave, I_PUSH, "ptem");
657
- ioctl(slave, I_PUSH, "ldterm");
658
- ioctl(slave, I_PUSH, "ttcompat");
763
+ posix_spawn_file_actions_t acts;
764
+ posix_spawn_file_actions_init(&acts);
765
+ posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO);
766
+ posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO);
767
+ posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO);
768
+ posix_spawn_file_actions_addclose(&acts, slave);
769
+ posix_spawn_file_actions_addclose(&acts, *master);
770
+ if (strlen(cwd)) {
771
+ if (__builtin_available(macOS 10.15, *)) {
772
+ posix_spawn_file_actions_addchdir_np(&acts, cwd);
773
+ } else {
774
+ *err = pthread_chdir_np(cwd);
775
+ if (*err != 0) {
776
+ goto done;
777
+ }
778
+ }
779
+ }
659
780
 
660
- if (termp) tcsetattr(slave, TCSAFLUSH, termp);
661
- if (winp) ioctl(slave, TIOCSWINSZ, winp);
781
+ posix_spawnattr_t attrs;
782
+ posix_spawnattr_init(&attrs);
783
+ *err = posix_spawnattr_setflags(&attrs, flags);
784
+ if (*err != 0) {
785
+ goto done;
786
+ }
662
787
 
663
- return 0;
788
+ sigset_t signal_set;
789
+ /* Reset all signal the child to their default behavior */
790
+ sigfillset(&signal_set);
791
+ *err = posix_spawnattr_setsigdefault(&attrs, &signal_set);
792
+ if (*err != 0) {
793
+ goto done;
794
+ }
664
795
 
665
- err:
666
- close(master);
667
- return -1;
668
- #else
669
- return openpty(amaster, aslave, name, (termios *)termp, (winsize *)winp);
670
- #endif
671
- }
796
+ /* Reset the signal mask for all signals */
797
+ sigemptyset(&signal_set);
798
+ *err = posix_spawnattr_setsigmask(&attrs, &signal_set);
799
+ if (*err != 0) {
800
+ goto done;
801
+ }
672
802
 
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;
803
+ // path resolution is copied from
804
+ // https://github.com/libuv/libuv/blob/7b84d5b0ecb737b4cc30ce63eade690d994e00a6/src/unix/process.c#L695-L764
805
+ if (strchr(argv[0], '/') != NULL) {
806
+ do
807
+ *err = posix_spawn(pid, argv[0], &acts, &attrs, argv, env);
808
+ while (*err == EINTR);
809
+ } else {
810
+
811
+ for (env_iterator = env; *env_iterator != NULL; env_iterator++) {
812
+ if (strncmp(*env_iterator, path_var, sizeof(path_var) - 1) == 0) {
813
+ /* Found "PATH=" at the beginning of the string */
814
+ path = *env_iterator + sizeof(path_var) - 1;
815
+ }
816
+ }
680
817
 
681
- int ret = pty_openpty(&master, &slave, name, termp, winp);
682
- if (ret == -1) return -1;
683
- if (amaster) *amaster = master;
818
+ if (path == NULL)
819
+ path = _PATH_DEFPATH;
684
820
 
685
- pid_t pid = fork();
821
+ k = strnlen(argv[0], NAME_MAX + 1);
822
+ if (k > NAME_MAX)
823
+ goto done;
686
824
 
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);
825
+ l = strnlen(path, PATH_MAX - 1) + 1;
826
+
827
+ for (p = path;; p = z) {
828
+ /* Compose the new process file from the entry in the PATH
829
+ * environment variable and the actual file name */
830
+ char b[PATH_MAX + NAME_MAX];
831
+ z = strchr(p, ':');
832
+ if (!z)
833
+ z = p + strlen(p);
834
+ if ((size_t)(z - p) >= l) {
835
+ if (!*z++)
836
+ break;
837
+
838
+ continue;
839
+ }
840
+ memcpy(b, p, z - p);
841
+ b[z - p] = '/';
842
+ memcpy(b + (z - p) + (z > p), argv[0], k + 1);
843
+
844
+ do
845
+ *err = posix_spawn(pid, b, &acts, &attrs, argv, env);
846
+ while (*err == EINTR);
847
+
848
+ switch (*err) {
849
+ case EACCES:
850
+ seen_eacces = 1;
851
+ break; /* continue search */
852
+ case ENOENT:
853
+ case ENOTDIR:
854
+ break; /* continue search */
855
+ default:
856
+ goto done;
700
857
  }
701
- #endif
702
858
 
703
- dup2(slave, 0);
704
- dup2(slave, 1);
705
- dup2(slave, 2);
859
+ if (!*z++)
860
+ break;
861
+ }
706
862
 
707
- if (slave > 2) close(slave);
863
+ if (seen_eacces)
864
+ goto done;
865
+ }
708
866
 
709
- return 0;
710
- default: // we are in the parent process
711
- close(slave);
712
- return pid;
867
+ // Restore the thread's working directory if it was changed.
868
+ if (strlen(cwd)) {
869
+ if (__builtin_available(macOS 10.15, *)) {
870
+ // __builtin_available is special and cannot be negated.
871
+ } else {
872
+ pthread_fchdir_np(-1);
873
+ }
713
874
  }
875
+ done:
876
+ posix_spawn_file_actions_destroy(&acts);
877
+ posix_spawnattr_destroy(&attrs);
714
878
 
715
- return -1;
716
- #else
717
- return forkpty(amaster, name, (termios *)termp, (winsize *)winp);
718
- #endif
879
+ for (; count > 0; count--) {
880
+ close(low_fds[count]);
881
+ }
719
882
  }
720
-
883
+ #endif
721
884
 
722
885
  /**
723
886
  * Init