1#!/bin/bash
2# Copyright (C) 2017 Luis R. Rodriguez <[email protected]>
3#
4# This program is free software; you can redistribute it and/or modify it
5# under the terms of the GNU General Public License as published by the Free
6# Software Foundation; either version 2 of the License, or at your option any
7# later version; or, when distributed separately from the Linux kernel or
8# when incorporated into other software packages, subject to the following
9# license:
10#
11# This program is free software; you can redistribute it and/or modify it
12# under the terms of copyleft-next (version 0.3.1 or later) as published
13# at http://copyleft-next.org/.
14
15# This performs a series tests against the proc sysctl interface.
16
17# Kselftest framework requirement - SKIP code is 4.
18ksft_skip=4
19
20TEST_NAME="sysctl"
21TEST_DRIVER="test_${TEST_NAME}"
22TEST_DIR=$(dirname $0)
23TEST_FILE=$(mktemp)
24
25# This represents
26#
27# TEST_ID:TEST_COUNT:ENABLED:TARGET
28#
29# TEST_ID: is the test id number
30# TEST_COUNT: number of times we should run the test
31# ENABLED: 1 if enabled, 0 otherwise
32# TARGET: test target file required on the test_sysctl module
33#
34# Once these are enabled please leave them as-is. Write your own test,
35# we have tons of space.
36ALL_TESTS="0001:1:1:int_0001"
37ALL_TESTS="$ALL_TESTS 0002:1:1:string_0001"
38ALL_TESTS="$ALL_TESTS 0003:1:1:int_0002"
39ALL_TESTS="$ALL_TESTS 0004:1:1:uint_0001"
40ALL_TESTS="$ALL_TESTS 0005:3:1:int_0003"
41ALL_TESTS="$ALL_TESTS 0006:50:1:bitmap_0001"
42
43function allow_user_defaults()
44{
45	if [ -z $DIR ]; then
46		DIR="/sys/module/test_sysctl/"
47	fi
48	if [ -z $DEFAULT_NUM_TESTS ]; then
49		DEFAULT_NUM_TESTS=50
50	fi
51	if [ -z $SYSCTL ]; then
52		SYSCTL="/proc/sys/debug/test_sysctl"
53	fi
54	if [ -z $PROD_SYSCTL ]; then
55		PROD_SYSCTL="/proc/sys"
56	fi
57	if [ -z $WRITES_STRICT ]; then
58		WRITES_STRICT="${PROD_SYSCTL}/kernel/sysctl_writes_strict"
59	fi
60}
61
62function check_production_sysctl_writes_strict()
63{
64	echo -n "Checking production write strict setting ... "
65	if [ ! -e ${WRITES_STRICT} ]; then
66		echo "FAIL, but skip in case of old kernel" >&2
67	else
68		old_strict=$(cat ${WRITES_STRICT})
69		if [ "$old_strict" = "1" ]; then
70			echo "ok"
71		else
72			echo "FAIL, strict value is 0 but force to 1 to continue" >&2
73			echo "1" > ${WRITES_STRICT}
74		fi
75	fi
76
77	if [ -z $PAGE_SIZE ]; then
78		PAGE_SIZE=$(getconf PAGESIZE)
79	fi
80	if [ -z $MAX_DIGITS ]; then
81		MAX_DIGITS=$(($PAGE_SIZE/8))
82	fi
83	if [ -z $INT_MAX ]; then
84		INT_MAX=$(getconf INT_MAX)
85	fi
86	if [ -z $UINT_MAX ]; then
87		UINT_MAX=$(getconf UINT_MAX)
88	fi
89}
90
91test_reqs()
92{
93	uid=$(id -u)
94	if [ $uid -ne 0 ]; then
95		echo $msg must be run as root >&2
96		exit $ksft_skip
97	fi
98
99	if ! which perl 2> /dev/null > /dev/null; then
100		echo "$0: You need perl installed"
101		exit $ksft_skip
102	fi
103	if ! which getconf 2> /dev/null > /dev/null; then
104		echo "$0: You need getconf installed"
105		exit $ksft_skip
106	fi
107	if ! which diff 2> /dev/null > /dev/null; then
108		echo "$0: You need diff installed"
109		exit $ksft_skip
110	fi
111}
112
113function load_req_mod()
114{
115	if [ ! -d $DIR ]; then
116		if ! modprobe -q -n $TEST_DRIVER; then
117			echo "$0: module $TEST_DRIVER not found [SKIP]"
118			echo "You must set CONFIG_TEST_SYSCTL=m in your kernel" >&2
119			exit $ksft_skip
120		fi
121		modprobe $TEST_DRIVER
122		if [ $? -ne 0 ]; then
123			echo "$0: modprobe $TEST_DRIVER failed."
124			exit
125		fi
126	fi
127}
128
129reset_vals()
130{
131	VAL=""
132	TRIGGER=$(basename ${TARGET})
133	case "$TRIGGER" in
134		int_0001)
135			VAL="60"
136			;;
137		int_0002)
138			VAL="1"
139			;;
140		uint_0001)
141			VAL="314"
142			;;
143		string_0001)
144			VAL="(none)"
145			;;
146		bitmap_0001)
147			VAL=""
148			;;
149		*)
150			;;
151	esac
152	echo -n $VAL > $TARGET
153}
154
155set_orig()
156{
157	if [ ! -z $TARGET ] && [ ! -z $ORIG ]; then
158		if [ -f ${TARGET} ]; then
159			echo "${ORIG}" > "${TARGET}"
160		fi
161	fi
162}
163
164set_test()
165{
166	echo "${TEST_STR}" > "${TARGET}"
167}
168
169verify()
170{
171	local seen
172	seen=$(cat "$1")
173	if [ "${seen}" != "${TEST_STR}" ]; then
174		return 1
175	fi
176	return 0
177}
178
179# proc files get read a page at a time, which can confuse diff,
180# and get you incorrect results on proc files with long data. To use
181# diff against them you must first extract the output to a file, and
182# then compare against that file.
183verify_diff_proc_file()
184{
185	TMP_DUMP_FILE=$(mktemp)
186	cat $1 > $TMP_DUMP_FILE
187
188	if ! diff -w -q $TMP_DUMP_FILE $2; then
189		return 1
190	else
191		return 0
192	fi
193}
194
195verify_diff_w()
196{
197	echo "$TEST_STR" | diff -q -w -u - $1 > /dev/null
198	return $?
199}
200
201test_rc()
202{
203	if [[ $rc != 0 ]]; then
204		echo "Failed test, return value: $rc" >&2
205		exit $rc
206	fi
207}
208
209test_finish()
210{
211	set_orig
212	rm -f "${TEST_FILE}"
213
214	if [ ! -z ${old_strict} ]; then
215		echo ${old_strict} > ${WRITES_STRICT}
216	fi
217	exit $rc
218}
219
220run_numerictests()
221{
222	echo "== Testing sysctl behavior against ${TARGET} =="
223
224	rc=0
225
226	echo -n "Writing test file ... "
227	echo "${TEST_STR}" > "${TEST_FILE}"
228	if ! verify "${TEST_FILE}"; then
229		echo "FAIL" >&2
230		exit 1
231	else
232		echo "ok"
233	fi
234
235	echo -n "Checking sysctl is not set to test value ... "
236	if verify "${TARGET}"; then
237		echo "FAIL" >&2
238		exit 1
239	else
240		echo "ok"
241	fi
242
243	echo -n "Writing sysctl from shell ... "
244	set_test
245	if ! verify "${TARGET}"; then
246		echo "FAIL" >&2
247		exit 1
248	else
249		echo "ok"
250	fi
251
252	echo -n "Resetting sysctl to original value ... "
253	set_orig
254	if verify "${TARGET}"; then
255		echo "FAIL" >&2
256		exit 1
257	else
258		echo "ok"
259	fi
260
261	# Now that we've validated the sanity of "set_test" and "set_orig",
262	# we can use those functions to set starting states before running
263	# specific behavioral tests.
264
265	echo -n "Writing entire sysctl in single write ... "
266	set_orig
267	dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null
268	if ! verify "${TARGET}"; then
269		echo "FAIL" >&2
270		rc=1
271	else
272		echo "ok"
273	fi
274
275	echo -n "Writing middle of sysctl after synchronized seek ... "
276	set_test
277	dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null
278	if ! verify "${TARGET}"; then
279		echo "FAIL" >&2
280		rc=1
281	else
282		echo "ok"
283	fi
284
285	echo -n "Writing beyond end of sysctl ... "
286	set_orig
287	dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null
288	if verify "${TARGET}"; then
289		echo "FAIL" >&2
290		rc=1
291	else
292		echo "ok"
293	fi
294
295	echo -n "Writing sysctl with multiple long writes ... "
296	set_orig
297	(perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \
298		dd of="${TARGET}" bs=50 2>/dev/null
299	if verify "${TARGET}"; then
300		echo "FAIL" >&2
301		rc=1
302	else
303		echo "ok"
304	fi
305	test_rc
306}
307
308check_failure()
309{
310	echo -n "Testing that $1 fails as expected..."
311	reset_vals
312	TEST_STR="$1"
313	orig="$(cat $TARGET)"
314	echo -n "$TEST_STR" > $TARGET 2> /dev/null
315
316	# write should fail and $TARGET should retain its original value
317	if [ $? = 0 ] || [ "$(cat $TARGET)" != "$orig" ]; then
318		echo "FAIL" >&2
319		rc=1
320	else
321		echo "ok"
322	fi
323	test_rc
324}
325
326run_wideint_tests()
327{
328	# sysctl conversion functions receive a boolean sign and ulong
329	# magnitude; here we list the magnitudes we want to test (each of
330	# which will be tested in both positive and negative forms).  Since
331	# none of these values fit in 32 bits, writing them to an int- or
332	# uint-typed sysctl should fail.
333	local magnitudes=(
334		# common boundary-condition values (zero, +1, -1, INT_MIN,
335		# and INT_MAX respectively) if truncated to lower 32 bits
336		# (potential for being falsely deemed in range)
337		0x0000000100000000
338		0x0000000100000001
339		0x00000001ffffffff
340		0x0000000180000000
341		0x000000017fffffff
342
343		# these look like negatives, but without a leading '-' are
344		# actually large positives (should be rejected as above
345		# despite being zero/+1/-1/INT_MIN/INT_MAX in the lower 32)
346		0xffffffff00000000
347		0xffffffff00000001
348		0xffffffffffffffff
349		0xffffffff80000000
350		0xffffffff7fffffff
351	)
352
353	for sign in '' '-'; do
354		for mag in "${magnitudes[@]}"; do
355			check_failure "${sign}${mag}"
356		done
357	done
358}
359
360# Your test must accept digits 3 and 4 to use this
361run_limit_digit()
362{
363	echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..."
364	reset_vals
365
366	LIMIT=$((MAX_DIGITS -1))
367	TEST_STR="3"
368	(perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
369		dd of="${TARGET}" 2>/dev/null
370
371	if ! verify "${TARGET}"; then
372		echo "FAIL" >&2
373		rc=1
374	else
375		echo "ok"
376	fi
377	test_rc
378
379	echo -n "Checking passing PAGE_SIZE of spaces fails on write ..."
380	reset_vals
381
382	LIMIT=$((MAX_DIGITS))
383	TEST_STR="4"
384	(perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
385		dd of="${TARGET}" 2>/dev/null
386
387	if verify "${TARGET}"; then
388		echo "FAIL" >&2
389		rc=1
390	else
391		echo "ok"
392	fi
393	test_rc
394}
395
396# You are using an int
397run_limit_digit_int()
398{
399	echo -n "Testing INT_MAX works ..."
400	reset_vals
401	TEST_STR="$INT_MAX"
402	echo -n $TEST_STR > $TARGET
403
404	if ! verify "${TARGET}"; then
405		echo "FAIL" >&2
406		rc=1
407	else
408		echo "ok"
409	fi
410	test_rc
411
412	echo -n "Testing INT_MAX + 1 will fail as expected..."
413	reset_vals
414	let TEST_STR=$INT_MAX+1
415	echo -n $TEST_STR > $TARGET 2> /dev/null
416
417	if verify "${TARGET}"; then
418		echo "FAIL" >&2
419		rc=1
420	else
421		echo "ok"
422	fi
423	test_rc
424
425	echo -n "Testing negative values will work as expected..."
426	reset_vals
427	TEST_STR="-3"
428	echo -n $TEST_STR > $TARGET 2> /dev/null
429	if ! verify "${TARGET}"; then
430		echo "FAIL" >&2
431		rc=1
432	else
433		echo "ok"
434	fi
435	test_rc
436}
437
438# You used an int array
439run_limit_digit_int_array()
440{
441	echo -n "Testing array works as expected ... "
442	TEST_STR="4 3 2 1"
443	echo -n $TEST_STR > $TARGET
444
445	if ! verify_diff_w "${TARGET}"; then
446		echo "FAIL" >&2
447		rc=1
448	else
449		echo "ok"
450	fi
451	test_rc
452
453	echo -n "Testing skipping trailing array elements works ... "
454	# Do not reset_vals, carry on the values from the last test.
455	# If we only echo in two digits the last two are left intact
456	TEST_STR="100 101"
457	echo -n $TEST_STR > $TARGET
458	# After we echo in, to help diff we need to set on TEST_STR what
459	# we expect the result to be.
460	TEST_STR="100 101 2 1"
461
462	if ! verify_diff_w "${TARGET}"; then
463		echo "FAIL" >&2
464		rc=1
465	else
466		echo "ok"
467	fi
468	test_rc
469
470	echo -n "Testing PAGE_SIZE limit on array works ... "
471	# Do not reset_vals, carry on the values from the last test.
472	# Even if you use an int array, you are still restricted to
473	# MAX_DIGITS, this is a known limitation. Test limit works.
474	LIMIT=$((MAX_DIGITS -1))
475	TEST_STR="9"
476	(perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
477		dd of="${TARGET}" 2>/dev/null
478
479	TEST_STR="9 101 2 1"
480	if ! verify_diff_w "${TARGET}"; then
481		echo "FAIL" >&2
482		rc=1
483	else
484		echo "ok"
485	fi
486	test_rc
487
488	echo -n "Testing exceeding PAGE_SIZE limit fails as expected ... "
489	# Do not reset_vals, carry on the values from the last test.
490	# Now go over limit.
491	LIMIT=$((MAX_DIGITS))
492	TEST_STR="7"
493	(perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
494		dd of="${TARGET}" 2>/dev/null
495
496	TEST_STR="7 101 2 1"
497	if verify_diff_w "${TARGET}"; then
498		echo "FAIL" >&2
499		rc=1
500	else
501		echo "ok"
502	fi
503	test_rc
504}
505
506# You are using an unsigned int
507run_limit_digit_uint()
508{
509	echo -n "Testing UINT_MAX works ..."
510	reset_vals
511	TEST_STR="$UINT_MAX"
512	echo -n $TEST_STR > $TARGET
513
514	if ! verify "${TARGET}"; then
515		echo "FAIL" >&2
516		rc=1
517	else
518		echo "ok"
519	fi
520	test_rc
521
522	echo -n "Testing UINT_MAX + 1 will fail as expected..."
523	reset_vals
524	TEST_STR=$(($UINT_MAX+1))
525	echo -n $TEST_STR > $TARGET 2> /dev/null
526
527	if verify "${TARGET}"; then
528		echo "FAIL" >&2
529		rc=1
530	else
531		echo "ok"
532	fi
533	test_rc
534
535	echo -n "Testing negative values will not work as expected ..."
536	reset_vals
537	TEST_STR="-3"
538	echo -n $TEST_STR > $TARGET 2> /dev/null
539
540	if verify "${TARGET}"; then
541		echo "FAIL" >&2
542		rc=1
543	else
544		echo "ok"
545	fi
546	test_rc
547}
548
549run_stringtests()
550{
551	echo -n "Writing entire sysctl in short writes ... "
552	set_orig
553	dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null
554	if ! verify "${TARGET}"; then
555		echo "FAIL" >&2
556		rc=1
557	else
558		echo "ok"
559	fi
560
561	echo -n "Writing middle of sysctl after unsynchronized seek ... "
562	set_test
563	dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null
564	if verify "${TARGET}"; then
565		echo "FAIL" >&2
566		rc=1
567	else
568		echo "ok"
569	fi
570
571	echo -n "Checking sysctl maxlen is at least $MAXLEN ... "
572	set_orig
573	perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \
574		dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
575	if ! grep -q B "${TARGET}"; then
576		echo "FAIL" >&2
577		rc=1
578	else
579		echo "ok"
580	fi
581
582	echo -n "Checking sysctl keeps original string on overflow append ... "
583	set_orig
584	perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
585		dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null
586	if grep -q B "${TARGET}"; then
587		echo "FAIL" >&2
588		rc=1
589	else
590		echo "ok"
591	fi
592
593	echo -n "Checking sysctl stays NULL terminated on write ... "
594	set_orig
595	perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
596		dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
597	if grep -q B "${TARGET}"; then
598		echo "FAIL" >&2
599		rc=1
600	else
601		echo "ok"
602	fi
603
604	echo -n "Checking sysctl stays NULL terminated on overwrite ... "
605	set_orig
606	perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \
607		dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null
608	if grep -q B "${TARGET}"; then
609		echo "FAIL" >&2
610		rc=1
611	else
612		echo "ok"
613	fi
614
615	test_rc
616}
617
618target_exists()
619{
620	TARGET="${SYSCTL}/$1"
621	TEST_ID="$2"
622
623	if [ ! -f ${TARGET} ] ; then
624		echo "Target for test $TEST_ID: $TARGET not exist, skipping test ..."
625		return 0
626	fi
627	return 1
628}
629
630run_bitmaptest() {
631	# Total length of bitmaps string to use, a bit under
632	# the maximum input size of the test node
633	LENGTH=$((RANDOM % 65000))
634
635	# First bit to set
636	BIT=$((RANDOM % 1024))
637
638	# String containing our list of bits to set
639	TEST_STR=$BIT
640
641	# build up the string
642	while [ "${#TEST_STR}" -le "$LENGTH" ]; do
643		# Make sure next entry is discontiguous,
644		# skip ahead at least 2
645		BIT=$((BIT + $((2 + RANDOM % 10))))
646
647		# Add new bit to the list
648		TEST_STR="${TEST_STR},${BIT}"
649
650		# Randomly make it a range
651		if [ "$((RANDOM % 2))" -eq "1" ]; then
652			RANGE_END=$((BIT + $((1 + RANDOM % 10))))
653			TEST_STR="${TEST_STR}-${RANGE_END}"
654			BIT=$RANGE_END
655		fi
656	done
657
658	echo -n "Checking bitmap handler... "
659	TEST_FILE=$(mktemp)
660	echo -n "$TEST_STR" > $TEST_FILE
661
662	cat $TEST_FILE > $TARGET 2> /dev/null
663	if [ $? -ne 0 ]; then
664		echo "FAIL" >&2
665		rc=1
666		test_rc
667	fi
668
669	if ! verify_diff_proc_file "$TARGET" "$TEST_FILE"; then
670		echo "FAIL" >&2
671		rc=1
672	else
673		echo "ok"
674		rc=0
675	fi
676	test_rc
677}
678
679sysctl_test_0001()
680{
681	TARGET="${SYSCTL}/$(get_test_target 0001)"
682	reset_vals
683	ORIG=$(cat "${TARGET}")
684	TEST_STR=$(( $ORIG + 1 ))
685
686	run_numerictests
687	run_wideint_tests
688	run_limit_digit
689}
690
691sysctl_test_0002()
692{
693	TARGET="${SYSCTL}/$(get_test_target 0002)"
694	reset_vals
695	ORIG=$(cat "${TARGET}")
696	TEST_STR="Testing sysctl"
697	# Only string sysctls support seeking/appending.
698	MAXLEN=65
699
700	run_numerictests
701	run_stringtests
702}
703
704sysctl_test_0003()
705{
706	TARGET="${SYSCTL}/$(get_test_target 0003)"
707	reset_vals
708	ORIG=$(cat "${TARGET}")
709	TEST_STR=$(( $ORIG + 1 ))
710
711	run_numerictests
712	run_wideint_tests
713	run_limit_digit
714	run_limit_digit_int
715}
716
717sysctl_test_0004()
718{
719	TARGET="${SYSCTL}/$(get_test_target 0004)"
720	reset_vals
721	ORIG=$(cat "${TARGET}")
722	TEST_STR=$(( $ORIG + 1 ))
723
724	run_numerictests
725	run_wideint_tests
726	run_limit_digit
727	run_limit_digit_uint
728}
729
730sysctl_test_0005()
731{
732	TARGET="${SYSCTL}/$(get_test_target 0005)"
733	reset_vals
734	ORIG=$(cat "${TARGET}")
735
736	run_limit_digit_int_array
737}
738
739sysctl_test_0006()
740{
741	TARGET="${SYSCTL}/bitmap_0001"
742	reset_vals
743	ORIG=""
744	run_bitmaptest
745}
746
747list_tests()
748{
749	echo "Test ID list:"
750	echo
751	echo "TEST_ID x NUM_TEST"
752	echo "TEST_ID:   Test ID"
753	echo "NUM_TESTS: Number of recommended times to run the test"
754	echo
755	echo "0001 x $(get_test_count 0001) - tests proc_dointvec_minmax()"
756	echo "0002 x $(get_test_count 0002) - tests proc_dostring()"
757	echo "0003 x $(get_test_count 0003) - tests proc_dointvec()"
758	echo "0004 x $(get_test_count 0004) - tests proc_douintvec()"
759	echo "0005 x $(get_test_count 0005) - tests proc_douintvec() array"
760	echo "0006 x $(get_test_count 0006) - tests proc_do_large_bitmap()"
761}
762
763usage()
764{
765	NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)
766	let NUM_TESTS=$NUM_TESTS+1
767	MAX_TEST=$(printf "%04d\n" $NUM_TESTS)
768	echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
769	echo "		 [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
770	echo "           [ all ] [ -h | --help ] [ -l ]"
771	echo ""
772	echo "Valid tests: 0001-$MAX_TEST"
773	echo ""
774	echo "    all     Runs all tests (default)"
775	echo "    -t      Run test ID the number amount of times is recommended"
776	echo "    -w      Watch test ID run until it runs into an error"
777	echo "    -c      Run test ID once"
778	echo "    -s      Run test ID x test-count number of times"
779	echo "    -l      List all test ID list"
780	echo " -h|--help  Help"
781	echo
782	echo "If an error every occurs execution will immediately terminate."
783	echo "If you are adding a new test try using -w <test-ID> first to"
784	echo "make sure the test passes a series of tests."
785	echo
786	echo Example uses:
787	echo
788	echo "$TEST_NAME.sh            -- executes all tests"
789	echo "$TEST_NAME.sh -t 0002    -- Executes test ID 0002 number of times is recomended"
790	echo "$TEST_NAME.sh -w 0002    -- Watch test ID 0002 run until an error occurs"
791	echo "$TEST_NAME.sh -s 0002    -- Run test ID 0002 once"
792	echo "$TEST_NAME.sh -c 0002 3  -- Run test ID 0002 three times"
793	echo
794	list_tests
795	exit 1
796}
797
798function test_num()
799{
800	re='^[0-9]+$'
801	if ! [[ $1 =~ $re ]]; then
802		usage
803	fi
804}
805
806function get_test_count()
807{
808	test_num $1
809	TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
810	echo ${TEST_DATA} | awk -F":" '{print $2}'
811}
812
813function get_test_enabled()
814{
815	test_num $1
816	TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
817	echo ${TEST_DATA} | awk -F":" '{print $3}'
818}
819
820function get_test_target()
821{
822	test_num $1
823	TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
824	echo ${TEST_DATA} | awk -F":" '{print $4}'
825}
826
827function run_all_tests()
828{
829	for i in $ALL_TESTS ; do
830		TEST_ID=${i%:*:*:*}
831		ENABLED=$(get_test_enabled $TEST_ID)
832		TEST_COUNT=$(get_test_count $TEST_ID)
833		TEST_TARGET=$(get_test_target $TEST_ID)
834		if target_exists $TEST_TARGET $TEST_ID; then
835			continue
836		fi
837		if [[ $ENABLED -eq "1" ]]; then
838			test_case $TEST_ID $TEST_COUNT $TEST_TARGET
839		fi
840	done
841}
842
843function watch_log()
844{
845	if [ $# -ne 3 ]; then
846		clear
847	fi
848	date
849	echo "Running test: $2 - run #$1"
850}
851
852function watch_case()
853{
854	i=0
855	while [ 1 ]; do
856
857		if [ $# -eq 1 ]; then
858			test_num $1
859			watch_log $i ${TEST_NAME}_test_$1
860			${TEST_NAME}_test_$1
861		else
862			watch_log $i all
863			run_all_tests
864		fi
865		let i=$i+1
866	done
867}
868
869function test_case()
870{
871	NUM_TESTS=$2
872
873	i=0
874
875	if target_exists $3 $1; then
876		continue
877	fi
878
879	while [ $i -lt $NUM_TESTS ]; do
880		test_num $1
881		watch_log $i ${TEST_NAME}_test_$1 noclear
882		RUN_TEST=${TEST_NAME}_test_$1
883		$RUN_TEST
884		let i=$i+1
885	done
886}
887
888function parse_args()
889{
890	if [ $# -eq 0 ]; then
891		run_all_tests
892	else
893		if [[ "$1" = "all" ]]; then
894			run_all_tests
895		elif [[ "$1" = "-w" ]]; then
896			shift
897			watch_case $@
898		elif [[ "$1" = "-t" ]]; then
899			shift
900			test_num $1
901			test_case $1 $(get_test_count $1) $(get_test_target $1)
902		elif [[ "$1" = "-c" ]]; then
903			shift
904			test_num $1
905			test_num $2
906			test_case $1 $2 $(get_test_target $1)
907		elif [[ "$1" = "-s" ]]; then
908			shift
909			test_case $1 1 $(get_test_target $1)
910		elif [[ "$1" = "-l" ]]; then
911			list_tests
912		elif [[ "$1" = "-h" || "$1" = "--help" ]]; then
913			usage
914		else
915			usage
916		fi
917	fi
918}
919
920test_reqs
921allow_user_defaults
922check_production_sysctl_writes_strict
923load_req_mod
924
925trap "test_finish" EXIT
926
927parse_args $@
928
929exit 0
930