node-pty 1.0.0 → 1.1.0-beta10

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,107 @@ 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
+ assert(status == napi_ok);
193
+
194
+ tsfn.Release();
195
+ });
196
+ }
197
+
114
198
  /**
115
199
  * Methods
116
200
  */
117
201
 
118
- NAN_METHOD(PtyFork);
119
- NAN_METHOD(PtyOpen);
120
- NAN_METHOD(PtyResize);
121
- NAN_METHOD(PtyGetProc);
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);
122
206
 
123
207
  /**
124
208
  * Functions
@@ -135,15 +219,6 @@ static char *
135
219
  pty_getproc(int, char *);
136
220
  #endif
137
221
 
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
222
  #if defined(__APPLE__) || defined(__OpenBSD__)
148
223
  static void
149
224
  pty_posix_spawn(char** argv, char** env,
@@ -154,62 +229,75 @@ pty_posix_spawn(char** argv, char** env,
154
229
  int* err);
155
230
  #endif
156
231
 
157
- NAN_METHOD(PtyFork) {
158
- Nan::HandleScope scope;
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);
159
247
 
160
248
  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)");
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)");
174
261
  }
175
262
 
176
263
  // file
177
- Nan::Utf8String file(info[0]);
264
+ std::string file = info[0].As<Napi::String>();
178
265
 
179
266
  // args
180
- v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
267
+ Napi::Array argv_ = info[1].As<Napi::Array>();
181
268
 
182
269
  // 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];
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();
186
274
  env[envc] = NULL;
187
275
  for (int i = 0; i < envc; i++) {
188
- Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
189
- env[i] = strdup(*pair);
276
+ std::string pair = env_.Get(i).As<Napi::String>();
277
+ env[i] = strdup(pair.c_str());
190
278
  }
191
279
 
192
280
  // cwd
193
- Nan::Utf8String cwd_(info[3]);
281
+ std::string cwd_ = info[3].As<Napi::String>();
194
282
 
195
283
  // size
196
284
  struct winsize winp;
197
- winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust();
198
- winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust();
285
+ winp.ws_col = info[4].As<Napi::Number>().Int32Value();
286
+ winp.ws_row = info[5].As<Napi::Number>().Int32Value();
199
287
  winp.ws_xpixel = 0;
200
288
  winp.ws_ypixel = 0;
201
289
 
202
290
  #if !defined(__APPLE__)
203
291
  // uid / gid
204
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
205
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
292
+ int uid = info[6].As<Napi::Number>().Int32Value();
293
+ int gid = info[7].As<Napi::Number>().Int32Value();
206
294
  #endif
207
295
 
208
296
  // termios
209
297
  struct termios t = termios();
210
298
  struct termios *term = &t;
211
299
  term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
212
- if (Nan::To<bool>(info[8]).FromJust()) {
300
+ if (info[8].As<Napi::Boolean>().Value()) {
213
301
  #if defined(IUTF8)
214
302
  term->c_iflag |= IUTF8;
215
303
  #endif
@@ -244,45 +332,44 @@ NAN_METHOD(PtyFork) {
244
332
  cfsetospeed(term, B38400);
245
333
 
246
334
  // helperPath
247
- Nan::Utf8String helper_path(info[9]);
335
+ std::string helper_path = info[9].As<Napi::String>();
248
336
 
249
337
  pid_t pid;
250
338
  int master;
251
339
  #if defined(__APPLE__)
252
- int argc = argv_->Length();
340
+ int argc = argv_.Length();
253
341
  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);
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());
258
347
  argv[argl - 1] = NULL;
259
348
  for (int i = 0; i < argc; i++) {
260
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
261
- argv[i + 3] = strdup(*arg);
349
+ std::string arg = argv_.Get(i).As<Napi::String>();
350
+ argv[i + 3] = strdup(arg.c_str());
262
351
  }
263
352
 
264
353
  int err = -1;
265
354
  pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
266
355
  if (err != 0) {
267
- Nan::ThrowError("posix_spawnp failed.");
268
- goto done;
356
+ throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
269
357
  }
270
358
  if (pty_nonblock(master) == -1) {
271
- Nan::ThrowError("Could not set master fd to nonblocking.");
272
- goto done;
359
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
273
360
  }
274
361
  #else
275
- int argc = argv_->Length();
362
+ int argc = argv_.Length();
276
363
  int argl = argc + 2;
277
- char **argv = new char*[argl];
278
- argv[0] = strdup(*file);
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());
279
367
  argv[argl - 1] = NULL;
280
368
  for (int i = 0; i < argc; i++) {
281
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
282
- argv[i + 1] = strdup(*arg);
369
+ std::string arg = argv_.Get(i).As<Napi::String>();
370
+ argv[i + 1] = strdup(arg.c_str());
283
371
  }
284
372
 
285
- char* cwd = strdup(*cwd_);
286
373
  sigset_t newmask, oldmask;
287
374
  struct sigaction sig_action;
288
375
  // temporarily block all signals
@@ -302,14 +389,6 @@ NAN_METHOD(PtyFork) {
302
389
  for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
303
390
  sigaction(i, &sig_action, NULL);
304
391
  }
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
392
  }
314
393
 
315
394
  // reenable signals
@@ -317,11 +396,10 @@ NAN_METHOD(PtyFork) {
317
396
 
318
397
  switch (pid) {
319
398
  case -1:
320
- Nan::ThrowError("forkpty(3) failed.");
321
- goto done;
399
+ throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
322
400
  case 0:
323
- if (strlen(cwd)) {
324
- if (chdir(cwd) == -1) {
401
+ if (strlen(cwd_.c_str())) {
402
+ if (chdir(cwd_.c_str()) == -1) {
325
403
  perror("chdir(2) failed.");
326
404
  _exit(1);
327
405
  }
@@ -348,62 +426,36 @@ NAN_METHOD(PtyFork) {
348
426
  }
349
427
  default:
350
428
  if (pty_nonblock(master) == -1) {
351
- Nan::ThrowError("Could not set master fd to nonblocking.");
352
- goto done;
429
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
353
430
  }
354
431
  }
355
432
  #endif
356
433
 
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;
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)));
387
438
 
388
- for (int i = 0; i < envc; i++) free(env[i]);
389
- delete[] env;
390
- #endif
391
- 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;
392
443
  }
393
444
 
394
- NAN_METHOD(PtyOpen) {
395
- Nan::HandleScope scope;
445
+ Napi::Value PtyOpen(const Napi::CallbackInfo& info) {
446
+ Napi::Env env(info.Env());
447
+ Napi::HandleScope scope(env);
396
448
 
397
449
  if (info.Length() != 2 ||
398
- !info[0]->IsNumber() ||
399
- !info[1]->IsNumber()) {
400
- return Nan::ThrowError("Usage: pty.open(cols, rows)");
450
+ !info[0].IsNumber() ||
451
+ !info[1].IsNumber()) {
452
+ throw Napi::Error::New(env, "Usage: pty.open(cols, rows)");
401
453
  }
402
454
 
403
455
  // size
404
456
  struct winsize winp;
405
- winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
406
- winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
457
+ winp.ws_col = info[0].As<Napi::Number>().Int32Value();
458
+ winp.ws_row = info[1].As<Napi::Number>().Int32Value();
407
459
  winp.ws_xpixel = 0;
408
460
  winp.ws_ypixel = 0;
409
461
 
@@ -412,98 +464,98 @@ NAN_METHOD(PtyOpen) {
412
464
  int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
413
465
 
414
466
  if (ret == -1) {
415
- return Nan::ThrowError("openpty(3) failed.");
467
+ throw Napi::Error::New(env, "openpty(3) failed.");
416
468
  }
417
469
 
418
470
  if (pty_nonblock(master) == -1) {
419
- return Nan::ThrowError("Could not set master fd to nonblocking.");
471
+ throw Napi::Error::New(env, "Could not set master fd to nonblocking.");
420
472
  }
421
473
 
422
474
  if (pty_nonblock(slave) == -1) {
423
- return Nan::ThrowError("Could not set slave fd to nonblocking.");
475
+ throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
424
476
  }
425
477
 
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());
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)));
436
482
 
437
- return info.GetReturnValue().Set(obj);
483
+ return obj;
438
484
  }
439
485
 
440
- NAN_METHOD(PtyResize) {
441
- Nan::HandleScope scope;
486
+ Napi::Value PtyResize(const Napi::CallbackInfo& info) {
487
+ Napi::Env env(info.Env());
488
+ Napi::HandleScope scope(env);
442
489
 
443
490
  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)");
491
+ !info[0].IsNumber() ||
492
+ !info[1].IsNumber() ||
493
+ !info[2].IsNumber()) {
494
+ throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)");
448
495
  }
449
496
 
450
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
497
+ int fd = info[0].As<Napi::Number>().Int32Value();
451
498
 
452
499
  struct winsize winp;
453
- winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
454
- winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust();
500
+ winp.ws_col = info[1].As<Napi::Number>().Int32Value();
501
+ winp.ws_row = info[2].As<Napi::Number>().Int32Value();
455
502
  winp.ws_xpixel = 0;
456
503
  winp.ws_ypixel = 0;
457
504
 
458
505
  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
459
506
  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");
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");
464
515
  }
465
- return Nan::ThrowError("ioctl(2) failed");
516
+ throw Napi::Error::New(env, "ioctl(2) failed");
466
517
  }
467
518
 
468
- return info.GetReturnValue().SetUndefined();
519
+ return env.Undefined();
469
520
  }
470
521
 
471
522
  /**
472
523
  * Foreground Process Name
473
524
  */
