xref: /kvm-unit-tests/scripts/arch-run.bash (revision 70fcb64b7d548240f6f08680ccbe94f9c16104fb)
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	# stdout to {stdout}, stderr to $errors and stderr
32	exec {stdout}>&1
33	errors=$("${@}" </dev/null 2> >(tee /dev/stderr) > /dev/fd/$stdout)
34	ret=$?
35	exec {stdout}>&-
36
37	[ $ret -eq 134 ] && echo "QEMU Aborted" >&2
38
39	if [ "$errors" ]; then
40		sig=$(grep 'terminating on signal' <<<"$errors")
41		if [ "$sig" ]; then
42			sig=$(sed 's/.*terminating on signal \([0-9][0-9]*\).*/\1/' <<<"$sig")
43		fi
44	fi
45
46	if [ $ret -eq 0 ]; then
47		# Some signals result in a zero return status, but the
48		# error log tells the truth.
49		if [ "$sig" ]; then
50			((ret=sig+128))
51		else
52			# Exiting with zero (non-debugexit) is an error
53			ret=1
54		fi
55	elif [ $ret -eq 1 ]; then
56		# Even when ret==1 (unittest success) if we also got stderr
57		# logs, then we assume a QEMU failure. Otherwise we translate
58		# status of 1 to 0 (SUCCESS)
59		if [ -z "$(echo "$errors" | grep -vi warning)" ]; then
60			ret=0
61		fi
62	fi
63
64	return $ret
65}
66
67timeout_cmd ()
68{
69	if [ "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then
70		echo "timeout -k 1s --foreground $TIMEOUT"
71	fi
72}
73
74qmp ()
75{
76	echo '{ "execute": "qmp_capabilities" }{ "execute":' "$2" '}' | nc -U $1
77}
78
79run_migration ()
80{
81	if ! command -v nc >/dev/null 2>&1; then
82		echo "$FUNCNAME needs nc (netcat)" >&2
83		exit 2
84	fi
85
86	qemu=$1
87	shift
88
89	migsock=`mktemp -u -t mig-helper-socket.XXXXXXXXXX`
90	migout1=`mktemp -t mig-helper-stdout1.XXXXXXXXXX`
91	qmp1=`mktemp -u -t mig-helper-qmp1.XXXXXXXXXX`
92	qmp2=`mktemp -u -t mig-helper-qmp2.XXXXXXXXXX`
93	qmpout1=/dev/null
94	qmpout2=/dev/null
95
96	trap 'rm -f ${migout1} ${migsock} ${qmp1} ${qmp2}' EXIT
97
98	$qemu "$@" -chardev socket,id=mon1,path=${qmp1},server,nowait \
99		 -mon chardev=mon1,mode=control | tee ${migout1} &
100
101	$qemu "$@" -chardev socket,id=mon2,path=${qmp2},server,nowait \
102		 -mon chardev=mon2,mode=control -incoming unix:${migsock} &
103
104	# The test must prompt the user to migrate, so wait for the "migrate" keyword
105	while ! grep -q -i "migrate" < ${migout1} ; do
106		sleep 1
107	done
108
109	qmp ${qmp1} '"migrate", "arguments": { "uri": "unix:'${migsock}'" }' > ${qmpout1}
110
111	# Wait for the migration to complete
112	migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
113	while ! grep -q '"completed"' <<<"$migstatus" ; do
114		sleep 1
115		migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
116		if grep -q '"failed"' <<<"$migstatus" ; then
117			echo "ERROR: Migration failed." >&2
118			qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
119			qmp ${qmp2} '"quit"'> ${qmpout2} 2>/dev/null
120			exit 2
121		fi
122	done
123	qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
124
125	qmp ${qmp2} '"inject-nmi"'> ${qmpout2}
126
127	wait
128}
129
130migration_cmd ()
131{
132	if [ "$MIGRATION" = "yes" ]; then
133		echo "run_migration"
134	fi
135}
136
137search_qemu_binary ()
138{
139	local save_path=$PATH
140	local qemucmd qemu
141
142	export PATH=$PATH:/usr/libexec
143	for qemucmd in ${QEMU:-qemu-system-$ARCH_NAME qemu-kvm}; do
144		if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then
145			qemu="$qemucmd"
146			break
147		fi
148	done
149
150	if [ -z "$qemu" ]; then
151		echo "A QEMU binary was not found."
152		echo "You can set a custom location by using the QEMU=<path> environment variable."
153		exit 2
154	fi
155	command -v $qemu
156	export PATH=$save_path
157}
158
159initrd_create ()
160{
161	env_add_errata
162	unset INITRD
163	[ -f "$ENV" ] && INITRD="-initrd $ENV"
164}
165
166env_add_errata ()
167{
168	local line errata
169
170	if [ -f "$ENV" ] && grep -q '^ERRATA_' <(env); then
171		for line in $(grep '^ERRATA_' "$ENV"); do
172			errata=${line%%=*}
173			test -v $errata && continue
174			eval export "$line"
175		done
176	elif [ ! -f "$ENV" ]; then
177		env_generate_errata
178	fi
179
180	if grep -q '^ERRATA_' <(env); then
181		export ENV_OLD="$ENV"
182		export ENV=$(mktemp)
183		trap_exit_push 'rm -f $ENV; [ "$ENV_OLD" ] && export ENV="$ENV_OLD" || unset ENV; unset ENV_OLD'
184		[ -f "$ENV_OLD" ] && grep -v '^ERRATA_' "$ENV_OLD" > $ENV
185		grep '^ERRATA_' <(env) >> $ENV
186	fi
187}
188
189env_generate_errata ()
190{
191	local kernel_version_string=$(uname -r)
192	local kernel_version kernel_patchlevel kernel_sublevel
193	local line commit minver errata v p s have
194
195	IFS=. read kernel_version kernel_patchlevel kernel_sublevel <<<$kernel_version_string
196	kernel_sublevel=${kernel_sublevel%%[!0-9]*}
197
198	[ "$ENVIRON_DEFAULT" != "yes" ] && return
199	[ ! -f errata.txt ] && return
200
201	for line in $(grep -v '^#' errata.txt | tr -d '[:blank:]' | cut -d: -f1,2); do
202		commit=${line%:*}
203		minver=${line#*:}
204
205		errata="ERRATA_$commit"
206		test -v $errata && continue
207
208		IFS=. read v p s <<<$minver
209		s=${s%%[!0-9]*}
210
211		if (( $kernel_version > $v ||
212		      ($kernel_version == $v && $kernel_patchlevel > $p) )); then
213			have=y
214		elif (( $kernel_version == $v && $kernel_patchlevel == $p )); then
215			if [ "$kernel_sublevel" ] && [ "$s" ]; then
216				if (( $kernel_sublevel >= $s )); then
217					have=y
218				else
219					have=n
220				fi
221			elif [ "$s" ] && (( $s != 0 )); then
222				have=n
223			else
224				have=y
225			fi
226		else
227			have=n
228		fi
229		eval export "$errata=$have"
230	done
231}
232
233trap_exit_push ()
234{
235	local old_exit=$(trap -p EXIT | sed "s/^[^']*'//;s/'[^']*$//")
236	trap -- "$1; $old_exit" EXIT
237}
238