]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - tools/testing/selftests/sysctl/sysctl.sh
test_sysctl: test against int proc_dointvec() array support
[karo-tx-linux.git] / tools / testing / selftests / sysctl / sysctl.sh
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 TEST_NAME="sysctl"
18 TEST_DRIVER="test_${TEST_NAME}"
19 TEST_DIR=$(dirname $0)
20 TEST_FILE=$(mktemp)
21
22 # This represents
23 #
24 # TEST_ID:TEST_COUNT:ENABLED
25 #
26 # TEST_ID: is the test id number
27 # TEST_COUNT: number of times we should run the test
28 # ENABLED: 1 if enabled, 0 otherwise
29 #
30 # Once these are enabled please leave them as-is. Write your own test,
31 # we have tons of space.
32 ALL_TESTS="0001:1:1"
33 ALL_TESTS="$ALL_TESTS 0002:1:1"
34 ALL_TESTS="$ALL_TESTS 0003:1:1"
35 ALL_TESTS="$ALL_TESTS 0004:1:1"
36 ALL_TESTS="$ALL_TESTS 0005:3:1"
37
38 test_modprobe()
39 {
40        if [ ! -d $DIR ]; then
41                echo "$0: $DIR not present" >&2
42                echo "You must have the following enabled in your kernel:" >&2
43                cat $TEST_DIR/config >&2
44                exit 1
45        fi
46 }
47
48 function allow_user_defaults()
49 {
50         if [ -z $DIR ]; then
51                 DIR="/sys/module/test_sysctl/"
52         fi
53         if [ -z $DEFAULT_NUM_TESTS ]; then
54                 DEFAULT_NUM_TESTS=50
55         fi
56         if [ -z $SYSCTL ]; then
57                 SYSCTL="/proc/sys/debug/test_sysctl"
58         fi
59         if [ -z $PROD_SYSCTL ]; then
60                 PROD_SYSCTL="/proc/sys"
61         fi
62         if [ -z $WRITES_STRICT ]; then
63                 WRITES_STRICT="${PROD_SYSCTL}/kernel/sysctl_writes_strict"
64         fi
65 }
66
67 function check_production_sysctl_writes_strict()
68 {
69         echo -n "Checking production write strict setting ... "
70         if [ ! -e ${WRITES_STRICT} ]; then
71                 echo "FAIL, but skip in case of old kernel" >&2
72         else
73                 old_strict=$(cat ${WRITES_STRICT})
74                 if [ "$old_strict" = "1" ]; then
75                         echo "ok"
76                 else
77                         echo "FAIL, strict value is 0 but force to 1 to continue" >&2
78                         echo "1" > ${WRITES_STRICT}
79                 fi
80         fi
81
82         if [ -z $PAGE_SIZE ]; then
83                 PAGE_SIZE=$(getconf PAGESIZE)
84         fi
85         if [ -z $MAX_DIGITS ]; then
86                 MAX_DIGITS=$(($PAGE_SIZE/8))
87         fi
88         if [ -z $INT_MAX ]; then
89                 INT_MAX=$(getconf INT_MAX)
90         fi
91         if [ -z $UINT_MAX ]; then
92                 UINT_MAX=$(getconf UINT_MAX)
93         fi
94 }
95
96 test_reqs()
97 {
98         uid=$(id -u)
99         if [ $uid -ne 0 ]; then
100                 echo $msg must be run as root >&2
101                 exit 0
102         fi
103
104         if ! which perl 2> /dev/null > /dev/null; then
105                 echo "$0: You need perl installed"
106                 exit 1
107         fi
108         if ! which getconf 2> /dev/null > /dev/null; then
109                 echo "$0: You need getconf installed"
110                 exit 1
111         fi
112         if ! which diff 2> /dev/null > /dev/null; then
113                 echo "$0: You need diff installed"
114                 exit 1
115         fi
116 }
117
118 function load_req_mod()
119 {
120         trap "test_modprobe" EXIT
121
122         if [ ! -d $DIR ]; then
123                 modprobe $TEST_DRIVER
124                 if [ $? -ne 0 ]; then
125                         exit
126                 fi
127         fi
128 }
129
130 reset_vals()
131 {
132         VAL=""
133         TRIGGER=$(basename ${TARGET})
134         case "$TRIGGER" in
135                 int_0001)
136                         VAL="60"
137                         ;;
138                 int_0002)
139                         VAL="1"
140                         ;;
141                 uint_0001)
142                         VAL="314"
143                         ;;
144                 string_0001)
145                         VAL="(none)"
146                         ;;
147                 *)
148                         ;;
149         esac
150         echo -n $VAL > $TARGET
151 }
152
153 set_orig()
154 {
155         if [ ! -z $TARGET ]; then
156                 echo "${ORIG}" > "${TARGET}"
157         fi
158 }
159
160 set_test()
161 {
162         echo "${TEST_STR}" > "${TARGET}"
163 }
164
165 verify()
166 {
167         local seen
168         seen=$(cat "$1")
169         if [ "${seen}" != "${TEST_STR}" ]; then
170                 return 1
171         fi
172         return 0
173 }
174
175 verify_diff_w()
176 {
177         echo "$TEST_STR" | diff -q -w -u - $1
178         return $?
179 }
180
181 test_rc()
182 {
183         if [[ $rc != 0 ]]; then
184                 echo "Failed test, return value: $rc" >&2
185                 exit $rc
186         fi
187 }
188
189 test_finish()
190 {
191         set_orig
192         rm -f "${TEST_FILE}"
193
194         if [ ! -z ${old_strict} ]; then
195                 echo ${old_strict} > ${WRITES_STRICT}
196         fi
197         exit $rc
198 }
199
200 run_numerictests()
201 {
202         echo "== Testing sysctl behavior against ${TARGET} =="
203
204         rc=0
205
206         echo -n "Writing test file ... "
207         echo "${TEST_STR}" > "${TEST_FILE}"
208         if ! verify "${TEST_FILE}"; then
209                 echo "FAIL" >&2
210                 exit 1
211         else
212                 echo "ok"
213         fi
214
215         echo -n "Checking sysctl is not set to test value ... "
216         if verify "${TARGET}"; then
217                 echo "FAIL" >&2
218                 exit 1
219         else
220                 echo "ok"
221         fi
222
223         echo -n "Writing sysctl from shell ... "
224         set_test
225         if ! verify "${TARGET}"; then
226                 echo "FAIL" >&2
227                 exit 1
228         else
229                 echo "ok"
230         fi
231
232         echo -n "Resetting sysctl to original value ... "
233         set_orig
234         if verify "${TARGET}"; then
235                 echo "FAIL" >&2
236                 exit 1
237         else
238                 echo "ok"
239         fi
240
241         # Now that we've validated the sanity of "set_test" and "set_orig",
242         # we can use those functions to set starting states before running
243         # specific behavioral tests.
244
245         echo -n "Writing entire sysctl in single write ... "
246         set_orig
247         dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null
248         if ! verify "${TARGET}"; then
249                 echo "FAIL" >&2
250                 rc=1
251         else
252                 echo "ok"
253         fi
254
255         echo -n "Writing middle of sysctl after synchronized seek ... "
256         set_test
257         dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null
258         if ! verify "${TARGET}"; then
259                 echo "FAIL" >&2
260                 rc=1
261         else
262                 echo "ok"
263         fi
264
265         echo -n "Writing beyond end of sysctl ... "
266         set_orig
267         dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null
268         if verify "${TARGET}"; then
269                 echo "FAIL" >&2
270                 rc=1
271         else
272                 echo "ok"
273         fi
274
275         echo -n "Writing sysctl with multiple long writes ... "
276         set_orig
277         (perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \
278                 dd of="${TARGET}" bs=50 2>/dev/null
279         if verify "${TARGET}"; then
280                 echo "FAIL" >&2
281                 rc=1
282         else
283                 echo "ok"
284         fi
285         test_rc
286 }
287
288 # Your test must accept digits 3 and 4 to use this
289 run_limit_digit()
290 {
291         echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..."
292         reset_vals
293
294         LIMIT=$((MAX_DIGITS -1))
295         TEST_STR="3"
296         (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
297                 dd of="${TARGET}" 2>/dev/null
298
299         if ! verify "${TARGET}"; then
300                 echo "FAIL" >&2
301                 rc=1
302         else
303                 echo "ok"
304         fi
305         test_rc
306
307         echo -n "Checking passing PAGE_SIZE of spaces fails on write ..."
308         reset_vals
309
310         LIMIT=$((MAX_DIGITS))
311         TEST_STR="4"
312         (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
313                 dd of="${TARGET}" 2>/dev/null
314
315         if verify "${TARGET}"; then
316                 echo "FAIL" >&2
317                 rc=1
318         else
319                 echo "ok"
320         fi
321         test_rc
322 }
323
324 # You are using an int
325 run_limit_digit_int()
326 {
327         echo -n "Testing INT_MAX works ..."
328         reset_vals
329         TEST_STR="$INT_MAX"
330         echo -n $TEST_STR > $TARGET
331
332         if ! verify "${TARGET}"; then
333                 echo "FAIL" >&2
334                 rc=1
335         else
336                 echo "ok"
337         fi
338         test_rc
339
340         echo -n "Testing INT_MAX + 1 will fail as expected..."
341         reset_vals
342         let TEST_STR=$INT_MAX+1
343         echo -n $TEST_STR > $TARGET 2> /dev/null
344
345         if verify "${TARGET}"; then
346                 echo "FAIL" >&2
347                 rc=1
348         else
349                 echo "ok"
350         fi
351         test_rc
352
353         echo -n "Testing negative values will work as expected..."
354         reset_vals
355         TEST_STR="-3"
356         echo -n $TEST_STR > $TARGET 2> /dev/null
357         if ! verify "${TARGET}"; then
358                 echo "FAIL" >&2
359                 rc=1
360         else
361                 echo "ok"
362         fi
363         test_rc
364 }
365
366 # You used an int array
367 run_limit_digit_int_array()
368 {
369         echo -n "Testing array works as expected ... "
370         TEST_STR="4 3 2 1"
371         echo -n $TEST_STR > $TARGET
372
373         if ! verify_diff_w "${TARGET}"; then
374                 echo "FAIL" >&2
375                 rc=1
376         else
377                 echo "ok"
378         fi
379         test_rc
380
381         echo -n "Testing skipping trailing array elements works ... "
382         # Do not reset_vals, carry on the values from the last test.
383         # If we only echo in two digits the last two are left intact
384         TEST_STR="100 101"
385         echo -n $TEST_STR > $TARGET
386         # After we echo in, to help diff we need to set on TEST_STR what
387         # we expect the result to be.
388         TEST_STR="100 101 2 1"
389
390         if ! verify_diff_w "${TARGET}"; then
391                 echo "FAIL" >&2
392                 rc=1
393         else
394                 echo "ok"
395         fi
396         test_rc
397
398         echo -n "Testing PAGE_SIZE limit on array works ... "
399         # Do not reset_vals, carry on the values from the last test.
400         # Even if you use an int array, you are still restricted to
401         # MAX_DIGITS, this is a known limitation. Test limit works.
402         LIMIT=$((MAX_DIGITS -1))
403         TEST_STR="9"
404         (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
405                 dd of="${TARGET}" 2>/dev/null
406
407         TEST_STR="9 101 2 1"
408         if ! verify_diff_w "${TARGET}"; then
409                 echo "FAIL" >&2
410                 rc=1
411         else
412                 echo "ok"
413         fi
414         test_rc
415
416         echo -n "Testing exceeding PAGE_SIZE limit fails as expected ... "
417         # Do not reset_vals, carry on the values from the last test.
418         # Now go over limit.
419         LIMIT=$((MAX_DIGITS))
420         TEST_STR="7"
421         (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
422                 dd of="${TARGET}" 2>/dev/null
423
424         TEST_STR="7 101 2 1"
425         if verify_diff_w "${TARGET}"; then
426                 echo "FAIL" >&2
427                 rc=1
428         else
429                 echo "ok"
430         fi
431         test_rc
432 }
433
434 # You are using an unsigned int
435 run_limit_digit_uint()
436 {
437         echo -n "Testing UINT_MAX works ..."
438         reset_vals
439         TEST_STR="$UINT_MAX"
440         echo -n $TEST_STR > $TARGET
441
442         if ! verify "${TARGET}"; then
443                 echo "FAIL" >&2
444                 rc=1
445         else
446                 echo "ok"
447         fi
448         test_rc
449
450         echo -n "Testing UINT_MAX + 1 will fail as expected..."
451         reset_vals
452         TEST_STR=$(($UINT_MAX+1))
453         echo -n $TEST_STR > $TARGET 2> /dev/null
454
455         if verify "${TARGET}"; then
456                 echo "FAIL" >&2
457                 rc=1
458         else
459                 echo "ok"
460         fi
461         test_rc
462
463         echo -n "Testing negative values will not work as expected ..."
464         reset_vals
465         TEST_STR="-3"
466         echo -n $TEST_STR > $TARGET 2> /dev/null
467
468         if verify "${TARGET}"; then
469                 echo "FAIL" >&2
470                 rc=1
471         else
472                 echo "ok"
473         fi
474         test_rc
475 }
476
477 run_stringtests()
478 {
479         echo -n "Writing entire sysctl in short writes ... "
480         set_orig
481         dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null
482         if ! verify "${TARGET}"; then
483                 echo "FAIL" >&2
484                 rc=1
485         else
486                 echo "ok"
487         fi
488
489         echo -n "Writing middle of sysctl after unsynchronized seek ... "
490         set_test
491         dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null
492         if verify "${TARGET}"; then
493                 echo "FAIL" >&2
494                 rc=1
495         else
496                 echo "ok"
497         fi
498
499         echo -n "Checking sysctl maxlen is at least $MAXLEN ... "
500         set_orig
501         perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \
502                 dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
503         if ! grep -q B "${TARGET}"; then
504                 echo "FAIL" >&2
505                 rc=1
506         else
507                 echo "ok"
508         fi
509
510         echo -n "Checking sysctl keeps original string on overflow append ... "
511         set_orig
512         perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
513                 dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null
514         if grep -q B "${TARGET}"; then
515                 echo "FAIL" >&2
516                 rc=1
517         else
518                 echo "ok"
519         fi
520
521         echo -n "Checking sysctl stays NULL terminated on write ... "
522         set_orig
523         perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
524                 dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
525         if grep -q B "${TARGET}"; then
526                 echo "FAIL" >&2
527                 rc=1
528         else
529                 echo "ok"
530         fi
531
532         echo -n "Checking sysctl stays NULL terminated on overwrite ... "
533         set_orig
534         perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \
535                 dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null
536         if grep -q B "${TARGET}"; then
537                 echo "FAIL" >&2
538                 rc=1
539         else
540                 echo "ok"
541         fi
542
543         test_rc
544 }
545
546 sysctl_test_0001()
547 {
548         TARGET="${SYSCTL}/int_0001"
549         reset_vals
550         ORIG=$(cat "${TARGET}")
551         TEST_STR=$(( $ORIG + 1 ))
552
553         run_numerictests
554         run_limit_digit
555 }
556
557 sysctl_test_0002()
558 {
559         TARGET="${SYSCTL}/string_0001"
560         reset_vals
561         ORIG=$(cat "${TARGET}")
562         TEST_STR="Testing sysctl"
563         # Only string sysctls support seeking/appending.
564         MAXLEN=65
565
566         run_numerictests
567         run_stringtests
568 }
569
570 sysctl_test_0003()
571 {
572         TARGET="${SYSCTL}/int_0002"
573         reset_vals
574         ORIG=$(cat "${TARGET}")
575         TEST_STR=$(( $ORIG + 1 ))
576
577         run_numerictests
578         run_limit_digit
579         run_limit_digit_int
580 }
581
582 sysctl_test_0004()
583 {
584         TARGET="${SYSCTL}/uint_0001"
585         reset_vals
586         ORIG=$(cat "${TARGET}")
587         TEST_STR=$(( $ORIG + 1 ))
588
589         run_numerictests
590         run_limit_digit
591         run_limit_digit_uint
592 }
593
594 sysctl_test_0005()
595 {
596         TARGET="${SYSCTL}/int_0003"
597         reset_vals
598         ORIG=$(cat "${TARGET}")
599
600         run_limit_digit_int_array
601 }
602
603 list_tests()
604 {
605         echo "Test ID list:"
606         echo
607         echo "TEST_ID x NUM_TEST"
608         echo "TEST_ID:   Test ID"
609         echo "NUM_TESTS: Number of recommended times to run the test"
610         echo
611         echo "0001 x $(get_test_count 0001) - tests proc_dointvec_minmax()"
612         echo "0002 x $(get_test_count 0002) - tests proc_dostring()"
613         echo "0003 x $(get_test_count 0003) - tests proc_dointvec()"
614         echo "0004 x $(get_test_count 0004) - tests proc_douintvec()"
615         echo "0005 x $(get_test_count 0005) - tests proc_douintvec() array"
616 }
617
618 test_reqs
619
620 usage()
621 {
622         NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)
623         let NUM_TESTS=$NUM_TESTS+1
624         MAX_TEST=$(printf "%04d\n" $NUM_TESTS)
625         echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
626         echo "           [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
627         echo "           [ all ] [ -h | --help ] [ -l ]"
628         echo ""
629         echo "Valid tests: 0001-$MAX_TEST"
630         echo ""
631         echo "    all     Runs all tests (default)"
632         echo "    -t      Run test ID the number amount of times is recommended"
633         echo "    -w      Watch test ID run until it runs into an error"
634         echo "    -c      Run test ID once"
635         echo "    -s      Run test ID x test-count number of times"
636         echo "    -l      List all test ID list"
637         echo " -h|--help  Help"
638         echo
639         echo "If an error every occurs execution will immediately terminate."
640         echo "If you are adding a new test try using -w <test-ID> first to"
641         echo "make sure the test passes a series of tests."
642         echo
643         echo Example uses:
644         echo
645         echo "$TEST_NAME.sh            -- executes all tests"
646         echo "$TEST_NAME.sh -t 0002    -- Executes test ID 0002 number of times is recomended"
647         echo "$TEST_NAME.sh -w 0002    -- Watch test ID 0002 run until an error occurs"
648         echo "$TEST_NAME.sh -s 0002    -- Run test ID 0002 once"
649         echo "$TEST_NAME.sh -c 0002 3  -- Run test ID 0002 three times"
650         echo
651         list_tests
652         exit 1
653 }
654
655 function test_num()
656 {
657         re='^[0-9]+$'
658         if ! [[ $1 =~ $re ]]; then
659                 usage
660         fi
661 }
662
663 function get_test_count()
664 {
665         test_num $1
666         TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
667         LAST_TWO=${TEST_DATA#*:*}
668         echo ${LAST_TWO%:*}
669 }
670
671 function get_test_enabled()
672 {
673         test_num $1
674         TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
675         echo ${TEST_DATA#*:*:}
676 }
677
678 function run_all_tests()
679 {
680         for i in $ALL_TESTS ; do
681                 TEST_ID=${i%:*:*}
682                 ENABLED=$(get_test_enabled $TEST_ID)
683                 TEST_COUNT=$(get_test_count $TEST_ID)
684                 if [[ $ENABLED -eq "1" ]]; then
685                         test_case $TEST_ID $TEST_COUNT
686                 fi
687         done
688 }
689
690 function watch_log()
691 {
692         if [ $# -ne 3 ]; then
693                 clear
694         fi
695         date
696         echo "Running test: $2 - run #$1"
697 }
698
699 function watch_case()
700 {
701         i=0
702         while [ 1 ]; do
703
704                 if [ $# -eq 1 ]; then
705                         test_num $1
706                         watch_log $i ${TEST_NAME}_test_$1
707                         ${TEST_NAME}_test_$1
708                 else
709                         watch_log $i all
710                         run_all_tests
711                 fi
712                 let i=$i+1
713         done
714 }
715
716 function test_case()
717 {
718         NUM_TESTS=$DEFAULT_NUM_TESTS
719         if [ $# -eq 2 ]; then
720                 NUM_TESTS=$2
721         fi
722
723         i=0
724         while [ $i -lt $NUM_TESTS ]; do
725                 test_num $1
726                 watch_log $i ${TEST_NAME}_test_$1 noclear
727                 RUN_TEST=${TEST_NAME}_test_$1
728                 $RUN_TEST
729                 let i=$i+1
730         done
731 }
732
733 function parse_args()
734 {
735         if [ $# -eq 0 ]; then
736                 run_all_tests
737         else
738                 if [[ "$1" = "all" ]]; then
739                         run_all_tests
740                 elif [[ "$1" = "-w" ]]; then
741                         shift
742                         watch_case $@
743                 elif [[ "$1" = "-t" ]]; then
744                         shift
745                         test_num $1
746                         test_case $1 $(get_test_count $1)
747                 elif [[ "$1" = "-c" ]]; then
748                         shift
749                         test_num $1
750                         test_num $2
751                         test_case $1 $2
752                 elif [[ "$1" = "-s" ]]; then
753                         shift
754                         test_case $1 1
755                 elif [[ "$1" = "-l" ]]; then
756                         list_tests
757                 elif [[ "$1" = "-h" || "$1" = "--help" ]]; then
758                         usage
759                 else
760                         usage
761                 fi
762         fi
763 }
764
765 test_reqs
766 allow_user_defaults
767 check_production_sysctl_writes_strict
768 load_req_mod
769
770 trap "test_finish" EXIT
771
772 parse_args $@
773
774 exit 0