// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2018 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . #ifdef _WIN64 #include "boinc_win.h" #include "str_replace.h" #include "hostinfo.h" bool get_available_wsls(std::vector& wsls, std::string& default_wsl) { const std::string lxss_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Lxss"; HKEY hKey; default_wsl = ""; LONG lRet = RegOpenKeyEx(HKEY_CURRENT_USER, lxss_path.c_str(), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey); if (lRet != ERROR_SUCCESS) return false; const int buf_len = 256; char default_wsl_guid[buf_len]; DWORD default_wsl_guid_len = sizeof(default_wsl_guid); lRet = RegQueryValueEx(hKey, "DefaultDistribution", NULL, NULL, (LPBYTE)default_wsl_guid, &default_wsl_guid_len); if ((lRet != ERROR_SUCCESS) || (default_wsl_guid_len > buf_len)) return false; int i = 0; while(true) { char wsl_guid[buf_len]; DWORD wsl_guid_len = sizeof(wsl_guid); LONG ret = RegEnumKeyEx(hKey, i++, wsl_guid, &wsl_guid_len, NULL, NULL, NULL, NULL); if (ret != ERROR_SUCCESS) { break; } HKEY hSubKey; const std::string sub_key = lxss_path + "\\" + wsl_guid; ret = RegOpenKeyEx(HKEY_CURRENT_USER, sub_key.c_str(), 0, KEY_QUERY_VALUE, &hSubKey); if (ret != ERROR_SUCCESS) { break; } char wsl_name[buf_len]; DWORD wsl_name_len = sizeof(wsl_name); DWORD wsl_state = 0; DWORD wsl_state_len = sizeof(wsl_state); ret = RegQueryValueEx(hSubKey, "State", NULL, NULL, (LPBYTE)&wsl_state, &wsl_state_len); if (ret != ERROR_SUCCESS || wsl_state != 1) { continue; } ret = RegQueryValueEx(hSubKey, "DistributionName", NULL, NULL, (LPBYTE)wsl_name, &wsl_name_len); if ((ret == ERROR_SUCCESS) && (wsl_name_len < buf_len)) { wsls.push_back(wsl_name); if (std::string(wsl_guid) == std::string(default_wsl_guid)) { default_wsl = wsl_name; } RegCloseKey(hSubKey); } } RegCloseKey(hKey); return default_wsl != ""; } typedef HRESULT(WINAPI *PWslLaunch)(PCWSTR, PCWSTR, BOOL, HANDLE, HANDLE, HANDLE, HANDLE*); HINSTANCE wsl_lib = NULL; HANDLE in_read = NULL; HANDLE in_write = NULL; HANDLE out_read = NULL; HANDLE out_write = NULL; PWslLaunch pWslLaunch = NULL; //convert std::string to PCWSTR //taken from https://stackoverflow.com/questions/27220/how-to-convert-stdstring-to-lpcwstr-in-c-unicode std::wstring s2ws(const std::string& s) { const int slength = (int)s.length() + 1; const int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); wchar_t* buf = new wchar_t[len]; MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len); std::wstring r(buf); delete[] buf; return r; } bool create_wsl_process(const std::string& wsl_distro_name, const std::string& command, HANDLE* handle) { return (pWslLaunch(s2ws(wsl_distro_name).c_str(), s2ws(command).c_str(), FALSE, in_read, out_write, out_write, handle) == S_OK); } bool CreateWslProcess(const std::string& wsl_app, const std::string& command, HANDLE& handle) { PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.hStdError = out_write; si.hStdOutput = out_write; si.hStdInput = NULL; si.dwFlags |= STARTF_USESTDHANDLES; const DWORD dwFlags = CREATE_NO_WINDOW; const std::string cmd = wsl_app + " " + command; const bool res = (CreateProcess(NULL, (LPSTR)cmd.c_str(), NULL, NULL, TRUE, dwFlags, NULL, NULL, &si, &pi) == TRUE); if (res) { handle = pi.hProcess; CloseHandle(pi.hThread); } return res; } inline void close_handle(HANDLE handle) { if (handle) { CloseHandle(handle); } } int free_resources_and_exit(const int return_code) { close_handle(in_read); close_handle(in_write); close_handle(out_read); close_handle(out_write); if (wsl_lib) { FreeLibrary(wsl_lib); } return return_code; } std::string read_from_pipe(HANDLE handle) { DWORD avail, read, exitcode; const int bufsize = 256; char buf[bufsize]; std::string res = ""; for (;;) { PeekNamedPipe(out_read, NULL, 0, NULL, &avail, NULL); if (avail) { if (!ReadFile(out_read, buf, bufsize - 1, &read, NULL) || read == 0) { break; } buf[read] = '\0'; res += buf; } else { if (!GetExitCodeProcess(handle, &exitcode) || exitcode != STILL_ACTIVE) { break; } Sleep(200); } } return res; } void parse_sysctl_output(const std::vector& lines, std::string& ostype, std::string& osrelease) { char buf[256], ostype_found[256], osrelease_found[256]; ostype = ""; osrelease = ""; for (size_t i = 0; i < lines.size(); ++i) { safe_strcpy(buf, lines[i].c_str()); strip_whitespace(buf); if (strstr(buf, "kernel.ostype =")) { safe_strcpy(ostype_found, strchr(buf, '=') + 1); ostype = ostype_found; strip_whitespace(ostype); continue; } if (strstr(buf, "kernel.osrelease =")) { safe_strcpy(osrelease_found, strchr(buf, '=') + 1); osrelease = osrelease_found; strip_whitespace(osrelease); } } } // Returns the OS name and version for WSL when enabled // int get_wsl_information(bool& wsl_available, WSLS& wsls) { wsl_lib = NULL; in_read = NULL; in_write = NULL; out_read = NULL; out_write = NULL; pWslLaunch = NULL; std::vector distros; std::string default_distro; if (!get_available_wsls(distros, default_distro)) { return 1; } wsl_lib = LoadLibrary("wslapi.dll"); if (!wsl_lib) { return 1; } pWslLaunch = (PWslLaunch) GetProcAddress(wsl_lib, "WslLaunch"); if (!pWslLaunch) { free_resources_and_exit(1); } wsl_available = false; SECURITY_ATTRIBUTES sa; HANDLE handle; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; if (!CreatePipe(&out_read, &out_write, &sa, 0)) { return 1; } if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { return free_resources_and_exit(1); } if (!CreatePipe(&in_read, &in_write, &sa, 0)) { return free_resources_and_exit(1); } if (!SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0)) { return free_resources_and_exit(1); } for (size_t i = 0; i < distros.size(); ++i) { char wsl_dist_name[256]; char wsl_dist_version[256]; const std::string& distro = distros[i]; WSL wsl; wsl.distro_name = distro; if (distro == default_distro) { wsl.is_default = true; } else { wsl.is_default = false; } // lsbrelease if (!create_wsl_process(distro, command_lsbrelease, &handle)) { continue; } wsl_available = HOST_INFO::parse_linux_os_info( read_from_pipe(handle), lsbrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version)); CloseHandle(handle); if (!wsl_available) { //osrelease const std::string command_osrelease = "cat " + std::string(file_osrelease); if (!create_wsl_process(distro, command_osrelease, &handle)) { continue; } wsl_available = HOST_INFO::parse_linux_os_info( read_from_pipe(handle), osrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version)); CloseHandle(handle); } //redhatrelease if (!wsl_available) { const std::string command_redhatrelease = "cat " + std::string(file_redhatrelease); if (!create_wsl_process(distro, command_redhatrelease, &handle)) { continue; } wsl_available = HOST_INFO::parse_linux_os_info( read_from_pipe(handle), redhatrelease, wsl_dist_name, sizeof(wsl_dist_name), wsl_dist_version, sizeof(wsl_dist_version)); CloseHandle(handle); } if (!wsl_available) { continue; } std::string os_name = ""; std::string os_version_extra = ""; // sysctl -a const std::string command_sysctl = "sysctl -a"; if (create_wsl_process(distro, command_sysctl, &handle)) { parse_sysctl_output(split(read_from_pipe(handle), '\n'), os_name, os_version_extra); CloseHandle(handle); } // uname -s if (os_name.empty()) { const std::string command_uname_s = "uname -s"; if (create_wsl_process(distro, command_uname_s, &handle)) { os_name = read_from_pipe(handle); strip_whitespace(os_name); CloseHandle(handle); } } // uname -r if (os_version_extra.empty()) { const std::string command_uname_r = "uname -r"; if (create_wsl_process(distro, command_uname_r ,&handle)) { os_version_extra = read_from_pipe(handle); strip_whitespace(os_version_extra); CloseHandle(handle); } } if (!os_name.empty()) { wsl.name = os_name + " " + wsl_dist_name; } else { wsl.name = wsl_dist_name; } if (!os_version_extra.empty()) { wsl.version = std::string(wsl_dist_version) + " [" + os_version_extra + "]"; } else { wsl.version = wsl_dist_version; } wsls.wsls.push_back(wsl); } return free_resources_and_exit(0); } #endif // _WIN64