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