File indexing completed on 2024-05-12 05:45:31
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 #include "kshimgen.h" 0026 #include "kshimdata.h" 0027 #include "kshimgen_p.h" 0028 0029 #include <algorithm> 0030 #include <cstring> 0031 0032 #ifndef _WIN32 0033 #include <sys/stat.h> 0034 #endif 0035 0036 #include <cmrc/cmrc.hpp> 0037 CMRC_DECLARE(KShimEmbeddeResource); 0038 0039 namespace { 0040 0041 std::filesystem::path normaliseApplicationName(const std::filesystem::path &app) 0042 { 0043 #ifdef _WIN32 0044 std::filesystem::path out = app; 0045 out.replace_extension(KShimLib::exeSuffixW); 0046 return out; 0047 #else 0048 return app; 0049 #endif 0050 } 0051 0052 std::vector<char> readBinary(bool createGuiApplication) 0053 { 0054 std::string name = "bin/kshim"s + std::string(KShimLib::exeSuffix); 0055 #ifdef _WIN32 0056 if (createGuiApplication) { 0057 name = "bin/kshimgui"s + std::string(KShimLib::exeSuffix); 0058 } 0059 #else 0060 (void)createGuiApplication; 0061 #endif 0062 const auto filesystem = cmrc::KShimEmbeddeResource::get_filesystem(); 0063 const auto binary = filesystem.open(name); 0064 return std::vector<char>(binary.begin(), binary.end()); 0065 } 0066 0067 bool writeBinary(const std::filesystem::path &name, const KShimData &shimData, 0068 const std::vector<char> &binary) 0069 { 0070 std::vector<char> dataOut = binary; 0071 0072 const auto &_name = name; 0073 { 0074 std::ofstream out(_name, std::ios::out | std::ios::binary); 0075 if (!out.is_open()) { 0076 kLog2(KLog::Type::Error) << "Failed to open out: " << name; 0077 return false; 0078 } 0079 out.write(dataOut.data(), static_cast<std::streamsize>(binary.size())); 0080 0081 kLog << "Wrote: " << name << " " << out.tellp() << " bytes"; 0082 out.close(); 0083 } 0084 0085 KShimGenPrivate::setPayload(name, shimData.toJson()); 0086 0087 #ifndef _WIN32 0088 chmod(name.string().data(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 0089 #ifdef __APPLE__ 0090 const auto codesign = KShimLib::findInPath(std::filesystem::path(KShimLib::string("codesign"))); 0091 const int result = KShimLib::run(KShimData(codesign), { "-s", "-", name.string() }); 0092 if (result != 0) { 0093 kLog << "Faied to sign" << name.string() << "exit code:" << result; 0094 return false; 0095 } 0096 #endif 0097 #else 0098 // we can't use shimData.appAbs() as it depends on the location of the current binary, which 0099 // atm is kshimgen 0100 auto src = shimData.app(); 0101 if (!src.is_absolute()) { 0102 src = name.parent_path() / shimData.app(); 0103 } 0104 if (!KShimLib::exists(src)) { 0105 src = KShimLib::findInPath(shimData.app()); 0106 } 0107 KShimGenPrivate::updateIcon(src, name); 0108 #endif 0109 return true; 0110 } 0111 } 0112 0113 bool KShimGen::createShim(const KShimLib::string_view &appName, const std::filesystem::path &target, 0114 const std::vector<KShimLib::string_view> &args, 0115 const std::vector<KShimLib::string_view> &_env, bool createGuiApplication, 0116 bool enableEnvOverride, bool keepArg0) 0117 { 0118 std::vector<std::pair<KShimLib::string_view, KShimLib::string_view>> env; 0119 env.reserve(_env.size()); 0120 for (const auto &e : _env) { 0121 const auto pos = e.find('='); 0122 env.push_back({ e.substr(0, pos), e.substr(pos + 1) }); 0123 } 0124 const auto outApp = normaliseApplicationName(appName); 0125 KShimData shimData; 0126 shimData.setApp(target); 0127 shimData.setArgs(args); 0128 shimData.setEnv(env); 0129 shimData.setEnvOverrideEnabled(enableEnvOverride); 0130 shimData.setKeepArgv0Enabled(keepArg0); 0131 const std::vector<char> binary = readBinary(createGuiApplication); 0132 if (!binary.empty()) { 0133 return writeBinary(outApp, shimData, binary); 0134 } 0135 return false; 0136 } 0137 0138 int KShimGen::main(const std::vector<KShimLib::string_view> &args) 0139 { 0140 KShimLib::string_view target; 0141 KShimLib::string_view app; 0142 std::vector<KShimLib::string_view> arguments; 0143 std::vector<KShimLib::string_view> env; 0144 bool gui = false; 0145 bool enableEnvOverride = false; 0146 bool keepArgv0 = false; 0147 0148 auto help = [](const KShimLib::string_view &msg) { 0149 kLog2(KLog::Type::Error) 0150 << msg << "\n" 0151 << "--create shim target\t\t\tCreate a shim\n" 0152 << "--env key=val\t\t\t\tadditional environment varriables for the shim\n" 0153 << "--enable-env-override\t\t\twhether to allow overriding the target with " 0154 "the env var KSHIM_shim\n" 0155 << "--keep-argv0\t\t\t\twhether to keep the original arg0 (emulates symlinks)\n" 0156 #ifdef _WIN32 0157 << "--gui\t\t\t\t\tcreate a gui application (only supported on Windows)\n" 0158 #endif 0159 << "-- arg1 arg2 arg3...\t\t\targuments that get passed to the target"; 0160 }; 0161 auto nextArg = [&](std::vector<KShimLib::string_view>::const_iterator &it, 0162 const KShimLib::string_view &helpText) -> KShimLib::string_view { 0163 if (it != args.cend()) { 0164 return *it++; 0165 } else { 0166 help(helpText); 0167 exit(1); 0168 } 0169 }; 0170 0171 auto it = args.cbegin() + 1; 0172 while (it != args.cend()) { 0173 const auto arg = nextArg(it, KSTRING("")); 0174 if (arg == KSTRING("--create")) { 0175 const auto msg = KSTRING("--create shim target"); 0176 app = nextArg(it, msg); 0177 target = nextArg(it, msg); 0178 } else if (arg == KSTRING("--env")) { 0179 env.push_back(nextArg(it, KSTRING("--env key=val"))); 0180 } else if (arg == KSTRING("--enable-env-override")) { 0181 enableEnvOverride = true; 0182 } else if (arg == KSTRING("--keep-argv0")) { 0183 keepArgv0 = true; 0184 } else if (arg == KSTRING("--")) { 0185 while (it != args.cend()) { 0186 arguments.push_back(nextArg(it, KSTRING(""))); 0187 } 0188 break; 0189 #ifdef _WIN32 0190 } else if (arg == KSTRING("--gui")) { 0191 gui = true; 0192 #endif 0193 } else if (arg == KSTRING("-h")) { 0194 help(KSTRING("")); 0195 return 0; 0196 } else { 0197 KShimLib::stringstream str; 0198 str << "Unknwon arg " << arg; 0199 help(str.str()); 0200 } 0201 } 0202 if (enableEnvOverride) { 0203 if (!env.empty()) { 0204 help(KSTRING("--enable-env-override and --env are mutual exclusive"s)); 0205 } 0206 if (!arguments.empty()) { 0207 help(KSTRING( 0208 "When using --enable-env-override it is not supported to provide extra args after --"s)); 0209 } 0210 } 0211 if (!target.empty()) { 0212 return KShimGen::createShim(app, target, arguments, env, gui, enableEnvOverride, keepArgv0) 0213 ? 0 0214 : -1; 0215 } 0216 help(KSTRING("")); 0217 return -1; 0218 }