]>
Commit | Line | Data |
---|---|---|
e940bc13 JC |
1 | #!/bin/bash |
2 | # | |
3 | # This allows for launching of multiple QEMU instances, with independent | |
4 | # communication possible to each instance. | |
5 | # | |
6 | # Each instance can choose, at launch, to use either the QMP or the | |
7 | # HMP (monitor) interface. | |
8 | # | |
9 | # All instances are cleaned up via _cleanup_qemu, including killing the | |
10 | # running qemu instance. | |
11 | # | |
12 | # Copyright (C) 2014 Red Hat, Inc. | |
13 | # | |
14 | # This program is free software; you can redistribute it and/or modify | |
15 | # it under the terms of the GNU General Public License as published by | |
16 | # the Free Software Foundation; either version 2 of the License, or | |
17 | # (at your option) any later version. | |
18 | # | |
19 | # This program is distributed in the hope that it will be useful, | |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | # GNU General Public License for more details. | |
23 | # | |
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
26 | # | |
27 | ||
28 | QEMU_COMM_TIMEOUT=10 | |
29 | ||
846a1d11 JC |
30 | QEMU_FIFO_IN="${QEMU_TEST_DIR}/qmp-in-$$" |
31 | QEMU_FIFO_OUT="${QEMU_TEST_DIR}/qmp-out-$$" | |
e940bc13 | 32 | |
e940bc13 JC |
33 | QEMU_HANDLE=0 |
34 | ||
35 | # If bash version is >= 4.1, these will be overwritten and dynamic | |
36 | # file descriptor values assigned. | |
37 | _out_fd=3 | |
38 | _in_fd=4 | |
39 | ||
40 | # Wait for expected QMP response from QEMU. Will time out | |
41 | # after 10 seconds, which counts as failure. | |
42 | # | |
43 | # Override QEMU_COMM_TIMEOUT for a timeout different than the | |
44 | # default 10 seconds | |
45 | # | |
46 | # $1: The handle to use | |
47 | # $2+ All remaining arguments comprise the string to search for | |
48 | # in the response. | |
49 | # | |
50 | # If $silent is set to anything but an empty string, then | |
51 | # response is not echoed out. | |
52 | function _timed_wait_for() | |
53 | { | |
54 | local h=${1} | |
55 | shift | |
56 | ||
57 | QEMU_STATUS[$h]=0 | |
58 | while read -t ${QEMU_COMM_TIMEOUT} resp <&${QEMU_OUT[$h]} | |
59 | do | |
60 | if [ -z "${silent}" ]; then | |
61 | echo "${resp}" | _filter_testdir | _filter_qemu \ | |
69404d9e | 62 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
e940bc13 JC |
63 | fi |
64 | grep -q "${*}" < <(echo ${resp}) | |
65 | if [ $? -eq 0 ]; then | |
66 | return | |
67 | fi | |
68 | done | |
69 | QEMU_STATUS[$h]=-1 | |
70 | if [ -z "${qemu_error_no_exit}" ]; then | |
71 | echo "Timeout waiting for ${*} on handle ${h}" | |
72 | exit 1 # Timeout means the test failed | |
73 | fi | |
74 | } | |
75 | ||
76 | ||
77 | # Sends QMP or HMP command to QEMU, and waits for the expected response | |
78 | # | |
79 | # $1: QEMU handle to use | |
80 | # $2: String of the QMP command to send | |
81 | # ${@: -1} (Last string passed) | |
82 | # String that the QEMU response should contain. If it is a null | |
83 | # string, do not wait for a response | |
84 | # | |
85 | # Set qemu_cmd_repeat to the number of times to repeat the cmd | |
86 | # until either timeout, or a response. If it is not set, or <=0, | |
87 | # then the command is only sent once. | |
88 | # | |
89 | # If $qemu_error_no_exit is set, then even if the expected response | |
90 | # is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in | |
91 | # that case. | |
92 | function _send_qemu_cmd() | |
93 | { | |
94 | local h=${1} | |
95 | local count=1 | |
96 | local cmd= | |
97 | local use_error=${qemu_error_no_exit} | |
98 | shift | |
99 | ||
100 | if [ ${qemu_cmd_repeat} -gt 0 ] 2>/dev/null; then | |
101 | count=${qemu_cmd_repeat} | |
102 | use_error="no" | |
103 | fi | |
5d831be2 | 104 | # This array element extraction is done to accommodate pathnames with spaces |
e940bc13 JC |
105 | cmd=${@: 1:${#@}-1} |
106 | shift $(($# - 1)) | |
107 | ||
108 | while [ ${count} -gt 0 ] | |
109 | do | |
110 | echo "${cmd}" >&${QEMU_IN[${h}]} | |
111 | if [ -n "${1}" ]; then | |
112 | qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" | |
113 | if [ ${QEMU_STATUS[$h]} -eq 0 ]; then | |
114 | return | |
115 | fi | |
116 | fi | |
117 | let count--; | |
118 | done | |
119 | if [ ${QEMU_STATUS[$h]} -ne 0 ] && [ -z "${qemu_error_no_exit}" ]; then | |
120 | echo "Timeout waiting for ${1} on handle ${h}" | |
121 | exit 1 #Timeout means the test failed | |
122 | fi | |
123 | } | |
124 | ||
125 | ||
126 | # Launch a QEMU process. | |
127 | # | |
128 | # Input parameters: | |
129 | # $qemu_comm_method: set this variable to 'monitor' (case insensitive) | |
130 | # to use the QEMU HMP monitor for communication. | |
131 | # Otherwise, the default of QMP is used. | |
15cfba69 HR |
132 | # $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr. |
133 | # If this variable is empty, stderr will be redirected to stdout. | |
e940bc13 JC |
134 | # Returns: |
135 | # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. | |
136 | # | |
137 | function _launch_qemu() | |
138 | { | |
139 | local comm= | |
140 | local fifo_out= | |
141 | local fifo_in= | |
142 | ||
143 | if (shopt -s nocasematch; [[ "${qemu_comm_method}" == "monitor" ]]) | |
144 | then | |
145 | comm="-monitor stdio" | |
146 | else | |
147 | local qemu_comm_method="qmp" | |
148 | comm="-monitor none -qmp stdio" | |
149 | fi | |
150 | ||
151 | fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE} | |
152 | fifo_in=${QEMU_FIFO_IN}_${_QEMU_HANDLE} | |
153 | mkfifo "${fifo_out}" | |
154 | mkfifo "${fifo_in}" | |
155 | ||
13a1d4a7 DB |
156 | object_options= |
157 | if [ -n "$IMGKEYSECRET" ]; then | |
158 | object_options="--object secret,id=keysec0,data=$IMGKEYSECRET" | |
159 | fi | |
160 | ||
15cfba69 HR |
161 | if [ -z "$keep_stderr" ]; then |
162 | QEMU_NEED_PID='y'\ | |
13a1d4a7 | 163 | ${QEMU} ${object_options} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ |
3bb8ef4b HR |
164 | 2>&1 \ |
165 | <"${fifo_in}" & | |
15cfba69 HR |
166 | elif [ "$keep_stderr" = "y" ]; then |
167 | QEMU_NEED_PID='y'\ | |
13a1d4a7 | 168 | ${QEMU} ${object_options} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ |
3bb8ef4b | 169 | <"${fifo_in}" & |
15cfba69 HR |
170 | else |
171 | exit 1 | |
172 | fi | |
e940bc13 JC |
173 | |
174 | if [[ "${BASH_VERSINFO[0]}" -ge "5" || | |
175 | ("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]] | |
176 | then | |
177 | # bash >= 4.1 required for automatic fd | |
178 | exec {_out_fd}<"${fifo_out}" | |
179 | exec {_in_fd}>"${fifo_in}" | |
180 | else | |
181 | let _out_fd++ | |
182 | let _in_fd++ | |
183 | eval "exec ${_out_fd}<'${fifo_out}'" | |
184 | eval "exec ${_in_fd}>'${fifo_in}'" | |
185 | fi | |
186 | ||
187 | QEMU_OUT[${_QEMU_HANDLE}]=${_out_fd} | |
188 | QEMU_IN[${_QEMU_HANDLE}]=${_in_fd} | |
189 | QEMU_STATUS[${_QEMU_HANDLE}]=0 | |
190 | ||
191 | if [ "${qemu_comm_method}" == "qmp" ] | |
192 | then | |
193 | # Don't print response, since it has version information in it | |
194 | silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities" | |
195 | fi | |
196 | QEMU_HANDLE=${_QEMU_HANDLE} | |
197 | let _QEMU_HANDLE++ | |
198 | } | |
199 | ||
200 | ||
201 | # Silenty kills the QEMU process | |
ea82aa42 HR |
202 | # |
203 | # If $wait is set to anything other than the empty string, the process will not | |
204 | # be killed but only waited for, and any output will be forwarded to stdout. If | |
205 | # $wait is empty, the process will be killed and all output will be suppressed. | |
e940bc13 JC |
206 | function _cleanup_qemu() |
207 | { | |
208 | # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices | |
209 | for i in "${!QEMU_OUT[@]}" | |
210 | do | |
f6c8c2e0 | 211 | local QEMU_PID |
846a1d11 JC |
212 | if [ -f "${QEMU_TEST_DIR}/qemu-${i}.pid" ]; then |
213 | read QEMU_PID < "${QEMU_TEST_DIR}/qemu-${i}.pid" | |
214 | rm -f "${QEMU_TEST_DIR}/qemu-${i}.pid" | |
f6c8c2e0 JC |
215 | if [ -z "${wait}" ] && [ -n "${QEMU_PID}" ]; then |
216 | kill -KILL ${QEMU_PID} 2>/dev/null | |
217 | fi | |
218 | if [ -n "${QEMU_PID}" ]; then | |
219 | wait ${QEMU_PID} 2>/dev/null # silent kill | |
220 | fi | |
ea82aa42 | 221 | fi |
f6c8c2e0 | 222 | |
ea82aa42 HR |
223 | if [ -n "${wait}" ]; then |
224 | cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \ | |
69404d9e | 225 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
ea82aa42 | 226 | fi |
e940bc13 JC |
227 | rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}" |
228 | eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors | |
229 | eval "exec ${QEMU_OUT[$i]}<&-" | |
ecaf8c8a KW |
230 | |
231 | unset QEMU_IN[$i] | |
232 | unset QEMU_OUT[$i] | |
e940bc13 JC |
233 | done |
234 | } |