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