1#!/bin/sh
2#
3# Copyright (c) Linux Test Project, 2014-2017
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18#
19# Written by Cyril Hrubis <chrubis@suse.cz>
20#
21# This is a LTP test library for shell.
22#
23
24export LTP_RET_VAL=0
25export TST_COUNT=1
26export TST_LIB_LOADED=1
27
28. tst_ansi_color.sh
29
30# Exit values map
31tst_flag2mask()
32{
33	case "$1" in
34	TPASS) return 0;;
35	TFAIL) return 1;;
36	TBROK) return 2;;
37	TWARN) return 4;;
38	TINFO) return 16;;
39	TCONF) return 32;;
40	*) tst_brkm TBROK "Invalid resm type '$1'";;
41	esac
42}
43
44tst_resm()
45{
46	local ttype="$1"
47
48	tst_flag2mask "$ttype"
49	local mask=$?
50	LTP_RET_VAL=$((LTP_RET_VAL|mask))
51
52	local ret=$1
53	shift
54
55	printf "$TCID $TST_COUNT "
56	tst_print_colored $ret "$ret:"
57	echo " $@"
58
59	case "$ret" in
60	TPASS|TFAIL)
61	TST_COUNT=$((TST_COUNT+1));;
62	esac
63}
64
65tst_brkm()
66{
67	case "$1" in
68	TFAIL) ;;
69	TBROK) ;;
70	TCONF) ;;
71	*) tst_brkm TBROK "Invalid tst_brkm type '$1'";;
72	esac
73
74	local ret=$1
75	shift
76	tst_resm "$ret" "$@"
77	tst_exit
78}
79
80tst_record_childstatus()
81{
82	if [ $# -ne 1 ]; then
83		tst_brkm TBROK "Requires child pid as parameter"
84	fi
85
86	local child_pid=$1
87	local ret=0
88
89	wait $child_pid
90	ret=$?
91	if [ $ret -eq 127 ]; then
92		tst_brkm TBROK "Child process pid='$child_pid' does not exist"
93	fi
94	LTP_RET_VAL=$((LTP_RET_VAL|ret))
95}
96
97tst_require_root()
98{
99	if [ "$(id -ru)" != 0 ]; then
100		tst_brkm TCONF "Must be super/root for this test!"
101	fi
102}
103
104tst_exit()
105{
106	if [ -n "$TST_CLEANUP" -a -z "$TST_NO_CLEANUP" ]; then
107		$TST_CLEANUP
108	fi
109
110	if [ -n "$LTP_IPC_PATH" -a -f "$LTP_IPC_PATH" ]; then
111		rm -f "$LTP_IPC_PATH"
112	fi
113
114	# Mask out TINFO
115	exit $((LTP_RET_VAL & ~16))
116}
117
118tst_tmpdir()
119{
120	if [ -z "$TMPDIR" ]; then
121		export TMPDIR="/tmp"
122	fi
123
124	TST_TMPDIR=$(mktemp -d "$TMPDIR/$TCID.XXXXXXXXXX")
125
126	chmod 777 "$TST_TMPDIR"
127
128	TST_STARTWD=$(pwd)
129
130	cd "$TST_TMPDIR"
131}
132
133tst_rmdir()
134{
135	cd "$LTPROOT"
136	rm -r "$TST_TMPDIR"
137}
138
139#
140# Checks if commands passed as arguments exists
141#
142tst_check_cmds()
143{
144	local cmd
145	for cmd in $*; do
146		if ! command -v $cmd > /dev/null 2>&1; then
147			tst_brkm TCONF "'$cmd' not found"
148		fi
149	done
150}
151
152# tst_retry "command" [times]
153# try run command for specified times, default is 3.
154# Function returns 0 if succeed in RETRIES times or the last retcode the cmd
155# returned
156tst_retry()
157{
158	local cmd="$1"
159	local RETRIES=${2:-"3"}
160	local i=$RETRIES
161
162	while [ $i -gt 0 ]; do
163		eval "$cmd"
164		ret=$?
165		if [ $ret -eq 0 ]; then
166			break
167		fi
168		i=$((i-1))
169		sleep 1
170	done
171
172	if [ $ret -ne 0 ]; then
173		tst_resm TINFO "Failed to execute '$cmd' after $RETRIES retries"
174	fi
175
176	return $ret
177}
178
179# tst_timeout "command arg1 arg2 ..." timeout
180# Runs command for specified timeout (in seconds).
181# Function returns retcode of command or 1 if arguments are invalid.
182tst_timeout()
183{
184	local command=$1
185	local timeout=$(echo $2 | grep -o "^[0-9]\+$")
186
187	# command must be non-empty string with command to run
188	if [ -z "$command" ]; then
189		echo "first argument must be non-empty string"
190		return 1
191	fi
192
193	# accept only numbers as timeout
194	if [ -z "$timeout" ]; then
195		echo "only numbers as second argument"
196		return 1
197	fi
198
199	setsid sh -c "eval $command" 2>&1 &
200	local pid=$!
201	while [ $timeout -gt 0 ]; do
202		kill -s 0 $pid 2>/dev/null
203		if [ $? -ne 0 ]; then
204			break
205		fi
206		timeout=$((timeout - 1))
207		sleep 1
208	done
209
210	local ret=0
211	if [ $timeout -le 0 ]; then
212		ret=128
213		kill -TERM -- -$pid
214	fi
215
216	wait $pid
217	ret=$((ret | $?))
218
219	return $ret
220}
221
222ROD_SILENT()
223{
224	$@ > /dev/null 2>&1
225	if [ $? -ne 0 ]; then
226		tst_brkm TBROK "$@ failed"
227	fi
228}
229
230ROD_BASE()
231{
232	local cmd
233	local arg
234	local file
235	local flag
236
237	for arg; do
238		file="${arg#\>}"
239		if [ "$file" != "$arg" ]; then
240			flag=1
241			if [ -n "$file" ]; then
242				break
243			fi
244			continue
245		fi
246
247		if [ -n "$flag" ]; then
248			file="$arg"
249			break
250		fi
251
252		cmd="$cmd $arg"
253	done
254
255	if [ -n "$flag" ]; then
256		$cmd > $file
257	else
258		$@
259	fi
260}
261
262ROD()
263{
264	ROD_BASE "$@"
265	if [ $? -ne 0 ]; then
266		tst_brkm TBROK "$@ failed"
267	fi
268}
269
270EXPECT_PASS()
271{
272	ROD_BASE "$@"
273	if [ $? -eq 0 ]; then
274		tst_resm TPASS "$@ passed as expected"
275	else
276		tst_resm TFAIL "$@ failed unexpectedly"
277	fi
278}
279
280EXPECT_FAIL()
281{
282	# redirect stderr since we expect the command to fail
283	ROD_BASE "$@" 2> /dev/null
284	if [ $? -ne 0 ]; then
285		tst_resm TPASS "$@ failed as expected"
286	else
287		tst_resm TFAIL "$@ passed unexpectedly"
288	fi
289}
290
291tst_mkfs()
292{
293	local fs_type=$1
294	local device=$2
295	shift 2
296	local fs_opts="$@"
297
298	if [ -z "$fs_type" ]; then
299		tst_brkm TBROK "No fs_type specified"
300	fi
301
302	if [ -z "$device" ]; then
303		tst_brkm TBROK "No device specified"
304	fi
305
306	tst_resm TINFO "Formatting $device with $fs_type extra opts='$fs_opts'"
307
308	ROD_SILENT mkfs.$fs_type $fs_opts $device
309}
310
311tst_umount()
312{
313	local device="$1"
314	local i=0
315
316	if ! grep -q "$device" /proc/mounts; then
317		tst_resm TINFO "The $device is not mounted, skipping umount"
318		return
319	fi
320
321	while [ "$i" -lt 50 ]; do
322		if umount "$device" > /dev/null; then
323			return
324		fi
325
326		i=$((i+1))
327
328		tst_resm TINFO "umount($device) failed, try $i ..."
329		tst_resm TINFO "Likely gvfsd-trash is probing newly mounted "\
330			       "fs, kill it to speed up tests."
331
332		tst_sleep 100ms
333	done
334
335	tst_resm TWARN "Failed to umount($device) after 50 retries"
336}
337
338# Check a module file existence
339# Should be called after tst_tmpdir()
340tst_module_exists()
341{
342	local mod_name="$1"
343
344	if [ -f "$mod_name" ]; then
345		TST_MODPATH="$mod_name"
346		return
347	fi
348
349	local mod_path="$LTPROOT/testcases/bin/$mod_name"
350	if [ -f "$mod_path" ]; then
351		TST_MODPATH="$mod_path"
352		return
353	fi
354
355	if [ -n "$TST_TMPDIR" ]; then
356		mod_path="$TST_STARTWD/$mod_name"
357		if [ -f "$mod_path" ]; then
358			TST_MODPATH="$mod_path"
359			return
360		fi
361	fi
362
363	tst_brkm TCONF "Failed to find module '$mod_name'"
364}
365
366# Appends LTP path when doing su
367tst_su()
368{
369	local usr="$1"
370	shift
371
372	su "$usr" -c "PATH=\$PATH:$LTPROOT/testcases/bin/ $@"
373}
374
375TST_CHECKPOINT_WAIT()
376{
377	ROD tst_checkpoint wait 10000 "$1"
378}
379
380TST_CHECKPOINT_WAKE()
381{
382	ROD tst_checkpoint wake 10000 "$1" 1
383}
384
385TST_CHECKPOINT_WAKE2()
386{
387	ROD tst_checkpoint wake 10000 "$1" "$2"
388}
389
390TST_CHECKPOINT_WAKE_AND_WAIT()
391{
392	TST_CHECKPOINT_WAKE "$1"
393	TST_CHECKPOINT_WAIT "$1"
394}
395
396# Check that test name is set
397if [ -z "$TCID" ]; then
398	tst_brkm TBROK "TCID is not defined"
399fi
400
401if [ -z "$TST_TOTAL" ]; then
402	tst_brkm TBROK "TST_TOTAL is not defined"
403fi
404
405export TCID="$TCID"
406export TST_TOTAL="$TST_TOTAL"
407
408# Setup LTPROOT, default to current directory if not set
409if [ -z "$LTPROOT" ]; then
410	export LTPROOT="$PWD"
411	export LTP_DATAROOT="$LTPROOT/datafiles"
412else
413	export LTP_DATAROOT="$LTPROOT/testcases/data/$TCID"
414fi
415
416if [ "$TST_NEEDS_CHECKPOINTS" = "1" ]; then
417	LTP_IPC_PATH="/dev/shm/ltp_${TCID}_$$"
418
419	LTP_IPC_SIZE=$(getconf PAGESIZE)
420	if [ $? -ne 0 ]; then
421		tst_brkm TBROK "getconf PAGESIZE failed"
422	fi
423
424	ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$LTP_IPC_SIZE" count=1
425	ROD_SILENT chmod 600 "$LTP_IPC_PATH"
426	export LTP_IPC_PATH
427fi
428