PageRenderTime 1652ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/ext/std/ext_std_process.cpp

https://gitlab.com/iranjith4/hhvm
C++ | 764 lines | 603 code | 96 blank | 65 comment | 144 complexity | 9d4dcb828f056fa111f802baecdc3b2a MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
  6. | Copyright (c) 1997-2010 The PHP Group |
  7. +----------------------------------------------------------------------+
  8. | This source file is subject to version 3.01 of the PHP license, |
  9. | that is bundled with this package in the file LICENSE, and is |
  10. | available through the world-wide-web at the following url: |
  11. | http://www.php.net/license/3_01.txt |
  12. | If you did not receive a copy of the PHP license and are unable to |
  13. | obtain it through the world-wide-web, please send a note to |
  14. | license@php.net so we can mail you a copy immediately. |
  15. +----------------------------------------------------------------------+
  16. */
  17. #include "hphp/runtime/ext/std/ext_std_process.h"
  18. #include <cstdlib>
  19. #include <vector>
  20. #include <string>
  21. #include <iostream>
  22. #include <sys/time.h>
  23. #include <sys/resource.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <signal.h>
  27. #include <folly/String.h>
  28. #include <folly/portability/Environment.h>
  29. #include "hphp/util/light-process.h"
  30. #include "hphp/util/lock.h"
  31. #include "hphp/util/logger.h"
  32. #include "hphp/runtime/base/array-init.h"
  33. #include "hphp/runtime/base/builtin-functions.h"
  34. #include "hphp/runtime/base/plain-file.h"
  35. #include "hphp/runtime/base/request-local.h"
  36. #include "hphp/runtime/base/string-buffer.h"
  37. #include "hphp/runtime/base/surprise-flags.h"
  38. #include "hphp/runtime/base/thread-info.h"
  39. #include "hphp/runtime/base/string-util.h"
  40. #include "hphp/runtime/base/zend-string.h"
  41. #include "hphp/runtime/ext/std/ext_std.h"
  42. #include "hphp/runtime/ext/std/ext_std_file.h"
  43. #include "hphp/runtime/ext/std/ext_std_function.h"
  44. #include "hphp/runtime/ext/string/ext_string.h"
  45. #include "hphp/runtime/vm/repo.h"
  46. #include "hphp/runtime/base/request-event-handler.h"
  47. #if !defined(_NSIG) && defined(NSIG)
  48. # define _NSIG NSIG
  49. #endif
  50. namespace HPHP {
  51. ///////////////////////////////////////////////////////////////////////////////
  52. // build environment pair list
  53. static char **build_envp(const Array& envs, std::vector<String> &senvs) {
  54. char **envp = NULL;
  55. int size = envs.size();
  56. if (size) {
  57. envp = (char **)malloc((size + 1) * sizeof(char *));
  58. int i = 0;
  59. for (ArrayIter iter(envs); iter; ++iter, ++i) {
  60. StringBuffer nvpair;
  61. nvpair.append(iter.first().toString());
  62. nvpair.append('=');
  63. nvpair.append(iter.second().toString());
  64. String env = nvpair.detach();
  65. senvs.push_back(env);
  66. *(envp + i) = (char *)env.data();
  67. }
  68. *(envp + i) = NULL;
  69. }
  70. return envp;
  71. }
  72. // check whitelist
  73. static bool check_cmd(const char *cmd) {
  74. const char *cmd_tmp = cmd;
  75. while (true) {
  76. bool allow = false;
  77. while (isblank(*cmd_tmp)) cmd_tmp++;
  78. const char *space = strchr(cmd_tmp, ' ');
  79. unsigned int cmd_len = strlen(cmd_tmp);
  80. if (space) {
  81. cmd_len = space - cmd_tmp;
  82. }
  83. for (unsigned int i = 0; i < RuntimeOption::AllowedExecCmds.size(); i++) {
  84. std::string &allowedCmd = RuntimeOption::AllowedExecCmds[i];
  85. if (allowedCmd.size() != cmd_len) {
  86. continue;
  87. }
  88. if (strncmp(allowedCmd.c_str(), cmd_tmp, allowedCmd.size()) == 0) {
  89. allow = true;
  90. break;
  91. }
  92. }
  93. if (!allow) {
  94. auto const file = g_context->getContainingFileName();
  95. int line = g_context->getLine();
  96. Logger::Warning("Command %s is not in the whitelist, called at %s:%d",
  97. cmd_tmp, file->data(), line);
  98. if (!RuntimeOption::WhitelistExecWarningOnly) {
  99. return false;
  100. }
  101. }
  102. const char *bar = strchr(cmd_tmp, '|');
  103. if (!bar) { // no pipe, we are done
  104. return true;
  105. }
  106. cmd_tmp = bar + 1;
  107. }
  108. return false;
  109. }
  110. ///////////////////////////////////////////////////////////////////////////////
  111. void StandardExtension::initProcess() {
  112. HHVM_FE(shell_exec);
  113. HHVM_FE(exec);
  114. HHVM_FE(passthru);
  115. HHVM_FE(system);
  116. HHVM_FE(proc_open);
  117. HHVM_FE(proc_terminate);
  118. HHVM_FE(proc_close);
  119. HHVM_FE(proc_get_status);
  120. HHVM_FE(proc_nice);
  121. HHVM_FE(escapeshellarg);
  122. HHVM_FE(escapeshellcmd);
  123. loadSystemlib("std_process");
  124. }
  125. ///////////////////////////////////////////////////////////////////////////////
  126. // popen
  127. #define EXEC_INPUT_BUF 4096
  128. namespace {
  129. struct ShellExecContext final {
  130. ShellExecContext() {
  131. m_sig_handler = signal(SIGCHLD, SIG_DFL);
  132. }
  133. ~ShellExecContext() {
  134. if (m_proc) {
  135. LightProcess::pclose(m_proc);
  136. }
  137. if (m_sig_handler) {
  138. signal(SIGCHLD, m_sig_handler);
  139. }
  140. }
  141. FILE *exec(const String& cmd_string) {
  142. assert(m_proc == nullptr);
  143. const auto cmd = cmd_string.c_str();
  144. if (RuntimeOption::WhitelistExec && !check_cmd(cmd)) {
  145. return nullptr;
  146. }
  147. if (strlen(cmd) != cmd_string.size()) {
  148. raise_warning("NULL byte detected. Possible attack");
  149. return nullptr;
  150. }
  151. m_proc = LightProcess::popen(cmd, "r", g_context->getCwd().data());
  152. if (m_proc == nullptr) {
  153. raise_warning("Unable to execute '%s'", cmd);
  154. }
  155. return m_proc;
  156. }
  157. int exit() {
  158. int status = LightProcess::pclose(m_proc);
  159. m_proc = nullptr;
  160. return status;
  161. }
  162. private:
  163. void (*m_sig_handler)(int);
  164. FILE *m_proc{nullptr};
  165. };
  166. }
  167. Variant HHVM_FUNCTION(shell_exec,
  168. const String& cmd) {
  169. ShellExecContext ctx;
  170. FILE *fp = ctx.exec(cmd);
  171. if (!fp) return init_null();
  172. StringBuffer sbuf;
  173. sbuf.read(fp);
  174. auto ret = sbuf.detach();
  175. if (ret.empty() && !RuntimeOption::EnableHipHopSyntax) {
  176. // Match php5
  177. return init_null();
  178. }
  179. return ret;
  180. }
  181. String HHVM_FUNCTION(exec,
  182. const String& command,
  183. VRefParam output /* = null */,
  184. VRefParam return_var /* = null */) {
  185. ShellExecContext ctx;
  186. FILE *fp = ctx.exec(command);
  187. if (!fp) return empty_string();
  188. StringBuffer sbuf;
  189. sbuf.read(fp);
  190. Array lines = StringUtil::Explode(sbuf.detach(), "\n").toArray();
  191. int ret = ctx.exit();
  192. if (WIFEXITED(ret)) ret = WEXITSTATUS(ret);
  193. return_var.assignIfRef(ret);
  194. int count = lines.size();
  195. if (count > 0 && lines[count - 1].toString().empty()) {
  196. count--; // remove explode()'s last empty line
  197. }
  198. PackedArrayInit pai(count);
  199. for (int i = 0; i < count; i++) {
  200. pai.append(lines[i]);
  201. }
  202. output.assignIfRef(pai.toArray());
  203. if (!count || lines.empty()) {
  204. return String();
  205. }
  206. return HHVM_FN(rtrim)(lines[count - 1].toString());
  207. }
  208. void HHVM_FUNCTION(passthru,
  209. const String& command,
  210. VRefParam return_var /* = null */) {
  211. ShellExecContext ctx;
  212. FILE *fp = ctx.exec(command);
  213. if (!fp) return;
  214. char buffer[1024];
  215. while (true) {
  216. int len = read(fileno(fp), buffer, sizeof(buffer) - 1);
  217. if (len == -1 && errno == EINTR) continue;
  218. if (len <= 0) break; // break on error or EOF
  219. buffer[len] = '\0';
  220. g_context->write(String(buffer, len, CopyString));
  221. }
  222. int ret = ctx.exit();
  223. if (WIFEXITED(ret)) ret = WEXITSTATUS(ret);
  224. return_var.assignIfRef(ret);
  225. }
  226. String HHVM_FUNCTION(system,
  227. const String& command,
  228. VRefParam return_var /* = null */) {
  229. ShellExecContext ctx;
  230. FILE *fp = ctx.exec(command);
  231. if (!fp) return empty_string();
  232. StringBuffer sbuf;
  233. if (fp) {
  234. sbuf.read(fp);
  235. }
  236. Array lines = StringUtil::Explode(sbuf.detach(), "\n").toArray();
  237. int ret = ctx.exit();
  238. if (WIFEXITED(ret)) ret = WEXITSTATUS(ret);
  239. return_var.assignIfRef(ret);
  240. int count = lines.size();
  241. if (count > 0 && lines[count - 1].toString().empty()) {
  242. count--; // remove explode()'s last empty line
  243. }
  244. auto& ectx = *g_context;
  245. for (int i = 0; i < count; i++) {
  246. ectx.write(lines[i].toString());
  247. ectx.write("\n");
  248. }
  249. if (!count || lines.empty()) {
  250. return String();
  251. }
  252. return HHVM_FN(rtrim)(lines[count - 1].toString());
  253. }
  254. ///////////////////////////////////////////////////////////////////////////////
  255. // proc
  256. struct ChildProcess : SweepableResourceData {
  257. DECLARE_RESOURCE_ALLOCATION(ChildProcess)
  258. pid_t child;
  259. Array pipes;
  260. String command;
  261. Variant env;
  262. CLASSNAME_IS("process");
  263. // overriding ResourceData
  264. const String& o_getClassNameHook() const override { return classnameof(); }
  265. int close() {
  266. // Although the PHP doc about proc_close() says that the pipes need to be
  267. // explicitly pclose()'ed, it seems that Zend is implicitly closing the
  268. // pipes when proc_close() is called.
  269. for (ArrayIter iter(pipes); iter; ++iter) {
  270. cast<PlainFile>(iter.second())->close();
  271. }
  272. pipes.clear();
  273. pid_t wait_pid;
  274. int wstatus;
  275. do {
  276. wait_pid = LightProcess::waitpid(child, &wstatus, 0,
  277. RuntimeOption::RequestTimeoutSeconds);
  278. } while (wait_pid == -1 && errno == EINTR);
  279. if (wait_pid == -1) {
  280. return -1;
  281. }
  282. if (WIFEXITED(wstatus)) {
  283. wstatus = WEXITSTATUS(wstatus);
  284. }
  285. return wstatus;
  286. }
  287. };
  288. void ChildProcess::sweep() {
  289. // do nothing here, as everything will be collected by MemoryManager
  290. }
  291. #define DESC_PIPE 1
  292. #define DESC_FILE 2
  293. #define DESC_PARENT_MODE_WRITE 8
  294. const StaticString s_w("w");
  295. struct DescriptorItem {
  296. DescriptorItem() :
  297. index(-1), parentend(-1), childend(-1), mode(-1), mode_flags(-1) {
  298. }
  299. ~DescriptorItem() {
  300. }
  301. void cleanup() {
  302. if (childend >= 0) close(childend);
  303. if (parentend >= 0) close(parentend);
  304. }
  305. int index; // desired fd number in child process
  306. int parentend, childend; // fds for pipes in parent/child
  307. int mode; // mode for proc_open code
  308. int mode_flags; // mode flags for opening fds
  309. static Mutex s_mutex; // Prevents another thread from forking at the
  310. // same time, before FD_CLOEXEC is set on the fds.
  311. // NOTE: no need to lock with light processes.
  312. bool readFile(const req::ptr<File>& file) {
  313. mode = DESC_FILE;
  314. childend = dup(file->fd());
  315. if (childend < 0) {
  316. raise_warning("unable to dup File-Handle for descriptor %d - %s",
  317. index, folly::errnoStr(errno).c_str());
  318. return false;
  319. }
  320. return true;
  321. }
  322. bool readPipe(const String& zmode) {
  323. mode = DESC_PIPE;
  324. int newpipe[2];
  325. if (0 != pipe(newpipe)) {
  326. raise_warning("unable to create pipe %s",
  327. folly::errnoStr(errno).c_str());
  328. return false;
  329. }
  330. if (zmode != s_w) {
  331. parentend = newpipe[1];
  332. childend = newpipe[0];
  333. mode |= DESC_PARENT_MODE_WRITE;
  334. } else {
  335. parentend = newpipe[0];
  336. childend = newpipe[1];
  337. }
  338. mode_flags = mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY;
  339. return true;
  340. }
  341. bool openFile(const String& zfile, const String& zmode) {
  342. mode = DESC_FILE;
  343. /* try a wrapper */
  344. Variant vfile = HHVM_FN(fopen)(zfile.c_str(), zmode.c_str());
  345. if (!vfile.isResource()) {
  346. raise_warning("Unable to open specified file: %s (mode %s)",
  347. zfile.data(), zmode.data());
  348. return false;
  349. } else {
  350. auto file = cast<File>(vfile);
  351. file->flush();
  352. childend = dup(file->fd());
  353. if (childend < 0) {
  354. raise_warning("unable to dup File-Handle for descriptor %d - %s",
  355. index, folly::errnoStr(errno).c_str());
  356. return false;
  357. }
  358. return true;
  359. }
  360. }
  361. void dupChild() {
  362. if ((mode & ~DESC_PARENT_MODE_WRITE) == DESC_PIPE) {
  363. close(parentend); parentend = -1;
  364. }
  365. if (dup2(childend, index) < 0) {
  366. perror("dup2");
  367. }
  368. if (childend != index) {
  369. close(childend); childend = -1;
  370. }
  371. }
  372. /* clean up all the child ends and then open streams on the parent
  373. * ends, where appropriate */
  374. Resource dupParent() {
  375. close(childend); childend = -1;
  376. if ((mode & ~DESC_PARENT_MODE_WRITE) == DESC_PIPE) {
  377. /* mark the descriptor close-on-exec, so that it won't be inherited
  378. by potential other children */
  379. fcntl(parentend, F_SETFD, FD_CLOEXEC);
  380. return Resource(req::make<PlainFile>(parentend, true));
  381. }
  382. return Resource();
  383. }
  384. };
  385. /**
  386. * This mutex must be non-reentrant so when the child process tries to unlock
  387. * it after a fork(), the call to pthread_mutex_unlock() will succeed.
  388. */
  389. Mutex DescriptorItem::s_mutex(false);
  390. const StaticString s_pipe("pipe");
  391. const StaticString s_file("file");
  392. static bool pre_proc_open(const Array& descriptorspec,
  393. std::vector<DescriptorItem> &items) {
  394. /* walk the descriptor spec and set up files/pipes */
  395. items.resize(descriptorspec.size());
  396. int i = 0;
  397. for (ArrayIter iter(descriptorspec); iter; ++iter, ++i) {
  398. DescriptorItem &item = items[i];
  399. auto const index = iter.first().toString();
  400. if (!index.isNumeric()) {
  401. raise_warning("descriptor spec must be an integer indexed array");
  402. break;
  403. }
  404. item.index = index.toInt32();
  405. Variant descitem = iter.second();
  406. if (descitem.isResource()) {
  407. auto file = cast<File>(descitem);
  408. if (!item.readFile(file)) break;
  409. } else if (!descitem.isArray()) {
  410. raise_warning("Descriptor must be either an array or a File-Handle");
  411. break;
  412. } else {
  413. Array descarr = descitem.toArray();
  414. if (!descarr.exists(int64_t(0))) {
  415. raise_warning("Missing handle qualifier in array");
  416. break;
  417. }
  418. String ztype = descarr[int64_t(0)].toString();
  419. if (ztype == s_pipe) {
  420. if (!descarr.exists(int64_t(1))) {
  421. raise_warning("Missing mode parameter for 'pipe'");
  422. break;
  423. }
  424. if (!item.readPipe(descarr[int64_t(1)].toString())) break;
  425. } else if (ztype == s_file) {
  426. if (!descarr.exists(int64_t(1))) {
  427. raise_warning("Missing file name parameter for 'file'");
  428. break;
  429. }
  430. if (!descarr.exists(int64_t(2))) {
  431. raise_warning("Missing mode parameter for 'file'");
  432. break;
  433. }
  434. if (!item.openFile(descarr[int64_t(1)].toString(),
  435. descarr[int64_t(2)].toString())) {
  436. break;
  437. }
  438. } else {
  439. raise_warning("%s is not a valid descriptor spec", ztype.data());
  440. break;
  441. }
  442. }
  443. }
  444. if (i >= descriptorspec.size()) return true;
  445. for (int j = 0; j < i; j++) {
  446. items[j].cleanup();
  447. }
  448. return false;
  449. }
  450. static Variant post_proc_open(const String& cmd, Variant& pipes,
  451. const Variant& env,
  452. std::vector<DescriptorItem> &items,
  453. pid_t child) {
  454. if (child < 0) {
  455. /* failed to fork() */
  456. for (auto& item : items) {
  457. item.cleanup();
  458. }
  459. raise_warning("fork failed - %s", folly::errnoStr(errno).c_str());
  460. return false;
  461. }
  462. /* we forked/spawned and this is the parent */
  463. auto proc = req::make<ChildProcess>();
  464. proc->command = cmd;
  465. proc->child = child;
  466. proc->env = env;
  467. // need to set pipes to a new empty array, ignoring whatever it was
  468. // previously set to
  469. pipes = Variant(Array::Create());
  470. for (auto& item : items) {
  471. Resource f = item.dupParent();
  472. if (!f.isNull()) {
  473. proc->pipes.append(f);
  474. pipes.toArrRef().set(item.index, f);
  475. }
  476. }
  477. return Variant(std::move(proc));
  478. }
  479. Variant HHVM_FUNCTION(proc_open,
  480. const String& cmd,
  481. const Array& descriptorspec,
  482. VRefParam pipesParam,
  483. const Variant& cwd /* = null_variant */,
  484. const Variant& env /* = null_variant */,
  485. const Variant& other_options /* = null_variant */) {
  486. if (RuntimeOption::WhitelistExec && !check_cmd(cmd.data())) {
  487. return false;
  488. }
  489. if (cmd.size() != strlen(cmd.c_str())) {
  490. raise_warning("NULL byte detected. Possible attack");
  491. return false;
  492. }
  493. Variant pipes(pipesParam, Variant::WithRefBind{});
  494. std::vector<DescriptorItem> items;
  495. std::string scwd = "";
  496. if (!cwd.isNull() && cwd.isString() && !cwd.asCStrRef().empty()) {
  497. scwd = cwd.asCStrRef().c_str();
  498. } else if (!g_context->getCwd().empty()) {
  499. scwd = g_context->getCwd().c_str();
  500. }
  501. Array enva;
  502. if (env.isNull()) {
  503. // Build out an environment that conceptually matches what we'd
  504. // see if we were to iterate the environment and call getenv()
  505. // for each name.
  506. // Env vars defined in the hdf file go in first
  507. for (const auto& envvar : RuntimeOption::EnvVariables) {
  508. enva.set(String(envvar.first), String(envvar.second));
  509. }
  510. // global environment overrides the hdf
  511. for (char **env = environ; env && *env; env++) {
  512. char *p = strchr(*env, '=');
  513. if (p) {
  514. String name(*env, p - *env, CopyString);
  515. String val(p + 1, CopyString);
  516. enva.set(name, val);
  517. }
  518. }
  519. // and then any putenv() changes take precedence
  520. for (ArrayIter iter(g_context->getEnvs()); iter; ++iter) {
  521. enva.set(iter.first(), iter.second());
  522. }
  523. } else {
  524. enva = env.toArray();
  525. }
  526. pid_t child;
  527. if (LightProcess::Available()) {
  528. // light process available
  529. // there is no need to do any locking, because the forking is delegated
  530. // to the light process
  531. if (!pre_proc_open(descriptorspec, items)) return false;
  532. const int item_size = items.size();
  533. std::vector<int> created;
  534. created.reserve(item_size);
  535. std::vector<int> intended;
  536. intended.reserve(item_size);
  537. for (int i = 0; i < item_size; i++) {
  538. const auto& item = items[i];
  539. created.push_back(item.childend);
  540. intended.push_back(item.index);
  541. }
  542. std::vector<std::string> envs;
  543. for (ArrayIter iter(enva); iter; ++iter) {
  544. StringBuffer nvpair;
  545. nvpair.append(iter.first().toString());
  546. nvpair.append('=');
  547. nvpair.append(iter.second().toString());
  548. std::string tmp = nvpair.detach().c_str();
  549. if (tmp.find('\n') == std::string::npos) {
  550. envs.push_back(tmp);
  551. }
  552. }
  553. child = LightProcess::proc_open(cmd.c_str(), created, intended,
  554. scwd.c_str(), envs);
  555. assert(child);
  556. return post_proc_open(cmd, pipes, enva, items, child);
  557. } else {
  558. /* the unix way */
  559. Lock lock(DescriptorItem::s_mutex);
  560. if (!pre_proc_open(descriptorspec, items)) return false;
  561. child = fork();
  562. if (child) {
  563. // the parent process
  564. return post_proc_open(cmd, pipes, enva, items, child);
  565. }
  566. }
  567. assert(child == 0);
  568. /* this is the child process */
  569. /* close those descriptors that we just opened for the parent stuff,
  570. * dup new descriptors into required descriptors and close the original
  571. * cruft */
  572. for (auto& item : items) {
  573. item.dupChild();
  574. }
  575. if (scwd.length() > 0 && chdir(scwd.c_str())) {
  576. // chdir failed, the working directory remains unchanged
  577. }
  578. std::vector<String> senvs; // holding those char *
  579. char **envp = build_envp(enva, senvs);
  580. execle("/bin/sh", "sh", "-c", cmd.data(), NULL, envp);
  581. free(envp);
  582. _exit(127);
  583. }
  584. bool HHVM_FUNCTION(proc_terminate,
  585. const Resource& process,
  586. int signal /* = SIGTERM */) {
  587. return kill(cast<ChildProcess>(process)->child, signal) == 0;
  588. }
  589. int64_t HHVM_FUNCTION(proc_close,
  590. const Resource& process) {
  591. return cast<ChildProcess>(process)->close();
  592. }
  593. const StaticString
  594. s_command("command"),
  595. s_pid("pid"),
  596. s_running("running"),
  597. s_signaled("signaled"),
  598. s_stopped("stopped"),
  599. s_exitcode("exitcode"),
  600. s_termsig("termsig"),
  601. s_stopsig("stopsig");
  602. Array HHVM_FUNCTION(proc_get_status,
  603. const Resource& process) {
  604. auto proc = cast<ChildProcess>(process);
  605. errno = 0;
  606. int wstatus;
  607. pid_t wait_pid =
  608. LightProcess::waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
  609. bool running = true, signaled = false, stopped = false;
  610. int exitcode = -1, termsig = 0, stopsig = 0;
  611. if (wait_pid == proc->child) {
  612. if (WIFEXITED(wstatus)) {
  613. running = false;
  614. exitcode = WEXITSTATUS(wstatus);
  615. }
  616. if (WIFSIGNALED(wstatus)) {
  617. running = false;
  618. signaled = true;
  619. termsig = WTERMSIG(wstatus);
  620. }
  621. if (WIFSTOPPED(wstatus)) {
  622. stopped = true;
  623. stopsig = WSTOPSIG(wstatus);
  624. }
  625. } else if (wait_pid == -1) {
  626. running = false;
  627. }
  628. return make_map_array(
  629. s_command, proc->command,
  630. s_pid, proc->child,
  631. s_running, running,
  632. s_signaled, signaled,
  633. s_stopped, stopped,
  634. s_exitcode, exitcode,
  635. s_termsig, termsig,
  636. s_stopsig, stopsig
  637. );
  638. }
  639. bool HHVM_FUNCTION(proc_nice,
  640. int increment) {
  641. if (nice(increment) < 0 && errno) {
  642. raise_warning("Only a super user may attempt to increase the "
  643. "priority of a process");
  644. return false;
  645. }
  646. return true;
  647. }
  648. ///////////////////////////////////////////////////////////////////////////////
  649. // string functions
  650. const StaticString s_twosinglequotes("''");
  651. String HHVM_FUNCTION(escapeshellarg,
  652. const String& arg) {
  653. if (!arg.empty()) {
  654. return string_escape_shell_arg(arg.c_str());
  655. } else if (!RuntimeOption::EnableHipHopSyntax) {
  656. return String(s_twosinglequotes);
  657. }
  658. return arg;
  659. }
  660. String HHVM_FUNCTION(escapeshellcmd,
  661. const String& command) {
  662. if (!command.empty()) {
  663. return string_escape_shell_cmd(command.c_str());
  664. }
  665. return command;
  666. }
  667. ///////////////////////////////////////////////////////////////////////////////
  668. }