PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/util/process.cpp

https://github.com/tmjnaid/hiphop-php
C++ | 489 lines | 383 code | 65 blank | 41 comment | 122 complexity | 83a3a85b6e311e31c74475b3652d06d3 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "process.h"
  17. #include "base.h"
  18. #include "util.h"
  19. #include "async_func.h"
  20. #include "text_color.h"
  21. #include <pwd.h>
  22. using namespace std;
  23. namespace HPHP {
  24. ///////////////////////////////////////////////////////////////////////////////
  25. // helpers
  26. static void swap_fd(const string &filename, FILE *fdesc) {
  27. FILE *f = fopen(filename.c_str(), "a");
  28. if (f == NULL || dup2(fileno(f), fileno(fdesc)) < 0) {
  29. if (f) fclose(f);
  30. _exit(-1);
  31. }
  32. }
  33. ///////////////////////////////////////////////////////////////////////////////
  34. class FileReader {
  35. public:
  36. FileReader(FilePtr f, string &out) : m_f(f), m_out(out) {}
  37. void read() { readString(m_f.get(), m_out); }
  38. static void readString(FILE *f, string &out) {
  39. size_t nread = 0;
  40. const unsigned int BUFFER_SIZE = 1024;
  41. char buf[BUFFER_SIZE];
  42. while ((nread = fread(buf, 1, BUFFER_SIZE, f)) != 0) {
  43. out.append(buf, nread);
  44. }
  45. }
  46. private:
  47. FilePtr m_f;
  48. string &m_out;
  49. };
  50. ///////////////////////////////////////////////////////////////////////////////
  51. // Cached process statics
  52. std::string Process::HostName;
  53. std::string Process::CurrentWorkingDirectory;
  54. void Process::InitProcessStatics() {
  55. HostName = GetHostName();
  56. CurrentWorkingDirectory = GetCurrentDirectory();
  57. }
  58. bool Process::Exec(const char *path, const char *argv[], const char *in,
  59. string &out, string *err /* = NULL */,
  60. bool color /* = false */) {
  61. int fdin = 0; int fdout = 0; int fderr = 0;
  62. int pid = Exec(path, argv, &fdin, &fdout, &fderr);
  63. if (pid == 0) return false;
  64. {
  65. FilePtr sin(fdopen(fdin, "w"), file_closer());
  66. if (!sin) return false;
  67. if (in && *in) {
  68. fwrite(in, 1, strlen(in), sin.get());
  69. }
  70. }
  71. char buffer[4096];
  72. if (fcntl(fdout, F_SETFL, O_NONBLOCK)) {
  73. perror("fcntl failed on fdout");
  74. }
  75. if (fcntl(fderr, F_SETFL, O_NONBLOCK)) {
  76. perror("fcntl failed on fderr");
  77. }
  78. while (fdout || fderr) {
  79. pollfd fds[2];
  80. int n = 0;
  81. if (fdout) {
  82. fds[n].fd = fdout;
  83. fds[n].events = POLLIN | POLLHUP;
  84. n++;
  85. }
  86. if (fderr) {
  87. fds[n].fd = fderr;
  88. fds[n].events = POLLIN | POLLHUP;
  89. n++;
  90. }
  91. n = poll(fds, n, -1);
  92. if (n < 0) {
  93. continue;
  94. }
  95. n = 0;
  96. if (fdout) {
  97. if (fds[n++].revents & (POLLIN | POLLHUP)) {
  98. int e = read(fdout, buffer, sizeof buffer);
  99. if (e <= 0) {
  100. close(fdout);
  101. fdout = 0;
  102. } else {
  103. if (color && Util::s_stdout_color) {
  104. out.append(Util::s_stdout_color);
  105. out.append(buffer, e);
  106. out.append(ANSI_COLOR_END);
  107. } else {
  108. out.append(buffer, e);
  109. }
  110. }
  111. }
  112. }
  113. if (fderr) {
  114. if (fds[n++].revents & (POLLIN | POLLHUP)) {
  115. int e = read(fderr, buffer, sizeof buffer);
  116. if (e <= 0) {
  117. close(fderr);
  118. fderr = 0;
  119. } else if (err) {
  120. if (color && Util::s_stdout_color) {
  121. err->append(Util::s_stderr_color);
  122. err->append(buffer, e);
  123. err->append(ANSI_COLOR_END);
  124. } else {
  125. err->append(buffer, e);
  126. }
  127. }
  128. }
  129. }
  130. }
  131. int status;
  132. bool ret = false;
  133. if (waitpid(pid, &status, 0) != pid) {
  134. Logger::Error("Failed to wait for `%s'\n", path);
  135. } else if (WIFEXITED(status)) {
  136. if (WEXITSTATUS(status) != 0) {
  137. Logger::Verbose("Status %d running command: `%s'\n",
  138. WEXITSTATUS(status), path);
  139. while (*argv) {
  140. Logger::Verbose(" arg: `%s'\n", *argv);
  141. argv++;
  142. }
  143. } else {
  144. ret = true;
  145. }
  146. } else {
  147. Logger::Verbose("Non-normal exit\n");
  148. if (WIFSIGNALED(status)) {
  149. Logger::Verbose(" signaled with %d\n", WTERMSIG(status));
  150. }
  151. }
  152. return ret;
  153. }
  154. int Process::Exec(const std::string &cmd, const std::string &outf,
  155. const std::string &errf) {
  156. vector<string> argvs;
  157. Util::split(' ', cmd.c_str(), argvs);
  158. if (argvs.empty()) {
  159. return -1;
  160. }
  161. int pid = fork();
  162. if (pid < 0) {
  163. Logger::Error("Unable to fork: %d %s", errno,
  164. Util::safe_strerror(errno).c_str());
  165. return 0;
  166. }
  167. if (pid == 0) {
  168. signal(SIGTSTP,SIG_IGN);
  169. swap_fd(outf, stdout);
  170. swap_fd(errf, stderr);
  171. int count = argvs.size();
  172. char **argv = (char**)calloc(count + 1, sizeof(char*));
  173. for (int i = 0; i < count; i++) {
  174. argv[i] = (char*)argvs[i].c_str();
  175. }
  176. argv[count] = NULL;
  177. execvp(argv[0], argv);
  178. Logger::Error("Failed to exec `%s'\n", cmd.c_str());
  179. _exit(-1);
  180. }
  181. int status = -1;
  182. wait(&status);
  183. return status;
  184. }
  185. int Process::Exec(const char *path, const char *argv[], int *fdin, int *fdout,
  186. int *fderr) {
  187. CPipe pipein, pipeout, pipeerr;
  188. if (!pipein.open() || !pipeout.open() || !pipeerr.open()) {
  189. return 0;
  190. }
  191. int pid = fork();
  192. if (pid < 0) {
  193. Logger::Error("Unable to fork: %d %s", errno,
  194. Util::safe_strerror(errno).c_str());
  195. return 0;
  196. }
  197. if (pid == 0) {
  198. /**
  199. * I don't know why, but things work alot better if this process ignores
  200. * the tstp signal (ctrl-Z). If not, it locks up if you hit ctrl-Z then
  201. * "bg" the program.
  202. */
  203. signal(SIGTSTP,SIG_IGN);
  204. if (pipein.dupOut2(fileno(stdin)) && pipeout.dupIn2(fileno(stdout)) &&
  205. pipeerr.dupIn2(fileno(stderr))) {
  206. pipeout.close(); pipeerr.close(); pipein.close();
  207. const char *argvnull[2] = {"", NULL};
  208. execvp(path, const_cast<char**>(argv ? argv : argvnull));
  209. }
  210. Logger::Error("Failed to exec `%s'\n", path);
  211. _exit(-1);
  212. }
  213. if (fdout) *fdout = pipeout.detachOut();
  214. if (fderr) *fderr = pipeerr.detachOut();
  215. if (fdin) *fdin = pipein.detachIn();
  216. return pid;
  217. }
  218. /**
  219. * Copied from http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize
  220. */
  221. #define EXIT_SUCCESS 0
  222. #define EXIT_FAILURE 1
  223. void Process::Daemonize(const char *stdoutFile /* = "/dev/null" */,
  224. const char *stderrFile /* = "/dev/null" */) {
  225. pid_t pid, sid;
  226. /* already a daemon */
  227. if (getppid() == 1) return;
  228. /* Fork off the parent process */
  229. pid = fork();
  230. if (pid < 0) {
  231. exit(EXIT_FAILURE);
  232. }
  233. /* If we got a good PID, then we can exit the parent process. */
  234. if (pid > 0) {
  235. exit(EXIT_SUCCESS);
  236. }
  237. /* At this point we are executing as the child process */
  238. /* Change the file mode mask */
  239. umask(0);
  240. /* Create a new SID for the child process */
  241. sid = setsid();
  242. if (sid < 0) {
  243. exit(EXIT_FAILURE);
  244. }
  245. /* Change the current working directory. This prevents the current
  246. directory from being locked; hence not being able to remove it. */
  247. if ((chdir("/")) < 0) {
  248. exit(EXIT_FAILURE);
  249. }
  250. /* Redirect standard files to /dev/null */
  251. if (!freopen("/dev/null", "r", stdin)) exit(EXIT_FAILURE);
  252. if (stdoutFile && *stdoutFile) {
  253. if (!freopen(stdoutFile, "a", stdout)) exit(EXIT_FAILURE);
  254. } else {
  255. if (!freopen("/dev/null", "w", stdout)) exit(EXIT_FAILURE);
  256. }
  257. if (stderrFile && *stderrFile) {
  258. if (!freopen(stderrFile, "a", stderr)) exit(EXIT_FAILURE);
  259. } else {
  260. if (!freopen("/dev/null", "w", stderr)) exit(EXIT_FAILURE);
  261. }
  262. }
  263. ///////////////////////////////////////////////////////////////////////////////
  264. // /proc/* parsing functions
  265. pid_t Process::GetProcessId(const std::string &cmd,
  266. bool matchAll /* = false */) {
  267. std::vector<pid_t> pids;
  268. GetProcessId(cmd, pids, matchAll);
  269. return pids.empty() ? 0 : pids[0];
  270. }
  271. void Process::GetProcessId(const std::string &cmd, std::vector<pid_t> &pids,
  272. bool matchAll /* = false */) {
  273. const char *argv[] = {"", "/proc", "-regex", "/proc/[0-9]+/cmdline", NULL};
  274. string out;
  275. Exec("find", argv, NULL, out);
  276. vector<string> files;
  277. Util::split('\n', out.c_str(), files, true);
  278. string ccmd = cmd;
  279. if (!matchAll) {
  280. size_t pos = ccmd.find(' ');
  281. if (pos != string::npos) {
  282. ccmd = ccmd.substr(0, pos);
  283. }
  284. pos = ccmd.rfind('/');
  285. if (pos != string::npos) {
  286. ccmd = ccmd.substr(pos + 1);
  287. }
  288. } else {
  289. ccmd += " ";
  290. }
  291. for (unsigned int i = 0; i < files.size(); i++) {
  292. string &filename = files[i];
  293. FILE * f = fopen(filename.c_str(), "r");
  294. if (f) {
  295. string cmdline;
  296. FileReader::readString(f, cmdline);
  297. fclose(f);
  298. string converted;
  299. if (matchAll) {
  300. for (unsigned int i = 0; i < cmdline.size(); i++) {
  301. char ch = cmdline[i];
  302. converted += ch ? ch : ' ';
  303. }
  304. } else {
  305. converted = cmdline;
  306. size_t pos = converted.find('\0');
  307. if (pos != string::npos) {
  308. converted = converted.substr(0, pos);
  309. }
  310. pos = converted.rfind('/');
  311. if (pos != string::npos) {
  312. converted = converted.substr(pos + 1);
  313. }
  314. }
  315. if (converted == ccmd && filename.find("/proc/") == 0) {
  316. long long pid = atoll(filename.c_str() + strlen("/proc/"));
  317. if (pid) {
  318. pids.push_back(pid);
  319. }
  320. }
  321. }
  322. }
  323. }
  324. std::string Process::GetCommandLine(pid_t pid) {
  325. string name = "/proc/" + boost::lexical_cast<string>((long long)pid) +
  326. "/cmdline";
  327. string cmdline;
  328. FILE * f = fopen(name.c_str(), "r");
  329. if (f) {
  330. FileReader::readString(f, cmdline);
  331. fclose(f);
  332. }
  333. string converted;
  334. for (unsigned int i = 0; i < cmdline.size(); i++) {
  335. char ch = cmdline[i];
  336. converted += ch ? ch : ' ';
  337. }
  338. return converted;
  339. }
  340. bool Process::CommandStartsWith(pid_t pid, const std::string &cmd) {
  341. if (!cmd.empty()) {
  342. std::string cmdline = GetCommandLine(pid);
  343. if (cmdline.length() >= cmd.length() &&
  344. cmdline.substr(0, cmd.length()) == cmd) {
  345. return true;
  346. }
  347. }
  348. return false;
  349. }
  350. bool Process::IsUnderGDB() {
  351. return CommandStartsWith(GetParentProcessId(), "gdb ");
  352. }
  353. int Process::GetProcessRSS(pid_t pid) {
  354. string name = "/proc/" + boost::lexical_cast<string>((long long)pid) +
  355. "/status";
  356. string status;
  357. FILE * f = fopen(name.c_str(), "r");
  358. if (f) {
  359. FileReader::readString(f, status);
  360. fclose(f);
  361. }
  362. vector<string> lines;
  363. Util::split('\n', status.c_str(), lines, true);
  364. for (unsigned int i = 0; i < lines.size(); i++) {
  365. string &line = lines[i];
  366. if (line.find("VmRSS:") == 0) {
  367. for (unsigned int j = strlen("VmRSS:"); j < line.size(); j++) {
  368. if (line[j] != ' ') {
  369. long long mem = atoll(line.c_str() + j);
  370. return mem/1024;
  371. }
  372. }
  373. }
  374. }
  375. return 0;
  376. }
  377. ///////////////////////////////////////////////////////////////////////////////
  378. std::string Process::GetAppName() {
  379. const char* progname = getenv("_");
  380. if (!progname || !*progname) {
  381. progname = "unknown program";
  382. }
  383. return progname;
  384. }
  385. std::string Process::GetHostName() {
  386. char hostbuf[128];
  387. gethostname(hostbuf, 127);
  388. hostbuf[127] = '\0';
  389. return hostbuf;
  390. }
  391. std::string Process::GetCurrentUser() {
  392. const char *name = getenv("LOGNAME");
  393. if (name && *name) {
  394. return name;
  395. }
  396. passwd *pwd = getpwuid(geteuid());
  397. if (pwd && pwd->pw_name) {
  398. return pwd->pw_name;
  399. }
  400. return "";
  401. }
  402. std::string Process::GetCurrentDirectory() {
  403. char buf[PATH_MAX];
  404. memset(buf, 0, PATH_MAX);
  405. return getcwd(buf, PATH_MAX);
  406. }
  407. std::string Process::GetHomeDirectory() {
  408. string ret;
  409. const char *home = getenv("HOME");
  410. if (home && *home) {
  411. ret = home;
  412. } else {
  413. passwd *pwd = getpwent();
  414. if (pwd && pwd->pw_dir) {
  415. ret = pwd->pw_dir;
  416. }
  417. }
  418. if (ret.empty() || ret[ret.size() - 1] != '/') {
  419. ret += '/';
  420. }
  421. return ret;
  422. }
  423. ///////////////////////////////////////////////////////////////////////////////
  424. }