blob: 4f95101f301edb930205b48feaa9841e283a39ec [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
16from qemurunner import QemuRunner
17
18class QemuTinyRunner(QemuRunner):
19
20 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, kernel, boottime):
21
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
43
44
45 def create_socket(self):
46 tries = 3
47 while tries > 0:
48 try:
49 self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
50 self.server_socket.connect(self.socketfile)
51 bb.note("Created listening socket for qemu serial console.")
52 tries = 0
53 except socket.error, msg:
54 self.server_socket.close()
55 bb.fatal("Failed to create listening socket.")
56 tries -= 1
57
58 def log(self, msg):
59 if self.logfile:
60 with open(self.logfile, "a") as f:
61 f.write("%s" % msg)
62
63 def start(self, qemuparams = None):
64
65 if self.display:
66 os.environ["DISPLAY"] = self.display
67 else:
68 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
69 return False
70 if not os.path.exists(self.rootfs):
71 bb.error("Invalid rootfs %s" % self.rootfs)
72 return False
73 if not os.path.exists(self.tmpdir):
74 bb.error("Invalid TMPDIR path %s" % self.tmpdir)
75 return False
76 else:
77 os.environ["OE_TMPDIR"] = self.tmpdir
78 if not os.path.exists(self.deploy_dir_image):
79 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
80 return False
81 else:
82 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
83
84 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
85 # badly with screensavers.
86 os.environ["QEMU_DONT_GRAB"] = "1"
87 self.qemuparams = '--append "root=/dev/ram0 console=ttyS0" -nographic -serial unix:%s,server,nowait' % self.socketfile
88
89 launch_cmd = 'qemu-system-i386 -kernel %s -initrd %s %s' % (self.kernel, self.rootfs, self.qemuparams)
90 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
91
92 bb.note("runqemu started, pid is %s" % self.runqemu.pid)
93 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
94 endtime = time.time() + self.runqemutime
95 while not self.is_alive() and time.time() < endtime:
96 time.sleep(1)
97
98 if self.is_alive():
99 bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
100 self.create_socket()
101 else:
102 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
103 output = self.runqemu.stdout
104 self.stop()
105 bb.note("Output from runqemu:\n%s" % output.read())
106 return False
107
108 return self.is_alive()
109
110 def run_serial(self, command):
111 self.server_socket.sendall(command+'\n')
112 data = ''
113 status = 0
114 stopread = False
115 endtime = time.time()+5
116 while time.time()<endtime and not stopread:
117 sread, _, _ = select.select([self.server_socket],[],[],5)
118 for sock in sread:
119 answer = sock.recv(1024)
120 if answer:
121 data += answer
122 else:
123 sock.close()
124 stopread = True
125 if not data:
126 status = 1
127 return (status, str(data))
128
129 def find_child(self,parent_pid):
130 #
131 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
132 #
133 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
134 processes = ps.split('\n')
135 nfields = len(processes[0].split()) - 1
136 pids = {}
137 commands = {}
138 for row in processes[1:]:
139 data = row.split(None, nfields)
140 if len(data) != 3:
141 continue
142 if data[1] not in pids:
143 pids[data[1]] = []
144
145 pids[data[1]].append(data[0])
146 commands[data[0]] = data[2]
147
148 if parent_pid not in pids:
149 return []
150
151 parents = []
152 newparents = pids[parent_pid]
153 while newparents:
154 next = []
155 for p in newparents:
156 if p in pids:
157 for n in pids[p]:
158 if n not in parents and n not in next:
159 next.append(n)
160 if p not in parents:
161 parents.append(p)
162 newparents = next
163 #print "Children matching %s:" % str(parents)
164 for p in parents:
165 # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
166 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
167 basecmd = commands[p].split()[0]
168 basecmd = os.path.basename(basecmd)
169 if "qemu-system" in basecmd and "-serial unix" in commands[p]:
170 return [int(p),commands[p]]