node-pty 1.1.0-beta1 → 1.1.0-beta11

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/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>
@@ -58,6 +61,7 @@
58
61
  #include <paths.h>
59
62
  #include <spawn.h>
60
63
  #include <sys/event.h>
64
+ #include <sys/sysctl.h>
61
65
  #include <termios.h>
62
66
  #endif
63
67
 
@@ -98,27 +102,120 @@ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
98
102
  })
99
103
  #endif
100
104
 
101
- /**
102
- * Structs
103
- */
104
-
105
- struct pty_baton {
106
- Nan::Persistent<v8::Function> cb;
107
- int exit_code;
108
- int signal_code;
109
- pid_t pid;
110
- uv_async_t async;
111
- uv_thread_t tid;
105
+ struct ExitEvent {
106
+ int exit_code = 0, signal_code = 0;
112
107
  };
113
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
+ switch (status) {
193
+ case napi_closing:
194
+ break;
195
+
196
+ case napi_queue_full:
197
+ Napi::Error::Fatal("SetupExitCallback", "Queue was full");
198
+
199
+ case napi_ok:
200
+ if (tsfn.Release() != napi_ok) {
201
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.Release() failed");
202
+ }
203
+ break;
204
+
205
+ default:
206
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.BlockingCall() failed");
207
+ }
208
+ });
209
+ }
210
+
114
211
  /**
115
212
  * Methods
116
213
  */
117
214
 
118
- NAN_METHOD(PtyFork);
119
- NAN_METHOD(PtyOpen);
120
- NAN_METHOD(PtyResize);
121
- NAN_METHOD(PtyGetProc);
215
+ Napi::Value PtyFork(const Napi::CallbackInfo& info);
216
+ Napi::Value PtyOpen(const Napi::CallbackInfo& info);
217
+ Napi::Value PtyResize(const Napi::CallbackInfo& info);
218
+ Napi::Value PtyGetProc(const Napi::CallbackInfo& info);
122
219
 
