xref: /kvm-unit-tests/scripts/arch-run.bash (revision 8b13a5b5cfdb26920c8f631a1e56e7a5eac917fe)
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	echo -n $@
32	initrd_create &&
33		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
72timeout_cmd ()
73{
74	if [ "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then
75		echo "timeout -k 1s --foreground $TIMEOUT"
76	fi
77}
78
79qmp ()
80{
81	echo '{ "execute": "qmp_capabilities" }{ "execute":' "$2" '}' | nc -U $1
82}
83
84run_migration ()
85{
86	if ! command -v nc >/dev/null 2>&1; then
87		echo "$FUNCNAME needs nc (netcat)" >&2
88		exit 2
89	fi
90
91	qemu=$1
92	shift
93
94	migsock=`mktemp -u -t mig-helper-socket.XXXXXXXXXX`
95	migout1=`mktemp -t mig-helper-stdout1.XXXXXXXXXX`
96	qmp1=`mktemp -u -t mig-helper-qmp1.XXXXXXXXXX`
97	qmp2=`mktemp -u -t mig-helper-qmp2.XXXXXXXXXX`
98	qmpout1=/dev/null
99	qmpout2=/dev/null
100
101	trap 'rm -f ${migout1} ${migsock} ${qmp1} ${qmp2}' EXIT
102
103	$qemu "$@" -chardev socket,id=mon1,path=${qmp1},server,nowait \
104		 -mon chardev=mon1,mode=control | tee ${migout1} &
105
106	$qemu "$@" -chardev socket,id=mon2,path=${qmp2},server,nowait \
107		 -mon chardev=mon2,mode=control -incoming unix:${migsock} &
108
109	# The test must prompt the user to migrate, so wait for the "migrate" keyword
110	while ! grep -q -i "migrate" < ${migout1} ; do
111		sleep 1
112	done
113
114	qmp ${qmp1} '"migrate", "arguments": { "uri": "unix:'${migsock}'" }' > ${qmpout1}
115
116	# Wait for the migration to complete
117	migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
118	while ! grep -q '"completed"' <<<"$migstatus" ; do
119		sleep 1
120		migstatus=`qmp ${qmp1} '"query-migrate"' | grep return`
121		if grep -q '"failed"' <<<"$migstatus" ; then
122			echo "ERROR: Migration failed." >&2
123			qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
124			qmp ${qmp2} '"quit"'> ${qmpout2} 2>/dev/null
125			exit 2
126		fi
127	done
128	qmp ${qmp1} '"quit"'> ${qmpout1} 2>/dev/null
129
130	qmp ${qmp2} '"inject-nmi"'> ${qmpout2}
131
132	wait
133}
134
135migration_cmd ()
136{
137	if [ "$MIGRATION" = "yes" ]; then
138		echo "run_migration"
139	fi
140}
141
142search_qemu_binary ()
143{
144	local save_path=$PATH
145	local qemucmd qemu
146
147	export PATH=$PATH:/usr/libexec
148	for qemucmd in ${QEMU:-qemu-system-$ARCH_NAME qemu-kvm}; do
149		if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then
150			qemu="$qemucmd"
151			break
152		fi
153	done
154
155	if [ -z "$qemu" ]; then
156		echo "A QEMU binary was not found."
157		echo "You can set a custom location by using the QEMU=<path> environment variable."
158		exit 2
159	fi
160	command -v $qemu
161	export PATH=$save_path
162}
163
164initrd_create ()
165{
166	local ret
167
168	env_add_errata
169	ret=$?
170
171	unset INITRD
172	[ -f "$ENV" ] && INITRD="-initrd $ENV"
173
174	return $ret
175}
176
177env_add_errata ()
178{
179	local line errata ret=1
180
181	if [ -f "$ENV" ] && grep -q '^ERRATA_' <(env); then
182		for line in $(grep '^ERRATA_' "$ENV"); do
183			errata=${line%%=*}
184			test -v $errata && continue
185			eval export "$line"
186		done
187	elif [ ! -f "$ENV" ]; then
188		env_generate_errata
189	fi
190
191	if grep -q '^ERRATA_' <(env); then
192		export ENV_OLD="$ENV"
193		export ENV=$(mktemp)
194		trap_exit_push 'rm -f $ENV; [ "$ENV_OLD" ] && export ENV="$ENV_OLD" || unset ENV; unset ENV_OLD'
195		[ -f "$ENV_OLD" ] && grep -v '^ERRATA_' "$ENV_OLD" > $ENV
196		grep '^ERRATA_' <(env) >> $ENV
197		ret=0
198	fi
199
200	return $ret
201}
202
203env_generate_errata ()
204{
205	local kernel_version_string=$(uname -r)
206	local kernel_version kernel_patchlevel kernel_sublevel
207	local line commit minver errata v p s have
208
209	IFS=. read kernel_version kernel_patchlevel kernel_sublevel <<<$kernel_version_string
210	kernel_sublevel=${kernel_sublevel%%[!0-9]*}
211
212	[ "$ENVIRON_DEFAULT" != "yes" ] && return
213	[ ! -f "$ERRATATXT" ] && return
214
215	for line in $(grep -v '^#' "$ERRATATXT" | tr -d '[:blank:]' | cut -d: -f1,2); do
216		commit=${line%:*}
217		minver=${line#*:}
218
219		errata="ERRATA_$commit"
220		test -v $errata && continue
221
222		IFS=. read v p s <<<$minver
223		s=${s%%[!0-9]*}
224
225		if (( $kernel_version > $v ||
226		      ($kernel_version == $v && $kernel_patchlevel > $p) )); then
227			have=y
228		elif (( $kernel_version == $v && $kernel_patchlevel == $p )); then
229			if [ "$kernel_sublevel" ] && [ "$s" ]; then
230				if (( $kernel_sublevel >= $s )); then
231					have=y
232				else
233					have=n
234				fi
235			elif [ "$s" ] && (( $s != 0 )); then
236				have=n
237			else
238				have=y
239			fi
240		else
241			have=n
242		fi
243		eval export "$errata=$have"
244	done
245}
246
247trap_exit_push ()
248{
249	local old_exit=$(trap -p EXIT | sed "s/^[^']*'//;s/'[^']*$//")
250	trap -- "$1; $old_exit" EXIT
251}
252