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