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 <unistd.h> 0030 #include <sys/stat.h> 0031 #include <spawn.h> 0032 #include <sys/wait.h> 0033 #include <cstring> 0034 0035 #ifdef __APPLE__ 0036 #include <libproc.h> 0037 #endif 0038 0039 #ifdef __FreeBSD__ 0040 #include <kvm.h> 0041 #include <sys/param.h> 0042 #include <sys/sysctl.h> 0043 #include <sys/user.h> 0044 #include <libprocstat.h> 0045 #endif 0046 0047 extern char **environ; 0048 0049 std::filesystem::path KShimLib::binaryName() 0050 { 0051 static std::filesystem::path _path = [] { 0052 size_t size; 0053 #ifdef __APPLE__ 0054 string out(PROC_PIDPATHINFO_MAXSIZE, 0); 0055 size = proc_pidpath(getpid(), const_cast<char *>(out.data()), out.size()); 0056 #elif defined(__FreeBSD__) 0057 string out(PATH_MAX, 0); 0058 int error, name[4]; 0059 size_t len = PATH_MAX; 0060 0061 name[0] = CTL_KERN; 0062 name[1] = KERN_PROC; 0063 name[2] = KERN_PROC_PATHNAME; 0064 name[3] = getpid(); 0065 0066 error = sysctl(name, nitems(name), const_cast<char *>(out.data()), &len, NULL, 0); 0067 len--; // cut off zero-terminator 0068 out.resize(len); 0069 size = len; 0070 #else 0071 string out; 0072 do { 0073 out.resize(out.size() + 1024); 0074 size = readlink("/proc/self/exe", const_cast<char *>(out.data()), out.size()); 0075 } while (out.size() == size); 0076 #endif 0077 if (size > 0) { 0078 out.resize(size); 0079 } else { 0080 kLog2(KLog::Type::Error) << "Failed to locate shimgen"; 0081 exit(1); 0082 } 0083 return std::filesystem::path(out); 0084 }(); 0085 return _path; 0086 } 0087 0088 int KShimLib::run(const KShimData &data, const std::vector<KShimLib::string_view> &args) 0089 { 0090 for (auto &var : data.env()) { 0091 kLog << "setenv: " << var.first << "=" << var.second; 0092 if (var.second.empty()) { 0093 unsetenv(var.first.data()); 0094 } else { 0095 setenv(var.first.data(), var.second.data(), true); 0096 } 0097 } 0098 std::vector<char *> arguments; 0099 auto addArg = [&arguments](const KShimLib::string_view &s) { 0100 arguments.push_back(const_cast<char *>(s.data())); 0101 }; 0102 // we need to copy the string as we only pass a pointer to posix_spawn 0103 const auto app = data.appAbsWithOverride().native(); 0104 auto argv0 = app; 0105 if (data.isKeepArgv0Enabled()) { 0106 argv0 = KShimLib::binaryName().native(); 0107 } 0108 addArg(argv0); 0109 for (const auto &s : data.args()) { 0110 addArg(s); 0111 } 0112 for (const auto &s : args) { 0113 addArg(s); 0114 } 0115 // the args need to end with a null pointer 0116 arguments.push_back(nullptr); 0117 0118 { 0119 auto log = kLog << "Command:"; 0120 log << app; 0121 for (const char *s : arguments) { 0122 if (s) { 0123 log << " " << s; 0124 } 0125 } 0126 } 0127 pid_t pid; 0128 int status = posix_spawn(&pid, app.data(), NULL, NULL, arguments.data(), environ); 0129 if (status == 0) { 0130 if (waitpid(pid, &status, 0) != -1) { 0131 if (WIFEXITED(status)) { 0132 return WEXITSTATUS(status); 0133 } else { 0134 if(WIFSIGNALED(status)) { 0135 if(WCOREDUMP(status)) { 0136 kLog2(KLog::Type::Error) << "KShim: the child process produced a core dump"; 0137 } 0138 if(WTERMSIG(status)) { 0139 kLog2(KLog::Type::Error) << "KShim: the child process was terminated";; 0140 } 0141 } 0142 } 0143 } else { 0144 kLog2(KLog::Type::Error) << "KShim: waitpid error"; 0145 } 0146 } else { 0147 kLog2(KLog::Type::Error) << "KShim: posix_spawn: " << strerror(status); 0148 } 0149 return -1; 0150 } 0151 0152 KShimLib::string KShimLib::getenv(const KShimLib::string_view &var, 0153 const KShimLib::string_view &fallback) 0154 { 0155 const char *env = ::getenv(var.data()); 0156 if (env) { 0157 return { env }; 0158 } 0159 return fallback.empty() ? "" : fallback.data(); 0160 } 0161 0162 std::filesystem::path KShimLib::findInPath(const std::filesystem::path &path) 0163 { 0164 auto path_env = std::stringstream(KShimLib::getenv("PATH")); 0165 std::string dir; 0166 while (std::getline(path_env, dir, ':')) { 0167 const auto file = std::filesystem::path(dir) / path; 0168 if (file != KShimLib::binaryName()) { 0169 struct stat sb; 0170 if (stat(file.string().data(), &sb) == 0 && sb.st_mode & S_IXUSR) { 0171 kLog << "Found: " << file << " for " << path; 0172 return file; 0173 } 0174 } 0175 } 0176 kLog2(KLog::Type::Fatal) << "Failed to locate" << path; 0177 return {}; 0178 } 0179 0180 bool KShimLib::exists(const std::filesystem::path &path) 0181 { 0182 struct stat sb; 0183 return stat(path.string().data(), &sb) == 0; 0184 }