exec.inc

  1. 8.0.x includes/exec.inc
  2. 6.x includes/exec.inc
  3. 7.x includes/exec.inc
  4. 5.x includes/exec.inc
  5. master includes/exec.inc

Functions for executing system commands. (e.g. exec(), system(), ...).

Functions

Namesort descending Description
drush_escapeshellarg Platform-dependent version of escapeshellarg(). Given the target platform, return an appropriately-escaped string. The target platform may be omitted for args that are /known/ to be for the local machine.
drush_get_editor Returns executable code for invoking preferred test editor.
drush_op_system Calls 'system()' function, passing through all arguments unchanged.
drush_os Determine the appropriate os value for the specified site record
drush_remote_host Determine the remote host (username@hostname.tld) for the specified site.
drush_shell_cd_and_exec Executes a shell command at a new working directory. The old cwd is restored on exit.
drush_shell_exec Executes a shell command. Output is only printed if in verbose mode. Output is stored and can be retrieved using drush_shell_exec_output(). If in simulation mode, no action is taken.
drush_shell_exec_interactive Executes a command in interactive mode.
drush_shell_exec_output Returns the output of the most recent shell command as an array of lines.
drush_shell_exec_proc_build_options Used by definition of ssh and other commands that call into drush_shell_proc_build() to declare their options.
drush_shell_proc_build Build an SSH string including an optional fragment of bash. Commands that use this should also merge drush_shell_proc_build_options() into their command options.
drush_shell_proc_open Execute bash command using proc_open().
drush_start_browser Starts a background browser/tab for the current site or a specified URL.
drush_wrap_with_quotes Make an attempt to simply wrap the arg with the kind of quote characters it does not already contain. If it contains both kinds, then this function reverts to drush_escapeshellarg.
_drush_escapeshellarg_linux Linux version of escapeshellarg().
_drush_escapeshellarg_windows Windows version of escapeshellarg().
_drush_shell_exec Internal function: executes a shell command on the local machine. This function should not be used in instances where ssh is utilized to execute a command remotely; otherwise, remote operations would fail if executed from a Windows machine to a…
_drush_shell_exec_output_set Stores output for the most recent shell command. This should only be run from drush_shell_exec().

File

