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