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