includes/exec.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Functions for executing system commands. (e.g. exec(), system(), ...).
  5. */
  6. /**
  7. * @defgroup commandwrappers Functions to execute commands.
  8. * @{
  9. */
  10. /**
  11. * Calls 'system()' function, passing through all arguments unchanged.
  12. *
  13. * This should be used when calling possibly mutative or destructive functions
  14. * (e.g. unlink() and other file system functions) so that can be suppressed
  15. * if the simulation mode is enabled.
  16. *
  17. * @param $exec
  18. * The shell command to execute. Parameters should already be escaped.
  19. * @return
  20. * The result code from system(): 0 == success.
  21. *
  22. * @see drush_shell_exec()
  23. */
  24. function drush_op_system($exec) {
  25. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  26. drush_print("Calling system($exec);", 0, STDERR);
  27. }
  28. if (drush_get_context('DRUSH_SIMULATE')) {
  29. return 0;
  30. }
  31. // Throw away output. Use drush_shell_exec() to capture output.
  32. system($exec, $result_code);
  33. return $result_code;
  34. }
  35. /**
  36. * Executes a shell command at a new working directory.
  37. * The old cwd is restored on exit.
  38. *
  39. * @param $effective_wd
  40. * The new working directory to execute the shell command at.
  41. * @param $cmd
  42. * The command to execute. May include placeholders used for sprintf.
  43. * @param ...
  44. * Values for the placeholders specified in $cmd. Each of these will be passed through escapeshellarg() to ensure they are safe to use on the command line.
  45. * @return
  46. * TRUE on success, FALSE on failure
  47. */
  48. function drush_shell_cd_and_exec($effective_wd, $cmd) {
  49. $args = func_get_args();
  50. $effective_wd = array_shift($args);
  51. $cwd = getcwd();
  52. drush_op('chdir', $effective_wd);
  53. $result = call_user_func_array('drush_shell_exec', $args);
  54. drush_op('chdir', $cwd);
  55. return $result;
  56. }
  57. /**
  58. * Executes a shell command.
  59. * Output is only printed if in verbose mode.
  60. * Output is stored and can be retrieved using drush_shell_exec_output().
  61. * If in simulation mode, no action is taken.
  62. *
  63. * @param $cmd
  64. * The command to execute. May include placeholders used for sprintf.
  65. * @param ...
  66. * Values for the placeholders specified in $cmd. Each of these will be passed through escapeshellarg() to ensure they are safe to use on the command line.
  67. * @return
  68. * TRUE on success, FALSE on failure
  69. */
  70. function drush_shell_exec($cmd) {
  71. return _drush_shell_exec(func_get_args());
  72. }
  73. /**
  74. * Returns executable code for invoking preferred test editor.
  75. *
  76. * The next line after calling this function is usually
  77. * @code drush_shell_exec_interactive($exec, $filepath, $filepath) @endcode
  78. *
  79. * @see drush_config_edit()
  80. */
  81. function drush_get_editor() {
  82. $bg = drush_get_option('bg') ? '&' : '';
  83. // see http://drupal.org/node/1740294
  84. $exec = drush_get_option('editor', '${VISUAL-${EDITOR-vi}}') . " %s $bg";
  85. return $exec;
  86. }
  87. /**
  88. * Executes a command in interactive mode.
  89. *
  90. * @see drush_shell_exec.
  91. */
  92. function drush_shell_exec_interactive($cmd) {
  93. return _drush_shell_exec(func_get_args(), TRUE);
  94. }
  95. /**
  96. * Internal function: executes a shell command on the
  97. * local machine. This function should not be used
  98. * in instances where ssh is utilized to execute a
  99. * command remotely; otherwise, remote operations would
  100. * fail if executed from a Windows machine to a remote
  101. * Linux server.
  102. *
  103. * @param $args
  104. * The command and its arguments.
  105. * @param $interactive
  106. * Whether to run in
  107. *
  108. * @return
  109. * TRUE on success, FALSE on failure
  110. *
  111. * @see drush_shell_exec.
  112. */
  113. function _drush_shell_exec($args, $interactive = FALSE) {
  114. // Do not change the command itself, just the parameters.
  115. for ($x = 1; $x < count($args); $x++) {
  116. $args[$x] = drush_escapeshellarg($args[$x]);
  117. }
  118. // Important: we allow $args to take one of two forms here. If
  119. // there is only one item in the array, it is the already-escaped
  120. // command string, but otherwise sprintf is used. In the case
  121. // of pre-escaped strings, sprintf will fail if any of the escaped
  122. // parameters contain '%', so we must not call sprintf unless necessary.
  123. if (count($args) == 1) {
  124. $command = $args[0];
  125. }
  126. else {
  127. $command = call_user_func_array('sprintf', $args);
  128. }
  129. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  130. drush_print('Executing: ' . $command, 0, STDERR);
  131. }
  132. if (!drush_get_context('DRUSH_SIMULATE')) {
  133. if ($interactive) {
  134. $result = drush_shell_proc_open($command);
  135. return ($result == 0) ? TRUE : FALSE;
  136. }
  137. else {
  138. exec($command . ' 2>&1', $output, $result);
  139. _drush_shell_exec_output_set($output);
  140. if (drush_get_context('DRUSH_DEBUG')) {
  141. foreach ($output as $line) {
  142. drush_print($line, 2);
  143. }
  144. }
  145. // Exit code 0 means success.
  146. return ($result == 0);
  147. }
  148. }
  149. else {
  150. return TRUE;
  151. }
  152. }
  153. /**
  154. * Build an SSH string including an optional fragment of bash. Commands that use
  155. * this should also merge drush_shell_proc_build_options() into their
  156. * command options. @see ssh_drush_command().
  157. *
  158. * @param array $site
  159. * A site alias record.
  160. * @param string $command
  161. * An optional bash fragment.
  162. * @param string $cd
  163. * An optional directory to change into before executing the $command. Set to
  164. * boolean TRUE to change into $site['root'] if available.
  165. * @param boolean $interactive
  166. * Force creation of a tty
  167. * @return string
  168. * A string suitable for execution with drush_shell_remote_exec().
  169. */
  170. function drush_shell_proc_build($site, $command = '', $cd = NULL, $interactive = FALSE) {
  171. // Build up the command. TODO: We maybe refactor this soon.
  172. $hostname = drush_remote_host($site);
  173. $ssh_options = drush_sitealias_get_option($site, 'ssh-options', "-o PasswordAuthentication=no");
  174. $os = drush_os($site);
  175. if (drush_sitealias_get_option($site, 'tty') || $interactive) {
  176. $ssh_options .= ' -t';
  177. }
  178. $cmd = "ssh " . $ssh_options . " " . $hostname;
  179. if ($cd === TRUE) {
  180. if (array_key_exists('root', $site)) {
  181. $cd = $site['root'];
  182. }
  183. else {
  184. $cd = FALSE;
  185. }
  186. }
  187. if ($cd) {
  188. $command = 'cd ' . drush_escapeshellarg($cd, $os) . ' && ' . $command;
  189. }
  190. if (!empty($command)) {
  191. if (!drush_get_option('escaped', FALSE)) {
  192. $cmd .= " " . drush_escapeshellarg($command, $os);
  193. }
  194. else {
  195. $cmd .= " $command";
  196. }
  197. }
  198. return $cmd;
  199. }
  200. /**
  201. * Execute bash command using proc_open().
  202. *
  203. * @returns
  204. * Exit code from launched application
  205. * 0 no error
  206. * 1 general error
  207. * 127 command not found
  208. */
  209. function drush_shell_proc_open($cmd) {
  210. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  211. drush_print("Calling proc_open($cmd);", 0, STDERR);
  212. }
  213. if (!drush_get_context('DRUSH_SIMULATE')) {
  214. $process = proc_open($cmd, array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes);
  215. $proc_status = proc_get_status($process);
  216. $exit_code = proc_close($process);
  217. return ($proc_status["running"] ? $exit_code : $proc_status["exitcode"] );
  218. }
  219. return 0;
  220. }
  221. /**
  222. * Used by definition of ssh and other commands that call into drush_shell_proc_build()
  223. * to declare their options.
  224. */
  225. function drush_shell_exec_proc_build_options() {
  226. return array(
  227. 'ssh-options' => 'A string of extra options that will be passed to the ssh command (e.g. "-p 100")',
  228. 'tty' => 'Create a tty (e.g. to run an interactive program).',
  229. 'escaped' => 'Command string already escaped; do not add additional quoting.',
  230. );
  231. }
  232. /**
  233. * Determine the appropriate os value for the
  234. * specified site record
  235. *
  236. * @returns
  237. * NULL for 'same as local machine', 'Windows' or 'Linux'.
  238. */
  239. function drush_os($site_record = NULL) {
  240. // Default to $os = NULL, meaning 'same as local machine'
  241. $os = NULL;
  242. // If the site record has an 'os' element, use it
  243. if (isset($site_record) && array_key_exists('os', $site_record)) {
  244. $os = $site_record['os'];
  245. }
  246. // Otherwise, we will assume that all remote machines are Linux
  247. // (or whatever value 'remote-os' is set to in drushrc.php).
  248. elseif (isset($site_record) && array_key_exists('remote-host', $site_record) && !empty($site_record['remote-host'])) {
  249. $os = drush_get_option('remote-os', 'Linux');
  250. }
  251. return $os;
  252. }
  253. /**
  254. * Determine the remote host (username@hostname.tld) for
  255. * the specified site.
  256. */
  257. function drush_remote_host($site, $prefix = '') {
  258. $hostname = drush_escapeshellarg(drush_sitealias_get_option($site, 'remote-host', '', $prefix), "LOCAL");
  259. $username = drush_escapeshellarg(drush_sitealias_get_option($site, 'remote-user', '', $prefix), "LOCAL");
  260. return $username . (empty($username) ? '' : '@') . $hostname;
  261. }
  262. /**
  263. * Make an attempt to simply wrap the arg with the
  264. * kind of quote characters it does not already contain.
  265. * If it contains both kinds, then this function reverts to drush_escapeshellarg.
  266. */
  267. function drush_wrap_with_quotes($arg) {
  268. $has_double = strpos($arg, '"') !== FALSE;
  269. $has_single = strpos($arg, "'") !== FALSE;
  270. if ($has_double && $has_single) {
  271. return drush_escapeshellarg($arg);
  272. }
  273. elseif ($has_double) {
  274. return "'" . $arg . "'";
  275. }
  276. else {
  277. return '"' . $arg . '"';
  278. }
  279. }
  280. /**
  281. * Platform-dependent version of escapeshellarg().
  282. * Given the target platform, return an appropriately-escaped
  283. * string. The target platform may be omitted for args that
  284. * are /known/ to be for the local machine.
  285. */
  286. function drush_escapeshellarg($arg, $os = NULL) {
  287. // Short-circuit escaping for simple params (keep stuff readable)
  288. if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
  289. return $arg;
  290. }
  291. elseif (drush_is_windows($os)) {
  292. return _drush_escapeshellarg_windows($arg);
  293. }
  294. else {
  295. return _drush_escapeshellarg_linux($arg);
  296. }
  297. }
  298. /**
  299. * Windows version of escapeshellarg().
  300. */
  301. function _drush_escapeshellarg_windows($arg) {
  302. // Double up existing backslashes
  303. $arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
  304. // Double up double quotes
  305. $arg = preg_replace('/"/', '""', $arg);
  306. // Double up percents.
  307. $arg = preg_replace('/%/', '%%', $arg);
  308. // Add surrounding quotes.
  309. $arg = '"' . $arg . '"';
  310. return $arg;
  311. }
  312. /**
  313. * Linux version of escapeshellarg().
  314. *
  315. * This is intended to work the same way that escapeshellarg() does on
  316. * Linux. If we need to escape a string that will be used remotely on
  317. * a Linux system, then we need our own implementation of escapeshellarg,
  318. * because the Windows version behaves differently.
  319. */
  320. function _drush_escapeshellarg_linux($arg) {
  321. // For single quotes existing in the string, we will "exit"
  322. // single-quote mode, add a \' and then "re-enter"
  323. // single-quote mode. The result of this is that
  324. // 'quote' becomes '\''quote'\''
  325. $arg = preg_replace('/\'/', '\'\\\'\'', $arg);
  326. // Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
  327. // Note that this replacement makes Drush's escapeshellarg work differently
  328. // than the built-in escapeshellarg in PHP on Linux, as these characters
  329. // usually are NOT replaced. However, this was done deliberately to be more
  330. // conservative when running _drush_escapeshellarg_linux on Windows
  331. // (this can happen when generating a command to run on a remote Linux server.)
  332. $arg = str_replace(array("\t", "\n", "\r", "\0", "\x0B"), ' ', $arg);
  333. // Add surrounding quotes.
  334. $arg = "'" . $arg . "'";
  335. return $arg;
  336. }
  337. /**
  338. * Stores output for the most recent shell command.
  339. * This should only be run from drush_shell_exec().
  340. *
  341. * @param $output
  342. * The output of the most recent shell command.
  343. * If this is not set the stored value will be returned.
  344. */
  345. function _drush_shell_exec_output_set($output = FALSE) {
  346. static $stored_output;
  347. if ($output === FALSE) return $stored_output;
  348. $stored_output = $output;
  349. }
  350. /**
  351. * Returns the output of the most recent shell command as an array of lines.
  352. */
  353. function drush_shell_exec_output() {
  354. return _drush_shell_exec_output_set();
  355. }
  356. /**
  357. * Starts a background browser/tab for the current site or a specified URL.
  358. *
  359. * Uses a non-blocking proc_open call, so Drush execution will continue.
  360. *
  361. * @param $uri
  362. * Optional URI or site path to open in browser. If omitted, or if a site path
  363. * is specified, the current site home page uri will be prepended if the sites
  364. * hostname resolves.
  365. * @return
  366. * TRUE if browser was opened, FALSE if browser was disabled by the user or a,
  367. * default browser could not be found.
  368. */
  369. function drush_start_browser($uri = NULL, $sleep = FALSE) {
  370. if ($browser = drush_get_option('browser', TRUE)) {
  371. // We can only open a browser if we have a DISPLAY environment variable on
  372. // POSIX or are running Windows or OS X.
  373. if (!drush_get_context('DRUSH_SIMULATE') && !getenv('DISPLAY') && !drush_is_windows() && !drush_is_osx()) {
  374. drush_log(dt('No graphical display appears to be available, not starting browser.'), 'notice');
  375. return FALSE;
  376. }
  377. $host = parse_url($uri, PHP_URL_HOST);
  378. if (!$host) {
  379. // Build a URI for the current site, if we were passed a path.
  380. $site = drush_get_context('DRUSH_URI');
  381. $host = parse_url($site, PHP_URL_HOST);
  382. $uri = $site . '/' . ltrim($uri, '/');
  383. }
  384. // Validate that the host part of the URL resolves, so we don't attempt to
  385. // open the browser for http://default or similar invalid hosts.
  386. $hosterror = (gethostbynamel($host) === FALSE);
  387. $iperror = (ip2long($host) && gethostbyaddr($host) == $host);
  388. if (!drush_get_context('DRUSH_SIMULATE') && ($hosterror || $iperror)) {
  389. drush_log(dt('!host does not appear to be a resolvable hostname or IP, not starting browser. You may need to use the --uri option in your command or site alias to indicate the correct URL of this site.', array('!host' => $host)), 'warning');
  390. return FALSE;
  391. }
  392. if ($browser === TRUE) {
  393. // See if we can find an OS helper to open URLs in default browser.
  394. if (drush_shell_exec('which xdg-open')) {
  395. $browser = 'xdg-open';
  396. }
  397. else if (drush_shell_exec('which open')) {
  398. $browser = 'open';
  399. }
  400. else if (!drush_has_bash()) {
  401. $browser = 'start';
  402. }
  403. else {
  404. // Can't find a valid browser.
  405. $browser = FALSE;
  406. }
  407. }
  408. $prefix = '';
  409. if ($sleep) {
  410. $prefix = 'sleep ' . $sleep . ' && ';
  411. }
  412. if ($browser) {
  413. drush_log(dt('Opening browser !browser at !uri', array('!browser' => $browser, '!uri' => $uri)));
  414. if (!drush_get_context('DRUSH_SIMULATE')) {
  415. $pipes = array();
  416. proc_close(proc_open($prefix . $browser . ' ' . drush_escapeshellarg($uri) . ' 2> ' . drush_bit_bucket() . ' &', array(), $pipes));
  417. }
  418. return TRUE;
  419. }
  420. }
  421. return FALSE;
  422. }
  423. /**
  424. * @} End of "defgroup commandwrappers".
  425. */