node-pty 1.2.0-beta.13 → 1.2.0-beta.14

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.
@@ -12,6 +12,7 @@ var path = require("path");
12
12
  var child_process_1 = require("child_process");
13
13
  var net_1 = require("net");
14
14
  var windowsConoutConnection_1 = require("./windowsConoutConnection");
15
+ var eventEmitter2_1 = require("./eventEmitter2");
15
16
  var utils_1 = require("./utils");
16
17
  var conptyNative;
17
18
  /**
@@ -30,6 +31,7 @@ var WindowsPtyAgent = /** @class */ (function () {
30
31
  var _this = this;
31
32
  this._useConptyDll = _useConptyDll;
32
33
  this._innerPid = 0;
34
+ this._onError = new eventEmitter2_1.EventEmitter2();
33
35
  if (!conptyNative) {
34
36
  conptyNative = (0, utils_1.loadNativeModule)('conpty').module;
35
37
  }
@@ -81,6 +83,11 @@ var WindowsPtyAgent = /** @class */ (function () {
81
83
  });
82
84
  this._inSocket.setEncoding('utf8');
83
85
  }
86
+ Object.defineProperty(WindowsPtyAgent.prototype, "onError", {
87
+ get: function () { return this._onError.event; },
88
+ enumerable: false,
89
+ configurable: true
90
+ });
84
91
  Object.defineProperty(WindowsPtyAgent.prototype, "inSocket", {
85
92
  get: function () { return this._inSocket; },
86
93
  enumerable: false,
@@ -108,13 +115,31 @@ var WindowsPtyAgent = /** @class */ (function () {
108
115
  });
109
116
  WindowsPtyAgent.prototype._completePtyConnection = function () {
110
117
  var _this = this;
118
+ var _a;
111
119
  if (!this._pendingPtyInfo) {
112
120
  return;
113
121
  }
114
- var _a = this._pendingPtyInfo, pty = _a.pty, commandLine = _a.commandLine, cwd = _a.cwd, env = _a.env;
122
+ var _b = this._pendingPtyInfo, pty = _b.pty, commandLine = _b.commandLine, cwd = _b.cwd, env = _b.env;
115
123
  this._pendingPtyInfo = undefined;
116
- var connect = conptyNative.connect(pty, commandLine, cwd, env, this._useConptyDll, function (c) { return _this._$onProcessExit(c); });
117
- this._innerPid = connect.pid;
124
+ try {
125
+ var connect = conptyNative.connect(pty, commandLine, cwd, env, this._useConptyDll, function (c) { return _this._$onProcessExit(c); });
126
+ this._innerPid = connect.pid;
127
+ }
128
+ catch (err) {
129
+ // connect() runs from the conout worker's onReady callback (or its
130
+ // timeout fallback), so a throw here would otherwise surface as an
131
+ // uncaughtException with no way for the consumer to observe it.
132
+ var code = (_a = /error code: (\d+)/.exec(err.message)) === null || _a === void 0 ? void 0 : _a[1];
133
+ this._exitCode = code ? parseInt(code, 10) : -1;
134
+ try {
135
+ this._ptyNative.kill(this._pty, this._useConptyDll);
136
+ }
137
+ catch ( /* already gone */_c) { /* already gone */ }
138
+ this._conoutSocketWorker.dispose();
139
+ this._inSocket.destroy();
140
+ this._outSocket.destroy();
141
+ this._onError.fire(err);
142
+ }
118
143
  };
119
144
  WindowsPtyAgent.prototype.resize = function (cols, rows) {
120
145
  if (this._exitCode !== undefined) {
@@ -52,6 +52,12 @@ var WindowsTerminal = /** @class */ (function (_super) {
52
52
  // Create new termal.
53
53
  _this._agent = new windowsPtyAgent_1.WindowsPtyAgent(file, args, parsedEnv, cwd, _this._cols, _this._rows, false, opt.useConptyDll, opt.conptyInheritCursor);
54
54
  _this._socket = _this._agent.outSocket;
55
+ // The socket 'close' handler that drives 'exit' is only attached after
56
+ // ready_datapipe, which never fires when connect() fails.
57
+ _this._agent.onError(function () {
58
+ _this._close();
59
+ _this.emit('exit', _this._agent.exitCode);
60
+ });
55
61
  // Not available until `ready` event emitted.
56
62
  _this._pid = _this._agent.innerPid;
57
63
  _this._fd = _this._agent.fd;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "author": {
5
5
  "name": "Microsoft Corporation"
6
6
  },
7
- "version": "1.2.0-beta.13",
7
+ "version": "1.2.0-beta.14",
8
8
  "license": "MIT",
9
9
  "main": "./lib/index.js",
10
10
  "types": "./typings/node-pty.d.ts",
@@ -56,7 +56,8 @@
56
56
  "@typescript-eslint/eslint-plugin": "^8.0.0",
57
57
  "@typescript-eslint/parser": "^8.0.0",
58
58
  "cross-env": "^5.1.4",
59
- "eslint": "^9.0.0",
59
+ "eslint": "^10.4.0",
60
+ "globals": "^14.0.0",
60
61
  "mocha": "10",
61
62
  "node-gyp": "^11.4.2",
62
63
  "ps-list": "^6.0.0",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/unix/pty.cc CHANGED
@@ -203,6 +203,7 @@ void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) {
203
203
  }
204
204
  }
205
205
  }
206
+ close(kq);
206
207
  #else
207
208
  while (true) {
208
209
  errno = 0;
package/src/win/conpty.cc CHANGED
@@ -46,7 +46,7 @@ struct pty_baton {
46
46
  HANDLE hOut;
47
47
  HPCON hpc;
48
48
 
49
- HANDLE hShell;
49
+ HANDLE hShell = nullptr;
50
50
 
51
51
  pty_baton(int _id, HANDLE _hIn, HANDLE _hOut, HPCON _hpc) : id(_id), hIn(_hIn), hOut(_hOut), hpc(_hpc) {};
52
52
  };
@@ -384,6 +384,14 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
384
384
  ConnectNamedPipe(handle->hIn, nullptr);
385
385
  ConnectNamedPipe(handle->hOut, nullptr);
386
386
 
387
+ // Close hIn/hOut on every exit so a throw doesn't leak the named-pipe server handles.
388
+ auto closePipeHandles = [handle]() {
389
+ CloseHandle(handle->hIn);
390
+ CloseHandle(handle->hOut);
391
+ handle->hIn = nullptr;
392
+ handle->hOut = nullptr;
393
+ };
394
+
387
395
  // Attach the pseudoconsole to the client application we're creating
388
396
  STARTUPINFOEXW siEx{0};
389
397
  siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
@@ -394,12 +402,14 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
394
402
 
395
403
  SIZE_T size = 0;
396
404
  InitializeProcThreadAttributeList(NULL, 1, 0, &size);
397
- BYTE *attrList = new BYTE[size];
398
- siEx.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(attrList);
405
+ std::unique_ptr<BYTE[]> attrList = std::make_unique<BYTE[]>(size);
406
+ siEx.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(attrList.get());
399
407
 
400
408
  fSuccess = InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &size);
401
409
  if (!fSuccess) {
402
- throw errorWithCode(info, "InitializeProcThreadAttributeList failed");
410
+ Napi::Error error = errorWithCode(info, "InitializeProcThreadAttributeList failed");
411
+ closePipeHandles();
412
+ throw error;
403
413
  }
404
414
  fSuccess = UpdateProcThreadAttribute(siEx.lpAttributeList,
405
415
  0,
@@ -409,7 +419,10 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
409
419
  NULL,
410
420
  NULL);
411
421
  if (!fSuccess) {
412
- throw errorWithCode(info, "UpdateProcThreadAttribute failed");
422
+ Napi::Error error = errorWithCode(info, "UpdateProcThreadAttribute failed");
423
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
424
+ closePipeHandles();
425
+ throw error;
413
426
  }
414
427
 
415
428
  PROCESS_INFORMATION piClient{};
@@ -426,9 +439,14 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
426
439
  &piClient // lpProcessInformation
427
440
  );
428
441
  if (!fSuccess) {
429
- throw errorWithCode(info, "Cannot create process");
442
+ Napi::Error error = errorWithCode(info, "Cannot create process");
443
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
444
+ closePipeHandles();
445
+ throw error;
430
446
  }
431
447
 
448
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
449
+
432
450
  HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
433
451
  bool fLoadedDll = hLibrary != nullptr;
434
452
  if (useConptyDll && fLoadedDll)
@@ -447,8 +465,7 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
447
465
  // Close the thread handle to avoid resource leak
448
466
  CloseHandle(piClient.hThread);
449
467
  // Close the input read and output write handle of the pseudoconsole
450
- CloseHandle(handle->hIn);
451
- CloseHandle(handle->hOut);
468
+ closePipeHandles();
452
469
 
453
470
  SetupExitCallback(env, exitCallback, handle);
454
471
 
@@ -550,7 +567,7 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
550
567
  const bool useConptyDll = info[1].As<Napi::Boolean>().Value();
551
568
 
552
569
  std::lock_guard<std::mutex> lock(g_ptyHandlesMutex);
553
- const pty_baton* handle = get_pty_baton(lock, id);
570
+ pty_baton* handle = get_pty_baton(lock, id);
554
571
 
555
572
  if (handle != nullptr) {
556
573
  HANDLE hLibrary = LoadConptyDll(info, useConptyDll);
@@ -565,7 +582,17 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
565
582
  pfnClosePseudoConsole(handle->hpc);
566
583
  }
567
584
  }
568
- if (useConptyDll) {
585
+ // Defensive: if PtyConnect was never called (or failed before closing the
586
+ // pipe handles), release the named pipe server handles here.
587
+ if (handle->hIn != nullptr) {
588
+ CloseHandle(handle->hIn);
589
+ handle->hIn = nullptr;
590
+ }
591
+ if (handle->hOut != nullptr) {
592
+ CloseHandle(handle->hOut);
593
+ handle->hOut = nullptr;
594
+ }
595
+ if (useConptyDll && handle->hShell != nullptr) {
569
596
  TerminateProcess(handle->hShell, 1);
570
597
  }
571
598
  }