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