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