blob: 5c92941c0a6a70f4d9a622b66151aaf0f28c0e17 [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
Brad Bishopd7bf8c12018-02-25 22:55:05 -050022 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, kernel, boottime, logger):
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
40
41 self.runqemutime = 60
42 self.socketfile = "console.sock"
43 self.server_socket = None
44 self.kernel = kernel
Brad Bishopd7bf8c12018-02-25 22:55:05 -050045 self.logger = logger
Patrick Williamsc124f4f2015-09-15 14:41:29 -050046
47
48 def create_socket(self):
49 tries = 3
50 while tries > 0:
51 try:
52 self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
53 self.server_socket.connect(self.socketfile)
54 bb.note("Created listening socket for qemu serial console.")
55 tries = 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -060056 except socket.error as msg:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057 self.server_socket.close()
58 bb.fatal("Failed to create listening socket.")
59 tries -= 1
60
61 def log(self, msg):
62 if self.logfile:
63 with open(self.logfile, "a") as f:
64 f.write("%s" % msg)
65
Brad Bishop6e60e8b2018-02-01 10:27:11 -050066 def start(self, qemuparams = None, ssh=True, extra_bootparams=None, runqemuparams='', discard_writes=True):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050067
68 if self.display:
69 os.environ["DISPLAY"] = self.display
70 else:
71 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
72 return False
73 if not os.path.exists(self.rootfs):
74 bb.error("Invalid rootfs %s" % self.rootfs)
75 return False
76 if not os.path.exists(self.tmpdir):
77 bb.error("Invalid TMPDIR path %s" % self.tmpdir)
78 return False
79 else:
80 os.environ["OE_TMPDIR"] = self.tmpdir
81 if not os.path.exists(self.deploy_dir_image):
82 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
83 return False
84 else:
85 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
86
87 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
88 # badly with screensavers.
89 os.environ["QEMU_DONT_GRAB"] = "1"
90 self.qemuparams = '--append "root=/dev/ram0 console=ttyS0" -nographic -serial unix:%s,server,nowait' % self.socketfile
91
92 launch_cmd = 'qemu-system-i386 -kernel %s -initrd %s %s' % (self.kernel, self.rootfs, self.qemuparams)
93 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
94
95 bb.note("runqemu started, pid is %s" % self.runqemu.pid)
96 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
97 endtime = time.time() + self.runqemutime
98 while not self.is_alive() and time.time() < endtime:
99 time.sleep(1)
100
101 if self.is_alive():
102 bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
103 self.create_socket()
104 else:
105 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
106 output = self.runqemu.stdout
107 self.stop()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108 bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109 return False
110
111 return self.is_alive()
112
Brad Bishop977dc1a2019-02-06 16:01:43 -0500113 def run_serial(self, command, timeout=60):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500114 self.server_socket.sendall(command+'\n')
115 data = ''
116 status = 0
117 stopread = False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500118 endtime = time.time()+timeout
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119 while time.time()<endtime and not stopread:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500120 try:
121 sread, _, _ = select.select([self.server_socket],[],[],1)
122 except InterruptedError:
123 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124 for sock in sread:
125 answer = sock.recv(1024)
126 if answer:
127 data += answer
128 else:
129 sock.close()
130 stopread = True
131 if not data:
132 status = 1
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500133 if not stopread:
134 data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500135 return (status, str(data))
136
137 def find_child(self,parent_pid):
138 #
139 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
140 #
Andrew Geisslerb7d28612020-07-24 16:15:54 -0500141 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,pri,ni,command'], stdout=subprocess.PIPE).communicate()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600142 processes = ps.decode("utf-8").split('\n')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143 nfields = len(processes[0].split()) - 1
144 pids = {}
145 commands = {}
146 for row in processes[1:]:
147 data = row.split(None, nfields)
148 if len(data) != 3:
149 continue
150 if data[1] not in pids:
151 pids[data[1]] = []
152
153 pids[data[1]].append(data[0])
154 commands[data[0]] = data[2]
155
156 if parent_pid not in pids:
157 return []
158
159 parents = []
160 newparents = pids[parent_pid]
161 while newparents:
162 next = []
163 for p in newparents:
164 if p in pids:
165 for n in pids[p]:
166 if n not in parents and n not in next:
167 next.append(n)
168 if p not in parents:
169 parents.append(p)
170 newparents = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600171 #print("Children matching %s:" % str(parents))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 for p in parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600173 # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
175 basecmd = commands[p].split()[0]
176 basecmd = os.path.basename(basecmd)
177 if "qemu-system" in basecmd and "-serial unix" in commands[p]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 return [int(p),commands[p]]