blob: 20009401ca8e2f4fa890b46cb217fdc15c6ec1fc [file] [log] [blame]
#
# Copyright (C) 2015 Intel Corporation
#
# SPDX-License-Identifier: MIT
#
# This module provides a class for starting qemu images of poky tiny.
# It's used by testimage.bbclass.
import subprocess
import os
import time
import signal
import re
import socket
import select
import bb
from .qemurunner import QemuRunner
class QemuTinyRunner(QemuRunner):
def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, kernel, boottime, logger, tmpfsdir=None):
# Popen object for runqemu
self.runqemu = None
# pid of the qemu process that runqemu will start
self.qemupid = None
# target ip - from the command line
self.ip = None
# host ip - where qemu is running
self.server_ip = None
self.machine = machine
self.rootfs = rootfs
self.display = display
self.tmpdir = tmpdir
self.deploy_dir_image = deploy_dir_image
self.logfile = logfile
self.boottime = boottime
self.tmpfsdir = tmpfsdir
self.runqemutime = 60
self.socketfile = "console.sock"
self.server_socket = None
self.kernel = kernel
self.logger = logger
def create_socket(self):
tries = 3
while tries > 0:
try:
self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.server_socket.connect(self.socketfile)
bb.note("Created listening socket for qemu serial console.")
tries = 0
except socket.error as msg:
self.server_socket.close()
bb.fatal("Failed to create listening socket.")
tries -= 1
def log(self, msg):
if self.logfile:
with open(self.logfile, "a") as f:
f.write("%s" % msg)
def start(self, qemuparams = None, ssh=True, extra_bootparams=None, runqemuparams='', discard_writes=True):
if self.display:
os.environ["DISPLAY"] = self.display
else:
bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
return False
if not os.path.exists(self.rootfs):
bb.error("Invalid rootfs %s" % self.rootfs)
return False
if not os.path.exists(self.tmpdir):
bb.error("Invalid TMPDIR path %s" % self.tmpdir)
return False
else:
os.environ["OE_TMPDIR"] = self.tmpdir
if not os.path.exists(self.deploy_dir_image):
bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
return False
else:
os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
if self.tmpfsdir:
env["RUNQEMU_TMPFS_DIR"] = self.tmpfsdir
# Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
# badly with screensavers.
os.environ["QEMU_DONT_GRAB"] = "1"
self.qemuparams = '--append "root=/dev/ram0 console=ttyS0" -nographic -serial unix:%s,server,nowait' % self.socketfile
launch_cmd = 'qemu-system-i386 -kernel %s -initrd %s %s' % (self.kernel, self.rootfs, self.qemuparams)
self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
bb.note("runqemu started, pid is %s" % self.runqemu.pid)
bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
endtime = time.time() + self.runqemutime
while not self.is_alive() and time.time() < endtime:
time.sleep(1)
if self.is_alive():
bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
self.create_socket()
else:
bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
output = self.runqemu.stdout
self.stop()
bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8"))
return False
return self.is_alive()
def run_serial(self, command, timeout=60):
self.server_socket.sendall(command+'\n')
data = ''
status = 0
stopread = False
endtime = time.time()+timeout
while time.time()<endtime and not stopread:
try:
sread, _, _ = select.select([self.server_socket],[],[],1)
except InterruptedError:
continue
for sock in sread:
answer = sock.recv(1024)
if answer:
data += answer
else:
sock.close()
stopread = True
if not data:
status = 1
if not stopread:
data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout
return (status, str(data))
def find_child(self,parent_pid):
#
# Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
#
ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command'], stdout=subprocess.PIPE).communicate()[0]
processes = ps.decode("utf-8").split('\n')
nfields = len(processes[0].split()) - 1
pids = {}
commands = {}
for row in processes[1:]:
data = row.split(None, nfields)
if len(data) != 3:
continue
if data[1] not in pids:
pids[data[1]] = []
pids[data[1]].append(data[0])
commands[data[0]] = data[2]
if parent_pid not in pids:
return []
parents = []
newparents = pids[parent_pid]
while newparents:
next = []
for p in newparents:
if p in pids:
for n in pids[p]:
if n not in parents and n not in next:
next.append(n)
if p not in parents:
parents.append(p)
newparents = next
#print("Children matching %s:" % str(parents))
for p in parents:
# Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
# Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
basecmd = commands[p].split()[0]
basecmd = os.path.basename(basecmd)
if "qemu-system" in basecmd and "-serial unix" in commands[p]:
return [int(p),commands[p]]