# 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. import logging import os import platform import signal import socket import subprocess import sys import time import threading DIR_SOURCE_ROOT = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) IMAGES_ROOT = os.path.join( DIR_SOURCE_ROOT, 'third_party', 'fuchsia-sdk', 'images') SDK_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'fuchsia-sdk', 'sdk') def EnsurePathExists(path): """Checks that the file |path| exists on the filesystem and returns the path if it does, raising an exception otherwise.""" if not os.path.exists(path): raise IOError('Missing file: ' + path) return path def GetHostOsFromPlatform(): host_platform = sys.platform if host_platform.startswith('linux'): return 'linux' elif host_platform.startswith('darwin'): return 'mac' raise Exception('Unsupported host platform: %s' % host_platform) def GetHostArchFromPlatform(): host_arch = platform.machine() if host_arch == 'x86_64': return 'x64' elif host_arch == 'aarch64': return 'arm64' raise Exception('Unsupported host architecture: %s' % host_arch) def GetHostToolPathFromPlatform(tool): host_arch = platform.machine() return os.path.join(SDK_ROOT, 'tools', GetHostArchFromPlatform(), tool) def GetEmuRootForPlatform(emulator): return os.path.join( DIR_SOURCE_ROOT, 'third_party', '{0}-{1}-{2}'.format( emulator, GetHostOsFromPlatform(), GetHostArchFromPlatform())) def ConnectPortForwardingTask(target, local_port, remote_port = 0): """Establishes a port forwarding SSH task to a localhost TCP endpoint hosted at port |local_port|. Blocks until port forwarding is established. Returns the remote port number.""" forwarding_flags = ['-O', 'forward', # Send SSH mux control signal. '-R', '%d:localhost:%d' % (remote_port, local_port), '-v', # Get forwarded port info from stderr. '-NT'] # Don't execute command; don't allocate terminal. if remote_port != 0: # Forward to a known remote port. task = target.RunCommand([], ssh_args=forwarding_flags) if task.returncode != 0: raise Exception('Could not establish a port forwarding connection.') return task = target.RunCommandPiped([], ssh_args=forwarding_flags, stdout=subprocess.PIPE, stderr=open('/dev/null')) output = task.stdout.readlines() task.wait() if task.returncode != 0: raise Exception('Got an error code when requesting port forwarding: %d' % task.returncode) parsed_port = int(output[0].strip()) logging.debug('Port forwarding established (local=%d, device=%d)' % (local_port, parsed_port)) return parsed_port def GetAvailableTcpPort(): """Finds a (probably) open port by opening and closing a listen socket.""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(("", 0)) port = sock.getsockname()[1] sock.close() return port def SubprocessCallWithTimeout(command, silent=False, timeout_secs=None): """Helper function for running a command. Args: command: The command to run. silent: If true, stdout and stderr of the command will not be printed. timeout_secs: Maximum amount of time allowed for the command to finish. Returns: A tuple of (return code, stdout, stderr) of the command. Raises an exception if the subprocess times out. """ if silent: devnull = open(os.devnull, 'w') process = subprocess.Popen(command, stdout=devnull, stderr=devnull) else: process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) timeout_timer = None if timeout_secs: def interrupt_process(): process.send_signal(signal.SIGKILL) timeout_timer = threading.Timer(timeout_secs, interrupt_process) # Ensure that keyboard interrupts are handled properly (crbug/1198113). timeout_timer.daemon = True timeout_timer.start() out, err = process.communicate() if timeout_timer: timeout_timer.cancel() if process.returncode == -9: raise Exception('Timeout when executing \"%s\".' % ' '.join(command)) return process.returncode, out, err