node-pty 1.1.0-beta5 → 1.1.0-beta7

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