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