|  | # Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Helper functions for remotely executing and copying files over a SSH | 
|  | connection.""" | 
|  |  | 
|  | import logging | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | _SSH = ['ssh'] | 
|  | _SCP = ['scp', '-C']  # Use gzip compression. | 
|  | _SSH_LOGGER = logging.getLogger('ssh') | 
|  |  | 
|  | COPY_TO_TARGET = 0 | 
|  | COPY_FROM_TARGET = 1 | 
|  |  | 
|  |  | 
|  | def _IsLinkLocalIPv6(hostname): | 
|  | return hostname.startswith('fe80::') | 
|  |  | 
|  |  | 
|  | def RunSsh(config_path, host, port, command, silent): | 
|  | """Executes an SSH command on the remote host and blocks until completion. | 
|  |  | 
|  | config_path: Full path to SSH configuration. | 
|  | host: The hostname or IP address of the remote host. | 
|  | port: The port to connect to. | 
|  | command: A list of strings containing the command and its arguments. | 
|  | silent: If true, suppresses all output from 'ssh'. | 
|  |  | 
|  | Returns the exit code from the remote command.""" | 
|  |  | 
|  | ssh_command = _SSH + ['-F', config_path, | 
|  | host, | 
|  | '-p', str(port)] + command | 
|  | _SSH_LOGGER.debug('ssh exec: ' + ' '.join(ssh_command)) | 
|  | if silent: | 
|  | devnull = open(os.devnull, 'w') | 
|  | return subprocess.call(ssh_command, stderr=devnull, stdout=devnull) | 
|  | else: | 
|  | return subprocess.call(ssh_command) | 
|  |  | 
|  |  | 
|  | def RunPipedSsh(config_path, host, port, command = None, ssh_args = None, | 
|  | **kwargs): | 
|  | """Executes an SSH command on the remote host and returns a process object | 
|  | with access to the command's stdio streams. Does not block. | 
|  |  | 
|  | config_path: Full path to SSH configuration. | 
|  | host: The hostname or IP address of the remote host. | 
|  | port: The port to connect to. | 
|  | command: A list of strings containing the command and its arguments. | 
|  | ssh_args: Arguments that will be passed to SSH. | 
|  | kwargs: A dictionary of parameters to be passed to subprocess.Popen(). | 
|  | The parameters can be used to override stdin and stdout, for example. | 
|  |  | 
|  | Returns a Popen object for the command.""" | 
|  |  | 
|  | if not command: | 
|  | command = [] | 
|  | if not ssh_args: | 
|  | ssh_args = [] | 
|  |  | 
|  | ssh_command = _SSH + ['-F', config_path, | 
|  | host, | 
|  | '-p', str(port)] + ssh_args + ['--'] + command | 
|  | _SSH_LOGGER.debug(' '.join(ssh_command)) | 
|  | return subprocess.Popen(ssh_command, **kwargs) | 
|  |  | 
|  |  | 
|  | def RunScp(config_path, host, port, sources, dest, direction, recursive=False): | 
|  | """Copies a file to or from a remote host using SCP and blocks until | 
|  | completion. | 
|  |  | 
|  | config_path: Full path to SSH configuration. | 
|  | host: The hostname or IP address of the remote host. | 
|  | port: The port to connect to. | 
|  | sources: Paths of the files to be copied. | 
|  | dest: The path that |source| will be copied to. | 
|  | direction: Indicates whether the file should be copied to | 
|  | or from the remote side. | 
|  | Valid values are COPY_TO_TARGET or COPY_FROM_TARGET. | 
|  | recursive: If true, performs a recursive copy. | 
|  |  | 
|  | Function will raise an assertion if a failure occurred.""" | 
|  |  | 
|  | scp_command = _SCP[:] | 
|  | if ':' in host: | 
|  | scp_command.append('-6') | 
|  | host = '[' + host + ']' | 
|  | if _SSH_LOGGER.getEffectiveLevel() == logging.DEBUG: | 
|  | scp_command.append('-v') | 
|  | if recursive: | 
|  | scp_command.append('-r') | 
|  |  | 
|  | if direction == COPY_TO_TARGET: | 
|  | dest = "%s:%s" % (host, dest) | 
|  | else: | 
|  | sources = ["%s:%s" % (host, source) for source in sources] | 
|  |  | 
|  | scp_command += ['-F', config_path, '-P', str(port)] | 
|  | scp_command += sources | 
|  | scp_command += [dest] | 
|  |  | 
|  | _SSH_LOGGER.debug(' '.join(scp_command)) | 
|  | subprocess.check_call(scp_command, stdout=open(os.devnull, 'w')) |