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 }