| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "utils/ExecUtils.hpp" |
| 4 | |
| 5 | #include "global.hpp" |
| 6 | |
| 7 | #include <fcntl.h> |
| 8 | #include <iostream> |
| 9 | #include <sys/stat.h> |
| 10 | #include <unistd.h> |
| 11 | |
| 12 | namespace ExecUtils |
| 13 | { |
| 14 | void RedirectLogFd(const std::string &unitBase, const std::string &fileName) |
| 15 | { |
| 16 | if (unitBase.empty() || fileName.empty()) |
| 17 | { |
| 18 | std::cerr << "RedirectLogFd: unitBase or fileName is empty" << std::endl; |
| 19 | exit(status: 1); |
| 20 | } |
| 21 | |
| 22 | const std::string log_dir = "/tmp/log/" + unitBase; |
| 23 | if (mkdir(pathname: log_dir.c_str(), mode: 0755) == -1 && errno != EEXIST) |
| 24 | { |
| 25 | std::cerr << "failed to create log directory " << log_dir << std::endl; |
| 26 | exit(status: 1); |
| 27 | } |
| 28 | |
| 29 | const std::string log_path = "/tmp/log/" + unitBase + "/" + fileName + ".log" ; |
| 30 | const int log_fd = open(path: log_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); |
| 31 | if (log_fd == -1) |
| 32 | { |
| 33 | std::cerr << "failed to open log file " << log_path << std::endl; |
| 34 | exit(status: 1); |
| 35 | } |
| 36 | |
| 37 | dup2(src_fd: log_fd, STDOUT_FILENO); |
| 38 | dup2(src_fd: log_fd, STDERR_FILENO); |
| 39 | close(fd: log_fd); |
| 40 | } |
| 41 | |
| 42 | std::string GetRandomString(size_t length) |
| 43 | { |
| 44 | static const char alphanum[] = "0123456789" |
| 45 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 46 | "abcdefghijklmnopqrstuvwxyz" ; |
| 47 | std::string s; |
| 48 | s.reserve(res: length); |
| 49 | for (size_t i = 0; i < length; i++) |
| 50 | s.push_back(c: alphanum[rand() % (sizeof(alphanum) - 1)]); |
| 51 | return s; |
| 52 | } |
| 53 | |
| 54 | pid_t DoFork(const std::vector<std::string> &exec, const std::string &token, const std::string &baseId) |
| 55 | { |
| 56 | int fds[2]; |
| 57 | if (pipe(pipefd: fds) == -1) |
| 58 | { |
| 59 | std::cerr << RED("failed to create pipe" ) << std::endl; |
| 60 | return -1; |
| 61 | } |
| 62 | |
| 63 | pid_t pid = fork(); |
| 64 | if (pid == 0) |
| 65 | { |
| 66 | // child write to pipe a status code, so close the read end |
| 67 | close(fd: fds[0]); |
| 68 | fcntl(fd: fds[1], F_SETFD, FD_CLOEXEC); |
| 69 | |
| 70 | std::vector<const char *> args; |
| 71 | for (const auto &arg : exec) |
| 72 | args.push_back(x: arg.c_str()); |
| 73 | args.push_back(x: nullptr); |
| 74 | |
| 75 | // redirect stdout and stderr to /tmp/log/<baseid>/<pid>.log |
| 76 | ExecUtils::RedirectLogFd(unitBase: baseId, fileName: std::to_string(val: getpid())); |
| 77 | setenv(name: "MOS_SERVICE_TOKEN" , value: token.c_str(), overwrite: true); |
| 78 | |
| 79 | const auto err = execve(path: exec[0].c_str(), argv: (char **) args.data(), envp: environ); |
| 80 | if (err == -1) |
| 81 | { |
| 82 | std::cerr << RED("failed to execute" ) << " " << exec[0] << ": " << strerror(errno) << std::endl; |
| 83 | // write error code to pipe |
| 84 | int error_code = errno; |
| 85 | write(fd: fds[1], buffer: &error_code, size: sizeof(error_code)); |
| 86 | close(fd: fds[1]); |
| 87 | _exit(status: 1); |
| 88 | } |
| 89 | // if execve was successful, we should never reach here |
| 90 | std::cerr << RED("unreachable code" ) << std::endl; |
| 91 | __builtin_unreachable(); |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | // parent process read from pipe to get the status code |
| 96 | close(fd: fds[1]); |
| 97 | |
| 98 | int status_code = 0; |
| 99 | if (const auto ret = read(fd: fds[0], buffer: &status_code, size: sizeof(status_code)); ret == 0) |
| 100 | { |
| 101 | // child process has closed the pipe, which means it has executed successfully |
| 102 | close(fd: fds[0]); |
| 103 | return pid; |
| 104 | } |
| 105 | else |
| 106 | { |
| 107 | close(fd: fds[0]); |
| 108 | std::cerr << RED("failed to start process" ) << " " << exec[0] << ": " << strerror(errnum: status_code) << std::to_string(val: status_code) << std::endl; |
| 109 | return status_code; |
| 110 | } |
| 111 | } |
| 112 | } // namespace ExecUtils |
| 113 | |