node-pty 1.1.0-beta3 → 1.1.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 +6 -5
- package/binding.gyp +4 -15
- package/deps/winpty/src/agent/Agent.cc +11 -3
- package/lib/unixTerminal.js +4 -27
- package/lib/unixTerminal.js.map +1 -1
- package/lib/unixTerminal.test.js +3 -5
- package/lib/unixTerminal.test.js.map +1 -1
- package/lib/windowsConoutConnection.js +6 -3
- package/lib/windowsConoutConnection.js.map +1 -1
- package/lib/windowsPtyAgent.js +42 -22
- package/lib/windowsPtyAgent.js.map +1 -1
- package/lib/windowsTerminal.js +1 -1
- package/lib/windowsTerminal.js.map +1 -1
- package/lib/windowsTerminal.test.js +38 -26
- package/lib/windowsTerminal.test.js.map +1 -1
- package/lib/worker/conoutSocketWorker.js.map +1 -1
- package/package.json +4 -2
- package/scripts/{publish.js → increment-version.js} +7 -15
- package/scripts/post-install.js +51 -10
- package/src/interfaces.ts +1 -0
- package/src/native.d.ts +5 -5
- package/src/unix/pty.cc +241 -284
- package/src/unixTerminal.test.ts +3 -6
- package/src/unixTerminal.ts +4 -18
- package/src/win/conpty.cc +266 -185
- package/src/win/conpty.h +41 -0
- package/src/win/conpty_console_list.cc +16 -15
- package/src/win/path_util.cc +39 -36
- package/src/win/path_util.h +5 -2
- package/src/win/winpty.cc +108 -151
- package/src/windowsConoutConnection.ts +6 -3
- package/src/windowsPtyAgent.ts +39 -21
- package/src/windowsTerminal.test.ts +43 -32
- package/src/windowsTerminal.ts +1 -1
- package/src/worker/conoutSocketWorker.ts +2 -2
- package/third_party/conpty/1.22.250204002/win10-arm64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.22.250204002/win10-arm64/conpty.dll +0 -0
- package/third_party/conpty/1.22.250204002/win10-x64/OpenConsole.exe +0 -0
- package/third_party/conpty/1.22.250204002/win10-x64/conpty.dll +0 -0
- package/typings/node-pty.d.ts +8 -0
package/src/unix/pty.cc
CHANGED
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
* Includes
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
#
|
|
20
|
+
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
|
21
|
+
#include <napi.h>
|
|
22
|
+
#include <assert.h>
|
|
21
23
|
#include <errno.h>
|
|
22
24
|
#include <string.h>
|
|
23
25
|
#include <stdlib.h>
|
|
24
26
|
#include <unistd.h>
|
|
27
|
+
#include <thread>
|
|
25
28
|
|
|
26
29
|
#include <sys/types.h>
|
|
27
30
|
#include <sys/stat.h>
|
|
@@ -38,6 +41,7 @@
|
|
|
38
41
|
#include <util.h>
|
|
39
42
|
#elif defined(__FreeBSD__)
|
|
40
43
|
#include <libutil.h>
|
|
44
|
+
#include <termios.h>
|
|
41
45
|
#endif
|
|
42
46
|
|
|
43
47
|
/* Some platforms name VWERASE and VDISCARD differently */
|
|
@@ -99,27 +103,120 @@ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
|
|
|
99
103
|
})
|
|
100
104
|
#endif
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*/
|
|
105
|
-
|
|
106
|
-
struct pty_baton {
|
|
107
|
-
Nan::Persistent<v8::Function> cb;
|
|
108
|
-
int exit_code;
|
|
109
|
-
int signal_code;
|
|
110
|
-
pid_t pid;
|
|
111
|
-
uv_async_t async;
|
|
112
|
-
uv_thread_t tid;
|
|
106
|
+
struct ExitEvent {
|
|
107
|
+
int exit_code = 0, signal_code = 0;
|
|
113
108
|
};
|
|
114
109
|
|
|
110
|
+
void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) {
|
|
111
|
+
std::thread *th = new std::thread;
|
|
112
|
+
// Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE.
|
|
113
|
+
auto tsfn = Napi::ThreadSafeFunction::New(
|
|
114
|
+
env,
|
|
115
|
+
cb, // JavaScript function called asynchronously
|
|
116
|
+
"SetupExitCallback_resource", // Name
|
|
117
|
+
0, // Unlimited queue
|
|
118
|
+
1, // Only one thread will use this initially
|
|
119
|
+
[th](Napi::Env) { // Finalizer used to clean threads up
|
|
120
|
+
th->join();
|
|
121
|
+
delete th;
|
|
122
|
+
});
|
|
123
|
+
*th = std::thread([tsfn = std::move(tsfn), pid] {
|
|
124
|
+
auto callback = [](Napi::Env env, Napi::Function cb, ExitEvent *exit_event) {
|
|
125
|
+
cb.Call({Napi::Number::New(env, exit_event->exit_code),
|
|
126
|
+
Napi::Number::New(env, exit_event->signal_code)});
|
|
127
|
+
delete exit_event;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
int ret;
|
|
131
|
+
int stat_loc;
|
|
132
|
+
#if defined(__APPLE__)
|
|
133
|
+
// Based on
|
|
134
|
+
// https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
|
|
135
|
+
int kq = HANDLE_EINTR(kqueue());
|
|
136
|
+
struct kevent change = {0};
|
|
137
|
+
EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
|
|
138
|
+
ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
|
|
139
|
+
if (ret == -1) {
|
|
140
|
+
if (errno == ESRCH) {
|
|
141
|
+
// At this point, one of the following has occurred:
|
|
142
|
+
// 1. The process has died but has not yet been reaped.
|
|
143
|
+
// 2. The process has died and has already been reaped.
|
|
144
|
+
// 3. The process is in the process of dying. It's no longer
|
|
145
|
+
// kqueueable, but it may not be waitable yet either. Mark calls
|
|
146
|
+
// this case the "zombie death race".
|
|
147
|
+
ret = HANDLE_EINTR(waitpid(pid, &stat_loc, WNOHANG));
|
|
148
|
+
if (ret == 0) {
|
|
149
|
+
ret = kill(pid, SIGKILL);
|
|
150
|
+
if (ret != -1) {
|
|
151
|
+
HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
struct kevent event = {0};
|
|
157
|
+
ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
|
|
158
|
+
if (ret == 1) {
|
|
159
|
+
if ((event.fflags & NOTE_EXIT) &&
|
|
160
|
+
(event.ident == static_cast<uintptr_t>(pid))) {
|
|
161
|
+
// The process is dead or dying. This won't block for long, if at
|
|
162
|
+
// all.
|
|
163
|
+
HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
#else
|
|
168
|
+
while (true) {
|
|
169
|
+
errno = 0;
|
|
170
|
+
if ((ret = waitpid(pid, &stat_loc, 0)) != pid) {
|
|
171
|
+
if (ret == -1 && errno == EINTR) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (ret == -1 && errno == ECHILD) {
|
|
175
|
+
// XXX node v0.8.x seems to have this problem.
|
|
176
|
+
// waitpid is already handled elsewhere.
|
|
177
|
+
;
|
|
178
|
+
} else {
|
|
179
|
+
assert(false);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
#endif
|
|
185
|
+
ExitEvent *exit_event = new ExitEvent;
|
|
186
|
+
if (WIFEXITED(stat_loc)) {
|
|
187
|
+
exit_event->exit_code = WEXITSTATUS(stat_loc); // errno?
|
|
188
|
+
}
|
|
189
|
+
if (WIFSIGNALED(stat_loc)) {
|
|
190
|
+
exit_event->signal_code = WTERMSIG(stat_loc);
|
|
191
|
+
}
|
|
192
|
+
auto status = tsfn.BlockingCall(exit_event, callback); // In main thread
|
|
193
|
+
switch (status) {
|
|
194
|
+
case napi_closing:
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case napi_queue_full:
|
|
198
|
+
Napi::Error::Fatal("SetupExitCallback", "Queue was full");
|
|
199
|
+
|
|
200
|
+
case napi_ok:
|
|
201
|
+
if (tsfn.Release() != napi_ok) {
|
|
202
|
+
Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.Release() failed");
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
default:
|
|
207
|
+
Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.BlockingCall() failed");
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
115
212
|
/**
|
|
116
213
|
* Methods
|
|
117
214
|
*/
|
|
118
215
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
216
|
+
Napi::Value PtyFork(const Napi::CallbackInfo& info);
|
|
217
|
+
Napi::Value PtyOpen(const Napi::CallbackInfo& info);
|
|
218
|
+
Napi::Value PtyResize(const Napi::CallbackInfo& info);
|
|
219
|
+
Napi::Value PtyGetProc(const Napi::CallbackInfo& info);
|
|
123
220
|
|
|
124
221
|
/**
|
|
125
222
|
* Functions
|
|
@@ -136,15 +233,6 @@ static char *
|
|
|
136
233
|
pty_getproc(int, char *);
|
|
137
234
|
#endif
|
|
138
235
|
|
|
139
|
-
static void
|
|
140
|
-
pty_waitpid(void *);
|
|
141
|
-
|
|
142
|
-
static void
|
|
143
|
-
pty_after_waitpid(uv_async_t *);
|
|
144
|
-
|
|
145
|
-
static void
|
|
146
|
-
pty_after_close(uv_handle_t *);
|
|
147
|
-
|
|
148
236
|
#if defined(__APPLE__) || defined(__OpenBSD__)
|
|
149
237
|
static void
|
|
150
238
|
pty_posix_spawn(char** argv, char** env,
|
|
@@ -155,62 +243,75 @@ pty_posix_spawn(char** argv, char** env,
|
|
|
155
243
|
int* err);
|
|
156
244
|
#endif
|
|
157
245
|
|
|
158
|
-
|
|
159
|
-
|
|
246
|
+
struct DelBuf {
|
|
247
|
+
int len;
|
|
248
|
+
DelBuf(int len) : len(len) {}
|
|
249
|
+
void operator()(char **p) {
|
|
250
|
+
if (p == nullptr)
|
|
251
|
+
return;
|
|
252
|
+
for (int i = 0; i < len; i++)
|
|
253
|
+
free(p[i]);
|
|
254
|
+
delete[] p;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
Napi::Value PtyFork(const Napi::CallbackInfo& info) {
|
|
259
|
+
Napi::Env napiEnv(info.Env());
|
|
260
|
+
Napi::HandleScope scope(napiEnv);
|
|
160
261
|
|
|
161
262
|
if (info.Length() != 11 ||
|
|
162
|
-
!info[0]
|
|
163
|
-
!info[1]
|
|
164
|
-
!info[2]
|
|
165
|
-
!info[3]
|
|
166
|
-
!info[4]
|
|
167
|
-
!info[5]
|
|
168
|
-
!info[6]
|
|
169
|
-
!info[7]
|
|
170
|
-
!info[8]
|
|
171
|
-
!info[9]
|
|
172
|
-
!info[10]
|
|
173
|
-
|
|
174
|
-
"Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
|
|
263
|
+
!info[0].IsString() ||
|
|
264
|
+
!info[1].IsArray() ||
|
|
265
|
+
!info[2].IsArray() ||
|
|
266
|
+
!info[3].IsString() ||
|
|
267
|
+
!info[4].IsNumber() ||
|
|
268
|
+
!info[5].IsNumber() ||
|
|
269
|
+
!info[6].IsNumber() ||
|
|
270
|
+
!info[7].IsNumber() ||
|
|
271
|
+
!info[8].IsBoolean() ||
|
|
272
|
+
!info[9].IsString() ||
|
|
273
|
+
!info[10].IsFunction()) {
|
|
274
|
+
throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
|
|
175
275
|
}
|
|
176
276
|
|
|
177
277
|
// file
|
|
178
|
-
|
|
278
|
+
std::string file = info[0].As<Napi::String>();
|
|
179
279
|
|
|
180
280
|
// args
|
|
181
|
-
|
|
281
|
+
Napi::Array argv_ = info[1].As<Napi::Array>();
|
|
182
282
|
|
|
183
283
|
// env
|
|
184
|
-
|
|
185
|
-
int envc = env_
|
|
186
|
-
char
|
|
284
|
+
Napi::Array env_ = info[2].As<Napi::Array>();
|
|
285
|
+
int envc = env_.Length();
|
|
286
|
+
std::unique_ptr<char *, DelBuf> env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1));
|
|
287
|
+
char **env = env_unique_ptr.get();
|
|
187
288
|
env[envc] = NULL;
|
|
188
289
|
for (int i = 0; i < envc; i++) {
|
|
189
|
-
|
|
190
|
-
env[i] = strdup(
|
|
290
|
+
std::string pair = env_.Get(i).As<Napi::String>();
|
|
291
|
+
env[i] = strdup(pair.c_str());
|
|
191
292
|
}
|
|
192
293
|
|
|
193
294
|
// cwd
|
|
194
|
-
|
|
295
|
+
std::string cwd_ = info[3].As<Napi::String>();
|
|
195
296
|
|
|
196
297
|
// size
|
|
197
298
|
struct winsize winp;
|
|
198
|
-
winp.ws_col = info[4]
|
|
199
|
-
winp.ws_row = info[5]
|
|
299
|
+
winp.ws_col = info[4].As<Napi::Number>().Int32Value();
|
|
300
|
+
winp.ws_row = info[5].As<Napi::Number>().Int32Value();
|
|
200
301
|
winp.ws_xpixel = 0;
|
|
201
302
|
winp.ws_ypixel = 0;
|
|
202
303
|
|
|
203
304
|
#if !defined(__APPLE__)
|
|
204
305
|
// uid / gid
|
|
205
|
-
int uid = info[6]
|
|
206
|
-
int gid = info[7]
|
|
306
|
+
int uid = info[6].As<Napi::Number>().Int32Value();
|
|
307
|
+
int gid = info[7].As<Napi::Number>().Int32Value();
|
|
207
308
|
#endif
|
|
208
309
|
|
|
209
310
|
// termios
|
|
210
311
|
struct termios t = termios();
|
|
211
312
|
struct termios *term = &t;
|
|
212
313
|
term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
|
|
213
|
-
if (
|
|
314
|
+
if (info[8].As<Napi::Boolean>().Value()) {
|
|
214
315
|
#if defined(IUTF8)
|
|
215
316
|
term->c_iflag |= IUTF8;
|
|
216
317
|
#endif
|
|
@@ -245,45 +346,44 @@ NAN_METHOD(PtyFork) {
|
|
|
245
346
|
cfsetospeed(term, B38400);
|
|
246
347
|
|
|
247
348
|
// helperPath
|
|
248
|
-
|
|
349
|
+
std::string helper_path = info[9].As<Napi::String>();
|
|
249
350
|
|
|
250
351
|
pid_t pid;
|
|
251
352
|
int master;
|
|
252
353
|
#if defined(__APPLE__)
|
|
253
|
-
int argc = argv_
|
|
354
|
+
int argc = argv_.Length();
|
|
254
355
|
int argl = argc + 4;
|
|
255
|
-
char
|
|
256
|
-
argv
|
|
257
|
-
argv[
|
|
258
|
-
argv[
|
|
356
|
+
std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
|
|
357
|
+
char **argv = argv_unique_ptr.get();
|
|
358
|
+
argv[0] = strdup(helper_path.c_str());
|
|
359
|
+
argv[1] = strdup(cwd_.c_str());
|
|
360
|
+
argv[2] = strdup(file.c_str());
|
|
259
361
|
argv[argl - 1] = NULL;
|
|
260
362
|
for (int i = 0; i < argc; i++) {
|
|
261
|
-
|
|
262
|
-
argv[i + 3] = strdup(
|
|
363
|
+
std::string arg = argv_.Get(i).As<Napi::String>();
|
|
364
|
+
argv[i + 3] = strdup(arg.c_str());
|
|
263
365
|
}
|
|
264
366
|
|
|
265
367
|
int err = -1;
|
|
266
368
|
pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
|
|
267
369
|
if (err != 0) {
|
|
268
|
-
|
|
269
|
-
goto done;
|
|
370
|
+
throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
|
|
270
371
|
}
|
|
271
372
|
if (pty_nonblock(master) == -1) {
|
|
272
|
-
|
|
273
|
-
goto done;
|
|
373
|
+
throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
|
|
274
374
|
}
|
|
275
375
|
#else
|
|
276
|
-
int argc = argv_
|
|
376
|
+
int argc = argv_.Length();
|
|
277
377
|
int argl = argc + 2;
|
|
278
|
-
char
|
|
279
|
-
argv
|
|
378
|
+
std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
|
|
379
|
+
char** argv = argv_unique_ptr.get();
|
|
380
|
+
argv[0] = strdup(file.c_str());
|
|
280
381
|
argv[argl - 1] = NULL;
|
|
281
382
|
for (int i = 0; i < argc; i++) {
|
|
282
|
-
|
|
283
|
-
argv[i + 1] = strdup(
|
|
383
|
+
std::string arg = argv_.Get(i).As<Napi::String>();
|
|
384
|
+
argv[i + 1] = strdup(arg.c_str());
|
|
284
385
|
}
|
|
285
386
|
|
|
286
|
-
char* cwd = strdup(*cwd_);
|
|
287
387
|
sigset_t newmask, oldmask;
|
|
288
388
|
struct sigaction sig_action;
|
|
289
389
|
// temporarily block all signals
|
|
@@ -303,14 +403,6 @@ NAN_METHOD(PtyFork) {
|
|
|
303
403
|
for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
|
|
304
404
|
sigaction(i, &sig_action, NULL);
|
|
305
405
|
}
|
|
306
|
-
} else {
|
|
307
|
-
for (int i = 0; i < argl; i++) free(argv[i]);
|
|
308
|
-
delete[] argv;
|
|
309
|
-
|
|
310
|
-
for (int i = 0; i < envc; i++) free(env[i]);
|
|
311
|
-
delete[] env;
|
|
312
|
-
|
|
313
|
-
free(cwd);
|
|
314
406
|
}
|
|
315
407
|
|
|
316
408
|
// reenable signals
|
|
@@ -318,11 +410,10 @@ NAN_METHOD(PtyFork) {
|
|
|
318
410
|
|
|
319
411
|
switch (pid) {
|
|
320
412
|
case -1:
|
|
321
|
-
|
|
322
|
-
goto done;
|
|
413
|
+
throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
|
|
323
414
|
case 0:
|
|
324
|
-
if (strlen(
|
|
325
|
-
if (chdir(
|
|
415
|
+
if (strlen(cwd_.c_str())) {
|
|
416
|
+
if (chdir(cwd_.c_str()) == -1) {
|
|
326
417
|
perror("chdir(2) failed.");
|
|
327
418
|
_exit(1);
|
|
328
419
|
}
|
|
@@ -349,62 +440,36 @@ NAN_METHOD(PtyFork) {
|
|
|
349
440
|
}
|
|
350
441
|
default:
|
|
351
442
|
if (pty_nonblock(master) == -1) {
|
|
352
|
-
|
|
353
|
-
goto done;
|
|
443
|
+
throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
|
|
354
444
|
}
|
|
355
445
|
}
|
|
356
446
|
#endif
|
|
357
447
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
Nan::New<v8::Number>(master));
|
|
363
|
-
Nan::Set(obj,
|
|
364
|
-
Nan::New<v8::String>("pid").ToLocalChecked(),
|
|
365
|
-
Nan::New<v8::Number>(pid));
|
|
366
|
-
Nan::Set(obj,
|
|
367
|
-
Nan::New<v8::String>("pty").ToLocalChecked(),
|
|
368
|
-
Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
|
|
369
|
-
|
|
370
|
-
pty_baton *baton = new pty_baton();
|
|
371
|
-
baton->exit_code = 0;
|
|
372
|
-
baton->signal_code = 0;
|
|
373
|
-
baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
|
|
374
|
-
baton->pid = pid;
|
|
375
|
-
baton->async.data = baton;
|
|
448
|
+
Napi::Object obj = Napi::Object::New(napiEnv);
|
|
449
|
+
obj.Set("fd", Napi::Number::New(napiEnv, master));
|
|
450
|
+
obj.Set("pid", Napi::Number::New(napiEnv, pid));
|
|
451
|
+
obj.Set("pty", Napi::String::New(napiEnv, ptsname(master)));
|
|
376
452
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return info.GetReturnValue().Set(obj);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
done:
|
|
385
|
-
#if defined(__APPLE__)
|
|
386
|
-
for (int i = 0; i < argl; i++) free(argv[i]);
|
|
387
|
-
delete[] argv;
|
|
388
|
-
|
|
389
|
-
for (int i = 0; i < envc; i++) free(env[i]);
|
|
390
|
-
delete[] env;
|
|
391
|
-
#endif
|
|
392
|
-
return info.GetReturnValue().SetUndefined();
|
|
453
|
+
// Set up process exit callback.
|
|
454
|
+
Napi::Function cb = info[10].As<Napi::Function>();
|
|
455
|
+
SetupExitCallback(napiEnv, cb, pid);
|
|
456
|
+
return obj;
|
|
393
457
|
}
|
|
394
458
|
|
|
395
|
-
|
|
396
|
-
|
|
459
|
+
Napi::Value PtyOpen(const Napi::CallbackInfo& info) {
|
|
460
|
+
Napi::Env env(info.Env());
|
|
461
|
+
Napi::HandleScope scope(env);
|
|
397
462
|
|
|
398
463
|
if (info.Length() != 2 ||
|
|
399
|
-
!info[0]
|
|
400
|
-
!info[1]
|
|
401
|
-
|
|
464
|
+
!info[0].IsNumber() ||
|
|
465
|
+
!info[1].IsNumber()) {
|
|
466
|
+
throw Napi::Error::New(env, "Usage: pty.open(cols, rows)");
|
|
402
467
|
}
|
|
403
468
|
|
|
404
469
|
// size
|
|
405
470
|
struct winsize winp;
|
|
406
|
-
winp.ws_col = info[0]
|
|
407
|
-
winp.ws_row = info[1]
|
|
471
|
+
winp.ws_col = info[0].As<Napi::Number>().Int32Value();
|
|
472
|
+
winp.ws_row = info[1].As<Napi::Number>().Int32Value();
|
|
408
473
|
winp.ws_xpixel = 0;
|
|
409
474
|
winp.ws_ypixel = 0;
|
|
410
475
|
|
|
@@ -413,98 +478,98 @@ NAN_METHOD(PtyOpen) {
|
|
|
413
478
|
int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
|
|
414
479
|
|
|
415
480
|
if (ret == -1) {
|
|
416
|
-
|
|
481
|
+
throw Napi::Error::New(env, "openpty(3) failed.");
|
|
417
482
|
}
|
|
418
483
|
|
|
419
484
|
if (pty_nonblock(master) == -1) {
|
|
420
|
-
|
|
485
|
+
throw Napi::Error::New(env, "Could not set master fd to nonblocking.");
|
|
421
486
|
}
|
|
422
487
|
|
|
423
488
|
if (pty_nonblock(slave) == -1) {
|
|
424
|
-
|
|
489
|
+
throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
|
|
425
490
|
}
|
|
426
491
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
Nan::Set(obj,
|
|
432
|
-
Nan::New<v8::String>("slave").ToLocalChecked(),
|
|
433
|
-
Nan::New<v8::Number>(slave));
|
|
434
|
-
Nan::Set(obj,
|
|
435
|
-
Nan::New<v8::String>("pty").ToLocalChecked(),
|
|
436
|
-
Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
|
|
492
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
493
|
+
obj.Set("master", Napi::Number::New(env, master));
|
|
494
|
+
obj.Set("slave", Napi::Number::New(env, slave));
|
|
495
|
+
obj.Set("pty", Napi::String::New(env, ptsname(master)));
|
|
437
496
|
|
|
438
|
-
return
|
|
497
|
+
return obj;
|
|
439
498
|
}
|
|
440
499
|
|
|
441
|
-
|
|
442
|
-
|
|
500
|
+
Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
501
|
+
Napi::Env env(info.Env());
|
|
502
|
+
Napi::HandleScope scope(env);
|
|
443
503
|
|
|
444
504
|
if (info.Length() != 3 ||
|
|
445
|
-
!info[0]
|
|
446
|
-
!info[1]
|
|
447
|
-
!info[2]
|
|
448
|
-
|
|
505
|
+
!info[0].IsNumber() ||
|
|
506
|
+
!info[1].IsNumber() ||
|
|
507
|
+
!info[2].IsNumber()) {
|
|
508
|
+
throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)");
|
|
449
509
|
}
|
|
450
510
|
|
|
451
|
-
int fd = info[0]
|
|
511
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
452
512
|
|
|
453
513
|
struct winsize winp;
|
|
454
|
-
winp.ws_col = info[1]
|
|
455
|
-
winp.ws_row = info[2]
|
|
514
|
+
winp.ws_col = info[1].As<Napi::Number>().Int32Value();
|
|
515
|
+
winp.ws_row = info[2].As<Napi::Number>().Int32Value();
|
|
456
516
|
winp.ws_xpixel = 0;
|
|
457
517
|
winp.ws_ypixel = 0;
|
|
458
518
|
|
|
459
519
|
if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
|
|
460
520
|
switch (errno) {
|
|
461
|
-
case EBADF:
|
|
462
|
-
|
|
463
|
-
case
|
|
464
|
-
|
|
521
|
+
case EBADF:
|
|
522
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EBADF");
|
|
523
|
+
case EFAULT:
|
|
524
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EFAULT");
|
|
525
|
+
case EINVAL:
|
|
526
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EINVAL");
|
|
527
|
+
case ENOTTY:
|
|
528
|
+
throw Napi::Error::New(env, "ioctl(2) failed, ENOTTY");
|
|
465
529
|
}
|
|
466
|
-
|
|
530
|
+
throw Napi::Error::New(env, "ioctl(2) failed");
|
|
467
531
|
}
|
|
468
532
|
|
|
469
|
-
return
|
|
533
|
+
return env.Undefined();
|
|
470
534
|
}
|
|
471
535
|
|
|
472
536
|
/**
|
|
473
537
|
* Foreground Process Name
|
|
474
538
|
*/
|
|
475
|
-
|
|
476
|
-
|
|
539
|
+
Napi::Value PtyGetProc(const Napi::CallbackInfo& info) {
|
|
540
|
+
Napi::Env env(info.Env());
|
|
541
|
+
Napi::HandleScope scope(env);
|
|
477
542
|
|
|
478
543
|
#if defined(__APPLE__)
|
|
479
544
|
if (info.Length() != 1 ||
|
|
480
|
-
!info[0]
|
|
481
|
-
|
|
545
|
+
!info[0].IsNumber()) {
|
|
546
|
+
throw Napi::Error::New(env, "Usage: pty.process(pid)");
|
|
482
547
|
}
|
|
483
548
|
|
|
484
|
-
int fd = info[0]
|
|
549
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
485
550
|
char *name = pty_getproc(fd);
|
|
486
551
|
#else
|
|
487
552
|
if (info.Length() != 2 ||
|
|
488
|
-
!info[0]
|
|
489
|
-
!info[1]
|
|
490
|
-
|
|
553
|
+
!info[0].IsNumber() ||
|
|
554
|
+
!info[1].IsString()) {
|
|
555
|
+
throw Napi::Error::New(env, "Usage: pty.process(fd, tty)");
|
|
491
556
|
}
|
|
492
557
|
|
|
493
|
-
int fd = info[0]
|
|
558
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
494
559
|
|
|
495
|
-
|
|
496
|
-
char *tty = strdup(
|
|
560
|
+
std::string tty_ = info[1].As<Napi::String>();
|
|
561
|
+
char *tty = strdup(tty_.c_str());
|
|
497
562
|
char *name = pty_getproc(fd, tty);
|
|
498
563
|
free(tty);
|
|
499
564
|
#endif
|
|
500
565
|
|
|
501
566
|
if (name == NULL) {
|
|
502
|
-
return
|
|
567
|
+
return env.Undefined();
|
|
503
568
|
}
|
|
504
569
|
|
|
505
|
-
|
|
570
|
+
Napi::String name_ = Napi::String::New(env, name);
|
|
506
571
|
free(name);
|
|
507
|
-
return
|
|
572
|
+
return name_;
|
|
508
573
|
}
|
|
509
574
|
|
|
510
575
|
/**
|
|
@@ -518,114 +583,6 @@ pty_nonblock(int fd) {
|
|
|
518
583
|
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
519
584
|
}
|
|
520
585
|
|
|
521
|
-
/**
|
|
522
|
-
* pty_waitpid
|
|
523
|
-
* Wait for SIGCHLD to read exit status.
|
|
524
|
-
*/
|
|
525
|
-
|
|
526
|
-
static void
|
|
527
|
-
pty_waitpid(void *data) {
|
|
528
|
-
int ret;
|
|
529
|
-
int stat_loc;
|
|
530
|
-
pty_baton *baton = static_cast<pty_baton*>(data);
|
|
531
|
-
errno = 0;
|
|
532
|
-
#if defined(__APPLE__)
|
|
533
|
-
// Based on
|
|
534
|
-
// https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
|
|
535
|
-
int kq = HANDLE_EINTR(kqueue());
|
|
536
|
-
struct kevent change = {0};
|
|
537
|
-
EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
|
|
538
|
-
ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
|
|
539
|
-
if (ret == -1) {
|
|
540
|
-
if (errno == ESRCH) {
|
|
541
|
-
// At this point, one of the following has occurred:
|
|
542
|
-
// 1. The process has died but has not yet been reaped.
|
|
543
|
-
// 2. The process has died and has already been reaped.
|
|
544
|
-
// 3. The process is in the process of dying. It's no longer
|
|
545
|
-
// kqueueable, but it may not be waitable yet either. Mark calls
|
|
546
|
-
// this case the "zombie death race".
|
|
547
|
-
ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG));
|
|
548
|
-
if (ret == 0) {
|
|
549
|
-
ret = kill(baton->pid, SIGKILL);
|
|
550
|
-
if (ret != -1) {
|
|
551
|
-
HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
struct kevent event = {0};
|
|
557
|
-
ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
|
|
558
|
-
if (ret == 1) {
|
|
559
|
-
if ((event.fflags & NOTE_EXIT) &&
|
|
560
|
-
(event.ident == static_cast<uintptr_t>(baton->pid))) {
|
|
561
|
-
// The process is dead or dying. This won't block for long, if at
|
|
562
|
-
// all.
|
|
563
|
-
HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
#else
|
|
568
|
-
if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
|
|
569
|
-
if (ret == -1 && errno == EINTR) {
|
|
570
|
-
return pty_waitpid(baton);
|
|
571
|
-
}
|
|
572
|
-
if (ret == -1 && errno == ECHILD) {
|
|
573
|
-
// XXX node v0.8.x seems to have this problem.
|
|
574
|
-
// waitpid is already handled elsewhere.
|
|
575
|
-
;
|
|
576
|
-
} else {
|
|
577
|
-
assert(false);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
#endif
|
|
581
|
-
|
|
582
|
-
if (WIFEXITED(stat_loc)) {
|
|
583
|
-
baton->exit_code = WEXITSTATUS(stat_loc); // errno?
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (WIFSIGNALED(stat_loc)) {
|
|
587
|
-
baton->signal_code = WTERMSIG(stat_loc);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
uv_async_send(&baton->async);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* pty_after_waitpid
|
|
595
|
-
* Callback after exit status has been read.
|
|
596
|
-
*/
|
|
597
|
-
|
|
598
|
-
static void
|
|
599
|
-
pty_after_waitpid(uv_async_t *async) {
|
|
600
|
-
Nan::HandleScope scope;
|
|
601
|
-
pty_baton *baton = static_cast<pty_baton*>(async->data);
|
|
602
|
-
|
|
603
|
-
v8::Local<v8::Value> argv[] = {
|
|
604
|
-
Nan::New<v8::Integer>(baton->exit_code),
|
|
605
|
-
Nan::New<v8::Integer>(baton->signal_code),
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
v8::Local<v8::Function> cb = Nan::New<v8::Function>(baton->cb);
|
|
609
|
-
baton->cb.Reset();
|
|
610
|
-
memset(&baton->cb, -1, sizeof(baton->cb));
|
|
611
|
-
Nan::AsyncResource resource("pty_after_waitpid");
|
|
612
|
-
resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv);
|
|
613
|
-
|
|
614
|
-
uv_close((uv_handle_t *)async, pty_after_close);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* pty_after_close
|
|
619
|
-
* uv_close() callback - free handle data
|
|
620
|
-
*/
|
|
621
|
-
|
|
622
|
-
static void
|
|
623
|
-
pty_after_close(uv_handle_t *handle) {
|
|
624
|
-
uv_async_t *async = (uv_async_t *)handle;
|
|
625
|
-
pty_baton *baton = static_cast<pty_baton*>(async->data);
|
|
626
|
-
delete baton;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
586
|
/**
|
|
630
587
|
* pty_getproc
|
|
631
588
|
* Taken from tmux.
|
|
@@ -828,12 +785,12 @@ done:
|
|
|
828
785
|
* Init
|
|
829
786
|
*/
|
|
830
787
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
788
|
+
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
789
|
+
exports.Set("fork", Napi::Function::New(env, PtyFork));
|
|
790
|
+
exports.Set("open", Napi::Function::New(env, PtyOpen));
|
|
791
|
+
exports.Set("resize", Napi::Function::New(env, PtyResize));
|
|
792
|
+
exports.Set("process", Napi::Function::New(env, PtyGetProc));
|
|
793
|
+
return exports;
|
|
837
794
|
}
|
|
838
795
|
|
|
839
|
-
|
|
796
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)
|