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.
Files changed (40) hide show
  1. package/README.md +6 -5
  2. package/binding.gyp +4 -15
  3. package/deps/winpty/src/agent/Agent.cc +11 -3
  4. package/lib/unixTerminal.js +4 -27
  5. package/lib/unixTerminal.js.map +1 -1
  6. package/lib/unixTerminal.test.js +3 -5
  7. package/lib/unixTerminal.test.js.map +1 -1
  8. package/lib/windowsConoutConnection.js +6 -3
  9. package/lib/windowsConoutConnection.js.map +1 -1
  10. package/lib/windowsPtyAgent.js +42 -22
  11. package/lib/windowsPtyAgent.js.map +1 -1
  12. package/lib/windowsTerminal.js +1 -1
  13. package/lib/windowsTerminal.js.map +1 -1
  14. package/lib/windowsTerminal.test.js +38 -26
  15. package/lib/windowsTerminal.test.js.map +1 -1
  16. package/lib/worker/conoutSocketWorker.js.map +1 -1
  17. package/package.json +4 -2
  18. package/scripts/{publish.js → increment-version.js} +7 -15
  19. package/scripts/post-install.js +51 -10
  20. package/src/interfaces.ts +1 -0
  21. package/src/native.d.ts +5 -5
  22. package/src/unix/pty.cc +241 -284
  23. package/src/unixTerminal.test.ts +3 -6
  24. package/src/unixTerminal.ts +4 -18
  25. package/src/win/conpty.cc +266 -185
  26. package/src/win/conpty.h +41 -0
  27. package/src/win/conpty_console_list.cc +16 -15
  28. package/src/win/path_util.cc +39 -36
  29. package/src/win/path_util.h +5 -2
  30. package/src/win/winpty.cc +108 -151
  31. package/src/windowsConoutConnection.ts +6 -3
  32. package/src/windowsPtyAgent.ts +39 -21
  33. package/src/windowsTerminal.test.ts +43 -32
  34. package/src/windowsTerminal.ts +1 -1
  35. package/src/worker/conoutSocketWorker.ts +2 -2
  36. package/third_party/conpty/1.22.250204002/win10-arm64/OpenConsole.exe +0 -0
  37. package/third_party/conpty/1.22.250204002/win10-arm64/conpty.dll +0 -0
  38. package/third_party/conpty/1.22.250204002/win10-x64/OpenConsole.exe +0 -0
  39. package/third_party/conpty/1.22.250204002/win10-x64/conpty.dll +0 -0
  40. 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
- #include <nan.h>
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
- * Structs
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
- NAN_METHOD(PtyFork);
120
- NAN_METHOD(PtyOpen);
121
- NAN_METHOD(PtyResize);
122
- NAN_METHOD(PtyGetProc);
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
- NAN_METHOD(PtyFork) {
159
- Nan::HandleScope scope;
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]->IsString() ||
163
- !info[1]->IsArray() ||
164
- !info[2]->IsArray() ||
165
- !info[3]->IsString() ||
166
- !info[4]->IsNumber() ||
167
- !info[5]->IsNumber() ||
168
- !info[6]->IsNumber() ||
169
- !info[7]->IsNumber() ||
170
- !info[8]->IsBoolean() ||
171
- !info[9]->IsString() ||
172
- !info[10]->IsFunction()) {
173
- return Nan::ThrowError(
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
- Nan::Utf8String file(info[0]);
278
+ std::string file = info[0].As<Napi::String>();
179
279
 
180
280
  // args
181
- v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
281
+ Napi::Array argv_ = info[1].As<Napi::Array>();
182
282
 
183
283
  // env
184
- v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
185
- int envc = env_->Length();
186
- char **env = new char*[envc+1];
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
- Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
190
- env[i] = strdup(*pair);
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
- Nan::Utf8String cwd_(info[3]);
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]->IntegerValue(Nan::GetCurrentContext()).FromJust();
199
- winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust();
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]->IntegerValue(Nan::GetCurrentContext()).FromJust();
206
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
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 (Nan::To<bool>(info[8]).FromJust()) {
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
- Nan::Utf8String helper_path(info[9]);
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_->Length();
354
+ int argc = argv_.Length();
254
355
  int argl = argc + 4;
255
- char **argv = new char*[argl];
256
- argv[0] = strdup(*helper_path);
257
- argv[1] = strdup(*cwd_);
258
- argv[2] = strdup(*file);
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
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
262
- argv[i + 3] = strdup(*arg);
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
- Nan::ThrowError("posix_spawnp failed.");
269
- goto done;
370
+ throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
270
371
  }
