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