|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #include "sys.h"
-
- #include <libgen.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <dirent.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <stdexcept>
- #include <system_error>
- #include <unordered_set>
- #include <atomic>
-
- #include "logger.h"
- #include "globals.h"
-
- namespace sys {
-
- static thread_local std::unordered_set<std::string> mkdirp_set;
-
- static std::atomic<int> signalCounter(0);
-
- struct SigCtx {
- SigCtx(bool enabled): enabled(enabled) {
- if (enabled && signalCounter++ == 0) {
- signal(SIGINT, SIG_IGN);
- }
- }
-
- ~SigCtx() {
- if (enabled && --signalCounter == 0) {
- signal(SIGINT, SIG_DFL);
- }
- }
-
- bool enabled;
- };
-
- std::string sanitizePath(const std::string &path) {
- std::string npath;
- size_t idx = 0;
- while (idx < path.size()) {
- if (idx >= path.size() - 2) {
- npath += path[idx++];
- } else if (path[idx] == '.' && path[idx + 1] == '.' && path[idx + 2] == '/') {
- npath += "__parent__/";
- idx += 3;
- } else {
- npath += path[idx++];
- }
- }
-
- if (npath[npath.size() - 1] == '.' && npath[npath.size() - 2] == '.') {
- npath.replace(npath.size() - 2, 2, "__parent__");
- }
-
- return npath;
- }
-
- FileInfo fileInfo(const std::string &path) {
- FileInfo finfo;
-
- struct stat st;
- if (stat(path.c_str(), &st) < 0) {
- throw std::runtime_error("stat '" + path + "': " + strerror(errno));
- }
-
- finfo.mTimeSec = st.st_mtim.tv_sec;
- finfo.mTimeNsec = st.st_mtim.tv_nsec;
- finfo.isDir = S_ISDIR(st.st_mode);
- return finfo;
- }
-
- bool fileExists(const std::string &path) {
- struct stat st;
- if (stat(path.c_str(), &st) < 0) {
- if (errno == ENOENT) {
- return false;
- } else {
- throw std::runtime_error("stat '" + path + "': " + strerror(errno));
- }
- }
-
- return true;
- }
-
- void mkdirp(const std::string &path) {
- auto res = mkdirp_set.emplace(path);
- if (!res.second)
- return;
-
- // TODO: Implement this in C++ instead
- std::vector<std::string> argv;
- argv.push_back("mkdir");
- argv.push_back("-p");
- argv.push_back("--");
- argv.push_back(path);
- ProcConf conf;
- conf.print = global::verbose >= 1;
- conf.prefix = "(MKDIRP)";
- execute(argv, conf);
- }
-
- void rmrf(const std::string &path) {
- mkdirp_set.erase(path);
- // TODO: Implement this in C++ instead
- std::vector<std::string> argv;
- argv.push_back("rm");
- argv.push_back("-rf");
- argv.push_back("--");
- argv.push_back(path);
- ProcConf conf;
- conf.print = global::verbose >= 1;
- conf.prefix = "(RMRF)";
- execute(argv, conf);
- }
-
- void execute(const std::vector<std::string> &args, const ProcConf &conf) {
- if (conf.print || global::verbose >= 2) {
- std::string str;
- for (size_t i = 0; i < args.size(); ++i) {
- if (i != 0) {
- str += " ";
- }
- str += args[i];
- }
- if (conf.prefix.size() > 0) {
- str = conf.prefix + " " + str;
- }
- logger::log(str);
- }
-
- // argv[0] should be interpreted as a shell command, because being able to run
- // CC='gcc --sysroot=/blah' is used by some systems.
- std::string command = std::string(args[0]) + " \"$@\"";
- static thread_local std::vector<const char *> argv;
- argv.clear();
- argv.push_back("/bin/sh"); // TODO: Use $SHELL?
- argv.push_back("-c");
- argv.push_back(command.c_str());
- argv.push_back("--");
- for (size_t i = 1; i < args.size(); ++i) {
- argv.push_back(args[i].c_str());
- }
- argv.push_back(nullptr);
-
- int stdoutPipe[2];
- if (conf.output == nullptr) {
- stdoutPipe[1] = STDOUT_FILENO;
- } else {
- if (pipe(stdoutPipe) < 0) {
- throw std::runtime_error(std::string("pipe: ") + strerror(errno));
- }
- }
-
- SigCtx sigCtx(conf.forwardSignals);
- pid_t child = fork();
- if (child == 0) {
- signal(SIGINT, SIG_DFL);
- if (conf.output != nullptr) {
- close(stdoutPipe[0]);
- dup2(stdoutPipe[1], 1);
- }
-
- // So, from what I've read, execvp should never modify its argv; so this should be fine?
- if (execvp(argv[0], (char *const *)argv.data()) < 0) {
- perror(argv[0]);
- abort();
- }
-
- // This shouldn't happen
- abort();
- } else if (child < 0) {
- throw std::runtime_error(std::string("fork: ") + strerror(errno));
- }
-
- if (conf.output != nullptr) {
- close(stdoutPipe[1]);
- char buf[1025];
- while (true) {
- ssize_t num = read(stdoutPipe[0], buf, sizeof(buf) - 1);
- if (num < 0 && errno != EAGAIN) {
- close(stdoutPipe[0]);
- throw std::runtime_error(
- std::string("read: ") + strerror(errno) +
- " (fd: " + std::to_string(stdoutPipe[0]) + ")");
- } else if (num < 0) {
- continue;
- }
-
- if (num == 0) {
- close(stdoutPipe[0]);
- break;
- }
-
- buf[num] = '\0';
- *conf.output += buf;
- }
- }
-
- int wstatus;
- waitpid(child, &wstatus, 0);
- if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
- throw std::runtime_error(std::string(args[0]) +
- " exited with code " + std::to_string(WEXITSTATUS(wstatus)));
- }
-
- if (WTERMSIG(wstatus)) {
- throw std::runtime_error(std::string(args[0]) +
- " terminated due to " + strsignal(WTERMSIG(wstatus)));
- }
-
- if (conf.output != nullptr && conf.output->back() == '\n') {
- conf.output->pop_back();
- }
- }
-
- void readDir(const std::string &path, std::vector<std::string> &files) {
- DIR *d = opendir(path.c_str());
- if (d == NULL) {
- throw std::runtime_error("opendir '" + path + "': " + strerror(errno));
- }
-
- struct dirent *ent;
- while ((ent = readdir(d)) != NULL) {
- if (ent->d_name[0] == '.')
- continue;
- files.emplace_back(ent->d_name);
- }
-
- closedir(d);
- }
-
- void chdir(const std::string &path) {
- if (::chdir(path.c_str()) < 0) {
- throw std::runtime_error("chdir '" + path + "': " + strerror(errno));
- }
- }
-
- std::string cwd() {
- std::vector<char> buf;
- buf.reserve(128);
- while (1) {
- char *res = getcwd(buf.data(), buf.capacity());
- if (res == NULL) {
- if (errno == ERANGE) {
- buf.reserve(buf.capacity() * 2);
- } else {
- throw std::system_error(EFAULT, std::generic_category());
- }
- } else {
- std::string str(buf.data());
- return str;
- }
- }
- }
-
- void symlink(const std::string &from, const std::string &to) {
- // TODO: Implement this in C++ instead
- std::vector<std::string> argv;
- argv.push_back("ln");
- argv.push_back("-s");
- argv.push_back("-f");
- argv.push_back("--");
- argv.push_back(from);
- argv.push_back(to);
- sys::execute(argv, ProcConf{});
- }
-
- std::string dirname(const std::string &path) {
- std::vector<char> buf(path.begin(), path.end());
- buf.push_back('\0');
- char *dir = ::dirname(buf.data());
- return std::string(dir);
- }
-
- }
|