123
220
  /**
124
221
  * Functions
@@ -135,15 +232,6 @@ static char *
135
232
  pty_getproc(int, char *);
136
233
  #endif
137
234
 
138
- static void
139
- pty_waitpid(void *);
140
-
141
- static void
142
- pty_after_waitpid(uv_async_t *);
143
-
144
- static void
145
- pty_after_close(uv_handle_t *);
146
-
147
235
  #if defined(__APPLE__) || defined(__OpenBSD__)
148
236
  static void
149
237
  pty_posix_spawn(char** argv, char** env,
@@ -154,62 +242,75 @@ pty_posix_spawn(char** argv, char** env,
154
242
  int* err);
155
243
  #endif
156
244
 
157
- NAN_METHOD(PtyFork) {
158
- Nan::HandleScope scope;
245
+ struct DelBuf {
246
+ int len;
247
+ DelBuf(int len) : len(len) {}
248
+ void operator()(char **p) {
249
+ if (p == nullptr)
250
+ return;
251
+ for (int i = 0; i < len; i++)
252
+ free(p[i]);
253
+ delete[] p;
254
+ }
255
+ };
256
+
257
+ Napi::Value PtyFork(const Napi::CallbackInfo& info) {
258
+ Napi::Env napiEnv(info.Env());
259
+ Napi::HandleScope scope(napiEnv);
159
260
 
160
261
  if (info.Length() != 11 ||
161
- !info[0]->IsString() ||
162
- !info[1]->IsArray() ||
163
- !info[2]->IsArray() ||
164
- !info[3]->IsString() ||
165
- !info[4]->IsNumber() ||
166
- !info[5]->IsNumber() ||
167
- !info[6]->IsNumber() ||
168
- !info[7]->IsNumber() ||
169
- !info[8]->IsBoolean() ||
170
- !info[9]->IsString() ||
171
- !info[10]->IsFunction()) {
172
- return Nan::ThrowError(
173
- "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
262
+ !info[0].IsString() ||
263
+ !info[1].IsArray() ||
264
+ !info[2].IsArray() ||
265
+ !info[3].IsString() ||
266
+ !info[4].IsNumber() ||
267
+ !info[5].IsNumber() ||
268
+ !info[6].IsNumber() ||
269
+ !info[7].IsNumber() ||
270
+ !info[8].IsBoolean() ||
271
+ !info[9].IsString() ||
272
+ !info[10].IsFunction()) {
273
+ throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
174
274
  }
175
275
 
176
276
  // file
177
- Nan::Utf8String file(info[0]);
277
+ std::string file = info[0].As<Napi::String>();
178
278
 
179
279
  // args
180
- v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
280
+ Napi::Array argv_ = info[1].As<Napi::Array>();
181
281
 
182
282
  // env
183
- v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
184
- int envc = env_->Length();
185
- char **env = new char*[envc+1];
283
+ Napi::Array env_ = info[2].As<Napi::Array>();
284
+ int envc = env_.Length();
285
+ std::unique_ptr<char *, DelBuf> env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1));
286
+ char **env = env_unique_ptr.get();
186
287
  env[envc] = NULL;
187
288
  for (int i = 0; i < envc; i++) {
188
- Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
189
- env[i] = strdup(*pair);
289
+ std::string pair = env_.Get(i).As<Napi::String>();
290
+ env[i] = strdup(pair.c_str());
190
291
  }
191
292
 
192
293
  // cwd
193
- Nan::Utf8String cwd_(info[3]);
294
+ std::string cwd_ = info[3].As<Napi::String>();
194
295
 
195
296
  // size
196
297
  struct winsize winp;
197
- winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust();
198
- winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust();
298
+ winp.ws_col = info[4].As<Napi::Number>().Int32Value();
299
+ winp.ws_row = info[5].As<Napi::Number>().Int32Value();
199
300
  winp.ws_xpixel = 0;
200
301
  winp.ws_ypixel = 0;
201
302
 
202
303
  #if !defined(__APPLE__)
203
304
  // uid / gid
204
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
205
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
305
+ int uid = info[6].As<Napi::Number>().Int32Value();
306
+ int gid = info[7].As<Napi::Number>().Int32Value();
206
307
  #endif
207
308
 
208
309
  // termios
209
310
  struct termios t = termios();
210
311
  struct termios *term = &t;
211
312
  term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
212
- if (Nan::To<bool>(info[8]).FromJust()) {
313
+ if (info[8].As<Napi::Boolean>().Value()) {
213
314
  #if defined(IUTF8)
214
315
  term->c_iflag |= IUTF8;
215
316
  #endif
@@ -244,45 +345,44 @@ NAN_METHOD(PtyFork) {
244
345
  cfsetospeed(term, B38400);
245
346
 
246
347
  // helperPath
247
- Nan::Utf8String helper_path(info[9]);
348
+ std::string helper_path = info[9].As<Napi::String>();
248
349
 
249
350
  pid_t pid;
250
351
  int master;
251
352
  #if defined(__APPLE__)
252
- int argc = argv_->Length();
353
+ int argc = argv_.Length();
253
354
  int argl = argc + 4;
254
- char **argv = new char*[argl];
255
- argv[0] = strdup(*helper_path);
256
- argv[1] = strdup(*cwd_);
257
- argv[2] = strdup(*file);
355
+ std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
356
+ char **argv = argv_unique_ptr.get();
357
+ argv[0] = strdup(helper_path.c_str());
358
+ argv[1] = strdup(cwd_.c_str());
359
+ argv[2] = strdup(file.c_str());
258
360
  argv[argl - 1] = NULL;
259
361
  for (int i = 0; i < argc; i++) {
260
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
261
- argv[i + 3] = strdup(*arg);
362
+ std::string arg = argv_.Get(i).As<Napi::String>();
363
+ argv[i + 3] = strdup(arg.c_str());
262
364
  }
263
365
 
264
366
  int err = -1;
265
367
  pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
266
368
  if (err != 0) {
267
- Nan::ThrowError("posix_spawnp failed.");
268
- goto done;
369
+ throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
269
370
  }
270
371
  if (pty_nonblock(master) == -1) {
271
- Nan::ThrowError("Could not set master fd to nonblocking.");
272
- goto done;
372
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
273
373
  }
274
374
  #else
275
- int argc = argv_->Length();
375
+ int argc = argv_.Length();
276
376
  int argl = argc + 2;
277
- char **argv = new char*[argl];
278
- argv[0] = strdup(*file);
377
+ std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
378
+ char** argv = argv_unique_ptr.get();
379
+ argv[0] = strdup(file.c_str());
279
380
  argv[argl - 1] = NULL;
280
381
  for (int i = 0; i < argc; i++) {
281
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
282
- argv[i + 1] = strdup(*arg);
382
+ std::string arg = argv_.Get(i).As<Napi::String>();
383
+ argv[i + 1] = strdup(arg.c_str());
283
384
  }
284
385
 
285
- char* cwd = strdup(*cwd_);
286
386
  sigset_t newmask, oldmask;
287
387
  struct sigaction sig_action;
288
388
  // temporarily block all signals
@@ -302,14 +402,6 @@ NAN_METHOD(PtyFork) {
302
402
  for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
303
403
  sigaction(i, &sig_action, NULL);
304
404
  }
305
- } else {
306
- for (int i = 0; i < argl; i++) free(argv[i]);
307
- delete[] argv;
308
-
309
- for (int i = 0; i < envc; i++) free(env[i]);
310
- delete[] env;
311
-
312
- free(cwd);
313
405
  }
314
406
 
315
407
  // reenable signals
@@ -317,11 +409,10 @@ NAN_METHOD(PtyFork) {
317
409
 
318
410
  switch (pid) {
319
411
  case -1:
320
- Nan::ThrowError("forkpty(3) failed.");
321
- goto done;
412
+ throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
322
413
  case 0:
323
- if (strlen(cwd)) {
324
- if (chdir(cwd) == -1) {
414
+ if (strlen(cwd_.c_str())) {
415
+ if (chdir(cwd_.c_str()) == -1) {
325
416
  perror("chdir(2) failed.");
326
417
  _exit(1);
327
418
  }
@@ -348,62 +439,36 @@ NAN_METHOD(PtyFork) {
348
439
  }
349
440
  default:
350
441
  if (pty_nonblock(master) == -1) {
351
- Nan::ThrowError("Could not set master fd to nonblocking.");
352
- goto done;
442
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
353
443
  }
354
444
  }
355
445
  #endif
356
446
 
357
- {
358
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
359
- Nan::Set(obj,
360
- Nan::New<v8::String>("fd").ToLocalChecked(),
361
- Nan::New<v8::Number>(master));
362
- Nan::Set(obj,
363
- Nan::New<v8::String>("pid").ToLocalChecked(),
364
- Nan::New<v8::Number>(pid));
365
- Nan::Set(obj,
366
- Nan::New<v8::String>("pty").ToLocalChecked(),
367
- Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
368
-
369
- pty_baton *baton = new pty_baton();
370
- baton->exit_code = 0;
371
- baton->signal_code = 0;
372
- baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
373
- baton->pid = pid;
374
- baton->async.data = baton;
375
-
376
- uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
377
-
378
- uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
379
-
380
- return info.GetReturnValue().Set(obj);
381
- }
382
-
383
- done:
384
- #if defined(__APPLE__)
385
- for (int i = 0; i < argl; i++) free(argv[i]);
386
- delete[] argv;
447
+ Napi::Object obj = Napi::Object::New(napiEnv);
448
+ obj.Set("fd", Napi::Number::New(napiEnv, master));
449
+ obj.Set("pid", Napi::Number::New(napiEnv, pid));
450
+ obj.Set("pty", Napi::String::New(napiEnv, ptsname(master)));
387
451
 
388
- for (int i = 0; i < envc; i++) free(env[i]);
389
- delete[] env;
390
- #endif
391
- return info.GetReturnValue().SetUndefined();
452
+ // Set up process exit callback.
453
+ Napi::Function cb = info[10].As<Napi::Function>();
454
+ SetupExitCallback(napiEnv, cb, pid);
455
+ return obj;
392
456
  }
393
457
 
394
- NAN_METHOD(PtyOpen) {
395
- Nan::HandleScope scope;
458
+ Napi::Value PtyOpen(const Napi::CallbackInfo& info) {
459
+ Napi::Env env(info.Env());
460
+ Napi::HandleScope scope(env);
396
461
 
397
462
  if (info.Length() != 2 ||
398
- !info[0]->IsNumber() ||
399
- !info[1]->IsNumber()) {
400
- return Nan::ThrowError("Usage: pty.open(cols, rows)");
463
+ !info[0].IsNumber() ||
464
+ !info[1].IsNumber()) {
465
+ throw Napi::Error::New(env, "Usage: pty.open(cols, rows)");
401
466
  }
402
467
 
403
468
  // size
404
469
  struct winsize winp;
405
- winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
406
- winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
470
+ winp.ws_col = info[0].As<Napi::Number>().Int32Value();
471
+ winp.ws_row = info[1].As<Napi::Number>().Int32Value();
407
472
  winp.ws_xpixel = 0;
408
473
  winp.ws_ypixel = 0;
409
474
 
@@ -412,98 +477,98 @@ NAN_METHOD(PtyOpen) {
412
477
  int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
413
478
 
414
479
  if (ret == -1) {
415
- return Nan::ThrowError("openpty(3) failed.");
480
+ throw Napi::Error::New(env, "openpty(3) failed.");
416
481
  }
417
482
 
418
483
  if (pty_nonblock(master) == -1) {
419
- return Nan::ThrowError("Could not set master fd to nonblocking.");
484
+ throw Napi::Error::New(env, "Could not set master fd to nonblocking.");
420
485
  }
421
486
 
422
487
  if (pty_nonblock(slave) == -1) {
423
- return Nan::ThrowError("Could not set slave fd to nonblocking.");
488
+ throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
424
489
  }
425
490
 
426
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
427
- Nan::Set(obj,
428
- Nan::New<v8::String>("master").ToLocalChecked(),
429
- Nan::New<v8::Number>(master));
430
- Nan::Set(obj,
431
- Nan::New<v8::String>("slave").ToLocalChecked(),
432
- Nan::New<v8::Number>(slave));
433
- Nan::Set(obj,
434
- Nan::New<v8::String>("pty").ToLocalChecked(),
435
- Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
491
+ Napi::Object obj = Napi::Object::New(env);
492
+ obj.Set("master", Napi::Number::New(env, master));
493
+ obj.Set("slave", Napi::Number::New(env, slave));
494
+ obj.Set("pty", Napi::String::New(env, ptsname(master)));
436
495
 
437
- return info.GetReturnValue().Set(obj);
496
+ return obj;
438
497
  }
439
498
 
440
- NAN_METHOD(PtyResize) {
441
- Nan::HandleScope scope;
499
+ Napi::Value PtyResize(const Napi::CallbackInfo& info) {
500
+ Napi::Env env(info.Env());
501
+ Napi::HandleScope scope(env);
442
502
 
443
503
  if (info.Length() != 3 ||
444
- !info[0]->IsNumber() ||
445
- !info[1]->IsNumber() ||
446
- !info[2]->IsNumber()) {
447
- return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)");
504
+ !info[0].IsNumber() ||
505
+ !info[1].IsNumber() ||
506
+ !info[2].IsNumber()) {
507
+ throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)");
448
508
  }
449
509
 
450
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
510
+ int fd = info[0].As<Napi::Number>().Int32Value();
451
511
 
452
512
  struct winsize winp;
453
- winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
454
- winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust();
513
+ winp.ws_col = info[1].As<Napi::Number>().Int32Value();
514
+ winp.ws_row = info[2].As<Napi::Number>().Int32Value();
455
515
  winp.ws_xpixel = 0;
456
516
  winp.ws_ypixel = 0;
457
517
 
458
518
  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
459
519
  switch (errno) {
460
- case EBADF: return Nan::ThrowError("ioctl(2) failed, EBADF");
461
- case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT");
462
- case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL");
463
- case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY");
520
+ case EBADF:
521
+ throw Napi::Error::New(env, "ioctl(2) failed, EBADF");
522
+ case EFAULT:
523
+ throw Napi::Error::New(env, "ioctl(2) failed, EFAULT");
524
+ case EINVAL:
525
+ throw Napi::Error::New(env, "ioctl(2) failed, EINVAL");
526
+ case ENOTTY:
527
+ throw Napi::Error::New(env, "ioctl(2) failed, ENOTTY");
464
528
  }
465
- return Nan::ThrowError("ioctl(2) failed");
529
+ throw Napi::Error::New(env, "ioctl(2) failed");
466
530
  }
467
531
 
468
- return info.GetReturnValue().SetUndefined();
532
+ return env.Undefined();
469
533
  }
470
534
 
471
535
  /**
472
536
  * Foreground Process Name
473
537
  */
