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.
- package/README.md +7 -3
- package/binding.gyp +54 -29
- package/lib/conpty_console_list_agent.js.map +1 -1
- package/lib/terminal.js +10 -4
- package/lib/terminal.js.map +1 -1
- package/lib/unixTerminal.js +17 -3
- package/lib/unixTerminal.js.map +1 -1
- package/lib/unixTerminal.test.js +161 -18
- package/lib/unixTerminal.test.js.map +1 -1
- package/lib/windowsConoutConnection.js +2 -1
- package/lib/windowsConoutConnection.js.map +1 -1
- package/lib/windowsPtyAgent.js +3 -0
- package/lib/windowsPtyAgent.js.map +1 -1
- package/lib/windowsTerminal.js +15 -3
- package/lib/windowsTerminal.js.map +1 -1
- package/lib/windowsTerminal.test.js +4 -5
- package/lib/windowsTerminal.test.js.map +1 -1
- package/lib/worker/conoutSocketWorker.js +3 -0
- package/lib/worker/conoutSocketWorker.js.map +1 -1
- package/package.json +2 -3
- package/src/conpty_console_list_agent.ts +1 -1
- package/src/interfaces.ts +2 -2
- package/src/terminal.ts +20 -20
- package/src/tsconfig.json +1 -3
- package/src/unix/pty.cc +344 -181
- package/src/unixTerminal.test.ts +128 -25
- package/src/unixTerminal.ts +15 -10
- package/src/win/conpty.cc +34 -22
- package/src/win/path_util.cc +1 -1
- package/src/windowsConoutConnection.ts +3 -2
- package/src/windowsPtyAgent.ts +5 -5
- package/src/windowsTerminal.test.ts +5 -6
- package/src/windowsTerminal.ts +17 -4
- package/src/worker/conoutSocketWorker.ts +4 -0
- package/typings/node-pty.d.ts +3 -3
- package/deps/winpty/.gitignore +0 -15
- package/deps/winpty/misc/.gitignore +0 -2
- 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(
|
|
35
|
+
#if defined(__linux__)
|
|
36
36
|
#include <pty.h>
|
|
37
|
-
#elif defined(__APPLE__)
|
|
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
|
|
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
|
-
|
|
229
|
-
int
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
int
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
330
|
+
Nan::ThrowError("Could not set master fd to nonblocking.");
|
|
331
|
+
goto done;
|
|
296
332
|
}
|
|
333
|
+
}
|
|
334
|
+
#endif
|
|
297
335
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
Nan::
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
Nan::
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
362
|
+
done:
|
|
363
|
+
#if defined(__APPLE__)
|
|
364
|
+
for (int i = 0; i < argl; i++) free(argv[i]);
|
|
365
|
+
delete[] argv;
|
|
319
366
|
|
|
320
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
-
|
|
642
|
-
|
|
643
|
-
if (
|
|
738
|
+
char slave_pty_name[128];
|
|
739
|
+
res = ioctl(*master, TIOCPTYGNAME, slave_pty_name);
|
|
740
|
+
if (res == -1) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
644
743
|
|
|
645
|
-
|
|
646
|
-
if (
|
|
744
|
+
slave = open(slave_pty_name, O_RDWR | O_NOCTTY);
|
|
745
|
+
if (slave == -1) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
647
748
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
749
|
+
if (termp) {
|
|
750
|
+
res = tcsetattr(slave, TCSANOW, termp);
|
|
751
|
+
if (res == -1) {
|
|
752
|
+
return;
|
|
753
|
+
};
|
|
754
|
+
}
|
|
651
755
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
756
|
+
if (winp) {
|
|
757
|
+
res = ioctl(slave, TIOCSWINSZ, winp);
|
|
758
|
+
if (res == -1) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
655
762
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
-
|
|
661
|
-
|
|
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
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
-
|
|
682
|
-
|
|
683
|
-
if (amaster) *amaster = master;
|
|
818
|
+
if (path == NULL)
|
|
819
|
+
path = _PATH_DEFPATH;
|
|
684
820
|
|
|
685
|
-
|
|
821
|
+
k = strnlen(argv[0], NAME_MAX + 1);
|
|
822
|
+
if (k > NAME_MAX)
|
|
823
|
+
goto done;
|
|
686
824
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
-
|
|
704
|
-
|
|
705
|
-
|
|
859
|
+
if (!*z++)
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
706
862
|
|
|
707
|
-
|
|
863
|
+
if (seen_eacces)
|
|
864
|
+
goto done;
|
|
865
|
+
}
|
|
708
866
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|