blob: d3ca19d499526ae19626ed464be86bfa1615deac [file] [log] [blame] [edit]
##############################################################################
# run_qemu translates the ambiguous exit status in Table1 to that in Table2.
# Table3 simply documents the complete status table.
#
# Table1: Before fixup
# --------------------
# 0 - Unexpected exit from QEMU (possible signal), or the unittest did
# not use debug-exit
# 1 - most likely unittest succeeded, or QEMU failed
#
# Table2: After fixup
# -------------------
# 0 - Everything succeeded
# 1 - most likely QEMU failed
#
# Table3: Complete table
# ----------------------
# 0 - SUCCESS
# 1 - most likely QEMU failed
# 2 - most likely a run script failed
# 3 - most likely the unittest failed
# 124 - most likely the unittest timed out
# 127 - most likely the unittest called abort()
# 1..127 - FAILURE (could be QEMU, a run script, or the unittest)
# >= 128 - Signal (signum = status - 128)
##############################################################################
run_qemu ()
{
local stdout errors ret sig
echo -n "$@"
initrd_create &&
echo -n " #"
echo " $INITRD"
# stdout to {stdout}, stderr to $errors and stderr
exec {stdout}>&1
errors=$("${@}" $INITRD </dev/null 2> >(tee /dev/stderr) > /dev/fd/$stdout)
ret=$?
exec {stdout}>&-
[ $ret -eq 134 ] && echo "QEMU Aborted" >&2
if [ "$errors" ]; then
sig=$(grep 'terminating on signal' <<<"$errors")
if [ "$sig" ]; then
sig=$(sed 's/.*terminating on signal \([0-9][0-9]*\).*/\1/' <<<"$sig")
fi
fi
if [ $ret -eq 0 ]; then
# Some signals result in a zero return status, but the
# error log tells the truth.
if [ "$sig" ]; then
((ret=sig+128))
else
# Exiting with zero (non-debugexit) is an error
ret=1
fi
elif [ $ret -eq 1 ]; then
# Even when ret==1 (unittest success) if we also got stderr
# logs, then we assume a QEMU failure. Otherwise we translate
# status of 1 to 0 (SUCCESS)
if [ -z "$(echo "$errors" | grep -vi warning)" ]; then
ret=0
fi
fi
return $ret
}
run_qemu_status ()
{
local stdout ret
exec {stdout}>&1
lines=$(run_qemu "$@" > >(tee /dev/fd/$stdout))
ret=$?
exec {stdout}>&-
if [ $ret -eq 1 ]; then
testret=$(grep '^EXIT: ' <<<"$lines" | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/')
if [ "$testret" ]; then
if [ $testret -eq 1 ]; then
ret=0
else
ret=$testret
fi
fi
fi
return $ret
}
timeout_cmd ()
{
if [ "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then
echo "timeout -k 1s --foreground $TIMEOUT"
fi
}
qmp ()
{
echo '{ "execute": "qmp_capabilities" }{ "execute":' "$2" '}' | nc -U $1
}
run_migration ()
{
if ! command -v nc >/dev/null 2>&1; then
echo "${FUNCNAME[0]} needs nc (netcat)" >&2
return 2
fi
migsock=`mktemp -u -t mig-helper-socket.XXXXXXXXXX`
migout1=`mktemp -t mig-helper-stdout1.XXXXXXXXXX`
qmp1=`mktemp -u -t mig-helper-qmp1.XXXXXXXXXX`
qmp2=`mktemp -u -t mig-helper-qmp2.XXXXXXXXXX`
fifo=`mktemp -u -t mig-helper-fifo.XXXXXXXXXX`
qmpout1=/dev/null
qmpout2=/dev/null
trap 'kill 0; exit 2' INT TERM
trap 'rm -f ${migout1} ${migsock} ${qmp1} ${qmp2} ${fifo}' RETURN EXIT
eval "$@" -chardev socket,id=mon1,path=${qmp1},server,nowait \
-mon chardev=mon1,mode=control | tee ${migout1} &
# We have to use cat to open the named FIFO, because named FIFO's, unlike
# pipes, will block on open() until the other end is also opened, and that
# totally breaks QEMU...
mkfifo ${fifo}
eval "$@" -chardev socket,id=mon2,path=${qmp2},server,nowait \
-mon chardev=mon2,mode=control -incoming unix:${migsock} < <(cat ${fifo}) &
incoming_pid=`jobs -l %+ | awk '{print$2}'`
# The test must prompt the user to migrate, so wait for the "migrate" keyword
while ! grep -q -i "migrate" < ${migout1} ; do
sleep 1
done
qmp ${qmp1} '"migrate", "arguments": { "uri": "unix:'${migsock}'" }' > ${qmpout1}
# Wait for the migration to complete
migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
while ! grep -q '"completed"' <<<"$migstatus" ; do
sleep 1
migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
if grep -q '"failed"' <<<"$migstatus" ; then
echo "ERROR: Migration failed." >&2
qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
qmp ${qmp2} '"quit"'> ${qmpout2} 2>/dev/null
return 2
fi
done
qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
echo > ${fifo}
wait $incoming_pid
ret=$?
wait
return $ret
}
migration_cmd ()
{
if [ "$MIGRATION" = "yes" ]; then
echo "run_migration"
fi
}
search_qemu_binary ()
{
local save_path=$PATH
local qemucmd qemu
export PATH=$PATH:/usr/libexec
for qemucmd in ${QEMU:-qemu-system-$ARCH_NAME qemu-kvm}; do
if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then
qemu="$qemucmd"
break
fi
done
if [ -z "$qemu" ]; then
echo "A QEMU binary was not found." >&2
echo "You can set a custom location by using the QEMU=<path> environment variable." >&2
return 2
fi
command -v $qemu
export PATH=$save_path
}
initrd_create ()
{
local ret
env_add_errata
ret=$?
unset INITRD
[ -f "$KVM_UNIT_TESTS_ENV" ] && INITRD="-initrd $KVM_UNIT_TESTS_ENV"
return $ret
}
env_add_errata ()
{
local line errata ret=1
if [ -f "$KVM_UNIT_TESTS_ENV" ] && grep -q '^ERRATA_' <(env); then
for line in $(grep '^ERRATA_' "$KVM_UNIT_TESTS_ENV"); do
errata=${line%%=*}
[ -n "${!errata}" ] && continue
eval export "$line"
done
elif [ ! -f "$KVM_UNIT_TESTS_ENV" ]; then
env_generate_errata
fi
if grep -q '^ERRATA_' <(env); then
export KVM_UNIT_TESTS_ENV_OLD="$KVM_UNIT_TESTS_ENV"
export KVM_UNIT_TESTS_ENV=$(mktemp)
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'
[ -f "$KVM_UNIT_TESTS_ENV_OLD" ] && grep -v '^ERRATA_' "$KVM_UNIT_TESTS_ENV_OLD" > $KVM_UNIT_TESTS_ENV
grep '^ERRATA_' <(env) >> $KVM_UNIT_TESTS_ENV
ret=0
fi
return $ret
}
env_generate_errata ()
{
local kernel_version_string=$(uname -r)
local kernel_version kernel_patchlevel kernel_sublevel kernel_extraversion
local line commit minver errata rest v p s x have
IFS=. read -r kernel_version kernel_patchlevel rest <<<"$kernel_version_string"
IFS=- read -r kernel_sublevel kernel_extraversion <<<"$rest"
kernel_sublevel=${kernel_sublevel%%[!0-9]*}
kernel_extraversion=${kernel_extraversion%%[!0-9]*}
! [[ $kernel_sublevel =~ ^[0-9]+$ ]] && unset $kernel_sublevel
! [[ $kernel_extraversion =~ ^[0-9]+$ ]] && unset $kernel_extraversion
[ "$ENVIRON_DEFAULT" != "yes" ] && return
[ ! -f "$ERRATATXT" ] && return
for line in $(grep -v '^#' "$ERRATATXT" | tr -d '[:blank:]' | cut -d: -f1,2); do
commit=${line%:*}
minver=${line#*:}
test -z "$commit" && continue
errata="ERRATA_$commit"
[ -n "${!errata}" ] && continue
IFS=. read -r v p rest <<<"$minver"
IFS=- read -r s x <<<"$rest"
s=${s%%[!0-9]*}
x=${x%%[!0-9]*}
if ! [[ $v =~ ^[0-9]+$ ]] || ! [[ $p =~ ^[0-9]+$ ]]; then
echo "Bad minimum kernel version in $ERRATATXT, $minver"
return 2
fi
! [[ $s =~ ^[0-9]+$ ]] && unset $s
! [[ $x =~ ^[0-9]+$ ]] && unset $x
if (( $kernel_version > $v ||
($kernel_version == $v && $kernel_patchlevel > $p) )); then
have=y
elif (( $kernel_version == $v && $kernel_patchlevel == $p )); then
if [ "$kernel_sublevel" ] && [ "$s" ]; then
if (( $kernel_sublevel > $s )); then
have=y
elif (( $kernel_sublevel == $s )); then
if [ "$kernel_extraversion" ] && [ "$x" ]; then
if (( $kernel_extraversion >= $x )); then
have=y
else
have=n
fi
elif [ "$x" ] && (( $x != 0 )); then
have=n
else
have=y
fi
else
have=n
fi
elif [ "$s" ] && (( $s != 0 )); then
have=n
else
have=y
fi
else
have=n
fi
eval export "$errata=$have"
done
}
trap_exit_push ()
{
local old_exit=$(trap -p EXIT | sed "s/^[^']*'//;s/'[^']*$//")
trap -- "$1; $old_exit" EXIT
}
kvm_available ()
{
[ -c /dev/kvm ] ||
return 1
[ "$HOST" = "$ARCH_NAME" ] ||
( [ "$HOST" = aarch64 ] && [ "$ARCH" = arm ] ) ||
( [ "$HOST" = x86_64 ] && [ "$ARCH" = i386 ] )
}
get_qemu_accelerator ()
{
if [ "$ACCEL" = "kvm" ] && ! kvm_available; then
echo "KVM is needed, but not available on this host" >&2
return 2
fi
if [ "$ACCEL" ]; then
echo $ACCEL
elif kvm_available; then
echo kvm
else
echo tcg
fi
}