1# 2# Copyright (C) 2015 Intel Corporation 3# 4# SPDX-License-Identifier: MIT 5# 6 7# This module provides a class for starting qemu images of poky tiny. 8# It's used by testimage.bbclass. 9 10import subprocess 11import os 12import time 13import signal 14import re 15import socket 16import select 17import bb 18from .qemurunner import QemuRunner 19 20class QemuTinyRunner(QemuRunner): 21 22 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, kernel, boottime, logger, tmpfsdir=None): 23 24 # Popen object for runqemu 25 self.runqemu = None 26 # pid of the qemu process that runqemu will start 27 self.qemupid = None 28 # target ip - from the command line 29 self.ip = None 30 # host ip - where qemu is running 31 self.server_ip = None 32 33 self.machine = machine 34 self.rootfs = rootfs 35 self.display = display 36 self.tmpdir = tmpdir 37 self.deploy_dir_image = deploy_dir_image 38 self.logfile = logfile 39 self.boottime = boottime 40 self.tmpfsdir = tmpfsdir 41 42 self.runqemutime = 60 43 self.socketfile = "console.sock" 44 self.server_socket = None 45 self.kernel = kernel 46 self.logger = logger 47 48 49 def create_socket(self): 50 tries = 3 51 while tries > 0: 52 try: 53 self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 54 self.server_socket.connect(self.socketfile) 55 bb.note("Created listening socket for qemu serial console.") 56 tries = 0 57 except socket.error as msg: 58 self.server_socket.close() 59 bb.fatal("Failed to create listening socket.") 60 tries -= 1 61 62 def log(self, msg): 63 if self.logfile: 64 with open(self.logfile, "a") as f: 65 f.write("%s" % msg) 66 67 def start(self, qemuparams = None, ssh=True, extra_bootparams=None, runqemuparams='', discard_writes=True): 68 69 if self.display: 70 os.environ["DISPLAY"] = self.display 71 else: 72 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)") 73 return False 74 if not os.path.exists(self.rootfs): 75 bb.error("Invalid rootfs %s" % self.rootfs) 76 return False 77 if not os.path.exists(self.tmpdir): 78 bb.error("Invalid TMPDIR path %s" % self.tmpdir) 79 return False 80 else: 81 os.environ["OE_TMPDIR"] = self.tmpdir 82 if not os.path.exists(self.deploy_dir_image): 83 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image) 84 return False 85 else: 86 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image 87 if self.tmpfsdir: 88 env["RUNQEMU_TMPFS_DIR"] = self.tmpfsdir 89 90 91 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact 92 # badly with screensavers. 93 os.environ["QEMU_DONT_GRAB"] = "1" 94 self.qemuparams = '--append "root=/dev/ram0 console=ttyS0" -nographic -serial unix:%s,server,nowait' % self.socketfile 95 96 launch_cmd = 'qemu-system-i386 -kernel %s -initrd %s %s' % (self.kernel, self.rootfs, self.qemuparams) 97 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp) 98 99 bb.note("runqemu started, pid is %s" % self.runqemu.pid) 100 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime) 101 endtime = time.time() + self.runqemutime 102 while not self.is_alive() and time.time() < endtime: 103 time.sleep(1) 104 105 if self.is_alive(): 106 bb.note("qemu started - qemu procces pid is %s" % self.qemupid) 107 self.create_socket() 108 else: 109 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime) 110 output = self.runqemu.stdout 111 self.stop() 112 bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8")) 113 return False 114 115 return self.is_alive() 116 117 def run_serial(self, command, timeout=60): 118 self.server_socket.sendall(command+'\n') 119 data = '' 120 status = 0 121 stopread = False 122 endtime = time.time()+timeout 123 while time.time()<endtime and not stopread: 124 try: 125 sread, _, _ = select.select([self.server_socket],[],[],1) 126 except InterruptedError: 127 continue 128 for sock in sread: 129 answer = sock.recv(1024) 130 if answer: 131 data += answer 132 else: 133 sock.close() 134 stopread = True 135 if not data: 136 status = 1 137 if not stopread: 138 data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout 139 return (status, str(data)) 140 141 def find_child(self,parent_pid): 142 # 143 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd] 144 # 145 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command'], stdout=subprocess.PIPE).communicate()[0] 146 processes = ps.decode("utf-8").split('\n') 147 nfields = len(processes[0].split()) - 1 148 pids = {} 149 commands = {} 150 for row in processes[1:]: 151 data = row.split(None, nfields) 152 if len(data) != 3: 153 continue 154 if data[1] not in pids: 155 pids[data[1]] = [] 156 157 pids[data[1]].append(data[0]) 158 commands[data[0]] = data[2] 159 160 if parent_pid not in pids: 161 return [] 162 163 parents = [] 164 newparents = pids[parent_pid] 165 while newparents: 166 next = [] 167 for p in newparents: 168 if p in pids: 169 for n in pids[p]: 170 if n not in parents and n not in next: 171 next.append(n) 172 if p not in parents: 173 parents.append(p) 174 newparents = next 175 #print("Children matching %s:" % str(parents)) 176 for p in parents: 177 # Need to be careful here since runqemu runs "ldd qemu-system-xxxx" 178 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx" 179 basecmd = commands[p].split()[0] 180 basecmd = os.path.basename(basecmd) 181 if "qemu-system" in basecmd and "-serial unix" in commands[p]: 182 return [int(p),commands[p]] 183