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 ascending Description
_drush_shell_exec_output_set Stores output for the most recent shell command. This should only be run from drush_shell_exec().
_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_escapeshellarg_windows Windows version of escapeshellarg().
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_which Determine whether 'which $command' can find a command on this system.
drush_start_browser Starts a background browser/tab for the current site or a specified URL.
drush_shell_proc_open Execute bash command using proc_open().
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_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_exec_output Returns the output of the most recent shell command as an array of lines.
drush_shell_exec_interactive Executes a command in interactive mode.
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_cd_and_exec Executes a shell command at a new working directory. The old cwd is restored on exit.
drush_remote_host Determine the remote host (username@hostname.tld) for the specified site.
drush_os Determine the appropriate os value for the specified site record
drush_op_system Calls 'system()' function, passing through all arguments unchanged.
drush_get_editor Returns executable code for invoking preferred test editor.
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. Use raw to get an unquoted version of the…

File

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