271
372
  if (pty_nonblock(master) == -1) {
272
- Nan::ThrowError("Could not set master fd to nonblocking.");
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_->Length();
376
+ int argc = argv_.Length();
277
377
  int argl = argc + 2;
278
- char **argv = new char*[argl];
279
- argv[0] = strdup(*file);
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
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
283
- argv[i + 1] = strdup(*arg);
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
- Nan::ThrowError("forkpty(3) failed.");
322
- goto done;
413
+ throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
323
414
  case 0:
324
- if (strlen(cwd)) {
325
- if (chdir(cwd) == -1) {
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
- Nan::ThrowError("Could not set master fd to nonblocking.");
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
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
360
- Nan::Set(obj,
361
- Nan::New<v8::String>("fd").ToLocalChecked(),
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
- 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();
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
- NAN_METHOD(PtyOpen) {
396
- Nan::HandleScope scope;
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]->IsNumber() ||
400
- !info[1]->IsNumber()) {
401
- return Nan::ThrowError("Usage: pty.open(cols, rows)");
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]->IntegerValue(Nan::GetCurrentContext()).FromJust();
407
- winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
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
- return Nan::ThrowError("openpty(3) failed.");
481
+ throw Napi::Error::New(env, "openpty(3) failed.");
417
482
  }
418
483
 
419
484
  if (pty_nonblock(master) == -1) {
420
- return Nan::ThrowError("Could not set master fd to nonblocking.");
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
- return Nan::ThrowError("Could not set slave fd to nonblocking.");
489
+ throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
425
490
  }
426
491
 
427
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
428
- Nan::Set(obj,
429
- Nan::New<v8::String>("master").ToLocalChecked(),
430
- Nan::New<v8::Number>(master));
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 info.GetReturnValue().Set(obj);
497
+ return obj;
439
498
  }
440
499
 
441
- NAN_METHOD(PtyResize) {
442
- Nan::HandleScope scope;
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]->IsNumber() ||
446
- !info[1]->IsNumber() ||
447
- !info[2]->IsNumber()) {
448
- return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)");
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]->IntegerValue(Nan::GetCurrentContext()).FromJust();
511
+ int fd = info[0].As<Napi::Number>().Int32Value();
452
512
 
453
513
  struct winsize winp;
454
- winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
455
- winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust();
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: return Nan::ThrowError("ioctl(2) failed, EBADF");
462
- case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT");
463
- case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL");
464
- case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY");
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
- return Nan::ThrowError("ioctl(2) failed");
530
+ throw Napi::Error::New(env, "ioctl(2) failed");
467
531
  }
468
532
 
469
- return info.GetReturnValue().SetUndefined();
533
+ return env.Undefined();
470
534
  }
471
535
 
472
536
  /**
473
537
  * Foreground Process Name
474
538
  */
475
- NAN_METHOD(PtyGetProc) {
476
- Nan::HandleScope scope;
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]->IsNumber()) {
481
- return Nan::ThrowError("Usage: pty.process(pid)");
545
+ !info[0].IsNumber()) {
546
+ throw Napi::Error::New(env, "Usage: pty.process(pid)");
482
547
  }
483
548
 
484
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
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]->IsNumber() ||
489
- !info[1]->IsString()) {
490
- return Nan::ThrowError("Usage: pty.process(fd, tty)");
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]->IntegerValue(Nan::GetCurrentContext()).FromJust();
558
+ int fd = info[0].As<Napi::Number>().Int32Value();
494
559
 
495
- Nan::Utf8String tty_(info[1]);
496
- char *tty = strdup(*tty_);
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 info.GetReturnValue().SetUndefined();
567
+ return env.Undefined();
503
568
  }
504
569
 
505
- v8::Local<v8::String> name_ = Nan::New<v8::String>(name).ToLocalChecked();
570
+ Napi::String name_ = Napi::String::New(env, name);
506
571
  free(name);
507
- return info.GetReturnValue().Set(name_);
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
- NAN_MODULE_INIT(init) {
832
- Nan::HandleScope scope;
833
- Nan::Export(target, "fork", PtyFork);
834
- Nan::Export(target, "open", PtyOpen);
835
- Nan::Export(target, "resize", PtyResize);
836
- Nan::Export(target, "process", PtyGetProc);
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
- NODE_MODULE(pty, init)
796
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)