blob: 20009401ca8e2f4fa890b46cb217fdc15c6ec1fc [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002# Copyright (C) 2015 Intel Corporation
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: MIT
5#
Patrick Williamsc124f4f2015-09-15 14:41:29 -05006
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060018from .qemurunner import QemuRunner
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019
20class QemuTinyRunner(QemuRunner):
21
Andrew Geissler3b8a17c2021-04-15 15:55:55 -050022 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, kernel, boottime, logger, tmpfsdir=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023
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
Andrew Geissler3b8a17c2021-04-15 15:55:55 -050040 self.tmpfsdir = tmpfsdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041
42 self.runqemutime = 60
43 self.socketfile = "console.sock"
44 self.server_socket = None
45 self.kernel = kernel
Brad Bishopd7bf8c12018-02-25 22:55:05 -050046 self.logger = logger
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060057 except socket.error as msg:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050058 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
Brad Bishop6e60e8b2018-02-01 10:27:11 -050067 def start(self, qemuparams = None, ssh=True, extra_bootparams=None, runqemuparams='', discard_writes=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068
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
Andrew Geissler3b8a17c2021-04-15 15:55:55 -050087 if self.tmpfsdir:
88 env["RUNQEMU_TMPFS_DIR"] = self.tmpfsdir
89
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
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()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112 bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500113 return False
114
115 return self.is_alive()
116
Brad Bishop977dc1a2019-02-06 16:01:43 -0500117 def run_serial(self, command, timeout=60):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118 self.server_socket.sendall(command+'\n')
119 data = ''
120 status = 0
121 stopread = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500122 endtime = time.time()+timeout
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 while time.time()<endtime and not stopread:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500124 try:
125 sread, _, _ = select.select([self.server_socket],[],[],1)
126 except InterruptedError:
127 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128 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
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 if not stopread:
138 data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139 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 #
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500145 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command'], stdout=subprocess.PIPE).communicate()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146 processes = ps.decode("utf-8").split('\n')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 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
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 #print("Children matching %s:" % str(parents))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500176 for p in parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600177 # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178 # 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]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 return [int(p),commands[p]]