blob: 135b314ea3a5d77c021f55ea2f5d8b4e533e6f32 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017 The Crashpad Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used
for running tests without a device.
"""
from __future__ import print_function
import getpass
import os
import random
import signal
import subprocess
import sys
import tempfile
import time
try:
from subprocess import DEVNULL
except ImportError:
DEVNULL = open(os.devnull, 'r+b')
CRASHPAD_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
os.pardir)
def _Stop(pid_file):
if os.path.isfile(pid_file):
with open(pid_file, 'rb') as f:
pid = int(f.read().strip())
try:
os.kill(pid, signal.SIGTERM)
except:
print('Unable to kill pid %d, continuing' % pid, file=sys.stderr)
os.unlink(pid_file)
def _CheckForTun():
"""Check for networking. TODO(scottmg): Currently, this is Linux-specific.
"""
returncode = subprocess.call(
['tunctl', '-b', '-u', getpass.getuser(), '-t', 'qemu'],
stdout=DEVNULL, stderr=DEVNULL)
if returncode != 0:
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
file=sys.stderr)
print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
file=sys.stderr)
return 2
return 0
def _Start(pid_file):
tun_result = _CheckForTun()
if tun_result != 0:
return tun_result
arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia')
qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin',
'qemu-system-x86_64')
kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64')
kernel_path = os.path.join(kernel_data_dir, 'zircon.bin')
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
instance_name = 'crashpad_qemu_' + \
''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8))
# These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
popen = subprocess.Popen([
qemu_path,
'-m', '2048',
'-nographic',
'-kernel', kernel_path,
'-initrd', initrd_path,
'-smp', '4',
'-serial', 'stdio',
'-monitor', 'none',
'-machine', 'q35',
'-cpu', 'host,migratable=no',
'-enable-kvm',
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
'-append', 'TERM=dumb zircon.nodename=' + instance_name,
], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
with open(pid_file, 'wb') as f:
f.write('%d\n' % popen.pid)
for i in range(10):
netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr')
if subprocess.call([netaddr_path, '--nowait', instance_name],
stdout=open(os.devnull), stderr=open(os.devnull)) == 0:
break
time.sleep(.5)
else:
print('instance did not respond after start', file=sys.stderr)
return 1
return 0
def main(args):
if len(args) != 1 or args[0] not in ('start', 'stop'):
print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr)
return 1
command = args[0]
pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
_Stop(pid_file)
if command == 'start':
return _Start(pid_file)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))