#include "sys.h" #include #include #include #include #include #include #include #include #include "globals.h" namespace sys { 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) { // TODO: Implement this in C++ instead std::vector argv; argv.push_back("mkdir"); argv.push_back("-p"); argv.push_back(path.c_str()); execute(argv, nullptr); } void execute(std::vector &args, std::string *output) { if (global::verbose) { for (size_t i = 0; i < args.size(); ++i) { if (i == 0) { fprintf(stderr, "%s", args[i]); } else { fprintf(stderr, " %s", args[i]); } } fprintf(stderr, "\n"); } // 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]) + " \"$@\""; std::vector argv; 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]); } argv.push_back(nullptr); int fds[2]; if (pipe(fds) < 0) { throw std::runtime_error(std::string("fork: ") + strerror(errno)); } pid_t child = fork(); if (child == 0) { close(fds[0]); dup2(fds[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(); } } else if (child < 0) { throw std::runtime_error(std::string("fork: ") + strerror(errno)); } close(fds[1]); if (output == nullptr) { close(fds[0]); } else { char buf[1025]; while (true) { ssize_t num = read(fds[0], buf, sizeof(buf) - 1); if (num < 0 && errno != EAGAIN) { close(fds[0]); throw std::runtime_error(std::string("read: ") + strerror(errno)); } else if (num < 0) { continue; } if (num == 0) { close(fds[1]); break; } buf[num] = '\0'; *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 (output != nullptr && output->back() == '\n') { output->pop_back(); } } }