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