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.3KB

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