node-pty 1.1.0-beta5 → 1.1.0-beta7
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/binding.gyp +4 -15
- package/package.json +2 -2
- package/src/unix/pty.cc +227 -284
- package/src/win/conpty.cc +156 -161
- package/src/win/conpty_console_list.cc +16 -15
- package/src/win/path_util.cc +16 -12
- package/src/win/path_util.h +5 -2
- package/src/win/winpty.cc +108 -151
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>
|
|
@@ -99,27 +102,107 @@ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
|
|
|
99
102
|
})
|
|
100
103
|
#endif
|
|
101
104
|
|
|
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;
|
|
105
|
+
struct ExitEvent {
|
|
106
|
+
int exit_code = 0, signal_code = 0;
|
|
113
107
|
};
|
|
114
108
|
|
|
109
|
+
void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) {
|
|
110
|
+
std::thread *th = new std::thread;
|
|
111
|
+
// Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE.
|
|
112
|
+
auto tsfn = Napi::ThreadSafeFunction::New(
|
|
113
|
+
env,
|
|
114
|
+
cb, // JavaScript function called asynchronously
|
|
115
|
+
"SetupExitCallback_resource", // Name
|
|
116
|
+
0, // Unlimited queue
|
|
117
|
+
1, // Only one thread will use this initially
|
|
118
|
+
[th](Napi::Env) { // Finalizer used to clean threads up
|
|
119
|
+
th->join();
|
|
120
|
+
delete th;
|
|
121
|
+
});
|
|
122
|
+
*th = std::thread([tsfn = std::move(tsfn), pid] {
|
|
123
|
+
auto callback = [](Napi::Env env, Napi::Function cb, ExitEvent *exit_event) {
|
|
124
|
+
cb.Call({Napi::Number::New(env, exit_event->exit_code),
|
|
125
|
+
Napi::Number::New(env, exit_event->signal_code)});
|
|
126
|
+
delete exit_event;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
int ret;
|
|
130
|
+
int stat_loc;
|
|
131
|
+
#if defined(__APPLE__)
|
|
132
|
+
// Based on
|
|
133
|
+
// https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
|
|
134
|
+
int kq = HANDLE_EINTR(kqueue());
|
|
135
|
+
struct kevent change = {0};
|
|
136
|
+
EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
|
|
137
|
+
ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
|
|
138
|
+
if (ret == -1) {
|
|
139
|
+
if (errno == ESRCH) {
|
|
140
|
+
// At this point, one of the following has occurred:
|
|
141
|
+
// 1. The process has died but has not yet been reaped.
|
|
142
|
+
// 2. The process has died and has already been reaped.
|
|
143
|
+
// 3. The process is in the process of dying. It's no longer
|
|
144
|
+
// kqueueable, but it may not be waitable yet either. Mark calls
|
|
145
|
+
// this case the "zombie death race".
|
|
146
|
+
ret = HANDLE_EINTR(waitpid(pid, &stat_loc, WNOHANG));
|
|
147
|
+
if (ret == 0) {
|
|
148
|
+
ret = kill(pid, SIGKILL);
|
|
149
|
+
if (ret != -1) {
|
|
150
|
+
HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
struct kevent event = {0};
|
|
156
|
+
ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
|
|
157
|
+
if (ret == 1) {
|
|
158
|
+
if ((event.fflags & NOTE_EXIT) &&
|
|
159
|
+
(event.ident == static_cast<uintptr_t>(pid))) {
|
|
160
|
+
// The process is dead or dying. This won't block for long, if at
|
|
161
|
+
// all.
|
|
162
|
+
HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
#else
|
|
167
|
+
while (true) {
|
|
168
|
+
errno = 0;
|
|
169
|
+
if ((ret = waitpid(pid, &stat_loc, 0)) != pid) {
|
|
170
|
+
if (ret == -1 && errno == EINTR) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ret == -1 && errno == ECHILD) {
|
|
174
|
+
// XXX node v0.8.x seems to have this problem.
|
|
175
|
+
// waitpid is already handled elsewhere.
|
|
176
|
+
;
|
|
177
|
+
} else {
|
|
178
|
+
assert(false);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
#endif
|
|
184
|
+
ExitEvent *exit_event = new ExitEvent;
|
|
185
|
+
if (WIFEXITED(stat_loc)) {
|
|
186
|
+
exit_event->exit_code = WEXITSTATUS(stat_loc); // errno?
|
|
187
|
+
}
|
|
188
|
+
if (WIFSIGNALED(stat_loc)) {
|
|
189
|
+
exit_event->signal_code = WTERMSIG(stat_loc);
|
|
190
|
+
}
|
|
191
|
+
auto status = tsfn.BlockingCall(exit_event, callback); // In main thread
|
|
192
|
+
assert(status == napi_ok);
|
|
193
|
+
|
|
194
|
+
tsfn.Release();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
115
198
|
/**
|
|
116
199
|
* Methods
|
|
117
200
|
*/
|
|
118
201
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
202
|
+
Napi::Value PtyFork(const Napi::CallbackInfo& info);
|
|
203
|
+
Napi::Value PtyOpen(const Napi::CallbackInfo& info);
|
|
204
|
+
Napi::Value PtyResize(const Napi::CallbackInfo& info);
|
|
205
|
+
Napi::Value PtyGetProc(const Napi::CallbackInfo& info);
|
|
123
206
|
|
|
124
207
|
/**
|
|
125
208
|
* Functions
|
|
@@ -136,15 +219,6 @@ static char *
|
|
|
136
219
|
pty_getproc(int, char *);
|
|
137
220
|
#endif
|
|
138
221
|
|
|
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
222
|
#if defined(__APPLE__) || defined(__OpenBSD__)
|
|
149
223
|
static void
|
|
150
224
|
pty_posix_spawn(char** argv, char** env,
|
|
@@ -155,62 +229,75 @@ pty_posix_spawn(char** argv, char** env,
|
|
|
155
229
|
int* err);
|
|
156
230
|
#endif
|
|
157
231
|
|
|
158
|
-
|
|
159
|
-
|
|
232
|
+
struct DelBuf {
|
|
233
|
+
int len;
|
|
234
|
+
DelBuf(int len) : len(len) {}
|
|
235
|
+
void operator()(char **p) {
|
|
236
|
+
if (p == nullptr)
|
|
237
|
+
return;
|
|
238
|
+
for (int i = 0; i < len; i++)
|
|
239
|
+
free(p[i]);
|
|
240
|
+
delete[] p;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
Napi::Value PtyFork(const Napi::CallbackInfo& info) {
|
|
245
|
+
Napi::Env napiEnv(info.Env());
|
|
246
|
+
Napi::HandleScope scope(napiEnv);
|
|
160
247
|
|
|
161
248
|
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)");
|
|
249
|
+
!info[0].IsString() ||
|
|
250
|
+
!info[1].IsArray() ||
|
|
251
|
+
!info[2].IsArray() ||
|
|
252
|
+
!info[3].IsString() ||
|
|
253
|
+
!info[4].IsNumber() ||
|
|
254
|
+
!info[5].IsNumber() ||
|
|
255
|
+
!info[6].IsNumber() ||
|
|
256
|
+
!info[7].IsNumber() ||
|
|
257
|
+
!info[8].IsBoolean() ||
|
|
258
|
+
!info[9].IsString() ||
|
|
259
|
+
!info[10].IsFunction()) {
|
|
260
|
+
throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
|
|
175
261
|
}
|
|
176
262
|
|
|
177
263
|
// file
|
|
178
|
-
|
|
264
|
+
std::string file = info[0].As<Napi::String>();
|
|
179
265
|
|
|
180
266
|
// args
|
|
181
|
-
|
|
267
|
+
Napi::Array argv_ = info[1].As<Napi::Array>();
|
|
182
268
|
|
|
183
269
|
// env
|
|
184
|
-
|
|
185
|
-
int envc = env_
|
|
186
|
-
char
|
|
270
|
+
Napi::Array env_ = info[2].As<Napi::Array>();
|
|
271
|
+
int envc = env_.Length();
|
|
272
|
+
std::unique_ptr<char *, DelBuf> env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1));
|
|
273
|
+
char **env = env_unique_ptr.get();
|
|
187
274
|
env[envc] = NULL;
|
|
188
275
|
for (int i = 0; i < envc; i++) {
|
|
189
|
-
|
|
190
|
-
env[i] = strdup(
|
|
276
|
+
std::string pair = env_.Get(i).As<Napi::String>();
|
|
277
|
+
env[i] = strdup(pair.c_str());
|
|
191
278
|
}
|
|
192
279
|
|
|
193
280
|
// cwd
|
|
194
|
-
|
|
281
|
+
std::string cwd_ = info[3].As<Napi::String>();
|
|
195
282
|
|
|
196
283
|
// size
|
|
197
284
|
struct winsize winp;
|
|
198
|
-
winp.ws_col = info[4]
|
|
199
|
-
winp.ws_row = info[5]
|
|
285
|
+
winp.ws_col = info[4].As<Napi::Number>().Int32Value();
|
|
286
|
+
winp.ws_row = info[5].As<Napi::Number>().Int32Value();
|
|
200
287
|
winp.ws_xpixel = 0;
|
|
201
288
|
winp.ws_ypixel = 0;
|
|
202
289
|
|
|
203
290
|
#if !defined(__APPLE__)
|
|
204
291
|
// uid / gid
|
|
205
|
-
int uid = info[6]
|
|
206
|
-
int gid = info[7]
|
|
292
|
+
int uid = info[6].As<Napi::Number>().Int32Value();
|
|
293
|
+
int gid = info[7].As<Napi::Number>().Int32Value();
|
|
207
294
|
#endif
|
|
208
295
|
|
|
209
296
|
// termios
|
|
210
297
|
struct termios t = termios();
|
|
211
298
|
struct termios *term = &t;
|
|
212
299
|
term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
|
|
213
|
-
if (
|
|
300
|
+
if (info[8].As<Napi::Boolean>().Value()) {
|
|
214
301
|
#if defined(IUTF8)
|
|
215
302
|
term->c_iflag |= IUTF8;
|
|
216
303
|
#endif
|
|
@@ -245,45 +332,44 @@ NAN_METHOD(PtyFork) {
|
|
|
245
332
|
cfsetospeed(term, B38400);
|
|
246
333
|
|
|
247
334
|
// helperPath
|
|
248
|
-
|
|
335
|
+
std::string helper_path = info[9].As<Napi::String>();
|
|
249
336
|
|
|
250
337
|
pid_t pid;
|
|
251
338
|
int master;
|
|
252
339
|
#if defined(__APPLE__)
|
|
253
|
-
int argc = argv_
|
|
340
|
+
int argc = argv_.Length();
|
|
254
341
|
int argl = argc + 4;
|
|
255
|
-
char
|
|
256
|
-
argv
|
|
257
|
-
argv[
|
|
258
|
-
argv[
|
|
342
|
+
std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
|
|
343
|
+
char **argv = argv_unique_ptr.get();
|
|
344
|
+
argv[0] = strdup(helper_path.c_str());
|
|
345
|
+
argv[1] = strdup(cwd_.c_str());
|
|
346
|
+
argv[2] = strdup(file.c_str());
|
|
259
347
|
argv[argl - 1] = NULL;
|
|
260
348
|
for (int i = 0; i < argc; i++) {
|
|
261
|
-
|
|
262
|
-
argv[i + 3] = strdup(
|
|
349
|
+
std::string arg = argv_.Get(i).As<Napi::String>();
|
|
350
|
+
argv[i + 3] = strdup(arg.c_str());
|
|
263
351
|
}
|
|
264
352
|
|
|
265
353
|
int err = -1;
|
|
266
354
|
pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
|
|
267
355
|
if (err != 0) {
|
|
268
|
-
|
|
269
|
-
goto done;
|
|
356
|
+
throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
|
|
270
357
|
}
|
|
271
358
|
if (pty_nonblock(master) == -1) {
|
|
272
|
-
|
|
273
|
-
goto done;
|
|
359
|
+
throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
|
|
274
360
|
}
|
|
275
361
|
#else
|
|
276
|
-
int argc = argv_
|
|
362
|
+
int argc = argv_.Length();
|
|
277
363
|
int argl = argc + 2;
|
|
278
|
-
char
|
|
279
|
-
argv
|
|
364
|
+
std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
|
|
365
|
+
char** argv = argv_unique_ptr.get();
|
|
366
|
+
argv[0] = strdup(file.c_str());
|
|
280
367
|
argv[argl - 1] = NULL;
|
|
281
368
|
for (int i = 0; i < argc; i++) {
|
|
282
|
-
|
|
283
|
-
argv[i + 1] = strdup(
|
|
369
|
+
std::string arg = argv_.Get(i).As<Napi::String>();
|
|
370
|
+
argv[i + 1] = strdup(arg.c_str());
|
|
284
371
|
}
|
|
285
372
|
|
|
286
|
-
char* cwd = strdup(*cwd_);
|
|
287
373
|
sigset_t newmask, oldmask;
|
|
288
374
|
struct sigaction sig_action;
|
|
289
375
|
// temporarily block all signals
|
|
@@ -303,14 +389,6 @@ NAN_METHOD(PtyFork) {
|
|
|
303
389
|
for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
|
|
304
390
|
sigaction(i, &sig_action, NULL);
|
|
305
391
|
}
|
|
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
392
|
}
|
|
315
393
|
|
|
316
394
|
// reenable signals
|
|
@@ -318,11 +396,10 @@ NAN_METHOD(PtyFork) {
|
|
|
318
396
|
|
|
319
397
|
switch (pid) {
|
|
320
398
|
case -1:
|
|
321
|
-
|
|
322
|
-
goto done;
|
|
399
|
+
throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
|
|
323
400
|
case 0:
|
|
324
|
-
if (strlen(
|
|
325
|
-
if (chdir(
|
|
401
|
+
if (strlen(cwd_.c_str())) {
|
|
402
|
+
if (chdir(cwd_.c_str()) == -1) {
|
|
326
403
|
perror("chdir(2) failed.");
|
|
327
404
|
_exit(1);
|
|
328
405
|
}
|
|
@@ -349,62 +426,36 @@ NAN_METHOD(PtyFork) {
|
|
|
349
426
|
}
|
|
350
427
|
default:
|
|
351
428
|
if (pty_nonblock(master) == -1) {
|
|
352
|
-
|
|
353
|
-
goto done;
|
|
429
|
+
throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
|
|
354
430
|
}
|
|
355
431
|
}
|
|
356
432
|
#endif
|
|
357
433
|
|
|
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;
|
|
376
|
-
|
|
377
|
-
uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
|
|
434
|
+
Napi::Object obj = Napi::Object::New(napiEnv);
|
|
435
|
+
obj.Set("fd", Napi::Number::New(napiEnv, master));
|
|
436
|
+
obj.Set("pid", Napi::Number::New(napiEnv, pid));
|
|
437
|
+
obj.Set("pty", Napi::String::New(napiEnv, ptsname(master)));
|
|
378
438
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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();
|
|
439
|
+
// Set up process exit callback.
|
|
440
|
+
Napi::Function cb = info[10].As<Napi::Function>();
|
|
441
|
+
SetupExitCallback(napiEnv, cb, pid);
|
|
442
|
+
return obj;
|
|
393
443
|
}
|
|
394
444
|
|
|
395
|
-
|
|
396
|
-
|
|
445
|
+
Napi::Value PtyOpen(const Napi::CallbackInfo& info) {
|
|
446
|
+
Napi::Env env(info.Env());
|
|
447
|
+
Napi::HandleScope scope(env);
|
|
397
448
|
|
|
398
449
|
if (info.Length() != 2 ||
|
|
399
|
-
!info[0]
|
|
400
|
-
!info[1]
|
|
401
|
-
|
|
450
|
+
!info[0].IsNumber() ||
|
|
451
|
+
!info[1].IsNumber()) {
|
|
452
|
+
throw Napi::Error::New(env, "Usage: pty.open(cols, rows)");
|
|
402
453
|
}
|
|
403
454
|
|
|
404
455
|
// size
|
|
405
456
|
struct winsize winp;
|
|
406
|
-
winp.ws_col = info[0]
|
|
407
|
-
winp.ws_row = info[1]
|
|
457
|
+
winp.ws_col = info[0].As<Napi::Number>().Int32Value();
|
|
458
|
+
winp.ws_row = info[1].As<Napi::Number>().Int32Value();
|
|
408
459
|
winp.ws_xpixel = 0;
|
|
409
460
|
winp.ws_ypixel = 0;
|
|
410
461
|
|
|
@@ -413,98 +464,98 @@ NAN_METHOD(PtyOpen) {
|
|
|
413
464
|
int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
|
|
414
465
|
|
|
415
466
|
if (ret == -1) {
|
|
416
|
-
|
|
467
|
+
throw Napi::Error::New(env, "openpty(3) failed.");
|
|
417
468
|
}
|
|
418
469
|
|
|
419
470
|
if (pty_nonblock(master) == -1) {
|
|
420
|
-
|
|
471
|
+
throw Napi::Error::New(env, "Could not set master fd to nonblocking.");
|
|
421
472
|
}
|
|
422
473
|
|
|
423
474
|
if (pty_nonblock(slave) == -1) {
|
|
424
|
-
|
|
475
|
+
throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
|
|
425
476
|
}
|
|
426
477
|
|
|
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());
|
|
478
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
479
|
+
obj.Set("master", Napi::Number::New(env, master));
|
|
480
|
+
obj.Set("slave", Napi::Number::New(env, slave));
|
|
481
|
+
obj.Set("pty", Napi::String::New(env, ptsname(master)));
|
|
437
482
|
|
|
438
|
-
return
|
|
483
|
+
return obj;
|
|
439
484
|
}
|
|
440
485
|
|
|
441
|
-
|
|
442
|
-
|
|
486
|
+
Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
487
|
+
Napi::Env env(info.Env());
|
|
488
|
+
Napi::HandleScope scope(env);
|
|
443
489
|
|
|
444
490
|
if (info.Length() != 3 ||
|
|
445
|
-
!info[0]
|
|
446
|
-
!info[1]
|
|
447
|
-
!info[2]
|
|
448
|
-
|
|
491
|
+
!info[0].IsNumber() ||
|
|
492
|
+
!info[1].IsNumber() ||
|
|
493
|
+
!info[2].IsNumber()) {
|
|
494
|
+
throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)");
|
|
449
495
|
}
|
|
450
496
|
|
|
451
|
-
int fd = info[0]
|
|
497
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
452
498
|
|
|
453
499
|
struct winsize winp;
|
|
454
|
-
winp.ws_col = info[1]
|
|
455
|
-
winp.ws_row = info[2]
|
|
500
|
+
winp.ws_col = info[1].As<Napi::Number>().Int32Value();
|
|
501
|
+
winp.ws_row = info[2].As<Napi::Number>().Int32Value();
|
|
456
502
|
winp.ws_xpixel = 0;
|
|
457
503
|
winp.ws_ypixel = 0;
|
|
458
504
|
|
|
459
505
|
if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
|
|
460
506
|
switch (errno) {
|
|
461
|
-
case EBADF:
|
|
462
|
-
|
|
463
|
-
case
|
|
464
|
-
|
|
507
|
+
case EBADF:
|
|
508
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EBADF");
|
|
509
|
+
case EFAULT:
|
|
510
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EFAULT");
|
|
511
|
+
case EINVAL:
|
|
512
|
+
throw Napi::Error::New(env, "ioctl(2) failed, EINVAL");
|
|
513
|
+
case ENOTTY:
|
|
514
|
+
throw Napi::Error::New(env, "ioctl(2) failed, ENOTTY");
|
|
465
515
|
}
|
|
466
|
-
|
|
516
|
+
throw Napi::Error::New(env, "ioctl(2) failed");
|
|
467
517
|
}
|
|
468
518
|
|
|
469
|
-
return
|
|
519
|
+
return env.Undefined();
|
|
470
520
|
}
|
|
471
521
|
|
|
472
522
|
/**
|
|
473
523
|
* Foreground Process Name
|
|
474
524
|
*/
|
|
475
|
-
|
|
476
|
-
|
|
525
|
+
Napi::Value PtyGetProc(const Napi::CallbackInfo& info) {
|
|
526
|
+
Napi::Env env(info.Env());
|
|
527
|
+
Napi::HandleScope scope(env);
|
|
477
528
|
|
|
478
529
|
#if defined(__APPLE__)
|
|
479
530
|
if (info.Length() != 1 ||
|
|
480
|
-
!info[0]
|
|
481
|
-
|
|
531
|
+
!info[0].IsNumber()) {
|
|
532
|
+
throw Napi::Error::New(env, "Usage: pty.process(pid)");
|
|
482
533
|
}
|
|
483
534
|
|
|
484
|
-
int fd = info[0]
|
|
535
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
485
536
|
char *name = pty_getproc(fd);
|
|
486
537
|
#else
|
|
487
538
|
if (info.Length() != 2 ||
|
|
488
|
-
!info[0]
|
|
489
|
-
!info[1]
|
|
490
|
-
|
|
539
|
+
!info[0].IsNumber() ||
|
|
540
|
+
!info[1].IsString()) {
|
|
541
|
+
throw Napi::Error::New(env, "Usage: pty.process(fd, tty)");
|
|
491
542
|
}
|
|
492
543
|
|
|
493
|
-
int fd = info[0]
|
|
544
|
+
int fd = info[0].As<Napi::Number>().Int32Value();
|
|
494
545
|
|
|
495
|
-
|
|
496
|
-
char *tty = strdup(
|
|
546
|
+
std::string tty_ = info[1].As<Napi::String>();
|
|
547
|
+
char *tty = strdup(tty_.c_str());
|
|
497
548
|
char *name = pty_getproc(fd, tty);
|
|
498
549
|
free(tty);
|
|
499
550
|
#endif
|
|
500
551
|
|
|
501
552
|
if (name == NULL) {
|
|
502
|
-
return
|
|
553
|
+
return env.Undefined();
|
|
503
554
|
}
|
|
504
555
|
|
|
505
|
-
|
|
556
|
+
Napi::String name_ = Napi::String::New(env, name);
|
|
506
557
|
free(name);
|
|
507
|
-
return
|
|
558
|
+
return name_;
|
|
508
559
|
}
|
|
509
560
|
|
|
510
561
|
/**
|
|
@@ -518,114 +569,6 @@ pty_nonblock(int fd) {
|
|
|
518
569
|
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
519
570
|
}
|
|
520
571
|
|
|
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
572
|
/**
|
|
630
573
|
* pty_getproc
|
|
631
574
|
* Taken from tmux.
|
|
@@ -828,12 +771,12 @@ done:
|
|
|
828
771
|
* Init
|
|
829
772
|
*/
|
|
830
773
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
774
|
+
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
775
|
+
exports.Set("fork", Napi::Function::New(env, PtyFork));
|
|
776
|
+
exports.Set("open", Napi::Function::New(env, PtyOpen));
|
|
777
|
+
exports.Set("resize", Napi::Function::New(env, PtyResize));
|
|
778
|
+
exports.Set("process", Napi::Function::New(env, PtyGetProc));
|
|
779
|
+
return exports;
|
|
837
780
|
}
|
|
838
781
|
|
|
839
|
-
|
|
782
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)
|