std::string read(HKEY hive) const { HKEY keyHandle; // RegGetValue would let us replace most of this function with a single line but "Requires Windows Vista or Windows XP Professional x64 Edition". LONG errorCode = RegOpenKey(hive, keyName, &keyHandle); if (errorCode != ERROR_SUCCESS) { throw WindowsError("RegOpenKey(" + toString(hive) + ", " + keyName + ", &keyHandle)", errorCode); } // MAX_PATH seems likely to be enough. std::vector<BYTE> buffer(MAX_PATH); DWORD type; DWORD size = buffer.size(); // FIXME: We could make some effort towards Unicode support. errorCode = RegQueryValueEx(keyHandle, valueName, 0, &type, &buffer[0], &size); if (errorCode != ERROR_SUCCESS) { throw WindowsError("RegQueryValueEx(" + toString(keyHandle) + " (\"" + keyName + "\"), \"" + valueName + "\", 0, &type, &buffer[0], &size)", errorCode); } if (type != REG_SZ) { throw std::runtime_error("The type of registry entry hive " + toString(hive) + ", key " + keyName + ", value " + valueName + " was not REG_SZ (" + toString(REG_SZ) + ") as expected but " + toString(type)); } // MSDN's article on RegQueryValueEx explains that the value may or may not include the null terminator. if (size != 0 && buffer[size - 1] == 0) { -- size; } return std::string(reinterpret_cast<const char*>(&buffer[0]), size); }
inline Try<Nothing> nonblock(int fd) { if (net::is_socket(fd)) { const u_long non_block_mode = 1; u_long mode = non_block_mode; int result = ioctlsocket(fd, FIONBIO, &mode); if (result != NO_ERROR) { return WindowsSocketError(); } } else { // Extract handle from file descriptor. HANDLE handle = reinterpret_cast<HANDLE>(::_get_osfhandle(fd)); if (handle == INVALID_HANDLE_VALUE) { return WindowsError("Failed to get `HANDLE` for file descriptor"); } if (GetFileType(handle) == FILE_TYPE_PIPE) { DWORD pipe_mode = PIPE_NOWAIT; if (SetNamedPipeHandleState(handle, &pipe_mode, nullptr, nullptr)) { return WindowsError(); } } } return Nothing(); }
inline Result<PROCESSENTRY32> process_entry(pid_t pid) { // Get a snapshot of the processes in the system. NOTE: We should not check // whether the handle is `NULL`, because this API will always return // `INVALID_HANDLE_VALUE` on error. HANDLE snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid); if (snapshot_handle == INVALID_HANDLE_VALUE) { return WindowsError( "os::process_entry: Call to `CreateToolhelp32Snapshot` failed"); } SharedHandle safe_snapshot_handle(snapshot_handle, ::CloseHandle); // Initialize process entry. PROCESSENTRY32 process_entry; ZeroMemory(&process_entry, sizeof(PROCESSENTRY32)); process_entry.dwSize = sizeof(PROCESSENTRY32); // Get first process so that we can loop through process entries until we // find the one we care about. SetLastError(ERROR_SUCCESS); bool has_next = Process32First(safe_snapshot_handle.get(), &process_entry); if (!has_next) { // No first process was found. We should never be here; it is arguable we // should return `None`, since we won't find the PID we're looking for, but // we elect to return `Error` because something terrible has probably // happened. if (GetLastError() != ERROR_SUCCESS) { return WindowsError("os::process_entry: Call to `Process32First` failed"); } else { return Error("os::process_entry: Call to `Process32First` failed"); } } // Loop through processes until we find the one we're looking for. while (has_next) { if (process_entry.th32ProcessID == pid) { // Process found. return process_entry; } has_next = Process32Next(safe_snapshot_handle.get(), &process_entry); if (!has_next) { DWORD last_error = GetLastError(); if (last_error != ERROR_NO_MORE_FILES && last_error != ERROR_SUCCESS) { return WindowsError( "os::process_entry: Call to `Process32Next` failed"); } } } return None(); }
// Overload of os::pids for filtering by groups and sessions. A group / session // id of 0 will fitler on the group / session ID of the calling process. // NOTE: Windows does not have the concept of a process group, so we need to // enumerate all processes. inline Try<std::set<pid_t>> pids(Option<pid_t> group, Option<pid_t> session) { DWORD max_items = 4096; DWORD bytes_returned; std::vector<pid_t> processes; size_t size_in_bytes; // Attempt to populate `processes` with PIDs. We repeatedly call // `EnumProcesses` with increasingly large arrays until it "succeeds" at // populating the array with PIDs. The criteria for determining when // `EnumProcesses` has succeeded are: // (1) the return value is nonzero. // (2) the `bytes_returned` is less than the number of bytes in the array. do { // TODO(alexnaparu): Set a limit to the memory that can be used. processes.resize(max_items); size_in_bytes = processes.size() * sizeof(pid_t); BOOL result = ::EnumProcesses(processes.data(), size_in_bytes, &bytes_returned); if (!result) { return WindowsError("os::pids: Call to `EnumProcesses` failed"); } max_items *= 2; } while (bytes_returned >= size_in_bytes); return std::set<pid_t>(processes.begin(), processes.end()); }
fmt::LongLong fmt::File::size() const { #ifdef _WIN32 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT // is less than 0x0500 as is the case with some default MinGW builds. // Both functions support large file sizes. DWORD size_upper = 0; HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); if (size_lower == INVALID_FILE_SIZE) { DWORD error = GetLastError(); if (error != NO_ERROR) throw WindowsError(GetLastError(), "cannot get file size"); } fmt::ULongLong long_size = size_upper; return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; #else typedef struct stat Stat; Stat file_stat = Stat(); if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) throw SystemError(errno, "cannot get file attributes"); FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), "return type of File::size is not large enough"); return file_stat.st_size; #endif }
// Creates a file for a subprocess's stdin, stdout, or stderr. // // NOTE: For portability, Libprocess implements POSIX-style semantics for these // files, and make no assumptions about who has access to them. For example, we // do not enforce a process-level write lock on stdin, and we do not enforce a // similar read lock from stdout. // // TODO(hausdorff): Rethink name here, write a comment about this function. static Try<HANDLE> createIoPath(const string& path, DWORD accessFlags) { // Per function comment, the flags `FILE_SHARE_READ`, `FILE_SHARE_WRITE`, and // `OPEN_ALWAYS` are all important. The former two make sure we do not // acquire a process-level lock on reading/writing the file, and the last one // ensures that we open the file if it exists, and create it if it does not. // Note that we specify both `FILE_SHARE_READ` and `FILE_SHARE_WRITE` because // the documentation is not clear about whether `FILE_SHARE_WRITE` also // ensures we don't take a read lock out. SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE }; const HANDLE handle = ::CreateFile( path.c_str(), accessFlags, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (handle == INVALID_HANDLE_VALUE) { return WindowsError("Failed to open '" + path + "'"); } return handle; }
inline Try<Nothing> rename( const std::string& from, const std::string& to, bool sync = false) { // Use `MoveFile` to perform the file move. The MSVCRT implementation of // `::rename` fails if the `to` file already exists[1], while some UNIX // implementations allow that[2]. // // Use `MOVEFILE_COPY_ALLOWED` to allow moving the file to another volume and // `MOVEFILE_REPLACE_EXISTING` to comply with the UNIX implementation and // replace an existing file[3]. // // [1] https://msdn.microsoft.com/en-us/library/zw5t957f.aspx // [2] http://man7.org/linux/man-pages/man2/rename.2.html // [3] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx const BOOL result = ::MoveFileExW( ::internal::windows::longpath(from).data(), ::internal::windows::longpath(to).data(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | (sync ? MOVEFILE_WRITE_THROUGH : 0)); if (!result) { return WindowsError( "`os::rename` failed to move file '" + from + "' to '" + to + "'"); } return Nothing(); }
// Returns a `Try` of the result of attempting to set the `hostname`. inline Try<Nothing> setHostname(const std::string& hostname) { if (SetComputerName(hostname.c_str()) == 0) { return WindowsError(); } return Nothing(); }
static inline T* checkIgnore (const char* function, T* value, ErrorNumType ignore) { if (!value) { ErrorNumType errnum = getLastError (); if (errnum != ignore) throw WindowsError (function, errnum); } return value; }
static inline uint32_t checkIgnore (const char* function, uint32_t value, ErrorNumType ignore) { if (value == 0) { ErrorNumType errnum = getLastError (); if (errnum != ignore) throw WindowsError (function, errnum); } return value; }
// Returns a `Try` of the result of attempting to set the `hostname`. inline Try<Nothing> setHostname(const std::string& hostname) { if (::SetComputerNameW(wide_stringify(hostname).data()) == 0) { return WindowsError(); } return Nothing(); }
void Keyboard::WindowsModifierKey(ModifierKeycode key, bool up) { WORD wkey; switch(key) { case ModCtrlL: wkey = VK_LCONTROL; break; case ModCtrlR: wkey = VK_RCONTROL; break; case ModCtrl: wkey = VK_CONTROL; break; case ModAltL: wkey = VK_LMENU; break; case ModAltR: wkey = VK_RMENU; break; case ModAlt: wkey = VK_MENU; break; case ModShiftL: wkey = VK_LSHIFT; break; case ModShiftR: wkey = VK_RSHIFT; break; case ModShift: wkey = VK_SHIFT; break; case ModMetaL: wkey = VK_LWIN; // This might not have any effect, Windows key may be read-only at user level break; case ModMetaR: wkey = VK_RWIN; break; case ModMeta: wkey = VK_LWIN; // Windows has no generic 'sideless' windows key, so just use left break; default: wkey = 0; break; } INPUT input; input.type = INPUT_KEYBOARD; input.ki.wVk = wkey; input.ki.wScan = 0; input.ki.dwFlags = up ? KEYEVENTF_KEYUP : 0; input.ki.time = 0; input.ki.dwExtraInfo = (ULONG_PTR)NULL; if (SendInput(1, &input, sizeof(INPUT)) != 1) { WindowsError("SendInput"); } }
inline Try<Version> release() { OSVERSIONINFOEX os_version; os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (!::GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&os_version))) { return WindowsError("os::release: Call to `GetVersionEx` failed"); } return Version(os_version.dwMajorVersion, os_version.dwMinorVersion, 0); }
static Try<HANDLE> getHandleFromFileDescriptor(int_fd fd) { // Extract handle from file descriptor. const HANDLE handle = fd; if (handle == INVALID_HANDLE_VALUE) { return WindowsError("Failed to get `HANDLE` for file descriptor"); } return handle; }
static Try<HANDLE> getHandleFromFileDescriptor(int fd) { // Extract handle from file descriptor. const HANDLE handle = reinterpret_cast<HANDLE>(::_get_osfhandle(fd)); if (handle == INVALID_HANDLE_VALUE) { return WindowsError("Failed to get `HANDLE` for file descriptor"); } return handle; }
// This function enables or disables inheritance for a Windows file handle. // // NOTE: By default, handles on Windows are not inheritable, so this is // primarily used to enable inheritance when passing handles to child processes, // and subsequently disable inheritance. inline Try<Nothing> set_inherit(const os::WindowsFD& fd, const bool inherit) { const BOOL result = ::SetHandleInformation( fd, HANDLE_FLAG_INHERIT, inherit ? HANDLE_FLAG_INHERIT : 0); if (result == FALSE) { return WindowsError(); } return Nothing(); }
inline Try<OSVERSIONINFOEX> os_version() { OSVERSIONINFOEX os_version; os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (!::GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&os_version))) { return WindowsError( "os::internal::os_version: Call to `GetVersionEx` failed"); } return os_version; }
inline Try<std::string> nodename() { // Get DNS name of the local computer. First, find the size of the output // buffer. DWORD size = 0; if (!::GetComputerNameEx(ComputerNameDnsHostname, NULL, &size) && ::GetLastError() != ERROR_MORE_DATA) { return WindowsError( "os::internal::nodename: Call to `GetComputerNameEx` failed"); } std::unique_ptr<char[]> name(new char[size + 1]); if (!::GetComputerNameEx(ComputerNameDnsHostname, name.get(), &size)) { return WindowsError( "os::internal::nodename: Call to `GetComputerNameEx` failed"); } return std::string(name.get()); }
// Opens an inheritable pipe[1] represented as a pair of file handles. On // success, the first handle returned recieves the 'read' handle of the pipe, // while the second receives the 'write' handle. The pipe handles can then be // passed to a child process, as exemplified in [2]. // // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx static Try<array<HANDLE, 2>> createPipeHandles() { // The `TRUE` in the last field makes this duplicate handle inheritable. SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; array<HANDLE, 2> handles{ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; if (!::CreatePipe(&handles[0], &handles[1], &sa, 0)) { return WindowsError("createPipeHandles: could not create pipe"); } return handles; }
Subprocess::IO Subprocess::PIPE() { return Subprocess::IO( []() -> Try<InputFileDescriptors> { const Try<array<HANDLE, 2>> handles = internal::createPipeHandles(); if (handles.isError()) { return Error(handles.error()); } // Create STDIN pipe and set the 'write' component to not be // inheritable. if (!::SetHandleInformation(handles.get()[1], HANDLE_FLAG_INHERIT, 0)) { return WindowsError( "PIPE: Failed to call SetHandleInformation on stdin pipe"); } InputFileDescriptors fds; fds.read = handles.get()[0]; fds.write = handles.get()[1]; return fds; }, []() -> Try<OutputFileDescriptors> { const Try<array<HANDLE, 2>> handles = internal::createPipeHandles(); if (handles.isError()) { return Error(handles.error()); } // Create OUT pipe and set the 'read' component to not be inheritable. if (!::SetHandleInformation(handles.get()[0], HANDLE_FLAG_INHERIT, 0)) { return WindowsError( "PIPE: Failed to call SetHandleInformation on out pipe"); } OutputFileDescriptors fds; fds.read = handles.get()[0]; fds.write = handles.get()[1]; return fds; }); }
void Keyboard::WindowsKey(Keycode key, bool up) { INPUT input; input.type = INPUT_KEYBOARD; input.ki.wVk = 0; input.ki.wScan = WindowsDIConvert( key ); input.ki.dwFlags = KEYEVENTF_SCANCODE | ( up ? KEYEVENTF_KEYUP : 0 ); input.ki.time = 0; input.ki.dwExtraInfo = (ULONG_PTR)NULL; if (SendInput(1, &input, sizeof(INPUT)) != 1) { WindowsError("SendInput"); } }
Try<Nothing> close() { if (handle_ == NULL) { return Error("Could not close library; handle was already `NULL`"); } if (!FreeLibrary(handle_)) { return WindowsError( "Could not close library '" + (path_.isSome() ? path_.get() : "")); } handle_ = NULL; path_ = None(); return Nothing(); }
Try<Nothing> open(const std::string& path) { // Check if we've already opened a library. if (handle_ != NULL) { return Error("Library already opened"); } handle_ = LoadLibrary(path.c_str()); if (handle_ == NULL) { return WindowsError("Could not load library '" + path + "'"); } path_ = path; return Nothing(); }
Try<void*> loadSymbol(const std::string& name) { if (handle_ == NULL) { return Error( "Could not get symbol '" + name + "'; library handle was `NULL`"); } void* symbol = GetProcAddress(handle_, name.c_str()); if (symbol == NULL) { return WindowsError( "Error looking up symbol '" + name + "' in '" + (path_.isSome() ? path_.get() : "")); } return symbol; }
// Returns the total size of main and free memory. inline Try<Memory> memory() { Memory memory; MEMORYSTATUSEX memory_status; memory_status.dwLength = sizeof(MEMORYSTATUSEX); if (!::GlobalMemoryStatusEx(&memory_status)) { return WindowsError("os::memory: Call to `GlobalMemoryStatusEx` failed"); } memory.total = Bytes(memory_status.ullTotalPhys); memory.free = Bytes(memory_status.ullAvailPhys); memory.totalSwap = Bytes(memory_status.ullTotalPageFile); memory.freeSwap = Bytes(memory_status.ullAvailPageFile); return memory; }
// TODO(hausdorff): Rethink name here, write a comment about this function. static Try<HANDLE> createIoPath(const string& path, DWORD accessFlags) { // The `TRUE` in the last field makes this duplicate handle inheritable. SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; const HANDLE handle = ::CreateFile( path.c_str(), accessFlags, FILE_SHARE_READ, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { return WindowsError("Failed to open '" + path + "'"); } return handle; }
void Mouse::WindowsButton(unsigned int button, bool up) { // Create INPUT struct, most feilds can be set as 0 INPUT input; input.type = INPUT_MOUSE; input.mi.dx = 0; input.mi.dy = 0; input.mi.mouseData = 0; input.mi.dwFlags = 0; input.mi.time = 0; input.mi.dwExtraInfo = (ULONG_PTR)NULL; // Fill dwFlags based on button and up switch(button) { case sLeft: input.mi.dwFlags = up ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN; break; case sRight: input.mi.dwFlags = up ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN; break; case sMiddle: input.mi.dwFlags = up ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN; break; case sMouse4: input.mi.dwFlags = up ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; input.mi.mouseData = XBUTTON1; break; case sMouse5: input.mi.dwFlags = up ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN; input.mi.mouseData = XBUTTON2; break; default: // Invalid button break; } // Send the single input and report errors if (SendInput(1, &input, sizeof(INPUT)) != 1) { WindowsError("SendInput"); } }
// Terminate the process tree rooted at the specified pid. // Note that if the process 'pid' has exited we'll terminate the process // tree(s) rooted at pids. // Returns the process trees that were succesfully or unsuccessfully // signaled. Note that the process trees can be stringified. inline Try<std::list<ProcessTree>> killtree( pid_t pid, int signal, bool groups = false, bool sessions = false) { std::list<ProcessTree> process_tree_list; Try<ProcessTree> process_tree = os::pstree(pid); if (process_tree.isError()) { return WindowsError(); } process_tree_list.push_back(process_tree.get()); Try<Nothing> kill_job = os::kill_job(pid); if (kill_job.isError()) { return Error(kill_job.error()); } return process_tree_list; }
static Try<HANDLE> duplicateHandle(const HANDLE handle) { HANDLE duplicate = INVALID_HANDLE_VALUE; // TODO(anaparu): Do we need to scope the duplicated handle // to the child process? BOOL result = ::DuplicateHandle( ::GetCurrentProcess(), // Source process == current. handle, // Handle to duplicate. ::GetCurrentProcess(), // Target process == current. &duplicate, 0, // Ignored (DUPLICATE_SAME_ACCESS). TRUE, // Inheritable handle. DUPLICATE_SAME_ACCESS); // Same access level as source. if (!result) { return WindowsError("Failed to duplicate handle of stdin file"); } return duplicate; }
Future<size_t> write(int_fd fd, const void* data, size_t size) { process::initialize(); // TODO(benh): Let the system calls do what ever they're supposed to // rather than return 0 here? if (size == 0) { return 0; } // Just do a synchronous call. if (!fd.is_overlapped()) { ssize_t result = os::write(fd, data, size); if (result == -1) { return Failure(WindowsError().message); } return static_cast<size_t>(result); } return windows::write(fd, data, size); }