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 4.3KB

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