File indexing completed on 2024-05-05 05:45:56
0001 /* 0002 Copyright Hannah von Reth <vonreth@kde.org> 0003 0004 Redistribution and use in source and binary forms, with or without 0005 modification, are permitted provided that the following conditions 0006 are met: 0007 1. Redistributions of source code must retain the above copyright 0008 notice, this list of conditions and the following disclaimer. 0009 2. Redistributions in binary form must reproduce the above copyright 0010 notice, this list of conditions and the following disclaimer in the 0011 documentation and/or other materials provided with the distribution. 0012 0013 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 0014 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 0015 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 0016 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 0017 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 0018 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 0019 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 0020 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 0021 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 0022 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 0023 SUCH DAMAGE. 0024 */ 0025 0026 #include "kshim.h" 0027 #include "kshimdata.h" 0028 0029 #include <algorithm> 0030 #include <comdef.h> 0031 #include <windows.h> 0032 0033 using namespace std; 0034 0035 namespace { 0036 auto formatCommand(const KShimData &data, const std::vector<KShimLib::string_view> &arguments) 0037 { 0038 auto app = data.appAbsWithOverride(); 0039 if (data.isKeepArgv0Enabled()) { 0040 app = KShimLib::binaryName(); 0041 } 0042 KShimLib::stringstream cmd; 0043 cmd << KShimLib::quote(app.native()) << data.formatArgs(arguments); 0044 return cmd.str(); 0045 } 0046 } 0047 0048 std::filesystem::path KShimLib::binaryName() 0049 { 0050 static const std::filesystem::path path = [] { 0051 std::wstring buf; 0052 size_t size; 0053 do { 0054 buf.resize(buf.size() + 1024); 0055 size = GetModuleFileNameW(nullptr, const_cast<wchar_t *>(buf.data()), 0056 static_cast<DWORD>(buf.size())); 0057 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER); 0058 buf.resize(size); 0059 return std::filesystem::path(buf); 0060 }(); 0061 return path; 0062 } 0063 0064 bool KShimLib::exists(const std::filesystem::path &path) 0065 { 0066 DWORD dwAttrib = GetFileAttributesW(path.wstring().data()); 0067 0068 return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 0069 } 0070 0071 std::filesystem::path KShimLib::findInPath(const std::filesystem::path &path) 0072 { 0073 auto find = [](const std::wstring &dir, const std::filesystem::path &name) { 0074 const std::wstring ext = name.extension().empty() ? L".exe" : std::wstring(name.extension()); 0075 std::wstring buf; 0076 size_t size; 0077 wchar_t *filePart; 0078 size = SearchPathW(dir.data(), name.wstring().data(), ext.data(), 0, nullptr, &filePart); 0079 if (size == 0) { 0080 return std::filesystem::path(); 0081 } 0082 buf.resize(size); 0083 size = SearchPathW(dir.data(), name.wstring().data(), ext.data(), 0084 static_cast<DWORD>(buf.size()), buf.data(), &filePart); 0085 if (size > buf.size()) { 0086 kLog2(KLog::Type::Fatal) << "SearchPathW failed to find " << name 0087 << " error: " << _com_error(GetLastError()).ErrorMessage() 0088 << buf.size() << " " << size; 0089 } 0090 buf.resize(size); 0091 return std::filesystem::path(buf); 0092 }; 0093 auto path_env = std::wstringstream(KShimLib::getenv(L"PATH")); 0094 std::wstring dir; 0095 while (std::getline(path_env, dir, L';')) { 0096 auto tmp = find(dir, path); 0097 if (!tmp.empty() && tmp != KShimLib::binaryName()) { 0098 kLog << "Found: " << tmp << " for " << path; 0099 return tmp; 0100 } 0101 } 0102 kLog2(KLog::Type::Fatal) << "Failed to locate" << path; 0103 return {}; 0104 } 0105 0106 int KShimLib::run(const KShimData &data, const std::vector<KShimLib::string_view> &args) 0107 { 0108 for (auto var : data.env()) { 0109 kLog << "SetEnvironmentVariable: " << var.first << "=" << var.second; 0110 SetEnvironmentVariableW(var.first.data(), var.second.empty() ? nullptr : var.second.data()); 0111 } 0112 // TODO: pass environment 0113 STARTUPINFOW info = {}; 0114 info.cb = sizeof(info); 0115 PROCESS_INFORMATION pInfo = {}; 0116 const auto app = data.appAbsWithOverride(); 0117 const auto arguments = formatCommand(data, args); 0118 kLog << app << " " << arguments; 0119 kLog << "CommandLength: " << arguments.size(); 0120 0121 if (!CreateProcessW(app.wstring().c_str(), const_cast<wchar_t *>(arguments.c_str()), nullptr, 0122 nullptr, true, INHERIT_PARENT_AFFINITY | CREATE_UNICODE_ENVIRONMENT, 0123 nullptr, nullptr, &info, &pInfo)) { 0124 const auto error = GetLastError(); 0125 kLog2(KLog::Type::Error) << "Failed to start target" << _com_error(error).ErrorMessage(); 0126 return static_cast<int>(error); 0127 } 0128 WaitForSingleObject(pInfo.hProcess, INFINITE); 0129 DWORD exitCode; 0130 GetExitCodeProcess(pInfo.hProcess, &exitCode); 0131 0132 CloseHandle(pInfo.hProcess); 0133 CloseHandle(pInfo.hThread); 0134 return static_cast<int>(exitCode); 0135 } 0136 0137 KShimLib::string KShimLib::getenv(const KShimLib::string_view &var, 0138 const KShimLib::string_view &fallback) 0139 { 0140 const auto size = GetEnvironmentVariableW(var.data(), nullptr, 0); 0141 if (!size) { 0142 return fallback.empty() ? L""s : fallback.data(); 0143 } 0144 KShimLib::string out(size, 0); 0145 GetEnvironmentVariableW(var.data(), const_cast<wchar_t *>(out.data()), size); 0146 out.resize(size - 1); 0147 return out; 0148 }