node-pty 1.1.0-beta5 → 1.1.0-beta6
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/binding.gyp +4 -15
- package/package.json +2 -2
- package/src/unix/pty.cc +227 -284
- package/src/win/conpty.cc +156 -161
- package/src/win/conpty_console_list.cc +16 -15
- package/src/win/path_util.cc +16 -12
- package/src/win/path_util.h +5 -2
- package/src/win/winpty.cc +108 -151
package/src/win/winpty.cc
CHANGED
|
@@ -8,9 +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>
|
|
14
|
+
#include <assert.h>
|
|
12
15
|
#include <map>
|
|
13
|
-
#include <nan.h>
|
|
14
16
|
#include <Shlwapi.h> // PathCombine, PathIsRelative
|
|
15
17
|
#include <sstream>
|
|
16
18
|
#include <stdlib.h>
|
|
@@ -24,8 +26,6 @@
|
|
|
24
26
|
/**
|
|
25
27
|
* Misc
|
|
26
28
|
*/
|
|
27
|
-
extern "C" void init(v8::Local<v8::Object>);
|
|
28
|
-
|
|
29
29
|
#define WINPTY_DBG_VARIABLE TEXT("WINPTYDBG")
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -66,28 +66,28 @@ static bool remove_pipe_handle(DWORD pid) {
|
|
|
66
66
|
return false;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
std::
|
|
71
|
-
|
|
72
|
-
why
|
|
73
|
-
|
|
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));
|
|
74
74
|
winpty_error_free(error_ptr);
|
|
75
|
+
return Napi::Error::New(env, why);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
static
|
|
78
|
-
|
|
78
|
+
static Napi::Value PtyGetExitCode(const Napi::CallbackInfo& info) {
|
|
79
|
+
Napi::Env env(info.Env());
|
|
80
|
+
Napi::HandleScope scope(env);
|
|
79
81
|
|
|
80
82
|
if (info.Length() != 1 ||
|
|
81
|
-
!info[0]
|
|
82
|
-
|
|
83
|
-
return;
|
|
83
|
+
!info[0].IsNumber()) {
|
|
84
|
+
throw Napi::Error::New(env, "Usage: pty.getExitCode(pid)");
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
DWORD pid = info[0]
|
|
87
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
87
88
|
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
|
88
89
|
if (handle == NULL) {
|
|
89
|
-
|
|
90
|
-
return;
|
|
90
|
+
return Napi::Number::New(env, -1);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
DWORD exitCode = 0;
|
|
@@ -97,95 +97,86 @@ static NAN_METHOD(PtyGetExitCode) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
CloseHandle(handle);
|
|
100
|
-
|
|
100
|
+
return Napi::Number::New(env, exitCode);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
static
|
|
104
|
-
|
|
103
|
+
static Napi::Value PtyGetProcessList(const Napi::CallbackInfo& info) {
|
|
104
|
+
Napi::Env env(info.Env());
|
|
105
|
+
Napi::HandleScope scope(env);
|
|
105
106
|
|
|
106
107
|
if (info.Length() != 1 ||
|
|
107
|
-
!info[0]
|
|
108
|
-
|
|
109
|
-
return;
|
|
108
|
+
!info[0].IsNumber()) {
|
|
109
|
+
throw Napi::Error::New(env, "Usage: pty.getProcessList(pid)");
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
DWORD pid = info[0]
|
|
112
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
113
113
|
winpty_t *pc = get_pipe_handle(pid);
|
|
114
114
|
if (pc == nullptr) {
|
|
115
|
-
|
|
116
|
-
return;
|
|
115
|
+
return Napi::Number::New(env, 0);
|
|
117
116
|
}
|
|
118
117
|
int processList[64];
|
|
119
118
|
const int processCount = 64;
|
|
120
119
|
int actualCount = winpty_get_console_process_list(pc, processList, processCount, nullptr);
|
|
121
120
|
if (actualCount <= 0) {
|
|
122
|
-
|
|
123
|
-
return;
|
|
121
|
+
return Napi::Number::New(env, 0);
|
|
124
122
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Nan::Set(result, i, Nan::New<v8::Number>(processList[i]));
|
|
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]));
|
|
129
126
|
}
|
|
130
|
-
|
|
127
|
+
return result;
|
|
131
128
|
}
|
|
132
129
|
|
|
133
|
-
static
|
|
134
|
-
|
|
130
|
+
static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
|
|
131
|
+
Napi::Env env(info.Env());
|
|
132
|
+
Napi::HandleScope scope(env);
|
|
135
133
|
|
|
136
134
|
if (info.Length() != 7 ||
|
|
137
|
-
!info[0]
|
|
138
|
-
!info[1]
|
|
139
|
-
!info[2]
|
|
140
|
-
!info[3]
|
|
141
|
-
!info[4]
|
|
142
|
-
!info[5]
|
|
143
|
-
!info[6]
|
|
144
|
-
|
|
145
|
-
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)");
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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>()));
|
|
151
148
|
|
|
152
149
|
// create environment block
|
|
153
|
-
std::wstring
|
|
154
|
-
const
|
|
150
|
+
std::wstring envStr;
|
|
151
|
+
const Napi::Array envValues = info[2].As<Napi::Array>();
|
|
155
152
|
if (!envValues.IsEmpty()) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked())));
|
|
161
|
-
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';
|
|
162
157
|
}
|
|
163
|
-
|
|
164
|
-
env = envBlock.str();
|
|
158
|
+
envStr = std::move(envBlock);
|
|
165
159
|
}
|
|
166
160
|
|
|
167
161
|
// use environment 'Path' variable to determine location of
|
|
168
162
|
// the relative path that we have recieved (e.g cmd.exe)
|
|
169
163
|
std::wstring shellpath;
|
|
170
|
-
if (::PathIsRelativeW(filename)) {
|
|
164
|
+
if (::PathIsRelativeW(filename.c_str())) {
|
|
171
165
|
shellpath = path_util::get_shell_path(filename);
|
|
172
166
|
} else {
|
|
173
167
|
shellpath = filename;
|
|
174
168
|
}
|
|
175
169
|
|
|
176
170
|
if (shellpath.empty() || !path_util::file_exists(shellpath)) {
|
|
177
|
-
std::
|
|
178
|
-
why
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
delete cmdline;
|
|
182
|
-
delete cwd;
|
|
183
|
-
return;
|
|
171
|
+
std::string why;
|
|
172
|
+
why += "File not found: ";
|
|
173
|
+
why += path_util::wstring_to_string(shellpath);
|
|
174
|
+
throw Napi::Error::New(env, why);
|
|
184
175
|
}
|
|
185
176
|
|
|
186
|
-
int cols = info[4]
|
|
187
|
-
int rows = info[5]
|
|
188
|
-
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();
|
|
189
180
|
|
|
190
181
|
// Enable/disable debugging
|
|
191
182
|
SetEnvironmentVariable(WINPTY_DBG_VARIABLE, debug ? "1" : NULL); // NULL = deletes variable
|
|
@@ -194,11 +185,7 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
194
185
|
winpty_error_ptr_t error_ptr = nullptr;
|
|
195
186
|
winpty_config_t* winpty_config = winpty_config_new(0, &error_ptr);
|
|
196
187
|
if (winpty_config == nullptr) {
|
|
197
|
-
|
|
198
|
-
delete filename;
|
|
199
|
-
delete cmdline;
|
|
200
|
-
delete cwd;
|
|
201
|
-
return;
|
|
188
|
+
throw error_with_winpty_msg("Error creating WinPTY config", error_ptr, env);
|
|
202
189
|
}
|
|
203
190
|
winpty_error_free(error_ptr);
|
|
204
191
|
|
|
@@ -209,23 +196,15 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
209
196
|
winpty_t *pc = winpty_open(winpty_config, &error_ptr);
|
|
210
197
|
winpty_config_free(winpty_config);
|
|
211
198
|
if (pc == nullptr) {
|
|
212
|
-
|
|
213
|
-
delete filename;
|
|
214
|
-
delete cmdline;
|
|
215
|
-
delete cwd;
|
|
216
|
-
return;
|
|
199
|
+
throw error_with_winpty_msg("Error launching WinPTY agent", error_ptr, env);
|
|
217
200
|
}
|
|
218
201
|
winpty_error_free(error_ptr);
|
|
219
202
|
|
|
220
203
|
// Create winpty spawn config
|
|
221
|
-
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);
|
|
222
205
|
if (config == nullptr) {
|
|
223
|
-
throw_winpty_error("Error creating WinPTY spawn config", error_ptr);
|
|
224
206
|
winpty_free(pc);
|
|
225
|
-
|
|
226
|
-
delete cmdline;
|
|
227
|
-
delete cwd;
|
|
228
|
-
return;
|
|
207
|
+
throw error_with_winpty_msg("Error creating WinPTY spawn config", error_ptr, env);
|
|
229
208
|
}
|
|
230
209
|
winpty_error_free(error_ptr);
|
|
231
210
|
|
|
@@ -234,121 +213,99 @@ static NAN_METHOD(PtyStartProcess) {
|
|
|
234
213
|
BOOL spawnSuccess = winpty_spawn(pc, config, &handle, nullptr, nullptr, &error_ptr);
|
|
235
214
|
winpty_spawn_config_free(config);
|
|
236
215
|
if (!spawnSuccess) {
|
|
237
|
-
throw_winpty_error("Unable to start terminal process", error_ptr);
|
|
238
216
|
if (handle) {
|
|
239
217
|
CloseHandle(handle);
|
|
240
218
|
}
|
|
241
219
|
winpty_free(pc);
|
|
242
|
-
|
|
243
|
-
delete cmdline;
|
|
244
|
-
delete cwd;
|
|
245
|
-
return;
|
|
220
|
+
throw error_with_winpty_msg("Unable to start terminal process", error_ptr, env);
|
|
246
221
|
}
|
|
247
222
|
winpty_error_free(error_ptr);
|
|
248
223
|
|
|
249
224
|
LPCWSTR coninPipeName = winpty_conin_name(pc);
|
|
250
225
|
std::string coninPipeNameStr(path_util::from_wstring(coninPipeName));
|
|
251
226
|
if (coninPipeNameStr.empty()) {
|
|
252
|
-
Nan::ThrowError("Failed to initialize winpty conin");
|
|
253
227
|
CloseHandle(handle);
|
|
254
228
|
winpty_free(pc);
|
|
255
|
-
|
|
256
|
-
delete cmdline;
|
|
257
|
-
delete cwd;
|
|
258
|
-
return;
|
|
229
|
+
throw Napi::Error::New(env, "Failed to initialize winpty conin");
|
|
259
230
|
}
|
|
260
231
|
|
|
261
232
|
LPCWSTR conoutPipeName = winpty_conout_name(pc);
|
|
262
233
|
std::string conoutPipeNameStr(path_util::from_wstring(conoutPipeName));
|
|
263
234
|
if (conoutPipeNameStr.empty()) {
|
|
264
|
-
Nan::ThrowError("Failed to initialize winpty conout");
|
|
265
235
|
CloseHandle(handle);
|
|
266
236
|
winpty_free(pc);
|
|
267
|
-
|
|
268
|
-
delete cmdline;
|
|
269
|
-
delete cwd;
|
|
270
|
-
return;
|
|
237
|
+
throw Napi::Error::New(env, "Failed to initialize winpty conout");
|
|
271
238
|
}
|
|
272
239
|
|
|
273
240
|
DWORD innerPid = GetProcessId(handle);
|
|
274
241
|
if (createdHandles[innerPid]) {
|
|
275
|
-
std::stringstream why;
|
|
276
|
-
why << "There is already a process with innerPid " << innerPid;
|
|
277
|
-
Nan::ThrowError(why.str().c_str());
|
|
278
242
|
CloseHandle(handle);
|
|
279
243
|
winpty_free(pc);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return;
|
|
244
|
+
std::stringstream why;
|
|
245
|
+
why << "There is already a process with innerPid " << innerPid;
|
|
246
|
+
throw Napi::Error::New(env, why.str());
|
|
284
247
|
}
|
|
285
248
|
createdHandles[innerPid] = handle;
|
|
286
249
|
|
|
287
250
|
// Save pty struct for later use
|
|
288
251
|
ptyHandles.push_back(pc);
|
|
289
252
|
|
|
290
|
-
v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
|
|
291
253
|
DWORD pid = GetProcessId(winpty_agent_process(pc));
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
delete cmdline;
|
|
302
|
-
delete cwd;
|
|
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;
|
|
303
263
|
}
|
|
304
264
|
|
|
305
|
-
static
|
|
306
|
-
|
|
265
|
+
static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
|
|
266
|
+
Napi::Env env(info.Env());
|
|
267
|
+
Napi::HandleScope scope(env);
|
|
307
268
|
|
|
308
269
|
if (info.Length() != 3 ||
|
|
309
|
-
!info[0]
|
|
310
|
-
!info[1]
|
|
311
|
-
!info[2]
|
|
312
|
-
|
|
313
|
-
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)");
|
|
314
274
|
}
|
|
315
275
|
|
|
316
|
-
DWORD pid = info[0]
|
|
317
|
-
int cols = info[1]
|
|
318
|
-
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();
|
|
319
279
|
|
|
320
280
|
winpty_t *pc = get_pipe_handle(pid);
|
|
321
281
|
|
|
322
282
|
if (pc == nullptr) {
|
|
323
|
-
|
|
324
|
-
return;
|
|
283
|
+
throw Napi::Error::New(env, "The pty doesn't appear to exist");
|
|
325
284
|
}
|
|
326
285
|
BOOL success = winpty_set_size(pc, cols, rows, nullptr);
|
|
327
286
|
if (!success) {
|
|
328
|
-
|
|
329
|
-
return;
|
|
287
|
+
throw Napi::Error::New(env, "The pty could not be resized");
|
|
330
288
|
}
|
|
331
289
|
|
|
332
|
-
return
|
|
290
|
+
return env.Undefined();
|
|
333
291
|
}
|
|
334
292
|
|
|
335
|
-
static
|
|
336
|
-
|
|
293
|
+
static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
|
|
294
|
+
Napi::Env env(info.Env());
|
|
295
|
+
Napi::HandleScope scope(env);
|
|
337
296
|
|
|
338
297
|
if (info.Length() != 2 ||
|
|
339
|
-
!info[0]
|
|
340
|
-
!info[1]
|
|
341
|
-
|
|
342
|
-
return;
|
|
298
|
+
!info[0].IsNumber() ||
|
|
299
|
+
!info[1].IsNumber()) {
|
|
300
|
+
throw Napi::Error::New(env, "Usage: pty.kill(pid, innerPid)");
|
|
343
301
|
}
|
|
344
302
|
|
|
345
|
-
DWORD pid = info[0]
|
|
346
|
-
DWORD innerPid = info[1]
|
|
303
|
+
DWORD pid = info[0].As<Napi::Number>().Uint32Value();
|
|
304
|
+
DWORD innerPid = info[1].As<Napi::Number>().Uint32Value();
|
|
347
305
|
|
|
348
306
|
winpty_t *pc = get_pipe_handle(pid);
|
|
349
307
|
if (pc == nullptr) {
|
|
350
|
-
|
|
351
|
-
return;
|
|
308
|
+
throw Napi::Error::New(env, "Pty seems to have been killed already");
|
|
352
309
|
}
|
|
353
310
|
|
|
354
311
|
assert(remove_pipe_handle(pid));
|
|
@@ -357,20 +314,20 @@ static NAN_METHOD(PtyKill) {
|
|
|
357
314
|
createdHandles.erase(innerPid);
|
|
358
315
|
CloseHandle(innerPidHandle);
|
|
359
316
|
|
|
360
|
-
return
|
|
317
|
+
return env.Undefined();
|
|
361
318
|
}
|
|
362
319
|
|
|
363
320
|
/**
|
|
364
321
|
* Init
|
|
365
322
|
*/
|
|
366
323
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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;
|
|
374
331
|
};
|
|
375
332
|
|
|
376
|
-
|
|
333
|
+
NODE_API_MODULE(NODE_GYP_MODULE_NAME, init);
|