474
- NAN_METHOD(PtyGetProc) {
475
- Nan::HandleScope scope;
525
+ Napi::Value PtyGetProc(const Napi::CallbackInfo& info) {
526
+ Napi::Env env(info.Env());
527
+ Napi::HandleScope scope(env);
476
528
 
477
529
  #if defined(__APPLE__)
478
530
  if (info.Length() != 1 ||
479
- !info[0]->IsNumber()) {
480
- return Nan::ThrowError("Usage: pty.process(pid)");
531
+ !info[0].IsNumber()) {
532
+ throw Napi::Error::New(env, "Usage: pty.process(pid)");
481
533
  }
482
534
 
483
- int pid = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
484
- char *name = pty_getproc(pid);
535
+ int fd = info[0].As<Napi::Number>().Int32Value();
536
+ char *name = pty_getproc(fd);
485
537
  #else
486
538
  if (info.Length() != 2 ||
487
- !info[0]->IsNumber() ||
488
- !info[1]->IsString()) {
489
- return Nan::ThrowError("Usage: pty.process(fd, tty)");
539
+ !info[0].IsNumber() ||
540
+ !info[1].IsString()) {
541
+ throw Napi::Error::New(env, "Usage: pty.process(fd, tty)");
490
542
  }
491
543
 
492
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
544
+ int fd = info[0].As<Napi::Number>().Int32Value();
493
545
 
494
- Nan::Utf8String tty_(info[1]);
495
- char *tty = strdup(*tty_);
546
+ std::string tty_ = info[1].As<Napi::String>();
547
+ char *tty = strdup(tty_.c_str());
496
548
  char *name = pty_getproc(fd, tty);
497
549
  free(tty);
498
550
  #endif
499
551
 
500
552
  if (name == NULL) {
501
- return info.GetReturnValue().SetUndefined();
553
+ return env.Undefined();
502
554
  }
503
555
 
504
- v8::Local<v8::String> name_ = Nan::New<v8::String>(name).ToLocalChecked();
556
+ Napi::String name_ = Napi::String::New(env, name);
505
557
  free(name);
506
- return info.GetReturnValue().Set(name_);
558
+ return name_;
507
559
  }
508
560
 
509
561
  /**
@@ -517,114 +569,6 @@ pty_nonblock(int fd) {
517
569
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
518
570
  }
519
571
 
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
572
  /**
629
573
  * pty_getproc
630
574
  * Taken from tmux.
@@ -692,13 +636,25 @@ pty_getproc(int fd, char *tty) {
692
636
  #elif defined(__APPLE__)
693
637
 
694
638
  static char *
695
- pty_getproc(int pid) {
696
- char pname[MAXCOMLEN + 1];
697
- if (!proc_name(pid, pname, sizeof(pname))) {
639
+ pty_getproc(int fd) {
640
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
641
+ size_t size;
642
+ struct kinfo_proc kp;
643
+
644
+ if ((mib[3] = tcgetpgrp(fd)) == -1) {
645
+ return NULL;
646
+ }
647
+
648
+ size = sizeof kp;
649
+ if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) {
650
+ return NULL;
651
+ }
652
+
653
+ if (size != (sizeof kp) || *kp.kp_proc.p_comm == '\0') {
698
654
  return NULL;
699
655
  }
700
656
 
701
- return strdup(pname);
657
+ return strdup(kp.kp_proc.p_comm);
702
658
  }
703
659
 
704
660
  #else
@@ -815,12 +771,12 @@ done:
815
771
  * Init
816
772
  */
817
773
 
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);
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;
824
780
  }
825
781
 
826
- NODE_MODULE(pty, init)
782
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)