blob: 8f1b5b9805cc94e81261a8623371212b03b5d34f [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module provides a class for starting qemu images using runqemu.
6# It's used by testimage.bbclass.
7
8import subprocess
9import os
10import time
11import signal
12import re
13import socket
14import select
15import errno
Patrick Williamsf1e5d692016-03-30 15:21:19 -050016import string
Patrick Williamsc124f4f2015-09-15 14:41:29 -050017import threading
Patrick Williamsf1e5d692016-03-30 15:21:19 -050018import codecs
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019from oeqa.utils.dump import HostDumper
20
21import logging
22logger = logging.getLogger("BitBake.QemuRunner")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060023logger.addHandler(logging.StreamHandler())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050024
Patrick Williamsf1e5d692016-03-30 15:21:19 -050025# Get Unicode non printable control chars
Patrick Williamsc0f7c042017-02-23 20:41:17 -060026control_range = list(range(0,32))+list(range(127,160))
27control_chars = [chr(x) for x in control_range
28 if chr(x) not in string.printable]
Patrick Williamsf1e5d692016-03-30 15:21:19 -050029re_control_char = re.compile('[%s]' % re.escape("".join(control_chars)))
30
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031class QemuRunner:
32
Patrick Williamsc0f7c042017-02-23 20:41:17 -060033 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds, use_kvm):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050034
35 # Popen object for runqemu
36 self.runqemu = None
37 # pid of the qemu process that runqemu will start
38 self.qemupid = None
39 # target ip - from the command line
40 self.ip = None
41 # host ip - where qemu is running
42 self.server_ip = None
43
44 self.machine = machine
45 self.rootfs = rootfs
46 self.display = display
47 self.tmpdir = tmpdir
48 self.deploy_dir_image = deploy_dir_image
49 self.logfile = logfile
50 self.boottime = boottime
51 self.logged = False
52 self.thread = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -060053 self.use_kvm = use_kvm
Patrick Williamsc124f4f2015-09-15 14:41:29 -050054
55 self.runqemutime = 60
56 self.host_dumper = HostDumper(dump_host_cmds, dump_dir)
57
58 def create_socket(self):
59 try:
60 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
61 sock.setblocking(0)
62 sock.bind(("127.0.0.1",0))
63 sock.listen(2)
64 port = sock.getsockname()[1]
65 logger.info("Created listening socket for qemu serial console on: 127.0.0.1:%s" % port)
66 return (sock, port)
67
68 except socket.error:
69 sock.close()
70 raise
71
72 def log(self, msg):
73 if self.logfile:
Patrick Williamsf1e5d692016-03-30 15:21:19 -050074 # It is needed to sanitize the data received from qemu
75 # because is possible to have control characters
Patrick Williamsc0f7c042017-02-23 20:41:17 -060076 msg = msg.decode("utf-8")
77 msg = re_control_char.sub('', msg)
Patrick Williamsf1e5d692016-03-30 15:21:19 -050078 with codecs.open(self.logfile, "a", encoding="utf-8") as f:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050079 f.write("%s" % msg)
80
81 def getOutput(self, o):
82 import fcntl
83 fl = fcntl.fcntl(o, fcntl.F_GETFL)
84 fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 return os.read(o.fileno(), 1000000).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086
87
88 def handleSIGCHLD(self, signum, frame):
89 if self.runqemu and self.runqemu.poll():
90 if self.runqemu.returncode:
91 logger.info('runqemu exited with code %d' % self.runqemu.returncode)
92 logger.info("Output from runqemu:\n%s" % self.getOutput(self.runqemu.stdout))
93 self.stop()
94 self._dump_host()
95 raise SystemExit
96
Patrick Williamsc0f7c042017-02-23 20:41:17 -060097 def start(self, qemuparams = None, get_ip = True, extra_bootparams = None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 if self.display:
99 os.environ["DISPLAY"] = self.display
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500100 # Set this flag so that Qemu doesn't do any grabs as SDL grabs
101 # interact badly with screensavers.
102 os.environ["QEMU_DONT_GRAB"] = "1"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103 if not os.path.exists(self.rootfs):
104 logger.error("Invalid rootfs %s" % self.rootfs)
105 return False
106 if not os.path.exists(self.tmpdir):
107 logger.error("Invalid TMPDIR path %s" % self.tmpdir)
108 return False
109 else:
110 os.environ["OE_TMPDIR"] = self.tmpdir
111 if not os.path.exists(self.deploy_dir_image):
112 logger.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
113 return False
114 else:
115 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
116
117 try:
118 threadsock, threadport = self.create_socket()
119 self.server_socket, self.serverport = self.create_socket()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600120 except socket.error as msg:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121 logger.error("Failed to create listening socket: %s" % msg[1])
122 return False
123
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500124
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600125 bootparams = 'console=tty1 console=ttyS0,115200n8 printk.time=1'
126 if extra_bootparams:
127 bootparams = bootparams + ' ' + extra_bootparams
128
129 self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500130 if not self.display:
131 self.qemuparams = 'nographic ' + self.qemuparams
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132 if qemuparams:
133 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
134
135 self.origchldhandler = signal.getsignal(signal.SIGCHLD)
136 signal.signal(signal.SIGCHLD, self.handleSIGCHLD)
137
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600138 launch_cmd = 'runqemu snapshot '
139 if self.use_kvm:
140 logger.info('Using kvm for runqemu')
141 launch_cmd += 'kvm '
142 else:
143 logger.info('Not using kvm for runqemu')
144 launch_cmd += 'tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams)
145 logger.info('launchcmd=%s'%(launch_cmd))
146
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147 # FIXME: We pass in stdin=subprocess.PIPE here to work around stty
148 # blocking at the end of the runqemu script when using this within
149 # oe-selftest (this makes stty error out immediately). There ought
150 # to be a proper fix but this will suffice for now.
151 self.runqemu = subprocess.Popen(launch_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, preexec_fn=os.setpgrp)
152 output = self.runqemu.stdout
153
154 #
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600155 # We need the preexec_fn above so that all runqemu processes can easily be killed
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 # (by killing their process group). This presents a problem if this controlling
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600157 # process itself is killed however since those processes don't notice the death
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158 # of the parent and merrily continue on.
159 #
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 # Rather than hack runqemu to deal with this, we add something here instead.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161 # Basically we fork off another process which holds an open pipe to the parent
162 # and also is setpgrp. If/when the pipe sees EOF from the parent dieing, it kills
163 # the process group. This is like pctrl's PDEATHSIG but for a process group
164 # rather than a single process.
165 #
166 r, w = os.pipe()
167 self.monitorpid = os.fork()
168 if self.monitorpid:
169 os.close(r)
170 self.monitorpipe = os.fdopen(w, "w")
171 else:
172 # child process
173 os.setpgrp()
174 os.close(w)
175 r = os.fdopen(r)
176 x = r.read()
177 os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM)
178 sys.exit(0)
179
180 logger.info("runqemu started, pid is %s" % self.runqemu.pid)
181 logger.info("waiting at most %s seconds for qemu pid" % self.runqemutime)
182 endtime = time.time() + self.runqemutime
183 while not self.is_alive() and time.time() < endtime:
184 if self.runqemu.poll():
185 if self.runqemu.returncode:
186 # No point waiting any longer
187 logger.info('runqemu exited with code %d' % self.runqemu.returncode)
188 self._dump_host()
189 self.stop()
190 logger.info("Output from runqemu:\n%s" % self.getOutput(output))
191 return False
192 time.sleep(1)
193
194 if self.is_alive():
195 logger.info("qemu started - qemu procces pid is %s" % self.qemupid)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500196 if get_ip:
197 cmdline = ''
198 with open('/proc/%s/cmdline' % self.qemupid) as p:
199 cmdline = p.read()
200 # It is needed to sanitize the data received
201 # because is possible to have control characters
202 cmdline = re_control_char.sub('', cmdline)
203 try:
204 ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
205 if not ips or len(ips) != 3:
206 raise ValueError
207 else:
208 self.ip = ips[0]
209 self.server_ip = ips[1]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600210 except (IndexError, ValueError):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500211 logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, self.getOutput(output)))
212 self._dump_host()
213 self.stop()
214 return False
215 logger.info("qemu cmdline used:\n{}".format(cmdline))
216 logger.info("Target IP: %s" % self.ip)
217 logger.info("Server IP: %s" % self.server_ip)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500218
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219 self.thread = LoggingThread(self.log, threadsock, logger)
220 self.thread.start()
221 if not self.thread.connection_established.wait(self.boottime):
222 logger.error("Didn't receive a console connection from qemu. "
223 "Here is the qemu command line used:\n%s\nand "
224 "output from runqemu:\n%s" % (cmdline,
225 self.getOutput(output)))
226 self.stop_thread()
227 return False
228
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500229 logger.info("Output from runqemu:\n%s", self.getOutput(output))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230 logger.info("Waiting at most %d seconds for login banner" % self.boottime)
231 endtime = time.time() + self.boottime
232 socklist = [self.server_socket]
233 reachedlogin = False
234 stopread = False
235 qemusock = None
236 bootlog = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 data = b''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238 while time.time() < endtime and not stopread:
239 sread, swrite, serror = select.select(socklist, [], [], 5)
240 for sock in sread:
241 if sock is self.server_socket:
242 qemusock, addr = self.server_socket.accept()
243 qemusock.setblocking(0)
244 socklist.append(qemusock)
245 socklist.remove(self.server_socket)
246 logger.info("Connection from %s:%s" % addr)
247 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600248 data = data + sock.recv(1024)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249 if data:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600250 try:
251 data = data.decode("utf-8", errors="surrogateescape")
252 bootlog += data
253 data = b''
254 if re.search(".* login:", bootlog):
255 self.server_socket = qemusock
256 stopread = True
257 reachedlogin = True
258 logger.info("Reached login banner")
259 except UnicodeDecodeError:
260 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500261 else:
262 socklist.remove(sock)
263 sock.close()
264 stopread = True
265
266 if not reachedlogin:
267 logger.info("Target didn't reached login boot in %d seconds" % self.boottime)
268 lines = "\n".join(bootlog.splitlines()[-25:])
269 logger.info("Last 25 lines of text:\n%s" % lines)
270 logger.info("Check full boot log: %s" % self.logfile)
271 self._dump_host()
272 self.stop()
273 return False
274
275 # If we are not able to login the tests can continue
276 try:
277 (status, output) = self.run_serial("root\n", raw=True)
278 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
279 self.logged = True
280 logger.info("Logged as root in serial console")
281 else:
282 logger.info("Couldn't login into serial console"
283 " as root using blank password")
284 except:
285 logger.info("Serial console failed while trying to login")
286
287 else:
288 logger.info("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
289 self._dump_host()
290 self.stop()
291 logger.info("Output from runqemu:\n%s" % self.getOutput(output))
292 return False
293
294 return self.is_alive()
295
296 def stop(self):
297 self.stop_thread()
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500298 if hasattr(self, "origchldhandler"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299 signal.signal(signal.SIGCHLD, self.origchldhandler)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500300 if self.runqemu:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600301 if hasattr(self, "monitorpid"):
302 os.kill(self.monitorpid, signal.SIGKILL)
303 logger.info("Sending SIGTERM to runqemu")
304 try:
305 os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM)
306 except OSError as e:
307 if e.errno != errno.ESRCH:
308 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309 endtime = time.time() + self.runqemutime
310 while self.runqemu.poll() is None and time.time() < endtime:
311 time.sleep(1)
312 if self.runqemu.poll() is None:
313 logger.info("Sending SIGKILL to runqemu")
314 os.killpg(os.getpgid(self.runqemu.pid), signal.SIGKILL)
315 self.runqemu = None
316 if hasattr(self, 'server_socket') and self.server_socket:
317 self.server_socket.close()
318 self.server_socket = None
319 self.qemupid = None
320 self.ip = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321
322 def stop_thread(self):
323 if self.thread and self.thread.is_alive():
324 self.thread.stop()
325 self.thread.join()
326
327 def restart(self, qemuparams = None):
328 logger.info("Restarting qemu process")
329 if self.runqemu.poll() is None:
330 self.stop()
331 if self.start(qemuparams):
332 return True
333 return False
334
335 def is_alive(self):
336 if not self.runqemu:
337 return False
338 qemu_child = self.find_child(str(self.runqemu.pid))
339 if qemu_child:
340 self.qemupid = qemu_child[0]
341 if os.path.exists("/proc/" + str(self.qemupid)):
342 return True
343 return False
344
345 def find_child(self,parent_pid):
346 #
347 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
348 #
349 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 processes = ps.decode("utf-8").split('\n')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 nfields = len(processes[0].split()) - 1
352 pids = {}
353 commands = {}
354 for row in processes[1:]:
355 data = row.split(None, nfields)
356 if len(data) != 3:
357 continue
358 if data[1] not in pids:
359 pids[data[1]] = []
360
361 pids[data[1]].append(data[0])
362 commands[data[0]] = data[2]
363
364 if parent_pid not in pids:
365 return []
366
367 parents = []
368 newparents = pids[parent_pid]
369 while newparents:
370 next = []
371 for p in newparents:
372 if p in pids:
373 for n in pids[p]:
374 if n not in parents and n not in next:
375 next.append(n)
376 if p not in parents:
377 parents.append(p)
378 newparents = next
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600379 #print("Children matching %s:" % str(parents))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380 for p in parents:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600381 # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
383 basecmd = commands[p].split()[0]
384 basecmd = os.path.basename(basecmd)
385 if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
386 return [int(p),commands[p]]
387
388 def run_serial(self, command, raw=False):
389 # We assume target system have echo to get command status
390 if not raw:
391 command = "%s; echo $?\n" % command
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500392
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393 data = ''
394 status = 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 self.server_socket.sendall(command.encode('utf-8'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500396 keepreading = True
397 while keepreading:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398 sread, _, _ = select.select([self.server_socket],[],[],5)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500399 if sread:
400 answer = self.server_socket.recv(1024)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401 if answer:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 data += answer.decode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403 # Search the prompt to stop
404 if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500405 keepreading = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500406 else:
407 raise Exception("No data on serial console socket")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500408 else:
409 keepreading = False
410
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500411 if data:
412 if raw:
413 status = 1
414 else:
415 # Remove first line (command line) and last line (prompt)
416 data = data[data.find('$?\r\n')+4:data.rfind('\r\n')]
417 index = data.rfind('\r\n')
418 if index == -1:
419 status_cmd = data
420 data = ""
421 else:
422 status_cmd = data[index+2:]
423 data = data[:index]
424 if (status_cmd == "0"):
425 status = 1
426 return (status, str(data))
427
428
429 def _dump_host(self):
430 self.host_dumper.create_dir("qemu")
431 logger.warn("Qemu ended unexpectedly, dump data from host"
432 " is in %s" % self.host_dumper.dump_dir)
433 self.host_dumper.dump_host()
434
435# This class is for reading data from a socket and passing it to logfunc
436# to be processed. It's completely event driven and has a straightforward
437# event loop. The mechanism for stopping the thread is a simple pipe which
438# will wake up the poll and allow for tearing everything down.
439class LoggingThread(threading.Thread):
440 def __init__(self, logfunc, sock, logger):
441 self.connection_established = threading.Event()
442 self.serversock = sock
443 self.logfunc = logfunc
444 self.logger = logger
445 self.readsock = None
446 self.running = False
447
448 self.errorevents = select.POLLERR | select.POLLHUP | select.POLLNVAL
449 self.readevents = select.POLLIN | select.POLLPRI
450
451 threading.Thread.__init__(self, target=self.threadtarget)
452
453 def threadtarget(self):
454 try:
455 self.eventloop()
456 finally:
457 self.teardown()
458
459 def run(self):
460 self.logger.info("Starting logging thread")
461 self.readpipe, self.writepipe = os.pipe()
462 threading.Thread.run(self)
463
464 def stop(self):
465 self.logger.info("Stopping logging thread")
466 if self.running:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600467 os.write(self.writepipe, bytes("stop", "utf-8"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500468
469 def teardown(self):
470 self.logger.info("Tearing down logging thread")
471 self.close_socket(self.serversock)
472
473 if self.readsock is not None:
474 self.close_socket(self.readsock)
475
476 self.close_ignore_error(self.readpipe)
477 self.close_ignore_error(self.writepipe)
478 self.running = False
479
480 def eventloop(self):
481 poll = select.poll()
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500482 event_read_mask = self.errorevents | self.readevents
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500483 poll.register(self.serversock.fileno())
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500484 poll.register(self.readpipe, event_read_mask)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500485
486 breakout = False
487 self.running = True
488 self.logger.info("Starting thread event loop")
489 while not breakout:
490 events = poll.poll()
491 for event in events:
492 # An error occurred, bail out
493 if event[1] & self.errorevents:
494 raise Exception(self.stringify_event(event[1]))
495
496 # Event to stop the thread
497 if self.readpipe == event[0]:
498 self.logger.info("Stop event received")
499 breakout = True
500 break
501
502 # A connection request was received
503 elif self.serversock.fileno() == event[0]:
504 self.logger.info("Connection request received")
505 self.readsock, _ = self.serversock.accept()
506 self.readsock.setblocking(0)
507 poll.unregister(self.serversock.fileno())
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500508 poll.register(self.readsock.fileno(), event_read_mask)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509
510 self.logger.info("Setting connection established event")
511 self.connection_established.set()
512
513 # Actual data to be logged
514 elif self.readsock.fileno() == event[0]:
515 data = self.recv(1024)
516 self.logfunc(data)
517
518 # Since the socket is non-blocking make sure to honor EAGAIN
519 # and EWOULDBLOCK.
520 def recv(self, count):
521 try:
522 data = self.readsock.recv(count)
523 except socket.error as e:
524 if e.errno == errno.EAGAIN or e.errno == errno.EWOULDBLOCK:
525 return ''
526 else:
527 raise
528
529 if data is None:
530 raise Exception("No data on read ready socket")
531 elif not data:
532 # This actually means an orderly shutdown
533 # happened. But for this code it counts as an
534 # error since the connection shouldn't go away
535 # until qemu exits.
536 raise Exception("Console connection closed unexpectedly")
537
538 return data
539
540 def stringify_event(self, event):
541 val = ''
542 if select.POLLERR == event:
543 val = 'POLLER'
544 elif select.POLLHUP == event:
545 val = 'POLLHUP'
546 elif select.POLLNVAL == event:
547 val = 'POLLNVAL'
548 return val
549
550 def close_socket(self, sock):
551 sock.shutdown(socket.SHUT_RDWR)
552 sock.close()
553
554 def close_ignore_error(self, fd):
555 try:
556 os.close(fd)
557 except OSError:
558 pass