node-pty 0.11.0-beta9 → 1.0.0

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 +5 -4
  2. package/binding.gyp +68 -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/terminal.test.js +3 -0
  7. package/lib/terminal.test.js.map +1 -1
  8. package/lib/unixTerminal.js +30 -4
  9. package/lib/unixTerminal.js.map +1 -1
  10. package/lib/unixTerminal.test.js +147 -18
  11. package/lib/unixTerminal.test.js.map +1 -1
  12. package/lib/windowsPtyAgent.js +8 -0
  13. package/lib/windowsPtyAgent.js.map +1 -1
  14. package/lib/windowsTerminal.js +21 -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 +3 -4
  21. package/scripts/post-install.js +1 -0
  22. package/src/conpty_console_list_agent.ts +1 -1
  23. package/src/interfaces.ts +8 -2
  24. package/src/native.d.ts +3 -1
  25. package/src/terminal.test.ts +3 -0
  26. package/src/terminal.ts +21 -20
  27. package/src/tsconfig.json +1 -3
  28. package/src/unix/pty.cc +297 -205
  29. package/src/unix/spawn-helper.cc +23 -0
  30. package/src/unixTerminal.test.ts +117 -25
  31. package/src/unixTerminal.ts +31 -11
  32. package/src/win/conpty.cc +65 -22
  33. package/src/win/path_util.cc +1 -1
  34. package/src/windowsConoutConnection.ts +1 -1
  35. package/src/windowsPtyAgent.ts +11 -5
  36. package/src/windowsTerminal.test.ts +5 -6
  37. package/src/windowsTerminal.ts +23 -4
  38. package/src/worker/conoutSocketWorker.ts +4 -0
  39. package/typings/node-pty.d.ts +11 -20
  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
@@ -32,20 +32,14 @@
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
39
  #elif defined(__FreeBSD__)
40
40
  #include <libutil.h>
41
- #elif defined(__sun)
42
- #include <stropts.h> /* for I_PUSH */
43
- #else
44
- #include <pty.h>
45
41
  #endif
46
42
 
47
- #include <termios.h> /* tcgetattr, tty_ioctl */
48
-
49
43
  /* Some platforms name VWERASE and VDISCARD differently */
50
44
  #if !defined(VWERASE) && defined(VWERSE)
51
45
  #define VWERASE VWERSE
@@ -54,22 +48,17 @@
54
48
  #define VDISCARD VDISCRD
55
49
  #endif
56
50
 
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
51
  /* for pty_getproc */
67
52
  #if defined(__linux__)
68
53
  #include <stdio.h>
69
54
  #include <stdint.h>
70
55
  #elif defined(__APPLE__)
71
- #include <sys/sysctl.h>
72
56
  #include <libproc.h>
57
+ #include <os/availability.h>
58
+ #include <paths.h>
59
+ #include <spawn.h>
60
+ #include <sys/event.h>
61
+ #include <termios.h>
73
62
  #endif
74
63
 
75
64
  /* NSIG - macro for highest signal + 1, should be defined */
@@ -77,6 +66,38 @@ extern char **environ;
77
66
  #define NSIG 32
78
67
  #endif
79
68
 
69
+ /* macOS 10.14 back does not define this constant */
70
+ #ifndef POSIX_SPAWN_SETSID
71
+ #define POSIX_SPAWN_SETSID 1024
72
+ #endif
73
+
74
+ /* environ for execvpe */
75
+ /* node/src/node_child_process.cc */
76
+ #if !defined(__APPLE__)
77
+ extern char **environ;
78
+ #endif
79
+
80
+ #if defined(__APPLE__)
81
+ extern "C" {
82
+ // Changes the current thread's directory to a path or directory file
83
+ // descriptor. libpthread only exposes a syscall wrapper starting in
84
+ // macOS 10.12, but the system call dates back to macOS 10.5. On older OSes,
85
+ // the syscall is issued directly.
86
+ int pthread_chdir_np(const char* dir) API_AVAILABLE(macosx(10.12));
87
+ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
88
+ }
89
+
90
+ #define HANDLE_EINTR(x) ({ \
91
+ int eintr_wrapper_counter = 0; \
92
+ decltype(x) eintr_wrapper_result; \
93
+ do { \
94
+ eintr_wrapper_result = (x); \
95
+ } while (eintr_wrapper_result == -1 && errno == EINTR && \
96
+ eintr_wrapper_counter++ < 100); \
97
+ eintr_wrapper_result; \
98
+ })
99
+ #endif
100
+
80
101
  /**
81
102
  * Structs
82
103
  */
