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/README.md +5 -5
- package/binding.gyp +6 -15
- package/lib/unixTerminal.js +2 -1
- package/lib/unixTerminal.js.map +1 -1
- package/lib/unixTerminal.test.js +23 -0
- package/lib/unixTerminal.test.js.map +1 -1
- package/lib/windowsPtyAgent.js +6 -7
- package/lib/windowsPtyAgent.js.map +1 -1
- package/lib/windowsTerminal.test.js +98 -96
- package/lib/windowsTerminal.test.js.map +1 -1
- package/package.json +3 -2
- package/src/native.d.ts +4 -6
- package/src/unix/pty.cc +245 -289
- package/src/unixTerminal.test.ts +35 -0
- package/src/unixTerminal.ts +2 -1
- package/src/win/conpty.cc +172 -173
- package/src/win/conpty_console_list.cc +18 -17
- package/src/win/path_util.cc +30 -7
- package/src/win/path_util.h +6 -2
- package/src/win/winpty.cc +162 -141
- package/src/windowsPtyAgent.ts +6 -7
- package/src/windowsTerminal.test.ts +99 -97
package/src/win/path_util.cc
CHANGED
|
@@ -4,18 +4,41 @@
|
|
|
4
4
|
* Copyright (c) 2018, Microsoft Corporation (MIT License).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
#include <
|
|
7
|
+
#include <stdexcept>
|
|
8
8
|
#include <Shlwapi.h> // PathCombine
|
|
9
|
-
|
|
9
|
+
#include <Windows.h>
|
|
10
10
|
#include "path_util.h"
|
|
11
11
|
|
|
12
12
|
namespace path_util {
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
std::wstring to_wstring(const Napi::String& str) {
|
|
15
|
+
const std::u16string & u16 = str.Utf16Value();
|
|
16
|
+
return std::wstring(u16.begin(), u16.end());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
std::string wstring_to_string(const std::wstring &wide_string) {
|
|
20
|
+
if (wide_string.empty()) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
const auto size_needed = WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0), (int)wide_string.size(), nullptr, 0, nullptr, nullptr);
|
|
24
|
+
if (size_needed <= 0) {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
std::string result(size_needed, 0);
|
|
28
|
+
WideCharToMultiByte(CP_UTF8, 0, &wide_string.at(0), (int)wide_string.size(), &result.at(0), size_needed, nullptr, nullptr);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const char* from_wstring(const wchar_t* wstr) {
|
|
33
|
+
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
|
|
34
|
+
if (bufferSize <= 0) {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
char *output = new char[bufferSize];
|
|
38
|
+
int status = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, output, bufferSize, NULL, NULL);
|
|
39
|
+
if (status == 0) {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
19
42
|
return output;
|
|
20
43
|
}
|
|
21
44
|
|
package/src/win/path_util.h
CHANGED
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
#ifndef NODE_PTY_PATH_UTIL_H_
|
|
8
8
|
#define NODE_PTY_PATH_UTIL_H_
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
|
11
|
+
#include <napi.h>
|
|
12
|
+
#include <string>
|
|
11
13
|
|
|
12
14
|
#define MAX_ENV 65536
|
|
13
15
|
|
|
14
16
|
namespace path_util {
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
std::wstring to_wstring(const Napi::String& str);
|
|
19
|
+
std::string wstring_to_string(const std::wstring &wide_string);
|
|
20
|
+
const char* from_wstring(const wchar_t* wstr);
|
|
17
21
|
bool file_exists(std::wstring filename);
|
|
18
22
|
std::wstring get_shell_path(std::wstring filename);
|
|
19
23
|
|
package/src/win/winpty.cc
CHANGED
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* with pseudo-terminal file descriptors.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
#define NODE_ADDON_API_DISABLE_DEPRECATED
|
|
12
|
+
#include <napi.h>
|
|
11
13
|
#include <iostream>
|
|
12
|
-
#include <
|
|
14
|
+
#include <assert.h>
|
|
15
|
+
#include <map>
|
|
13
16
|
#include <Shlwapi.h> // PathCombine, PathIsRelative
|
|
14
17
|
#include <sstream>
|
|
15
18
|
#include <stdlib.h>
|
|
@@ -23,8 +26,6 @@
|
|
|
23
26
|
/**
|
|
24
27
|
* Misc
|
|
25
28
|
*/
|
|
26
|
-
extern "C" void init(v8::Local<v8::Object>);
|
|
27
|
-
|
|
28
29
|
#define WINPTY_DBG_VARIABLE TEXT("WINPTYDBG")
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -37,21 +38,25 @@ static volatile LONG ptyCounter;
|
|
|
37
38
|
* Helpers
|
|
38
39
|
*/
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
/** Keeps track of the handles created by PtyStartProcess */
|
|
42
|
+
static std::map<DWORD, HANDLE> createdHandles;
|
|
43
|
+
|
|
44
|
+
static winpty_t *get_pipe_handle(DWORD pid) {
|
|
41
45
|
for (size_t i = 0; i < ptyHandles.size(); ++i) {
|
|
42
46
|
winpty_t *ptyHandle = ptyHandles[i];
|
|
43
|
-
|
|
44
|
-
if (current ==
|
|
47
|
+
HANDLE current = winpty_agent_process(ptyHandle);
|
|
48
|
+
if (GetProcessId(current) == pid) {
|
|
45
49
|
return ptyHandle;
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
return nullptr;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
static bool remove_pipe_handle(
|
|
55
|
+
static bool remove_pipe_handle(DWORD pid) {
|
|
52
56
|
for (size_t i = 0; i < ptyHandles.size(); ++i) {
|
|
53
57
|
winpty_t *ptyHandle = ptyHandles[i];
|
|
54
|
-
|
|
58
|
+
HANDLE current = winpty_agent_process(ptyHandle);
|
|
59
|
+
if (GetProcessId(current) == pid) {
|
|
55
60
|
winpty_free(ptyHandle);
|
|
56
61
|
ptyHandles.erase(ptyHandles.begin() + i);
|
|
57
62
|
ptyHandle = nullptr;
|
|
@@ -61,113 +66,117 @@ static bool remove_pipe_handle(int handle) {
|
|
|
61
66
|
return false;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
std::
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
why
|
|
69
|
-
Nan::ThrowError(why.str().c_str());
|
|
69
|
+
Napi::Error error_with_winpty_msg(const char *generalMsg, winpty_error_ptr_t error_ptr, Napi::Env env) {
|
|
70
|
+
std::string why;
|
|
71
|
+
why += generalMsg;
|
|
72
|
+
why += ": ";
|
|
73
|
+
why += path_util::wstring_to_string(winpty_error_msg(error_ptr));
|
|
70
74
|
winpty_error_free(error_ptr);
|
|
75
|
+
return Napi::Error::New(env, why);
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
static
|
|
74
|
-
|
|
78
|
+
static Napi::Value PtyGetExitCode(const Napi::CallbackInfo& info) {
|
|
79
|
+
Napi::Env env(info.Env());
|
|
80
|
+
Napi::HandleScope scope(env);
|
|
75
81
|
|
|
76
82
|
if (info.Length() != 1 ||
|
|
77
|
-
!info[0]
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
!info[0].IsNumber()) {
|
|
84
|
+
throw Napi::Error::New(env, "Usage: pty.getExitCode(pid)");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
88
|
+
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
|
89
|
+
if (handle == NULL) {
|
|
90
|
+
return Napi::Number::New(env, -1);
|
|
80
91
|
}
|
|
81
92
|
|
|
82
93
|
DWORD exitCode = 0;
|
|
83
|
-
GetExitCodeProcess(
|
|
94
|
+
BOOL success = GetExitCodeProcess(handle, &exitCode);
|
|
95
|
+
if (success == FALSE) {
|
|
96
|
+
exitCode = -1;
|
|
97
|
+
}
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
CloseHandle(handle);
|
|
100
|
+
return Napi::Number::New(env, exitCode);
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
static
|
|
89
|
-
|
|
103
|
+
static Napi::Value PtyGetProcessList(const Napi::CallbackInfo& info) {
|
|
104
|
+
Napi::Env env(info.Env());
|
|
105
|
+
Napi::HandleScope scope(env);
|
|
90
106
|
|
|
91
107
|
if (info.Length() != 1 ||
|
|
92
|
-
!info[0]
|
|
93
|
-
|
|
94
|
-
return;
|
|
108
|
+
!info[0].IsNumber()) {
|
|
109
|
+
throw Napi::Error::New(env, "Usage: pty.getProcessList(pid)");
|
|
95
110
|
}
|
|
96
111
|
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
99
113
|
winpty_t *pc = get_pipe_handle(pid);
|
|
100
114
|
if (pc == nullptr) {
|
|
101
|
-
|
|
102
|
-
return;
|
|
115
|
+
return Napi::Number::New(env, 0);
|
|
103
116
|
}
|
|
104
117
|
int processList[64];
|
|
105
118
|
const int processCount = 64;
|
|
106
119
|
int actualCount = winpty_get_console_process_list(pc, processList, processCount, nullptr);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
if (actualCount <= 0) {
|
|
121
|
+
return Napi::Number::New(env, 0);
|
|
122
|
+
}
|
|
123
|
+
Napi::Array result = Napi::Array::New(env, actualCount);
|
|
124
|
+
for (int i = 0; i < actualCount; i++) {
|
|
125
|
+
result.Set(i, Napi::Number::New(env, processList[i]));
|
|
111
126
|
}
|
|
112
|
-
|
|
127
|
+
return result;
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
static
|
|
116
|
-
|
|
130
|
+
static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
|
|
131
|
+
Napi::Env env(info.Env());
|
|
132
|
+
Napi::HandleScope scope(env);
|
|
117
133
|
|
|
118
134
|
if (info.Length() != 7 ||
|
|
119
|
-
!info[0]
|
|
120
|
-
!info[1]
|
|
121
|
-
!info[2]
|
|
122
|
-
!info[3]
|
|
123
|
-
!info[4]
|
|
124
|
-
!info[5]
|
|
125
|
-
!info[6]
|
|
126
|
-
|
|
127
|
-
return;
|
|
135
|
+
!info[0].IsString() ||
|
|
136
|
+
!info[1].IsString() ||
|
|
137
|
+
!info[2].IsArray() ||
|
|
138
|
+
!info[3].IsString() ||
|
|
139
|
+
!info[4].IsNumber() ||
|
|
140
|
+
!info[5].IsNumber() ||
|
|
141
|
+
!info[6].IsBoolean()) {
|
|
142
|
+
throw Napi::Error::New(env, "Usage: pty.startProcess(file, cmdline, env, cwd, cols, rows, debug)");
|
|
128
143
|
}
|
|
129
144
|
|
|
130
|
-
std::
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const wchar_t *cmdline = path_util::to_wstring(Nan::Utf8String(info[1]));
|
|
134
|
-
const wchar_t *cwd = path_util::to_wstring(Nan::Utf8String(info[3]));
|
|
145
|
+
std::wstring filename(path_util::to_wstring(info[0].As<Napi::String>()));
|
|
146
|
+
std::wstring cmdline(path_util::to_wstring(info[1].As<Napi::String>()));
|
|
147
|
+
std::wstring cwd(path_util::to_wstring(info[3].As<Napi::String>()));
|
|
135
148
|
|
|
136
149
|
// create environment block
|
|
137
|
-
std::wstring
|
|
138
|
-
const
|
|
150
|
+
std::wstring envStr;
|
|
151
|
+
const Napi::Array envValues = info[2].As<Napi::Array>();
|
|
139
152
|
if (!envValues.IsEmpty()) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked())));
|
|
145
|
-
envBlock << envValue << L'\0';
|
|
153
|
+
std::wstring envBlock;
|
|
154
|
+
for(uint32_t i = 0; i < envValues.Length(); i++) {
|
|
155
|
+
envBlock += path_util::to_wstring(envValues.Get(i).As<Napi::String>());
|
|
156
|
+
envBlock += L'\0';
|
|
146
157
|
}
|
|
147
|
-
|
|
148
|
-
env = envBlock.str();
|
|
158
|
+
envStr = std::move(envBlock);
|
|
149
159
|
}
|
|
150
160
|
|
|
151
161
|
// use environment 'Path' variable to determine location of
|
|
152
162
|
// the relative path that we have recieved (e.g cmd.exe)
|
|
153
163
|
std::wstring shellpath;
|
|
154
|
-
if (::PathIsRelativeW(filename)) {
|
|
164
|
+
if (::PathIsRelativeW(filename.c_str())) {
|
|
155
165
|
shellpath = path_util::get_shell_path(filename);
|
|
156
166
|
} else {
|
|
157
167
|
shellpath = filename;
|
|
158
168
|
}
|
|
159
169
|
|
|
160
|
-
std::string shellpath_(shellpath.begin(), shellpath.end());
|
|
161
|
-
|
|
162
170
|
if (shellpath.empty() || !path_util::file_exists(shellpath)) {
|
|
163
|
-
why
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
std::string why;
|
|
172
|
+
why += "File not found: ";
|
|
173
|
+
why += path_util::wstring_to_string(shellpath);
|
|
174
|
+
throw Napi::Error::New(env, why);
|
|
166
175
|
}
|
|
167
176
|
|
|
168
|
-
int cols = info[4]
|
|
169
|
-
int rows = info[5]
|
|
170
|
-
bool debug =
|
|
177
|
+
int cols = info[4].As<Napi::Number>().Int32Value();
|
|
178
|
+
int rows = info[5].As<Napi::Number>().Int32Value();
|
|
179
|
+
bool debug = info[6].As<Napi::Boolean>().Value();
|
|
171
180
|
|
|
172
181
|
// Enable/disable debugging
|
|
173
182
|
SetEnvironmentVariable(WINPTY_DBG_VARIABLE, debug ? "1" : NULL); // NULL = deletes variable
|
|
@@ -176,8 +185,7 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
176
185
|
winpty_error_ptr_t error_ptr = nullptr;
|
|
177
186
|
winpty_config_t* winpty_config = winpty_config_new(0, &error_ptr);
|
|
178
187
|
if (winpty_config == nullptr) {
|
|
179
|
-
|
|
180
|
-
goto cleanup;
|
|
188
|
+
throw error_with_winpty_msg("Error creating WinPTY config", error_ptr, env);
|
|
181
189
|
}
|
|
182
190
|
winpty_error_free(error_ptr);
|
|
183
191
|
|
|
@@ -188,19 +196,15 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
188
196
|
winpty_t *pc = winpty_open(winpty_config, &error_ptr);
|
|
189
197
|
winpty_config_free(winpty_config);
|
|
190
198
|
if (pc == nullptr) {
|
|
191
|
-
|
|
192
|
-
goto cleanup;
|
|
199
|
+
throw error_with_winpty_msg("Error launching WinPTY agent", error_ptr, env);
|
|
193
200
|
}
|
|
194
201
|
winpty_error_free(error_ptr);
|
|
195
202
|
|
|
196
|
-
// Save pty struct for later use
|
|
197
|
-
ptyHandles.insert(ptyHandles.end(), pc);
|
|
198
|
-
|
|
199
203
|
// Create winpty spawn config
|
|
200
|
-
winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline, cwd,
|
|
204
|
+
winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline.c_str(), cwd.c_str(), envStr.c_str(), &error_ptr);
|
|
201
205
|
if (config == nullptr) {
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
winpty_free(pc);
|
|
207
|
+
throw error_with_winpty_msg("Error creating WinPTY spawn config", error_ptr, env);
|
|
204
208
|
}
|
|
205
209
|
winpty_error_free(error_ptr);
|
|
206
210
|
|
|
@@ -209,104 +213,121 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
209
213
|
BOOL spawnSuccess = winpty_spawn(pc, config, &handle, nullptr, nullptr, &error_ptr);
|
|
210
214
|
winpty_spawn_config_free(config);
|
|
211
215
|
if (!spawnSuccess) {
|
|
212
|
-
|
|
213
|
-
|
|
216
|
+
if (handle) {
|
|
217
|
+
CloseHandle(handle);
|
|
218
|
+
}
|
|
219
|
+
winpty_free(pc);
|
|
220
|
+
throw error_with_winpty_msg("Unable to start terminal process", error_ptr, env);
|
|
214
221
|
}
|
|
215
222
|
winpty_error_free(error_ptr);
|
|
216
223
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
Nan::Set(marshal, Nan::New<v8::String>("fd").ToLocalChecked(), Nan::New<v8::Number>(-1));
|
|
224
|
-
{
|
|
225
|
-
LPCWSTR coninPipeName = winpty_conin_name(pc);
|
|
226
|
-
std::wstring coninPipeNameWStr(coninPipeName);
|
|
227
|
-
std::string coninPipeNameStr(coninPipeNameWStr.begin(), coninPipeNameWStr.end());
|
|
228
|
-
Nan::Set(marshal, Nan::New<v8::String>("conin").ToLocalChecked(), Nan::New<v8::String>(coninPipeNameStr).ToLocalChecked());
|
|
229
|
-
LPCWSTR conoutPipeName = winpty_conout_name(pc);
|
|
230
|
-
std::wstring conoutPipeNameWStr(conoutPipeName);
|
|
231
|
-
std::string conoutPipeNameStr(conoutPipeNameWStr.begin(), conoutPipeNameWStr.end());
|
|
232
|
-
Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(conoutPipeNameStr).ToLocalChecked());
|
|
224
|
+
LPCWSTR coninPipeName = winpty_conin_name(pc);
|
|
225
|
+
std::string coninPipeNameStr(path_util::from_wstring(coninPipeName));
|
|
226
|
+
if (coninPipeNameStr.empty()) {
|
|
227
|
+
CloseHandle(handle);
|
|
228
|
+
winpty_free(pc);
|
|
229
|
+
throw Napi::Error::New(env, "Failed to initialize winpty conin");
|
|
233
230
|
}
|
|
234
|
-
info.GetReturnValue().Set(marshal);
|
|
235
231
|
|
|
236
|
-
|
|
232
|
+
LPCWSTR conoutPipeName = winpty_conout_name(pc);
|
|
233
|
+
std::string conoutPipeNameStr(path_util::from_wstring(conoutPipeName));
|
|
234
|
+
if (conoutPipeNameStr.empty()) {
|
|
235
|
+
CloseHandle(handle);
|
|
236
|
+
winpty_free(pc);
|
|
237
|
+
throw Napi::Error::New(env, "Failed to initialize winpty conout");
|
|
238
|
+
}
|
|
237
239
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
DWORD innerPid = GetProcessId(handle);
|
|
241
|
+
if (createdHandles[innerPid]) {
|
|
242
|
+
CloseHandle(handle);
|
|
243
|
+
winpty_free(pc);
|
|
244
|
+
std::stringstream why;
|
|
245
|
+
why << "There is already a process with innerPid " << innerPid;
|
|
246
|
+
throw Napi::Error::New(env, why.str());
|
|
247
|
+
}
|
|
248
|
+
createdHandles[innerPid] = handle;
|
|
249
|
+
|
|
250
|
+
// Save pty struct for later use
|
|
251
|
+
ptyHandles.push_back(pc);
|
|
252
|
+
|
|
253
|
+
DWORD pid = GetProcessId(winpty_agent_process(pc));
|
|
254
|
+
Napi::Object marshal = Napi::Object::New(env);
|
|
255
|
+
marshal.Set("innerPid", Napi::Number::New(env, (int)innerPid));
|
|
256
|
+
marshal.Set("pid", Napi::Number::New(env, (int)pid));
|
|
257
|
+
marshal.Set("pty", Napi::Number::New(env, InterlockedIncrement(&ptyCounter)));
|
|
258
|
+
marshal.Set("fd", Napi::Number::New(env, -1));
|
|
259
|
+
marshal.Set("conin", Napi::String::New(env, coninPipeNameStr));
|
|
260
|
+
marshal.Set("conout", Napi::String::New(env, conoutPipeNameStr));
|
|
261
|
+
|
|
262
|
+
return marshal;
|
|
242
263
|
}
|
|
243
264
|
|
|
244
|
-
static
|
|
245
|
-
|
|
265
|
+
static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
266
|
+
Napi::Env env(info.Env());
|
|
267
|
+
Napi::HandleScope scope(env);
|
|
246
268
|
|
|
247
269
|
if (info.Length() != 3 ||
|
|
248
|
-
!info[0]
|
|
249
|
-
!info[1]
|
|
250
|
-
!info[2]
|
|
251
|
-
|
|
252
|
-
return;
|
|
270
|
+
!info[0].IsNumber() ||
|
|
271
|
+
!info[1].IsNumber() ||
|
|
272
|
+
!info[2].IsNumber()) {
|
|
273
|
+
throw Napi::Error::New(env, "Usage: pty.resize(pid, cols, rows)");
|
|
253
274
|
}
|
|
254
275
|
|
|
255
|
-
|
|
256
|
-
int cols = info[1]
|
|
257
|
-
int rows = info[2]
|
|
276
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
277
|
+
int cols = info[1].As<Napi::Number>().Int32Value();
|
|
278
|
+
int rows = info[2].As<Napi::Number>().Int32Value();
|
|
258
279
|
|
|
259
|
-
winpty_t *pc = get_pipe_handle(
|
|
280
|
+
winpty_t *pc = get_pipe_handle(pid);
|
|
260
281
|
|
|
261
282
|
if (pc == nullptr) {
|
|
262
|
-
|
|
263
|
-
return;
|
|
283
|
+
throw Napi::Error::New(env, "The pty doesn't appear to exist");
|
|
264
284
|
}
|
|
265
285
|
BOOL success = winpty_set_size(pc, cols, rows, nullptr);
|
|
266
286
|
if (!success) {
|
|
267
|
-
|
|
268
|
-
return;
|
|
287
|
+
throw Napi::Error::New(env, "The pty could not be resized");
|
|
269
288
|
}
|
|
270
289
|
|
|
271
|
-
return
|
|
290
|
+
return env.Undefined();
|
|
272
291
|
}
|
|
273
292
|
|
|
274
|
-
static
|
|
275
|
-
|
|
293
|
+
static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
|
|
294
|
+
Napi::Env env(info.Env());
|
|
295
|
+
Napi::HandleScope scope(env);
|
|
276
296
|
|
|
277
297
|
if (info.Length() != 2 ||
|
|
278
|
-
!info[0]
|
|
279
|
-
!info[1]
|
|
280
|
-
|
|
281
|
-
return;
|
|
298
|
+
!info[0].IsNumber() ||
|
|
299
|
+
!info[1].IsNumber()) {
|
|
300
|
+
throw Napi::Error::New(env, "Usage: pty.kill(pid, innerPid)");
|
|
282
301
|
}
|
|
283
302
|
|
|
284
|
-
|
|
285
|
-
|
|
303
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
304
|
+
DWORD innerPid = info[1].As<Napi::Number>().Uint32Value();
|
|
286
305
|
|
|
287
|
-
winpty_t *pc = get_pipe_handle(
|
|
306
|
+
winpty_t *pc = get_pipe_handle(pid);
|
|
288
307
|
if (pc == nullptr) {
|
|
289
|
-
|
|
290
|
-
return;
|
|
308
|
+
throw Napi::Error::New(env, "Pty seems to have been killed already");
|
|
291
309
|
}
|
|
292
310
|
|
|
293
|
-
assert(remove_pipe_handle(
|
|
311
|
+
assert(remove_pipe_handle(pid));
|
|
312
|
+
|
|
313
|
+
HANDLE innerPidHandle = createdHandles[innerPid];
|
|
314
|
+
createdHandles.erase(innerPid);
|
|
294
315
|
CloseHandle(innerPidHandle);
|
|
295
316
|
|
|
296
|
-
return
|
|
317
|
+
return env.Undefined();
|
|
297
318
|
}
|
|
298
319
|
|
|
299
320
|
/**
|
|
300
321
|
* Init
|
|
301
322
|
*/
|
|
302
323
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
325
|
+
exports.Set("startProcess", Napi::Function::New(env, PtyStartProcess));
|
|
326
|
+
exports.Set("resize", Napi::Function::New(env, PtyResize));
|
|
327
|
+
exports.Set("kill", Napi::Function::New(env, PtyKill));
|
|
328
|
+
exports.Set("getExitCode", Napi::Function::New(env, PtyGetExitCode));
|
|
329
|
+
exports.Set("getProcessList", Napi::Function::New(env, PtyGetProcessList));
|
|
330
|
+
return exports;
|
|
310
331
|
};
|
|
311
332
|
|
|
312
|
-
|
|
333
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
|
package/src/windowsPtyAgent.ts
CHANGED
|
@@ -31,7 +31,6 @@ export class WindowsPtyAgent {
|
|
|
31
31
|
private _outSocket: Socket;
|
|
32
32
|
private _pid: number = 0;
|
|
33
33
|
private _innerPid: number = 0;
|
|
34
|
-
private _innerPidHandle: number = 0;
|
|
35
34
|
private _closeTimeout: NodeJS.Timer | undefined;
|
|
36
35
|
private _exitCode: number | undefined;
|
|
37
36
|
private _conoutSocketWorker: ConoutConnection;
|
|
@@ -105,7 +104,6 @@ export class WindowsPtyAgent {
|
|
|
105
104
|
term = (this._ptyNative as IWinptyNative).startProcess(file, commandLine, env, cwd, cols, rows, debug);
|
|
106
105
|
this._pid = (term as IWinptyProcess).pid;
|
|
107
106
|
this._innerPid = (term as IWinptyProcess).innerPid;
|
|
108
|
-
this._innerPidHandle = (term as IWinptyProcess).innerPidHandle;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
// Not available on windows.
|
|
@@ -174,14 +172,14 @@ export class WindowsPtyAgent {
|
|
|
174
172
|
(this._ptyNative as IConptyNative).kill(this._pty);
|
|
175
173
|
});
|
|
176
174
|
} else {
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// dropped, we want to immediately kill the entire console process list.
|
|
175
|
+
// Because pty.kill closes the handle, it will kill most processes by itself.
|
|
176
|
+
// Process IDs can be reused as soon as all handles to them are
|
|
177
|
+
// dropped, so we want to immediately kill the entire console process list.
|
|
181
178
|
// If we do not force kill all processes here, node servers in particular
|
|
182
179
|
// seem to become detached and remain running (see
|
|
183
180
|
// Microsoft/vscode#26807).
|
|
184
181
|
const processList: number[] = (this._ptyNative as IWinptyNative).getProcessList(this._pid);
|
|
182
|
+
(this._ptyNative as IWinptyNative).kill(this._pid, this._innerPid);
|
|
185
183
|
processList.forEach(pid => {
|
|
186
184
|
try {
|
|
187
185
|
process.kill(pid);
|
|
@@ -212,7 +210,8 @@ export class WindowsPtyAgent {
|
|
|
212
210
|
if (this._useConpty) {
|
|
213
211
|
return this._exitCode;
|
|
214
212
|
}
|
|
215
|
-
|
|
213
|
+
const winptyExitCode = (this._ptyNative as IWinptyNative).getExitCode(this._innerPid);
|
|
214
|
+
return winptyExitCode === -1 ? undefined : winptyExitCode;
|
|
216
215
|
}
|
|
217
216
|
|
|
218
217
|
private _getWindowsBuildNumber(): number {
|