X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fnode-pty%2Fsrc%2Fwin%2Fwinpty.cc;fp=node_modules%2Fnode-pty%2Fsrc%2Fwin%2Fwinpty.cc;h=b054dee2194226a823f5c98befdecf06cda2e5dd;hp=0000000000000000000000000000000000000000;hb=e79e4a5a87f3e84f7c1777f10a954453a69bf540;hpb=4339da12467b75fb8b6ca831f4bf0081c485ed2c diff --git a/node_modules/node-pty/src/win/winpty.cc b/node_modules/node-pty/src/win/winpty.cc new file mode 100644 index 0000000..b054dee --- /dev/null +++ b/node_modules/node-pty/src/win/winpty.cc @@ -0,0 +1,312 @@ +/** + * Copyright (c) 2013-2015, Christopher Jeffrey, Peter Sunde (MIT License) + * Copyright (c) 2016, Daniel Imms (MIT License). + * Copyright (c) 2018, Microsoft Corporation (MIT License). + * + * pty.cc: + * This file is responsible for starting processes + * with pseudo-terminal file descriptors. + */ + +#include +#include +#include // PathCombine, PathIsRelative +#include +#include +#include +#include +#include +#include + +#include "path_util.h" + +/** +* Misc +*/ +extern "C" void init(v8::Local); + +#define WINPTY_DBG_VARIABLE TEXT("WINPTYDBG") + +/** +* winpty +*/ +static std::vector ptyHandles; +static volatile LONG ptyCounter; + +/** +* Helpers +*/ + +static winpty_t *get_pipe_handle(int handle) { + for (size_t i = 0; i < ptyHandles.size(); ++i) { + winpty_t *ptyHandle = ptyHandles[i]; + int current = (int)winpty_agent_process(ptyHandle); + if (current == handle) { + return ptyHandle; + } + } + return nullptr; +} + +static bool remove_pipe_handle(int handle) { + for (size_t i = 0; i < ptyHandles.size(); ++i) { + winpty_t *ptyHandle = ptyHandles[i]; + if ((int)winpty_agent_process(ptyHandle) == handle) { + winpty_free(ptyHandle); + ptyHandles.erase(ptyHandles.begin() + i); + ptyHandle = nullptr; + return true; + } + } + return false; +} + +void throw_winpty_error(const char *generalMsg, winpty_error_ptr_t error_ptr) { + std::stringstream why; + std::wstring msg(winpty_error_msg(error_ptr)); + std::string msg_(msg.begin(), msg.end()); + why << generalMsg << ": " << msg_; + Nan::ThrowError(why.str().c_str()); + winpty_error_free(error_ptr); +} + +static NAN_METHOD(PtyGetExitCode) { + Nan::HandleScope scope; + + if (info.Length() != 1 || + !info[0]->IsNumber()) { + Nan::ThrowError("Usage: pty.getExitCode(pidHandle)"); + return; + } + + DWORD exitCode = 0; + GetExitCodeProcess((HANDLE)info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(), &exitCode); + + info.GetReturnValue().Set(Nan::New(exitCode)); +} + +static NAN_METHOD(PtyGetProcessList) { + Nan::HandleScope scope; + + if (info.Length() != 1 || + !info[0]->IsNumber()) { + Nan::ThrowError("Usage: pty.getProcessList(pid)"); + return; + } + + int pid = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); + + winpty_t *pc = get_pipe_handle(pid); + if (pc == nullptr) { + info.GetReturnValue().Set(Nan::New(0)); + return; + } + int processList[64]; + const int processCount = 64; + int actualCount = winpty_get_console_process_list(pc, processList, processCount, nullptr); + + v8::Local result = Nan::New(actualCount); + for (uint32_t i = 0; i < actualCount; i++) { + Nan::Set(result, i, Nan::New(processList[i])); + } + info.GetReturnValue().Set(result); +} + +static NAN_METHOD(PtyStartProcess) { + Nan::HandleScope scope; + + if (info.Length() != 7 || + !info[0]->IsString() || + !info[1]->IsString() || + !info[2]->IsArray() || + !info[3]->IsString() || + !info[4]->IsNumber() || + !info[5]->IsNumber() || + !info[6]->IsBoolean()) { + Nan::ThrowError("Usage: pty.startProcess(file, cmdline, env, cwd, cols, rows, debug)"); + return; + } + + std::stringstream why; + + const wchar_t *filename = path_util::to_wstring(Nan::Utf8String(info[0])); + const wchar_t *cmdline = path_util::to_wstring(Nan::Utf8String(info[1])); + const wchar_t *cwd = path_util::to_wstring(Nan::Utf8String(info[3])); + + // create environment block + std::wstring env; + const v8::Local envValues = v8::Local::Cast(info[2]); + if (!envValues.IsEmpty()) { + + std::wstringstream envBlock; + + for(uint32_t i = 0; i < envValues->Length(); i++) { + std::wstring envValue(path_util::to_wstring(Nan::Utf8String(Nan::Get(envValues, i).ToLocalChecked()))); + envBlock << envValue << L'\0'; + } + + env = envBlock.str(); + } + + // use environment 'Path' variable to determine location of + // the relative path that we have recieved (e.g cmd.exe) + std::wstring shellpath; + if (::PathIsRelativeW(filename)) { + shellpath = path_util::get_shell_path(filename); + } else { + shellpath = filename; + } + + std::string shellpath_(shellpath.begin(), shellpath.end()); + + if (shellpath.empty() || !path_util::file_exists(shellpath)) { + why << "File not found: " << shellpath_; + Nan::ThrowError(why.str().c_str()); + goto cleanup; + } + + int cols = info[4]->Int32Value(Nan::GetCurrentContext()).FromJust(); + int rows = info[5]->Int32Value(Nan::GetCurrentContext()).FromJust(); + bool debug = Nan::To(info[6]).FromJust(); + + // Enable/disable debugging + SetEnvironmentVariable(WINPTY_DBG_VARIABLE, debug ? "1" : NULL); // NULL = deletes variable + + // Create winpty config + winpty_error_ptr_t error_ptr = nullptr; + winpty_config_t* winpty_config = winpty_config_new(0, &error_ptr); + if (winpty_config == nullptr) { + throw_winpty_error("Error creating WinPTY config", error_ptr); + goto cleanup; + } + winpty_error_free(error_ptr); + + // Set pty size on config + winpty_config_set_initial_size(winpty_config, cols, rows); + + // Start the pty agent + winpty_t *pc = winpty_open(winpty_config, &error_ptr); + winpty_config_free(winpty_config); + if (pc == nullptr) { + throw_winpty_error("Error launching WinPTY agent", error_ptr); + goto cleanup; + } + winpty_error_free(error_ptr); + + // Save pty struct for later use + ptyHandles.insert(ptyHandles.end(), pc); + + // Create winpty spawn config + winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline, cwd, env.c_str(), &error_ptr); + if (config == nullptr) { + throw_winpty_error("Error creating WinPTY spawn config", error_ptr); + goto cleanup; + } + winpty_error_free(error_ptr); + + // Spawn the new process + HANDLE handle = nullptr; + BOOL spawnSuccess = winpty_spawn(pc, config, &handle, nullptr, nullptr, &error_ptr); + winpty_spawn_config_free(config); + if (!spawnSuccess) { + throw_winpty_error("Unable to start terminal process", error_ptr); + goto cleanup; + } + winpty_error_free(error_ptr); + + // Set return values + v8::Local marshal = Nan::New(); + Nan::Set(marshal, Nan::New("innerPid").ToLocalChecked(), Nan::New((int)GetProcessId(handle))); + Nan::Set(marshal, Nan::New("innerPidHandle").ToLocalChecked(), Nan::New((int)handle)); + Nan::Set(marshal, Nan::New("pid").ToLocalChecked(), Nan::New((int)winpty_agent_process(pc))); + Nan::Set(marshal, Nan::New("pty").ToLocalChecked(), Nan::New(InterlockedIncrement(&ptyCounter))); + Nan::Set(marshal, Nan::New("fd").ToLocalChecked(), Nan::New(-1)); + { + LPCWSTR coninPipeName = winpty_conin_name(pc); + std::wstring coninPipeNameWStr(coninPipeName); + std::string coninPipeNameStr(coninPipeNameWStr.begin(), coninPipeNameWStr.end()); + Nan::Set(marshal, Nan::New("conin").ToLocalChecked(), Nan::New(coninPipeNameStr).ToLocalChecked()); + LPCWSTR conoutPipeName = winpty_conout_name(pc); + std::wstring conoutPipeNameWStr(conoutPipeName); + std::string conoutPipeNameStr(conoutPipeNameWStr.begin(), conoutPipeNameWStr.end()); + Nan::Set(marshal, Nan::New("conout").ToLocalChecked(), Nan::New(conoutPipeNameStr).ToLocalChecked()); + } + info.GetReturnValue().Set(marshal); + + goto cleanup; + +cleanup: + delete filename; + delete cmdline; + delete cwd; +} + +static NAN_METHOD(PtyResize) { + Nan::HandleScope scope; + + if (info.Length() != 3 || + !info[0]->IsNumber() || + !info[1]->IsNumber() || + !info[2]->IsNumber()) { + Nan::ThrowError("Usage: pty.resize(pid, cols, rows)"); + return; + } + + int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); + int cols = info[1]->Int32Value(Nan::GetCurrentContext()).FromJust(); + int rows = info[2]->Int32Value(Nan::GetCurrentContext()).FromJust(); + + winpty_t *pc = get_pipe_handle(handle); + + if (pc == nullptr) { + Nan::ThrowError("The pty doesn't appear to exist"); + return; + } + BOOL success = winpty_set_size(pc, cols, rows, nullptr); + if (!success) { + Nan::ThrowError("The pty could not be resized"); + return; + } + + return info.GetReturnValue().SetUndefined(); +} + +static NAN_METHOD(PtyKill) { + Nan::HandleScope scope; + + if (info.Length() != 2 || + !info[0]->IsNumber() || + !info[1]->IsNumber()) { + Nan::ThrowError("Usage: pty.kill(pid, innerPidHandle)"); + return; + } + + int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust(); + HANDLE innerPidHandle = (HANDLE)info[1]->Int32Value(Nan::GetCurrentContext()).FromJust(); + + winpty_t *pc = get_pipe_handle(handle); + if (pc == nullptr) { + Nan::ThrowError("Pty seems to have been killed already"); + return; + } + + assert(remove_pipe_handle(handle)); + CloseHandle(innerPidHandle); + + return info.GetReturnValue().SetUndefined(); +} + +/** +* Init +*/ + +extern "C" void init(v8::Local target) { + Nan::HandleScope scope; + Nan::SetMethod(target, "startProcess", PtyStartProcess); + Nan::SetMethod(target, "resize", PtyResize); + Nan::SetMethod(target, "kill", PtyKill); + Nan::SetMethod(target, "getExitCode", PtyGetExitCode); + Nan::SetMethod(target, "getProcessList", PtyGetProcessList); +}; + +NODE_MODULE(pty, init);