| 1 | #!/usr/bin/env 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 | |
| 30 | QEMU_FIFO_IN="${QEMU_TEST_DIR}/qmp-in-$$" |
| 31 | QEMU_FIFO_OUT="${QEMU_TEST_DIR}/qmp-out-$$" |
| 32 | |
| 33 | QEMU_HANDLE=0 |
| 34 | export _QEMU_HANDLE=0 |
| 35 | |
| 36 | # If bash version is >= 4.1, these will be overwritten and dynamic |
| 37 | # file descriptor values assigned. |
| 38 | _out_fd=3 |
| 39 | _in_fd=4 |
| 40 | |
| 41 | # Wait for expected QMP response from QEMU. Will time out |
| 42 | # after 10 seconds, which counts as failure. |
| 43 | # |
| 44 | # Override QEMU_COMM_TIMEOUT for a timeout different than the |
| 45 | # default 10 seconds |
| 46 | # |
| 47 | # $1: The handle to use |
| 48 | # $2+ All remaining arguments comprise the string to search for |
| 49 | # in the response. |
| 50 | # |
| 51 | # If $silent is set to anything but an empty string, then |
| 52 | # response is not echoed out. |
| 53 | # If $mismatch_only is set, only non-matching responses will |
| 54 | # be echoed. |
| 55 | # |
| 56 | # If $capture_events is non-empty, then any QMP event names it lists |
| 57 | # will not be echoed out, but instead collected in the $QEMU_EVENTS |
| 58 | # variable. The _wait_event function can later be used to receive |
| 59 | # the cached events. |
| 60 | # |
| 61 | # If $only_capture_events is set to anything but an empty string, |
| 62 | # then an error will be raised if a QMP message is seen which is |
| 63 | # not an event listed in $capture_events. |
| 64 | # |
| 65 | # If $success_or_failure is set, the meaning of the arguments is |
| 66 | # changed as follows: |
| 67 | # $2: A string to search for in the response; if found, this indicates |
| 68 | # success and ${QEMU_STATUS[$1]} is set to 0. |
| 69 | # $3: A string to search for in the response; if found, this indicates |
| 70 | # failure and the test is either aborted (if $qemu_error_no_exit |
| 71 | # is not set) or ${QEMU_STATUS[$1]} is set to -1 (otherwise). |
| 72 | _timed_wait_for() |
| 73 | { |
| 74 | local h=${1} |
| 75 | shift |
| 76 | |
| 77 | if [ -z "${success_or_failure}" ]; then |
| 78 | success_match=${*} |
| 79 | failure_match= |
| 80 | else |
| 81 | success_match=${1} |
| 82 | failure_match=${2} |
| 83 | fi |
| 84 | |
| 85 | timeout=yes |
| 86 | |
| 87 | QEMU_STATUS[$h]=0 |
| 88 | read_timeout="-t ${QEMU_COMM_TIMEOUT}" |
| 89 | if [ -n "${GDB_OPTIONS}" ]; then |
| 90 | read_timeout= |
| 91 | fi |
| 92 | |
| 93 | while IFS= read ${read_timeout} resp <&${QEMU_OUT[$h]} |
| 94 | do |
| 95 | if [ -n "$capture_events" ]; then |
| 96 | capture=0 |
| 97 | local evname |
| 98 | for evname in $capture_events |
| 99 | do |
| 100 | case ${resp} in |
| 101 | *\"event\":\ \"${evname}\"* ) capture=1 ;; |
| 102 | esac |
| 103 | done |
| 104 | if [ $capture = 1 ]; |
| 105 | then |
| 106 | ev=$(echo "${resp}" | tr -d '\r' | tr % .) |
| 107 | QEMU_EVENTS="${QEMU_EVENTS:+${QEMU_EVENTS}%}${ev}" |
| 108 | if [ -n "$only_capture_events" ]; then |
| 109 | return |
| 110 | else |
| 111 | continue |
| 112 | fi |
| 113 | fi |
| 114 | fi |
| 115 | if [ -n "$only_capture_events" ]; then |
| 116 | echo "Only expected $capture_events but got ${resp}" |
| 117 | exit 1 |
| 118 | fi |
| 119 | |
| 120 | if [ -z "${silent}" ] && [ -z "${mismatch_only}" ]; then |
| 121 | echo "${resp}" | _filter_testdir | _filter_qemu \ |
| 122 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
| 123 | fi |
| 124 | if [ -n "${failure_match}" ]; then |
| 125 | grep -q "${failure_match}" < <(echo "${resp}") |
| 126 | if [ $? -eq 0 ]; then |
| 127 | timeout= |
| 128 | break |
| 129 | fi |
| 130 | fi |
| 131 | grep -q "${success_match}" < <(echo "${resp}") |
| 132 | if [ $? -eq 0 ]; then |
| 133 | return |
| 134 | fi |
| 135 | if [ -z "${silent}" ] && [ -n "${mismatch_only}" ]; then |
| 136 | echo "${resp}" | _filter_testdir | _filter_qemu \ |
| 137 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
| 138 | fi |
| 139 | |
| 140 | done |
| 141 | QEMU_STATUS[$h]=-1 |
| 142 | if [ -z "${qemu_error_no_exit}" ]; then |
| 143 | if [ -n "${timeout}" ]; then |
| 144 | echo "Timeout waiting for ${success_match} on handle ${h}" |
| 145 | else |
| 146 | echo "Wrong response matching ${failure_match} on handle ${h}" |
| 147 | fi |
| 148 | exit 1 # Timeout or wrong match mean the test failed |
| 149 | fi |
| 150 | } |
| 151 | |
| 152 | |
| 153 | # Sends QMP or HMP command to QEMU, and waits for the expected response |
| 154 | # |
| 155 | # $1: QEMU handle to use |
| 156 | # $2: String of the QMP command to send |
| 157 | # ${@: -1} (Last string passed) |
| 158 | # String that the QEMU response should contain. If it is a null |
| 159 | # string, do not wait for a response |
| 160 | # |
| 161 | # Set qemu_cmd_repeat to the number of times to repeat the cmd |
| 162 | # until either timeout, or a response. If it is not set, or <=0, |
| 163 | # then the command is only sent once. |
| 164 | # |
| 165 | # If neither $silent nor $mismatch_only is set, and $cmd begins with '{', |
| 166 | # echo the command before sending it the first time. |
| 167 | # |
| 168 | # If $qemu_error_no_exit is set, then even if the expected response |
| 169 | # is not seen, we will not exit. $QEMU_STATUS[$1] will be set it -1 in |
| 170 | # that case. |
| 171 | # |
| 172 | # If $success_or_failure is set, then the last two strings are the |
| 173 | # strings the response will be scanned for. The first of the two |
| 174 | # indicates success, the latter indicates failure. Failure is handled |
| 175 | # like a timeout. |
| 176 | _send_qemu_cmd() |
| 177 | { |
| 178 | local h=${1} |
| 179 | local count=1 |
| 180 | local cmd= |
| 181 | local use_error=${qemu_error_no_exit} |
| 182 | shift |
| 183 | |
| 184 | if [ ${qemu_cmd_repeat} -gt 0 ] 2>/dev/null; then |
| 185 | count=${qemu_cmd_repeat} |
| 186 | use_error="no" |
| 187 | fi |
| 188 | |
| 189 | cmd=$1 |
| 190 | shift |
| 191 | |
| 192 | # Display QMP being sent, but not HMP (since HMP already echoes its |
| 193 | # input back to output); decide based on leading '{' |
| 194 | if [ -z "$silent" ] && [ -z "$mismatch_only" ] && |
| 195 | [ "$cmd" != "${cmd#\{}" ]; then |
| 196 | echo "${cmd}" | _filter_testdir | _filter_imgfmt |
| 197 | fi |
| 198 | while [ ${count} -gt 0 ] |
| 199 | do |
| 200 | echo "${cmd}" >&${QEMU_IN[${h}]} |
| 201 | if [ -n "${1}" ]; then |
| 202 | if [ -z "${success_or_failure}" ]; then |
| 203 | qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" |
| 204 | else |
| 205 | qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" "${2}" |
| 206 | fi |
| 207 | if [ ${QEMU_STATUS[$h]} -eq 0 ]; then |
| 208 | return |
| 209 | fi |
| 210 | fi |
| 211 | let count--; |
| 212 | done |
| 213 | if [ ${QEMU_STATUS[$h]} -ne 0 ] && [ -z "${qemu_error_no_exit}" ]; then |
| 214 | echo "Timeout waiting for command ${1} response on handle ${h}" |
| 215 | exit 1 #Timeout means the test failed |
| 216 | fi |
| 217 | } |
| 218 | |
| 219 | |
| 220 | # Check event cache for a named QMP event |
| 221 | # |
| 222 | # Input parameters: |
| 223 | # $1: Name of the QMP event to check for |
| 224 | # |
| 225 | # Checks if the named QMP event that was previously captured |
| 226 | # into $QEMU_EVENTS. When matched, the QMP event will be echoed |
| 227 | # and the $matched variable set to 1. |
| 228 | # |
| 229 | # _wait_event is more suitable for test usage in most cases |
| 230 | _check_cached_events() |
| 231 | { |
| 232 | local evname=${1} |
| 233 | |
| 234 | local match="\"event\": \"$evname\"" |
| 235 | |
| 236 | matched=0 |
| 237 | if [ -n "$QEMU_EVENTS" ]; then |
| 238 | CURRENT_QEMU_EVENTS=$QEMU_EVENTS |
| 239 | QEMU_EVENTS= |
| 240 | old_IFS=$IFS |
| 241 | IFS="%" |
| 242 | for ev in $CURRENT_QEMU_EVENTS |
| 243 | do |
| 244 | grep -q "$match" < <(echo "${ev}") |
| 245 | if [ $? -eq 0 ] && [ $matched = 0 ]; then |
| 246 | echo "${ev}" | _filter_testdir | _filter_qemu \ |
| 247 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
| 248 | matched=1 |
| 249 | else |
| 250 | QEMU_EVENTS="${QEMU_EVENTS:+${QEMU_EVENTS}%}${ev}" |
| 251 | fi |
| 252 | done |
| 253 | IFS=$old_IFS |
| 254 | fi |
| 255 | } |
| 256 | |
| 257 | # Wait for a named QMP event |
| 258 | # |
| 259 | # Input parameters: |
| 260 | # $1: QEMU handle to use |
| 261 | # $2: Name of the QMP event to wait for |
| 262 | # |
| 263 | # Checks if the named QMP even was previously captured |
| 264 | # into $QEMU_EVENTS. If none are present, then waits for the |
| 265 | # event to arrive on the QMP channel. When matched, the QMP |
| 266 | # event will be echoed |
| 267 | _wait_event() |
| 268 | { |
| 269 | local h=${1} |
| 270 | local evname=${2} |
| 271 | |
| 272 | while true |
| 273 | do |
| 274 | _check_cached_events $evname |
| 275 | |
| 276 | if [ $matched = 1 ]; |
| 277 | then |
| 278 | return |
| 279 | fi |
| 280 | |
| 281 | only_capture_events=1 qemu_error_no_exit=1 _timed_wait_for ${h} |
| 282 | |
| 283 | if [ ${QEMU_STATUS[$h]} -ne 0 ] ; then |
| 284 | echo "Timeout waiting for event ${evname} on handle ${h}" |
| 285 | exit 1 #Timeout means the test failed |
| 286 | fi |
| 287 | done |
| 288 | } |
| 289 | |
| 290 | # Launch a QEMU process. |
| 291 | # |
| 292 | # Input parameters: |
| 293 | # $qemu_comm_method: set this variable to 'monitor' (case insensitive) |
| 294 | # to use the QEMU HMP monitor for communication. |
| 295 | # Otherwise, the default of QMP is used. |
| 296 | # $qmp_pretty: Set this variable to 'y' to enable QMP pretty printing. |
| 297 | # $keep_stderr: Set this variable to 'y' to keep QEMU's stderr output on stderr. |
| 298 | # If this variable is empty, stderr will be redirected to stdout. |
| 299 | # Returns: |
| 300 | # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. |
| 301 | # |
| 302 | _launch_qemu() |
| 303 | { |
| 304 | local comm= |
| 305 | local fifo_out= |
| 306 | local fifo_in= |
| 307 | |
| 308 | if (shopt -s nocasematch; [[ "${qemu_comm_method}" == "monitor" ]]) |
| 309 | then |
| 310 | comm="-monitor stdio" |
| 311 | else |
| 312 | local qemu_comm_method="qmp" |
| 313 | if [ "$qmp_pretty" = "y" ]; then |
| 314 | comm="-monitor none -qmp-pretty stdio" |
| 315 | else |
| 316 | comm="-monitor none -qmp stdio" |
| 317 | fi |
| 318 | fi |
| 319 | |
| 320 | fifo_out=${QEMU_FIFO_OUT}_${_QEMU_HANDLE} |
| 321 | fifo_in=${QEMU_FIFO_IN}_${_QEMU_HANDLE} |
| 322 | mkfifo "${fifo_out}" |
| 323 | mkfifo "${fifo_in}" |
| 324 | |
| 325 | object_options= |
| 326 | if [ -n "$IMGKEYSECRET" ]; then |
| 327 | object_options="--object secret,id=keysec0,data=$IMGKEYSECRET" |
| 328 | fi |
| 329 | |
| 330 | if [ -z "$keep_stderr" ]; then |
| 331 | QEMU_NEED_PID='y'\ |
| 332 | ${QEMU} ${object_options} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ |
| 333 | 2>&1 \ |
| 334 | <"${fifo_in}" & |
| 335 | elif [ "$keep_stderr" = "y" ]; then |
| 336 | QEMU_NEED_PID='y'\ |
| 337 | ${QEMU} ${object_options} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ |
| 338 | <"${fifo_in}" & |
| 339 | else |
| 340 | exit 1 |
| 341 | fi |
| 342 | |
| 343 | if [[ "${BASH_VERSINFO[0]}" -ge "5" || |
| 344 | ("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]] |
| 345 | then |
| 346 | # bash >= 4.1 required for automatic fd |
| 347 | exec {_out_fd}<"${fifo_out}" |
| 348 | exec {_in_fd}>"${fifo_in}" |
| 349 | else |
| 350 | let _out_fd++ |
| 351 | let _in_fd++ |
| 352 | eval "exec ${_out_fd}<'${fifo_out}'" |
| 353 | eval "exec ${_in_fd}>'${fifo_in}'" |
| 354 | fi |
| 355 | |
| 356 | QEMU_OUT[${_QEMU_HANDLE}]=${_out_fd} |
| 357 | QEMU_IN[${_QEMU_HANDLE}]=${_in_fd} |
| 358 | QEMU_STATUS[${_QEMU_HANDLE}]=0 |
| 359 | |
| 360 | if [ "${qemu_comm_method}" == "qmp" ] |
| 361 | then |
| 362 | # Don't print response, since it has version information in it |
| 363 | silent=yes _timed_wait_for ${_QEMU_HANDLE} "capabilities" |
| 364 | if [ "$qmp_pretty" = "y" ]; then |
| 365 | silent=yes _timed_wait_for ${_QEMU_HANDLE} "^}" |
| 366 | fi |
| 367 | fi |
| 368 | QEMU_HANDLE=${_QEMU_HANDLE} |
| 369 | let _QEMU_HANDLE++ |
| 370 | } |
| 371 | |
| 372 | |
| 373 | # Silently kills the QEMU process |
| 374 | # |
| 375 | # If $wait is set to anything other than the empty string, the process will not |
| 376 | # be killed but only waited for, and any output will be forwarded to stdout. If |
| 377 | # $wait is empty, the process will be killed and all output will be suppressed. |
| 378 | _cleanup_qemu() |
| 379 | { |
| 380 | # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices |
| 381 | for i in "${!QEMU_OUT[@]}" |
| 382 | do |
| 383 | local QEMU_PID |
| 384 | if [ -f "${QEMU_TEST_DIR}/qemu-${i}.pid" ]; then |
| 385 | read QEMU_PID < "${QEMU_TEST_DIR}/qemu-${i}.pid" |
| 386 | rm -f "${QEMU_TEST_DIR}/qemu-${i}.pid" |
| 387 | if [ -z "${wait}" ] && [ -n "${QEMU_PID}" ]; then |
| 388 | kill -KILL ${QEMU_PID} 2>/dev/null |
| 389 | fi |
| 390 | if [ -n "${QEMU_PID}" ]; then |
| 391 | wait ${QEMU_PID} 2>/dev/null # silent kill |
| 392 | fi |
| 393 | fi |
| 394 | |
| 395 | if [ -n "${wait}" ]; then |
| 396 | cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \ |
| 397 | | _filter_qemu_io | _filter_qmp | _filter_hmp |
| 398 | fi |
| 399 | rm -f "${QEMU_FIFO_IN}_${i}" "${QEMU_FIFO_OUT}_${i}" |
| 400 | eval "exec ${QEMU_IN[$i]}<&-" # close file descriptors |
| 401 | eval "exec ${QEMU_OUT[$i]}<&-" |
| 402 | |
| 403 | unset QEMU_IN[$i] |
| 404 | unset QEMU_OUT[$i] |
| 405 | done |
| 406 | } |