Build tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sys.cc 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #include "sys.h"
  2. #include <libgen.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <unistd.h>
  6. #include <dirent.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <sys/types.h>
  10. #include <sys/wait.h>
  11. #include <stdexcept>
  12. #include <system_error>
  13. #include <unordered_set>
  14. #include "logger.h"
  15. #include "globals.h"
  16. namespace sys {
  17. static thread_local std::unordered_set<std::string> mkdirp_set;
  18. std::string sanitizePath(const std::string &path) {
  19. std::string npath;
  20. size_t idx = 0;
  21. while (idx < path.size()) {
  22. if (idx >= path.size() - 2) {
  23. npath += path[idx++];
  24. } else if (path[idx] == '.' && path[idx + 1] == '.' && path[idx + 2] == '/') {
  25. npath += "__parent__/";
  26. idx += 3;
  27. } else {
  28. npath += path[idx++];
  29. }
  30. }
  31. if (npath[npath.size() - 1] == '.' && npath[npath.size() - 2] == '.') {
  32. npath.replace(npath.size() - 2, 2, "__parent__");
  33. }
  34. return npath;
  35. }
  36. FileInfo fileInfo(const std::string &path) {
  37. FileInfo finfo;
  38. struct stat st;
  39. if (stat(path.c_str(), &st) < 0) {
  40. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  41. }
  42. finfo.mTimeSec = st.st_mtim.tv_sec;
  43. finfo.mTimeNsec = st.st_mtim.tv_nsec;
  44. finfo.isDir = S_ISDIR(st.st_mode);
  45. return finfo;
  46. }
  47. bool fileExists(const std::string &path) {
  48. struct stat st;
  49. if (stat(path.c_str(), &st) < 0) {
  50. if (errno == ENOENT) {
  51. return false;
  52. } else {
  53. throw std::runtime_error("stat '" + path + "': " + strerror(errno));
  54. }
  55. }
  56. return true;
  57. }
  58. void mkdirp(const std::string &path) {
  59. auto res = mkdirp_set.emplace(path);
  60. if (!res.second)
  61. return;
  62. // TODO: Implement this in C++ instead
  63. std::vector<std::string> argv;
  64. argv.push_back("mkdir");
  65. argv.push_back("-p");
  66. argv.push_back("--");
  67. argv.push_back(path);
  68. execute(argv, nullptr, global::verbose >= 2);
  69. }
  70. void rmrf(const std::string &path) {
  71. mkdirp_set.erase(path);
  72. // TODO: Implement this in C++ instead
  73. std::vector<std::string> argv;
  74. argv.push_back("rm");
  75. argv.push_back("-rf");
  76. argv.push_back("--");
  77. argv.push_back(path);
  78. execute(argv, nullptr, global::verbose >= 2);
  79. }
  80. void execute(const std::vector<std::string> &args, std::string *output, bool print) {
  81. if (print) {
  82. std::string str;
  83. for (size_t i = 0; i < args.size(); ++i) {
  84. if (i != 0) {
  85. str += " ";
  86. }
  87. str += args[i];
  88. }
  89. logger::log(str);
  90. }
  91. // argv[0] should be interpreted as a shell command, because being able to run
  92. // CC='gcc --sysroot=/blah' is used by some systems.
  93. std::string command = std::string(args[0]) + " \"$@\"";
  94. static thread_local std::vector<const char *> argv;
  95. argv.clear();
  96. argv.push_back("/bin/sh"); // TODO: Use $SHELL?
  97. argv.push_back("-c");
  98. argv.push_back(command.c_str());
  99. argv.push_back("--");
  100. for (size_t i = 1; i < args.size(); ++i) {
  101. argv.push_back(args[i].c_str());
  102. }
  103. argv.push_back(nullptr);
  104. int fds[2];
  105. if (pipe(fds) < 0) {
  106. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  107. }
  108. pid_t child = fork();
  109. if (child == 0) {
  110. close(fds[0]);
  111. dup2(fds[1], 1);
  112. // So, from what I've read, execvp should never modify its argv; so this should be fine?
  113. if (execvp(argv[0], (char *const *)argv.data()) < 0) {
  114. perror(argv[0]);
  115. abort();
  116. }
  117. // This shouldn't happen
  118. exit(0);
  119. } else if (child < 0) {
  120. throw std::runtime_error(std::string("fork: ") + strerror(errno));
  121. }
  122. close(fds[1]);
  123. if (output == nullptr) {
  124. close(fds[0]);
  125. } else {
  126. char buf[1025];
  127. while (true) {
  128. ssize_t num = read(fds[0], buf, sizeof(buf) - 1);
  129. if (num < 0 && errno != EAGAIN) {
  130. close(fds[0]);
  131. throw std::runtime_error(
  132. std::string("read: ") + strerror(errno) +
  133. " (fd: " + std::to_string(fds[0]) + ")");
  134. } else if (num < 0) {
  135. continue;
  136. }
  137. if (num == 0) {
  138. close(fds[0]);
  139. break;
  140. }
  141. buf[num] = '\0';
  142. *output += buf;
  143. }
  144. }
  145. int wstatus;
  146. waitpid(child, &wstatus, 0);
  147. if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) {
  148. throw std::runtime_error(std::string(args[0]) +
  149. " exited with code " + std::to_string(WEXITSTATUS(wstatus)));
  150. }
  151. if (WTERMSIG(wstatus)) {
  152. throw std::runtime_error(std::string(args[0]) +
  153. " terminated due to " + strsignal(WTERMSIG(wstatus)));
  154. }
  155. if (output != nullptr && output->back() == '\n') {
  156. output->pop_back();
  157. }
  158. }
  159. void readDir(const std::string &path, std::vector<std::string> &files) {
  160. DIR *d = opendir(path.c_str());
  161. if (d == NULL) {
  162. throw std::runtime_error("opendir '" + path + "': " + strerror(errno));
  163. }
  164. struct dirent *ent;
  165. while ((ent = readdir(d)) != NULL) {
  166. if (ent->d_name[0] == '.')
  167. continue;
  168. files.emplace_back(ent->d_name);
  169. }
  170. closedir(d);
  171. }
  172. void chdir(const std::string &path) {
  173. if (::chdir(path.c_str()) < 0) {
  174. throw std::runtime_error("chdir '" + path + "': " + strerror(errno));
  175. }
  176. }
  177. std::string cwd() {
  178. std::vector<char> buf;
  179. buf.reserve(128);
  180. while (1) {
  181. char *res = getcwd(buf.data(), buf.capacity());
  182. if (res == NULL) {
  183. if (errno == ERANGE) {
  184. buf.reserve(buf.capacity() * 2);
  185. } else {
  186. throw std::system_error(EFAULT, std::generic_category());
  187. }
  188. } else {
  189. std::string str(buf.data());
  190. return str;
  191. }
  192. }
  193. }
  194. void symlink(const std::string &from, const std::string &to) {
  195. // TODO: Implement this in C++ instead
  196. std::vector<std::string> argv;
  197. argv.push_back("ln");
  198. argv.push_back("-s");
  199. argv.push_back("-f");
  200. argv.push_back("--");
  201. argv.push_back(from);
  202. argv.push_back(to);
  203. sys::execute(argv, nullptr, global::verbose > 2);
  204. }
  205. std::string dirname(const std::string &path) {
  206. std::vector<char> buf(path.begin(), path.end());
  207. buf.push_back('\0');
  208. char *dir = ::dirname(buf.data());
  209. return std::string(dir);
  210. }
  211. }