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 }