1############################################################################## 2# run_qemu translates the ambiguous exit status in Table1 to that in Table2. 3# Table3 simply documents the complete status table. 4# 5# Table1: Before fixup 6# -------------------- 7# 0 - Unexpected exit from QEMU (possible signal), or the unittest did 8# not use debug-exit 9# 1 - most likely unittest succeeded, or QEMU failed 10# 11# Table2: After fixup 12# ------------------- 13# 0 - Everything succeeded 14# 1 - most likely QEMU failed 15# 16# Table3: Complete table 17# ---------------------- 18# 0 - SUCCESS 19# 1 - most likely QEMU failed 20# 2 - most likely a run script failed 21# 3 - most likely the unittest failed 22# 124 - most likely the unittest timed out 23# 127 - most likely the unittest called abort() 24# 1..127 - FAILURE (could be QEMU, a run script, or the unittest) 25# >= 128 - Signal (signum = status - 128) 26############################################################################## 27run_qemu () 28{ 29 local stdout errors ret sig 30 31 initrd_create || return $? 32 echo -n "$@" 33 [ "$ENVIRON_DEFAULT" = "yes" ] && echo -n " #" 34 echo " $INITRD" 35 36 # stdout to {stdout}, stderr to $errors and stderr 37 exec {stdout}>&1 38 errors=$("${@}" $INITRD </dev/null 2> >(tee /dev/stderr) > /dev/fd/$stdout) 39 ret=$? 40 exec {stdout}>&- 41 42 [ $ret -eq 134 ] && echo "QEMU Aborted" >&2 43 44 if [ "$errors" ]; then 45 sig=$(grep 'terminating on signal' <<<"$errors") 46 if [ "$sig" ]; then 47 sig=$(sed 's/.*terminating on signal \([0-9][0-9]*\).*/\1/' <<<"$sig") 48 fi 49 fi 50 51 if [ $ret -eq 0 ]; then 52 # Some signals result in a zero return status, but the 53 # error log tells the truth. 54 if [ "$sig" ]; then 55 ((ret=sig+128)) 56 else 57 # Exiting with zero (non-debugexit) is an error 58 ret=1 59 fi 60 elif [ $ret -eq 1 ]; then 61 # Even when ret==1 (unittest success) if we also got stderr 62 # logs, then we assume a QEMU failure. Otherwise we translate 63 # status of 1 to 0 (SUCCESS) 64 if [ -z "$(echo "$errors" | grep -vi warning)" ]; then 65 ret=0 66 fi 67 fi 68 69 return $ret 70} 71 72run_qemu_status () 73{ 74 local stdout ret 75 76 exec {stdout}>&1 77 lines=$(run_qemu "$@" > >(tee /dev/fd/$stdout)) 78 ret=$? 79 exec {stdout}>&- 80 81 if [ $ret -eq 1 ]; then 82 testret=$(grep '^EXIT: ' <<<"$lines" | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/') 83 if [ "$testret" ]; then 84 if [ $testret -eq 1 ]; then 85 ret=0 86 else 87 ret=$testret 88 fi 89 fi 90 fi 91 92 return $ret 93} 94 95timeout_cmd () 96{ 97 if [ "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then 98 echo "timeout -k 1s --foreground $TIMEOUT" 99 fi 100} 101 102qmp () 103{ 104 echo '{ "execute": "qmp_capabilities" }{ "execute":' "$2" '}' | ncat -U $1 105} 106 107run_migration () 108{ 109 if ! command -v ncat >/dev/null 2>&1; then 110 echo "${FUNCNAME[0]} needs ncat (netcat)" >&2 111 return 2 112 fi 113 114 migsock=`mktemp -u -t mig-helper-socket.XXXXXXXXXX` 115 migout1=`mktemp -t mig-helper-stdout1.XXXXXXXXXX` 116 qmp1=`mktemp -u -t mig-helper-qmp1.XXXXXXXXXX` 117 qmp2=`mktemp -u -t mig-helper-qmp2.XXXXXXXXXX` 118 fifo=`mktemp -u -t mig-helper-fifo.XXXXXXXXXX` 119 qmpout1=/dev/null 120 qmpout2=/dev/null 121 122 trap 'kill 0; exit 2' INT TERM 123 trap 'rm -f ${migout1} ${migsock} ${qmp1} ${qmp2} ${fifo}' RETURN EXIT 124 125 eval "$@" -chardev socket,id=mon1,path=${qmp1},server,nowait \ 126 -mon chardev=mon1,mode=control | tee ${migout1} & 127 128 # We have to use cat to open the named FIFO, because named FIFO's, unlike 129 # pipes, will block on open() until the other end is also opened, and that 130 # totally breaks QEMU... 131 mkfifo ${fifo} 132 eval "$@" -chardev socket,id=mon2,path=${qmp2},server,nowait \ 133 -mon chardev=mon2,mode=control -incoming unix:${migsock} < <(cat ${fifo}) & 134 incoming_pid=`jobs -l %+ | awk '{print$2}'` 135 136 # The test must prompt the user to migrate, so wait for the "migrate" keyword 137 while ! grep -q -i "migrate" < ${migout1} ; do 138 sleep 1 139 done 140 141 qmp ${qmp1} '"migrate", "arguments": { "uri": "unix:'${migsock}'" }' > ${qmpout1} 142 143 # Wait for the migration to complete 144 migstatus=`qmp ${qmp1} '"query-migrate"' | grep return` 145 while ! grep -q '"completed"' <<<"$migstatus" ; do 146 sleep 1 147 migstatus=`qmp ${qmp1} '"query-migrate"' | grep return` 148 if grep -q '"failed"' <<<"$migstatus" ; then 149 echo "ERROR: Migration failed." >&2 150 qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null 151 qmp ${qmp2} '"quit"'> ${qmpout2} 2>/dev/null 152 return 2 153 fi 154 done 155 qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null 156 echo > ${fifo} 157 wait $incoming_pid 158 ret=$? 159 160 while (( $(jobs -r | wc -l) > 0 )); do 161 sleep 0.5 162 done 163 164 return $ret 165} 166 167migration_cmd () 168{ 169 if [ "$MIGRATION" = "yes" ]; then 170 echo "run_migration" 171 fi 172} 173 174search_qemu_binary () 175{ 176 local save_path=$PATH 177 local qemucmd qemu 178 179 export PATH=$PATH:/usr/libexec 180 for qemucmd in ${QEMU:-qemu-system-$ARCH_NAME qemu-kvm}; do 181 if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then 182 qemu="$qemucmd" 183 break 184 fi 185 done 186 187 if [ -z "$qemu" ]; then 188 echo "A QEMU binary was not found." >&2 189 echo "You can set a custom location by using the QEMU=<path> environment variable." >&2 190 return 2 191 fi 192 command -v $qemu 193 export PATH=$save_path 194} 195 196initrd_create () 197{ 198 if [ "$ENVIRON_DEFAULT" = "yes" ]; then 199 trap_exit_push 'rm -f $KVM_UNIT_TESTS_ENV; [ "$KVM_UNIT_TESTS_ENV_OLD" ] && export KVM_UNIT_TESTS_ENV="$KVM_UNIT_TESTS_ENV_OLD" || unset KVM_UNIT_TESTS_ENV; unset KVM_UNIT_TESTS_ENV_OLD' 200 [ -f "$KVM_UNIT_TESTS_ENV" ] && export KVM_UNIT_TESTS_ENV_OLD="$KVM_UNIT_TESTS_ENV" 201 export KVM_UNIT_TESTS_ENV=$(mktemp) 202 env_params 203 env_file 204 env_errata || return $? 205 fi 206 207 unset INITRD 208 [ -f "$KVM_UNIT_TESTS_ENV" ] && INITRD="-initrd $KVM_UNIT_TESTS_ENV" 209 210 return 0 211} 212 213env_add_params () 214{ 215 local p 216 217 for p in "$@"; do 218 if eval test -v $p; then 219 eval export "$p" 220 else 221 eval export "$p=" 222 fi 223 grep "^$p=" <(env) >>$KVM_UNIT_TESTS_ENV 224 done 225} 226 227env_params () 228{ 229 local qemu have_qemu 230 local _ rest 231 232 qemu=$(search_qemu_binary) && have_qemu=1 233 234 if [ "$have_qemu" ]; then 235 if [ -n "$ACCEL" ] || [ -n "$QEMU_ACCEL" ]; then 236 [ -n "$ACCEL" ] && QEMU_ACCEL=$ACCEL 237 fi 238 QEMU_VERSION_STRING="$($qemu -h | head -1)" 239 IFS='[ .]' read -r _ _ _ QEMU_MAJOR QEMU_MINOR QEMU_MICRO rest <<<"$QEMU_VERSION_STRING" 240 fi 241 env_add_params QEMU_ACCEL QEMU_VERSION_STRING QEMU_MAJOR QEMU_MINOR QEMU_MICRO 242 243 KERNEL_VERSION_STRING=$(uname -r) 244 IFS=. read -r KERNEL_VERSION KERNEL_PATCHLEVEL rest <<<"$KERNEL_VERSION_STRING" 245 IFS=- read -r KERNEL_SUBLEVEL KERNEL_EXTRAVERSION <<<"$rest" 246 KERNEL_SUBLEVEL=${KERNEL_SUBLEVEL%%[!0-9]*} 247 KERNEL_EXTRAVERSION=${KERNEL_EXTRAVERSION%%[!0-9]*} 248 ! [[ $KERNEL_SUBLEVEL =~ ^[0-9]+$ ]] && unset $KERNEL_SUBLEVEL 249 ! [[ $KERNEL_EXTRAVERSION =~ ^[0-9]+$ ]] && unset $KERNEL_EXTRAVERSION 250 env_add_params KERNEL_VERSION_STRING KERNEL_VERSION KERNEL_PATCHLEVEL KERNEL_SUBLEVEL KERNEL_EXTRAVERSION 251} 252 253env_file () 254{ 255 local line var 256 257 [ ! -f "$KVM_UNIT_TESTS_ENV_OLD" ] && return 258 259 for line in $(grep -E '^[[:blank:]]*[[:alpha:]_][[:alnum:]_]*=' "$KVM_UNIT_TESTS_ENV_OLD"); do 260 var=${line%%=*} 261 if ! grep -q "^$var=" $KVM_UNIT_TESTS_ENV; then 262 eval export "$line" 263 grep "^$var=" <(env) >>$KVM_UNIT_TESTS_ENV 264 fi 265 done 266} 267 268env_errata () 269{ 270 if [ "$ERRATATXT" ] && [ ! -f "$ERRATATXT" ]; then 271 echo "$ERRATATXT not found. (ERRATATXT=$ERRATATXT)" >&2 272 return 2 273 elif [ "$ERRATATXT" ]; then 274 env_generate_errata 275 fi 276 sort <(env | grep '^ERRATA_') <(grep '^ERRATA_' $KVM_UNIT_TESTS_ENV) | uniq -u >>$KVM_UNIT_TESTS_ENV 277} 278 279env_generate_errata () 280{ 281 local line commit minver errata rest v p s x have 282 283 for line in $(grep -v '^#' "$ERRATATXT" | tr -d '[:blank:]' | cut -d: -f1,2); do 284 commit=${line%:*} 285 minver=${line#*:} 286 287 test -z "$commit" && continue 288 errata="ERRATA_$commit" 289 [ -n "${!errata}" ] && continue 290 291 IFS=. read -r v p rest <<<"$minver" 292 IFS=- read -r s x <<<"$rest" 293 s=${s%%[!0-9]*} 294 x=${x%%[!0-9]*} 295 296 if ! [[ $v =~ ^[0-9]+$ ]] || ! [[ $p =~ ^[0-9]+$ ]]; then 297 echo "Bad minimum kernel version in $ERRATATXT, $minver" 298 return 2 299 fi 300 ! [[ $s =~ ^[0-9]+$ ]] && unset $s 301 ! [[ $x =~ ^[0-9]+$ ]] && unset $x 302 303 if (( $KERNEL_VERSION > $v || 304 ($KERNEL_VERSION == $v && $KERNEL_PATCHLEVEL > $p) )); then 305 have=y 306 elif (( $KERNEL_VERSION == $v && $KERNEL_PATCHLEVEL == $p )); then 307 if [ "$KERNEL_SUBLEVEL" ] && [ "$s" ]; then 308 if (( $KERNEL_SUBLEVEL > $s )); then 309 have=y 310 elif (( $KERNEL_SUBLEVEL == $s )); then 311 if [ "$KERNEL_EXTRAVERSION" ] && [ "$x" ]; then 312 if (( $KERNEL_EXTRAVERSION >= $x )); then 313 have=y 314 else 315 have=n 316 fi 317 elif [ "$x" ] && (( $x != 0 )); then 318 have=n 319 else 320 have=y 321 fi 322 else 323 have=n 324 fi 325 elif [ "$s" ] && (( $s != 0 )); then 326 have=n 327 else 328 have=y 329 fi 330 else 331 have=n 332 fi 333 eval export "$errata=$have" 334 done 335} 336 337trap_exit_push () 338{ 339 local old_exit=$(trap -p EXIT | sed "s/^[^']*'//;s/'[^']*$//") 340 trap -- "$1; $old_exit" EXIT 341} 342 343kvm_available () 344{ 345 [ -c /dev/kvm ] || 346 return 1 347 348 [ "$HOST" = "$ARCH_NAME" ] || 349 ( [ "$HOST" = aarch64 ] && [ "$ARCH" = arm ] ) || 350 ( [ "$HOST" = x86_64 ] && [ "$ARCH" = i386 ] ) 351} 352 353hvf_available () 354{ 355 [ "$(sysctl -n kern.hv_support 2>/dev/null)" = "1" ] || return 1 356 [ "$HOST" = "$ARCH_NAME" ] || 357 ( [ "$HOST" = x86_64 ] && [ "$ARCH" = i386 ] ) 358} 359 360get_qemu_accelerator () 361{ 362 if [ "$ACCEL" = "kvm" ] && ! kvm_available; then 363 echo "KVM is needed, but not available on this host" >&2 364 return 2 365 fi 366 if [ "$ACCEL" = "hvf" ] && ! hvf_available; then 367 echo "HVF is needed, but not available on this host" >&2 368 return 2 369 fi 370 371 if [ "$ACCEL" ]; then 372 echo $ACCEL 373 elif kvm_available; then 374 echo kvm 375 elif hvf_available; then 376 echo hvf 377 else 378 echo tcg 379 fi 380} 381