474
- NAN_METHOD(PtyGetProc) {
475
- Nan::HandleScope scope;
538
+ Napi::Value PtyGetProc(const Napi::CallbackInfo& info) {
539
+ Napi::Env env(info.Env());
540
+ Napi::HandleScope scope(env);
476
541
 
477
542
  #if defined(__APPLE__)
478
543
  if (info.Length() != 1 ||
479
- !info[0]->IsNumber()) {
480
- return Nan::ThrowError("Usage: pty.process(pid)");
544
+ !info[0].IsNumber()) {
545
+ throw Napi::Error::New(env, "Usage: pty.process(pid)");
481
546
  }
482
547
 
483
- int pid = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
484
- char *name = pty_getproc(pid);
548
+ int fd = info[0].As<Napi::Number>().Int32Value();
549
+ char *name = pty_getproc(fd);
485
550
  #else
486
551
  if (info.Length() != 2 ||
487
- !info[0]->IsNumber() ||
488
- !info[1]->IsString()) {
489
- return Nan::ThrowError("Usage: pty.process(fd, tty)");
552
+ !info[0].IsNumber() ||
553
+ !info[1].IsString()) {
554
+ throw Napi::Error::New(env, "Usage: pty.process(fd, tty)");
490
555
  }
491
556
 
492
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
557
+ int fd = info[0].As<Napi::Number>().Int32Value();
493
558
 
494
- Nan::Utf8String tty_(info[1]);
495
- char *tty = strdup(*tty_);
559
+ std::string tty_ = info[1].As<Napi::String>();
560
+ char *tty = strdup(tty_.c_str());
496
561
  char *name = pty_getproc(fd, tty);
497
562
  free(tty);
498
563
  #endif
499
564
 
500
565
  if (name == NULL) {
501
- return info.GetReturnValue().SetUndefined();
566
+ return env.Undefined();
502
567
  }
503
568
 
504
- v8::Local<v8::String> name_ = Nan::New<v8::String>(name).ToLocalChecked();
569
+ Napi::String name_ = Napi::String::New(env, name);
505
570
  free(name);
506
- return info.GetReturnValue().Set(name_);
571
+ return name_;
507
572
  }
508
573
 
509
574
  /**
@@ -517,114 +582,6 @@ pty_nonblock(int fd) {
517
582
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
518
583
  }
519
584
 
520
- /**
521
- * pty_waitpid
522
- * Wait for SIGCHLD to read exit status.
523
- */
524
-
525
- static void
526
- pty_waitpid(void *data) {
527
- int ret;
528
- int stat_loc;
529
- pty_baton *baton = static_cast<pty_baton*>(data);
530
- errno = 0;
531
- #if defined(__APPLE__)
532
- // Based on
533
- // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
534
- int kq = HANDLE_EINTR(kqueue());
535
- struct kevent change = {0};
536
- EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
537
- ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
538
- if (ret == -1) {
539
- if (errno == ESRCH) {
540
- // At this point, one of the following has occurred:
541
- // 1. The process has died but has not yet been reaped.
542
- // 2. The process has died and has already been reaped.
543
- // 3. The process is in the process of dying. It's no longer
544
- // kqueueable, but it may not be waitable yet either. Mark calls
545
- // this case the "zombie death race".
546
- ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG));
547
- if (ret == 0) {
548
- ret = kill(baton->pid, SIGKILL);
549
- if (ret != -1) {
550
- HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
551
- }
552
- }
553
- }
554
- } else {
555
- struct kevent event = {0};
556
- ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
557
- if (ret == 1) {
558
- if ((event.fflags & NOTE_EXIT) &&
559
- (event.ident == static_cast<uintptr_t>(baton->pid))) {
560
- // The process is dead or dying. This won't block for long, if at
561
- // all.
562
- HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
563
- }
564
- }
565
- }
566
- #else
567
- if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
568
- if (ret == -1 && errno == EINTR) {
569
- return pty_waitpid(baton);
570
- }
571
- if (ret == -1 && errno == ECHILD) {
572
- // XXX node v0.8.x seems to have this problem.
573
- // waitpid is already handled elsewhere.
574
- ;
575
- } else {
576
- assert(false);
577
- }
578
- }
579
- #endif
580
-
581
- if (WIFEXITED(stat_loc)) {
582
- baton->exit_code = WEXITSTATUS(stat_loc); // errno?
583
- }
584
-
585
- if (WIFSIGNALED(stat_loc)) {
586
- baton->signal_code = WTERMSIG(stat_loc);
587
- }
588
-
589
- uv_async_send(&baton->async);
590
- }
591
-
592
- /**
593
- * pty_after_waitpid
594
- * Callback after exit status has been read.
595
- */
596
-
597
- static void
598
- pty_after_waitpid(uv_async_t *async) {
599
- Nan::HandleScope scope;
600
- pty_baton *baton = static_cast<pty_baton*>(async->data);
601
-
602
- v8::Local<v8::Value> argv[] = {
603
- Nan::New<v8::Integer>(baton->exit_code),
604
- Nan::New<v8::Integer>(baton->signal_code),
605
- };
606
-
607
- v8::Local<v8::Function> cb = Nan::New<v8::Function>(baton->cb);
608
- baton->cb.Reset();
609
- memset(&baton->cb, -1, sizeof(baton->cb));
610
- Nan::AsyncResource resource("pty_after_waitpid");
611
- resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv);
612
-
613
- uv_close((uv_handle_t *)async, pty_after_close);
614
- }
615
-
616
- /**
617
- * pty_after_close
618
- * uv_close() callback - free handle data
619
- */
620
-
621
- static void
622
- pty_after_close(uv_handle_t *handle) {
623
- uv_async_t *async = (uv_async_t *)handle;
624
- pty_baton *baton = static_cast<pty_baton*>(async->data);
625
- delete baton;
626
- }
627
-
628
585
  /**
629
586
  * pty_getproc
630
587
  * Taken from tmux.
@@ -692,13 +649,25 @@ pty_getproc(int fd, char *tty) {
692
649
  #elif defined(__APPLE__)
693
650
 
694
651
  static char *
695
- pty_getproc(int pid) {
696
- char pname[MAXCOMLEN + 1];
697
- if (!proc_name(pid, pname, sizeof(pname))) {
652
+ pty_getproc(int fd) {
653
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
654
+ size_t size;
655
+ struct kinfo_proc kp;
656
+
657
+ if ((mib[3] = tcgetpgrp(fd)) == -1) {
658
+ return NULL;
659
+ }
660
+
661
+ size = sizeof kp;
662
+ if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
663
+ return NULL;
664
+ }
665
+
666
+ if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') {
698
667
  return NULL;
699
668
  }
700
669
 
701
- return strdup(pname);
670
+ return strdup(kp.kp_proc.p_comm);
702
671
  }
703
672
 
704
673
  #else
@@ -815,12 +784,12 @@ done:
815
784
  * Init
816
785
  */
817
786
 
818
- NAN_MODULE_INIT(init) {
819
- Nan::HandleScope scope;
820
- Nan::Export(target, "fork", PtyFork);
821
- Nan::Export(target, "open", PtyOpen);
822
- Nan::Export(target, "resize", PtyResize);
823
- Nan::Export(target, "process", PtyGetProc);
787
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
788
+ exports.Set("fork", Napi::Function::New(env, PtyFork));
789
+ exports.Set("open", Napi::Function::New(env, PtyOpen));
790
+ exports.Set("resize", Napi::Function::New(env, PtyResize));
791
+ exports.Set("process", Napi::Function::New(env, PtyGetProc));
792
+ return exports;
824
793
  }
825
794
 
826
- NODE_MODULE(pty, init)
795
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)