function _drush_backend_proc_open

8.0.x backend.inc _drush_backend_proc_open($cmds, $process_limit, $context = NULL)
6.x backend.inc _drush_backend_proc_open($cmds, $process_limit, $context = NULL)
7.x backend.inc _drush_backend_proc_open($cmds, $process_limit, $context = NULL)
5.x backend.inc _drush_backend_proc_open($cmds, $process_limit, $context = NULL)
master backend.inc _drush_backend_proc_open($cmds, $process_limit, $context = NULL)

Call an external command using proc_open.

Parameters

cmds: An array of records containing the following elements: 'cmd' - The command to execute, already properly escaped 'post-options' - An associative array that will be JSON encoded and passed to the script being called. Objects are not allowed, as they do not json_decode gracefully. 'backend-options' - Options that control the operation of the backend invoke

  • OR -

An array of commands to execute. These commands already need to be properly escaped. In this case, post-options will default to empty, and a default output label will be generated.

data: An associative array that will be JSON encoded and passed to the script being called. Objects are not allowed, as they do not json_decode gracefully.

Return value

False if the command could not be executed, or did not return any output. If it executed successfully, it returns an associative array containing the command called, the output of the command, and the error code of the command.

1 call to _drush_backend_proc_open()
_drush_backend_invoke in includes/backend.inc
Create a new pipe with proc_open, and attempt to parse the output.

File

includes/backend.inc, line 343
Drush backend API

Code

function _drush_backend_proc_open($cmds, $process_limit, $context = NULL) {
  $descriptorspec = array(
    0 => array("pipe", "r"), // stdin is a pipe that the child will read from
    1 => array("pipe", "w"), // stdout is a pipe that the child will write to
  );

  $open_processes = array();
  $bucket = array();
  $process_limit = max($process_limit, 1);
  $is_windows = drush_is_windows();
  // Loop through processes until they all close, having a nap as needed.
  $nap_time = 0;
  while (count($open_processes) || count($cmds)) {
    $nap_time++;
    if (count($cmds) && (count($open_processes) < $process_limit)) {
      // Pop the site and command (key / value) from the cmds array
      end($cmds);
      list($site, $cmd) = each($cmds);
      unset($cmds[$site]);

      if (is_array($cmd)) {
        $c = $cmd['cmd'];
        $post_options = $cmd['post-options'];
        $backend_options = $cmd['backend-options'];
      }
      else {
        $c = $cmd;
        $post_options = array();
        $backend_options = array();
      }
      $backend_options += array(
        '#output-label' => '',
        '#process-read-size' => 4096,
      );
      $process = array();
      drush_log($backend_options['#output-label'] . $c);
      $process['process'] = proc_open($c, $descriptorspec, $process['pipes'], null, null, array('context' => $context));
      if (is_resource($process['process'])) {
        if ($post_options) {
          fwrite($process['pipes'][0], json_encode($post_options)); // pass the data array in a JSON encoded string
        }
        // If we do not close stdin here, then we cause a deadlock;
        // see: http://drupal.org/node/766080#comment-4309936
        // If we reimplement interactive commands to also use
        // _drush_proc_open, then clearly we would need to keep
        // this open longer.
        fclose($process['pipes'][0]);

        $process['info'] = stream_get_meta_data($process['pipes'][1]);
        stream_set_blocking($process['pipes'][1], FALSE);
        stream_set_timeout($process['pipes'][1], 1);
        $bucket[$site]['cmd'] = $c;
        $bucket[$site]['output'] = '';
        $bucket[$site]['remainder'] = '';
        $bucket[$site]['backend-options'] = $backend_options;
        $bucket[$site]['end_of_output'] = FALSE;
        $bucket[$site]['outputted'] = FALSE;
        $open_processes[$site] = $process;
      }
      // Reset the $nap_time variable as there might be output to process next
      // time around:
      $nap_time = 0;
    }
    // Set up to call stream_select(). See:
    // http://php.net/manual/en/function.stream-select.php
    // We can't use stream_select on Windows, because it doesn't work for
    // streams returned by proc_open.
    if (!$is_windows) {
      $ss_result = 0;
      $read_streams = array();
      $write_streams = array();
      $except_streams = array();
      foreach ($open_processes as $site => &$current_process) {
        if (isset($current_process['pipes'][1])) {
          $read_streams[] = $current_process['pipes'][1];
        }
      }
      // Wait up to 2s for data to become ready on one of the read streams.
      if (count($read_streams)) {
        $ss_result = stream_select($read_streams, $write_streams, $except_streams, 2);
        // If stream_select returns a error, then fallback to using $nap_time.
        if ($ss_result !== FALSE) {
          $nap_time = 0;
        }
      }
    }

    foreach ($open_processes as $site => &$current_process) {
      if (isset($current_process['pipes'][1])) {
        // Collect output from stdout
        $bucket[$site][1] = '';
        $info = stream_get_meta_data($current_process['pipes'][1]);

        if (!feof($current_process['pipes'][1]) && !$info['timed_out']) {
          $string = $bucket[$site]['remainder'] . fread($current_process['pipes'][1], $backend_options['#process-read-size']);
          $bucket[$site]['remainder'] = '';
          $output_end_pos = strpos($string, DRUSH_BACKEND_OUTPUT_START);
          if ($output_end_pos !== FALSE) {
            $trailing_string = substr($string, 0, $output_end_pos);
            $trailing_remainder = '';
            // If there is any data in the trailing string (characters prior
            // to the backend output start), then process any backend packets
            // embedded inside.
            if (strlen($trailing_string) > 0) {
              drush_backend_parse_packets($trailing_string, $trailing_remainder, $bucket[$site]['backend-options']);
            }
            // If there is any data remaining in the trailing string after
            // the backend packets are removed, then print it.
            if (strlen($trailing_string) > 0) {
              _drush_backend_print_output($trailing_string . $trailing_remainder, $bucket[$site]['backend-options']);
              $bucket[$site]['outputted'] = TRUE;
            }
            $bucket[$site]['end_of_output'] = TRUE;
          }
          if (!$bucket[$site]['end_of_output']) {
            drush_backend_parse_packets($string, $bucket[$site]['remainder'], $bucket[$site]['backend-options']);
            // Pass output through.
            _drush_backend_print_output($string, $bucket[$site]['backend-options']);
            if (strlen($string) > 0) {
              $bucket[$site]['outputted'] = TRUE;
            }
          }
          $bucket[$site][1] .= $string;
          $bucket[$site]['output'] .= $string;
          $info = stream_get_meta_data($current_process['pipes'][1]);
          flush();

          // Reset the $nap_time variable as there might be output to process
          // next time around:
          if (strlen($string) > 0) {
            $nap_time = 0;
          }
        }
        else {
          fclose($current_process['pipes'][1]);
          unset($current_process['pipes'][1]);
          // close the pipe , set a marker

          // Reset the $nap_time variable as there might be output to process
          // next time around:
          $nap_time = 0;
        }
      }
      else {
        // if both pipes are closed for the process, remove it from active loop and add a new process to open.
        $bucket[$site]['code'] = proc_close($current_process['process']);
        unset($open_processes[$site]);

        // Reset the $nap_time variable as there might be output to process next
        // time around:
        $nap_time = 0;
      }
    }

    // We should sleep for a bit if we need to, up to a maximum of 1/10 of a
    // second.
    if ($nap_time > 0) {
      usleep(max($nap_time * 500, 100000));
    }
  }
  return $bucket;
  // TODO: Handle bad proc handles
  //}
  //return FALSE;
}