@@ -103,24 +124,16 @@ NAN_METHOD(PtyGetProc);
103
124
  * Functions
104
125
  */
105
126
 
106
- static int
107
- pty_execvpe(const char *, char **, char **);
108
-
109
127
  static int
110
128
  pty_nonblock(int);
111
129
 
130
+ #if defined(__APPLE__)
131
+ static char *
132
+ pty_getproc(int);
133
+ #else
112
134
  static char *
113
135
  pty_getproc(int, char *);
114
-
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 *);
136
+ #endif
124
137
 
125
138
  static void
126
139
  pty_waitpid(void *);
@@ -131,10 +144,20 @@ pty_after_waitpid(uv_async_t *);
131
144
  static void
132
145
  pty_after_close(uv_handle_t *);
133
146
 
147
+ #if defined(__APPLE__) || defined(__OpenBSD__)
148
+ static void
149
+ pty_posix_spawn(char** argv, char** env,
150
+ const struct termios *termp,
151
+ const struct winsize *winp,
152
+ int* master,
153
+ pid_t* pid,
154
+ int* err);
155
+ #endif
156
+
134
157
  NAN_METHOD(PtyFork) {
135
158
  Nan::HandleScope scope;
136
159
 
137
- if (info.Length() != 10 ||
160
+ if (info.Length() != 11 ||
138
161
  !info[0]->IsString() ||
139
162
  !info[1]->IsArray() ||
140
163
  !info[2]->IsArray() ||
@@ -144,41 +167,30 @@ NAN_METHOD(PtyFork) {
144
167
  !info[6]->IsNumber() ||
145
168
  !info[7]->IsNumber() ||
146
169
  !info[8]->IsBoolean() ||
147
- !info[9]->IsFunction()) {
170
+ !info[9]->IsString() ||
171
+ !info[10]->IsFunction()) {
148
172
  return Nan::ThrowError(
149
- "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, onexit)");
173
+ "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
150
174
  }
151
175
 
152
176
  // file
153
177
  Nan::Utf8String file(info[0]);
154
178
 
155
179
  // args
156
- int i = 0;
157
180
  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
181
 
168
182
  // env
169
- i = 0;
170
183
  v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
171
184
  int envc = env_->Length();
172
185
  char **env = new char*[envc+1];
173
186
  env[envc] = NULL;
174
- for (; i < envc; i++) {
187
+ for (int i = 0; i < envc; i++) {
175
188
  Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
176
189
  env[i] = strdup(*pair);
177
190
  }
178
191
 
179
192
  // cwd
180
193
  Nan::Utf8String cwd_(info[3]);
181
- char *cwd = strdup(*cwd_);
182
194
 
183
195
  // size
184
196
  struct winsize winp;
@@ -187,6 +199,12 @@ NAN_METHOD(PtyFork) {
187
199
  winp.ws_xpixel = 0;
188
200
  winp.ws_ypixel = 0;
189
201
 
202
+ #if !defined(__APPLE__)
203
+ // uid / gid
204
+ int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
205
+ int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
206
+ #endif
207
+
190
208
  // termios
191
209
  struct termios t = termios();
192
210
  struct termios *term = &t;
@@ -225,16 +243,48 @@ NAN_METHOD(PtyFork) {
225
243
  cfsetispeed(term, B38400);
226
244
  cfsetospeed(term, B38400);
227
245
 
228
- // uid / gid
229
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
230
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
246
+ // helperPath
247
+ Nan::Utf8String helper_path(info[9]);
231
248
 
232
- // fork the pty
233
- int master = -1;
249
+ pid_t pid;
250
+ int master;
251
+ #if defined(__APPLE__)
252
+ int argc = argv_->Length();
253
+ int argl = argc + 4;
254
+ char **argv = new char*[argl];
255
+ argv[0] = strdup(*helper_path);
256
+ argv[1] = strdup(*cwd_);
257
+ argv[2] = strdup(*file);
258
+ argv[argl - 1] = NULL;
259
+ for (int i = 0; i < argc; i++) {
260
+ Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
261
+ argv[i + 3] = strdup(*arg);
262
+ }
263
+
264
+ int err = -1;
265
+ pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
266
+ if (err != 0) {
267
+ Nan::ThrowError("posix_spawnp failed.");
268
+ goto done;
269
+ }
270
+ if (pty_nonblock(master) == -1) {
271
+ Nan::ThrowError("Could not set master fd to nonblocking.");
272
+ goto done;
273
+ }
274
+ #else
275
+ int argc = argv_->Length();
276
+ int argl = argc + 2;
277
+ char **argv = new char*[argl];
278
+ argv[0] = strdup(*file);
279
+ argv[argl - 1] = NULL;
280
+ for (int i = 0; i < argc; i++) {
281
+ Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
282
+ argv[i + 1] = strdup(*arg);
283
+ }
234
284
 
285
+ char* cwd = strdup(*cwd_);
235
286
  sigset_t newmask, oldmask;
236
287
  struct sigaction sig_action;
237
-
238
288
  // temporarily block all signals
239
289
  // this is needed due to a race condition in openpty
240
290
  // and to avoid running signal handlers in the child
@@ -242,7 +292,7 @@ NAN_METHOD(PtyFork) {
242
292
  sigfillset(&newmask);
243
293
  pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
244
294
 
245
- pid_t pid = pty_forkpty(&master, nullptr, term, &winp);
295
+ pid = forkpty(&master, nullptr, static_cast<termios*>(term), static_cast<winsize*>(&winp));
246
296
 
247
297
  if (!pid) {
248
298
  // remove all signal handler from child
@@ -252,21 +302,23 @@ NAN_METHOD(PtyFork) {
252
302
  for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
253
303
  sigaction(i, &sig_action, NULL);
254
304
  }
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]);
305
+ } else {
306
+ for (int i = 0; i < argl; i++) free(argv[i]);
261
307
  delete[] argv;
262
- for (i = 0; i < envc; i++) free(env[i]);
308
+
309
+ for (int i = 0; i < envc; i++) free(env[i]);
263
310
  delete[] env;
311
+
264
312
  free(cwd);
265
313
  }
266
314
 
315
+ // reenable signals
316
+ pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
317
+
267
318
  switch (pid) {
268
319
  case -1:
269
- return Nan::ThrowError("forkpty(3) failed.");
320
+ Nan::ThrowError("forkpty(3) failed.");
321
+ goto done;
270
322
  case 0:
271
323
  if (strlen(cwd)) {
272
324
  if (chdir(cwd) == -1) {
@@ -286,40 +338,56 @@ NAN_METHOD(PtyFork) {
286
338
  }
287
339
  }
288
340
 
289
- pty_execvpe(argv[0], argv, env);
290
-
291
- perror("execvp(3) failed.");
292
- _exit(1);
341
+ {
342
+ char **old = environ;
343
+ environ = env;
344
+ execvp(argv[0], argv);
345
+ environ = old;
346
+ perror("execvp(3) failed.");
347
+ _exit(1);
348
+ }
293
349
  default:
294
350
  if (pty_nonblock(master) == -1) {
295
- return Nan::ThrowError("Could not set master fd to nonblocking.");
351
+ Nan::ThrowError("Could not set master fd to nonblocking.");
352
+ goto done;
296
353
  }
354
+ }
355
+ #endif
297
356
 
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);
357
+ {
358
+ v8::Local<v8::Object> obj = Nan::New<v8::Object>();
359
+ Nan::Set(obj,
360
+ Nan::New<v8::String>("fd").ToLocalChecked(),
361
+ Nan::New<v8::Number>(master));
362
+ Nan::Set(obj,
363
+ Nan::New<v8::String>("pid").ToLocalChecked(),
364
+ Nan::New<v8::Number>(pid));
365
+ Nan::Set(obj,
366
+ Nan::New<v8::String>("pty").ToLocalChecked(),
367
+ Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
368
+
369
+ pty_baton *baton = new pty_baton();
370
+ baton->exit_code = 0;
371
+ baton->signal_code = 0;
372
+ baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
373
+ baton->pid = pid;
374
+ baton->async.data = baton;
375
+
376
+ uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
377
+
378
+ uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
379
+
380
+ return info.GetReturnValue().Set(obj);
321
381
  }
322
382
 
383
+ done:
384
+ #if defined(__APPLE__)
385
+ for (int i = 0; i < argl; i++) free(argv[i]);
386
+ delete[] argv;
387
+
388
+ for (int i = 0; i < envc; i++) free(env[i]);
389
+ delete[] env;
390
+ #endif
323
391
  return info.GetReturnValue().SetUndefined();
324
392
  }
325
393
 
@@ -341,7 +409,7 @@ NAN_METHOD(PtyOpen) {
341
409
 
342
410
  // pty
343
411
  int master, slave;
344
- int ret = pty_openpty(&master, &slave, nullptr, NULL, &winp);
412
+ int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
345
413
 
346
414
  if (ret == -1) {
347
415
  return Nan::ThrowError("openpty(3) failed.");
@@ -406,6 +474,15 @@ NAN_METHOD(PtyResize) {
406
474
  NAN_METHOD(PtyGetProc) {
407
475
  Nan::HandleScope scope;
408
476
 
477
+ #if defined(__APPLE__)
478
+ if (info.Length() != 1 ||
479
+ !info[0]->IsNumber()) {
480
+ return Nan::ThrowError("Usage: pty.process(pid)");
481
+ }
482
+
483
+ int pid = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
484
+ char *name = pty_getproc(pid);
485
+ #else
409
486
  if (info.Length() != 2 ||
410
487
  !info[0]->IsNumber() ||
411
488
  !info[1]->IsString()) {
@@ -418,6 +495,7 @@ NAN_METHOD(PtyGetProc) {
418
495
  char *tty = strdup(*tty_);
419
496
  char *name = pty_getproc(fd, tty);
420
497
  free(tty);
498
+ #endif
421
499
 
422
500
  if (name == NULL) {
423
501
  return info.GetReturnValue().SetUndefined();
@@ -428,21 +506,6 @@ NAN_METHOD(PtyGetProc) {
428
506
  return info.GetReturnValue().Set(name_);
429
507
  }
430
508
 
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
509
  /**
447
510
  * Nonblocking FD
448
511
  */
@@ -463,11 +526,44 @@ static void
463
526
  pty_waitpid(void *data) {
464
527
  int ret;
465
528
  int stat_loc;
466
-
467
529
  pty_baton *baton = static_cast<pty_baton*>(data);
468
-
469
530
  errno = 0;
470
-
531
+ #if defined(__APPLE__)
532
+ // Based on
533
+ // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
534
+ int kq = HANDLE_EINTR(kqueue());
535
+ struct kevent change = {0};
536
+ EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
537
+ ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
538
+ if (ret == -1) {
539
+ if (errno == ESRCH) {
540
+ // At this point, one of the following has occurred:
541
+ // 1. The process has died but has not yet been reaped.
542
+ // 2. The process has died and has already been reaped.
543
+ // 3. The process is in the process of dying. It's no longer
544
+ // kqueueable, but it may not be waitable yet either. Mark calls
545
+ // this case the "zombie death race".
546
+ ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG));
547
+ if (ret == 0) {
548
+ ret = kill(baton->pid, SIGKILL);
549
+ if (ret != -1) {
550
+ HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
551
+ }
552
+ }
553
+ }
554
+ } else {
555
+ struct kevent event = {0};
556
+ ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
557
+ if (ret == 1) {
558
+ if ((event.fflags & NOTE_EXIT) &&
559
+ (event.ident == static_cast<uintptr_t>(baton->pid))) {
560
+ // The process is dead or dying. This won't block for long, if at
561
+ // all.
562
+ HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
563
+ }
564
+ }
565
+ }
566
+ #else
471
567
  if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
472
568
  if (ret == -1 && errno == EINTR) {
473
569
  return pty_waitpid(baton);
@@ -480,6 +576,7 @@ pty_waitpid(void *data) {
480
576
  assert(false);
481
577
  }
482
578
  }
579
+ #endif
483
580
 
484
581
  if (WIFEXITED(stat_loc)) {
485
582
  baton->exit_code = WEXITSTATUS(stat_loc); // errno?
@@ -595,25 +692,13 @@ pty_getproc(int fd, char *tty) {
595
692
  #elif defined(__APPLE__)
596
693
 
597
694
  static char *
598
- pty_getproc(int fd, char *tty) {
599
- int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
600
- size_t size;
601
- struct kinfo_proc kp;
602
-
603
- if ((mib[3] = tcgetpgrp(fd)) == -1) {
695
+ pty_getproc(int pid) {
696
+ char pname[MAXCOMLEN + 1];
697
+ if (!proc_name(pid, pname, sizeof(pname))) {
604
698
  return NULL;
605
699
  }
606
700
 
607
- size = sizeof kp;
608
- if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
609
- return NULL;
610
- }
611
-
612
- if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') {
613
- return NULL;
614
- }
615
-
616
- return strdup(kp.kp_proc.p_comm);
701
+ return strdup(pname);
617
702
  }
618
703
 
619
704
  #else
@@ -625,99 +710,106 @@ pty_getproc(int fd, char *tty) {
625
710
 
626
711
  #endif
627
712
 
628
- /**
629
- * openpty(3) / forkpty(3)
630
- */
631
-
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;
640
- int slave;
641
- int master = open("/dev/ptmx", O_RDWR | O_NOCTTY);
642
- if (master == -1) return -1;
643
- if (amaster) *amaster = master;
644
-
645
- if (grantpt(master) == -1) goto err;
646
- if (unlockpt(master) == -1) goto err;
647
-
648
- slave_name = ptsname(master);
649
- if (slave_name == NULL) goto err;
650
- if (name) strcpy(name, slave_name);
651
-
652
- slave = open(slave_name, O_RDWR | O_NOCTTY);
653
- if (slave == -1) goto err;
654
- if (aslave) *aslave = slave;
713
+ #if defined(__APPLE__)
714
+ static void
715
+ pty_posix_spawn(char** argv, char** env,
716
+ const struct termios *termp,
717
+ const struct winsize *winp,
718
+ int* master,
719
+ pid_t* pid,
720
+ int* err) {
721
+ int low_fds[3];
722
+ size_t count = 0;
723
+
724
+ for (; count < 3; count++) {
725
+ low_fds[count] = posix_openpt(O_RDWR);
726
+ if (low_fds[count] >= STDERR_FILENO)
727
+ break;
728
+ }
655
729
 
656
- ioctl(slave, I_PUSH, "ptem");
657
- ioctl(slave, I_PUSH, "ldterm");
658
- ioctl(slave, I_PUSH, "ttcompat");
730
+ int flags = POSIX_SPAWN_CLOEXEC_DEFAULT |
731
+ POSIX_SPAWN_SETSIGDEF |
732
+ POSIX_SPAWN_SETSIGMASK |
733
+ POSIX_SPAWN_SETSID;
734
+ *master = posix_openpt(O_RDWR);
735
+ if (*master == -1) {
736
+ return;
737
+ }
659
738
 
660
- if (termp) tcsetattr(slave, TCSAFLUSH, termp);
661
- if (winp) ioctl(slave, TIOCSWINSZ, winp);
739
+ int res = grantpt(*master) || unlockpt(*master);
740
+ if (res == -1) {
741
+ return;
742
+ }
662
743
 
663
- return 0;
744
+ // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
745
+ int slave;
746
+ char slave_pty_name[128];
747
+ res = ioctl(*master, TIOCPTYGNAME, slave_pty_name);
748
+ if (res == -1) {
749
+ return;
750
+ }
664
751
 
665
- err:
666
- close(master);
667
- return -1;
668
- #else
669
- return openpty(amaster, aslave, name, (termios *)termp, (winsize *)winp);
670
- #endif
671
- }
752
+ slave = open(slave_pty_name, O_RDWR | O_NOCTTY);
753
+ if (slave == -1) {
754
+ return;
755
+ }
672
756
 
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;
757
+ if (termp) {
758
+ res = tcsetattr(slave, TCSANOW, termp);
759
+ if (res == -1) {
760
+ return;
761
+ };
762
+ }
680
763
 
681
- int ret = pty_openpty(&master, &slave, name, termp, winp);
682
- if (ret == -1) return -1;
683
- if (amaster) *amaster = master;
764
+ if (winp) {
765
+ res = ioctl(slave, TIOCSWINSZ, winp);
766
+ if (res == -1) {
767
+ return;
768
+ }
769
+ }
684
770
 
685
- pid_t pid = fork();
771
+ posix_spawn_file_actions_t acts;
772
+ posix_spawn_file_actions_init(&acts);
773
+ posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO);
774
+ posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO);
775
+ posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO);
776
+ posix_spawn_file_actions_addclose(&acts, slave);
777
+ posix_spawn_file_actions_addclose(&acts, *master);
778
+
779
+ posix_spawnattr_t attrs;
780
+ posix_spawnattr_init(&attrs);
781
+ *err = posix_spawnattr_setflags(&attrs, flags);
782
+ if (*err != 0) {
783
+ goto done;
784
+ }
686
785
 
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
786
+ sigset_t signal_set;
787
+ /* Reset all signal the child to their default behavior */
788
+ sigfillset(&signal_set);
789
+ *err = posix_spawnattr_setsigdefault(&attrs, &signal_set);
790
+ if (*err != 0) {
791
+ goto done;
792
+ }
702
793
 
703
- dup2(slave, 0);
704
- dup2(slave, 1);
705
- dup2(slave, 2);
794
+ /* Reset the signal mask for all signals */
795
+ sigemptyset(&signal_set);
796
+ *err = posix_spawnattr_setsigmask(&attrs, &signal_set);
797
+ if (*err != 0) {
798
+ goto done;
799
+ }
706
800
 
707
- if (slave > 2) close(slave);
801
+ do
802
+ *err = posix_spawn(pid, argv[0], &acts, &attrs, argv, env);
803
+ while (*err == EINTR);
804
+ done:
805
+ posix_spawn_file_actions_destroy(&acts);
806
+ posix_spawnattr_destroy(&attrs);
708
807
 
709
- return 0;
710
- default: // we are in the parent process
711
- close(slave);
712
- return pid;
808
+ for (; count > 0; count--) {
809
+ close(low_fds[count]);
713
810
  }
714
-
715
- return -1;
716
- #else
717
- return forkpty(amaster, name, (termios *)termp, (winsize *)winp);
718
- #endif
719
811
  }
720
-
812
+ #endif
721
813
 
722
814
  /**
723
815
  * Init