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