blob: abeef675a8845f109fb4daec6afec966a76fa850 [file] [log] [blame]
Luis R. Rodriguez64b67122017-07-12 14:33:46 -07001#!/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
17TEST_NAME="sysctl"
18TEST_DRIVER="test_${TEST_NAME}"
19TEST_DIR=$(dirname $0)
20TEST_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.
32ALL_TESTS="0001:1:1"
33ALL_TESTS="$ALL_TESTS 0002:1:1"
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -070034ALL_TESTS="$ALL_TESTS 0003:1:1"
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -070035ALL_TESTS="$ALL_TESTS 0004:1:1"
Luis R. Rodriguez64b67122017-07-12 14:33:46 -070036
37test_modprobe()
38{
39 if [ ! -d $DIR ]; then
40 echo "$0: $DIR not present" >&2
41 echo "You must have the following enabled in your kernel:" >&2
42 cat $TEST_DIR/config >&2
43 exit 1
44 fi
45}
46
47function allow_user_defaults()
48{
49 if [ -z $DIR ]; then
50 DIR="/sys/module/test_sysctl/"
51 fi
52 if [ -z $DEFAULT_NUM_TESTS ]; then
53 DEFAULT_NUM_TESTS=50
54 fi
55 if [ -z $SYSCTL ]; then
56 SYSCTL="/proc/sys/debug/test_sysctl"
57 fi
58 if [ -z $PROD_SYSCTL ]; then
59 PROD_SYSCTL="/proc/sys"
60 fi
61 if [ -z $WRITES_STRICT ]; then
62 WRITES_STRICT="${PROD_SYSCTL}/kernel/sysctl_writes_strict"
63 fi
64}
65
66function check_production_sysctl_writes_strict()
67{
68 echo -n "Checking production write strict setting ... "
69 if [ ! -e ${WRITES_STRICT} ]; then
70 echo "FAIL, but skip in case of old kernel" >&2
71 else
72 old_strict=$(cat ${WRITES_STRICT})
73 if [ "$old_strict" = "1" ]; then
74 echo "ok"
75 else
76 echo "FAIL, strict value is 0 but force to 1 to continue" >&2
77 echo "1" > ${WRITES_STRICT}
78 fi
79 fi
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -070080
81 if [ -z $PAGE_SIZE ]; then
82 PAGE_SIZE=$(getconf PAGESIZE)
83 fi
84 if [ -z $MAX_DIGITS ]; then
85 MAX_DIGITS=$(($PAGE_SIZE/8))
86 fi
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -070087 if [ -z $INT_MAX ]; then
88 INT_MAX=$(getconf INT_MAX)
89 fi
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -070090 if [ -z $UINT_MAX ]; then
91 UINT_MAX=$(getconf UINT_MAX)
92 fi
Luis R. Rodriguez64b67122017-07-12 14:33:46 -070093}
94
95test_reqs()
96{
97 uid=$(id -u)
98 if [ $uid -ne 0 ]; then
99 echo $msg must be run as root >&2
100 exit 0
101 fi
102
103 if ! which perl 2> /dev/null > /dev/null; then
104 echo "$0: You need perl installed"
105 exit 1
106 fi
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700107 if ! which getconf 2> /dev/null > /dev/null; then
108 echo "$0: You need getconf installed"
109 exit 1
110 fi
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700111}
112
113function load_req_mod()
114{
115 trap "test_modprobe" EXIT
116
117 if [ ! -d $DIR ]; then
118 modprobe $TEST_DRIVER
119 if [ $? -ne 0 ]; then
120 exit
121 fi
122 fi
123}
124
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700125reset_vals()
126{
127 VAL=""
128 TRIGGER=$(basename ${TARGET})
129 case "$TRIGGER" in
130 int_0001)
131 VAL="60"
132 ;;
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -0700133 int_0002)
134 VAL="1"
135 ;;
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -0700136 uint_0001)
137 VAL="314"
138 ;;
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700139 string_0001)
140 VAL="(none)"
141 ;;
142 *)
143 ;;
144 esac
145 echo -n $VAL > $TARGET
146}
147
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700148set_orig()
149{
150 if [ ! -z $TARGET ]; then
151 echo "${ORIG}" > "${TARGET}"
152 fi
153}
154
155set_test()
156{
157 echo "${TEST_STR}" > "${TARGET}"
158}
159
160verify()
161{
162 local seen
163 seen=$(cat "$1")
164 if [ "${seen}" != "${TEST_STR}" ]; then
165 return 1
166 fi
167 return 0
168}
169
170test_rc()
171{
172 if [[ $rc != 0 ]]; then
173 echo "Failed test, return value: $rc" >&2
174 exit $rc
175 fi
176}
177
178test_finish()
179{
180 set_orig
181 rm -f "${TEST_FILE}"
182
183 if [ ! -z ${old_strict} ]; then
184 echo ${old_strict} > ${WRITES_STRICT}
185 fi
186 exit $rc
187}
188
189run_numerictests()
190{
191 echo "== Testing sysctl behavior against ${TARGET} =="
192
193 rc=0
194
195 echo -n "Writing test file ... "
196 echo "${TEST_STR}" > "${TEST_FILE}"
197 if ! verify "${TEST_FILE}"; then
198 echo "FAIL" >&2
199 exit 1
200 else
201 echo "ok"
202 fi
203
204 echo -n "Checking sysctl is not set to test value ... "
205 if verify "${TARGET}"; then
206 echo "FAIL" >&2
207 exit 1
208 else
209 echo "ok"
210 fi
211
212 echo -n "Writing sysctl from shell ... "
213 set_test
214 if ! verify "${TARGET}"; then
215 echo "FAIL" >&2
216 exit 1
217 else
218 echo "ok"
219 fi
220
221 echo -n "Resetting sysctl to original value ... "
222 set_orig
223 if verify "${TARGET}"; then
224 echo "FAIL" >&2
225 exit 1
226 else
227 echo "ok"
228 fi
229
230 # Now that we've validated the sanity of "set_test" and "set_orig",
231 # we can use those functions to set starting states before running
232 # specific behavioral tests.
233
234 echo -n "Writing entire sysctl in single write ... "
235 set_orig
236 dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null
237 if ! verify "${TARGET}"; then
238 echo "FAIL" >&2
239 rc=1
240 else
241 echo "ok"
242 fi
243
244 echo -n "Writing middle of sysctl after synchronized seek ... "
245 set_test
246 dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null
247 if ! verify "${TARGET}"; then
248 echo "FAIL" >&2
249 rc=1
250 else
251 echo "ok"
252 fi
253
254 echo -n "Writing beyond end of sysctl ... "
255 set_orig
256 dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null
257 if verify "${TARGET}"; then
258 echo "FAIL" >&2
259 rc=1
260 else
261 echo "ok"
262 fi
263
264 echo -n "Writing sysctl with multiple long writes ... "
265 set_orig
266 (perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \
267 dd of="${TARGET}" bs=50 2>/dev/null
268 if verify "${TARGET}"; then
269 echo "FAIL" >&2
270 rc=1
271 else
272 echo "ok"
273 fi
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700274 test_rc
275}
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700276
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700277# Your test must accept digits 3 and 4 to use this
278run_limit_digit()
279{
280 echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..."
281 reset_vals
282
283 LIMIT=$((MAX_DIGITS -1))
284 TEST_STR="3"
285 (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
286 dd of="${TARGET}" 2>/dev/null
287
288 if ! verify "${TARGET}"; then
289 echo "FAIL" >&2
290 rc=1
291 else
292 echo "ok"
293 fi
294 test_rc
295
296 echo -n "Checking passing PAGE_SIZE of spaces fails on write ..."
297 reset_vals
298
299 LIMIT=$((MAX_DIGITS))
300 TEST_STR="4"
301 (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \
302 dd of="${TARGET}" 2>/dev/null
303
304 if verify "${TARGET}"; then
305 echo "FAIL" >&2
306 rc=1
307 else
308 echo "ok"
309 fi
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700310 test_rc
311}
312
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -0700313# You are using an int
314run_limit_digit_int()
315{
316 echo -n "Testing INT_MAX works ..."
317 reset_vals
318 TEST_STR="$INT_MAX"
319 echo -n $TEST_STR > $TARGET
320
321 if ! verify "${TARGET}"; then
322 echo "FAIL" >&2
323 rc=1
324 else
325 echo "ok"
326 fi
327 test_rc
328
329 echo -n "Testing INT_MAX + 1 will fail as expected..."
330 reset_vals
331 let TEST_STR=$INT_MAX+1
332 echo -n $TEST_STR > $TARGET 2> /dev/null
333
334 if verify "${TARGET}"; then
335 echo "FAIL" >&2
336 rc=1
337 else
338 echo "ok"
339 fi
340 test_rc
341
342 echo -n "Testing negative values will work as expected..."
343 reset_vals
344 TEST_STR="-3"
345 echo -n $TEST_STR > $TARGET 2> /dev/null
346 if ! verify "${TARGET}"; then
347 echo "FAIL" >&2
348 rc=1
349 else
350 echo "ok"
351 fi
352 test_rc
353}
354
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -0700355# You are using an unsigned int
356run_limit_digit_uint()
357{
358 echo -n "Testing UINT_MAX works ..."
359 reset_vals
360 TEST_STR="$UINT_MAX"
361 echo -n $TEST_STR > $TARGET
362
363 if ! verify "${TARGET}"; then
364 echo "FAIL" >&2
365 rc=1
366 else
367 echo "ok"
368 fi
369 test_rc
370
371 echo -n "Testing UINT_MAX + 1 will fail as expected..."
372 reset_vals
373 TEST_STR=$(($UINT_MAX+1))
374 echo -n $TEST_STR > $TARGET 2> /dev/null
375
376 if verify "${TARGET}"; then
377 echo "FAIL" >&2
378 rc=1
379 else
380 echo "ok"
381 fi
382 test_rc
383
384 echo -n "Testing negative values will not work as expected ..."
385 reset_vals
386 TEST_STR="-3"
387 echo -n $TEST_STR > $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
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700398run_stringtests()
399{
400 echo -n "Writing entire sysctl in short writes ... "
401 set_orig
402 dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null
403 if ! verify "${TARGET}"; then
404 echo "FAIL" >&2
405 rc=1
406 else
407 echo "ok"
408 fi
409
410 echo -n "Writing middle of sysctl after unsynchronized seek ... "
411 set_test
412 dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null
413 if verify "${TARGET}"; then
414 echo "FAIL" >&2
415 rc=1
416 else
417 echo "ok"
418 fi
419
420 echo -n "Checking sysctl maxlen is at least $MAXLEN ... "
421 set_orig
422 perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \
423 dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
424 if ! grep -q B "${TARGET}"; then
425 echo "FAIL" >&2
426 rc=1
427 else
428 echo "ok"
429 fi
430
431 echo -n "Checking sysctl keeps original string on overflow append ... "
432 set_orig
433 perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
434 dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null
435 if grep -q B "${TARGET}"; then
436 echo "FAIL" >&2
437 rc=1
438 else
439 echo "ok"
440 fi
441
442 echo -n "Checking sysctl stays NULL terminated on write ... "
443 set_orig
444 perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \
445 dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null
446 if grep -q B "${TARGET}"; then
447 echo "FAIL" >&2
448 rc=1
449 else
450 echo "ok"
451 fi
452
453 echo -n "Checking sysctl stays NULL terminated on overwrite ... "
454 set_orig
455 perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \
456 dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null
457 if grep -q B "${TARGET}"; then
458 echo "FAIL" >&2
459 rc=1
460 else
461 echo "ok"
462 fi
463
464 test_rc
465}
466
467sysctl_test_0001()
468{
469 TARGET="${SYSCTL}/int_0001"
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700470 reset_vals
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700471 ORIG=$(cat "${TARGET}")
472 TEST_STR=$(( $ORIG + 1 ))
473
474 run_numerictests
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700475 run_limit_digit
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700476}
477
478sysctl_test_0002()
479{
480 TARGET="${SYSCTL}/string_0001"
Luis R. Rodriguez1c0357c2017-07-12 14:33:49 -0700481 reset_vals
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700482 ORIG=$(cat "${TARGET}")
483 TEST_STR="Testing sysctl"
484 # Only string sysctls support seeking/appending.
485 MAXLEN=65
486
487 run_numerictests
488 run_stringtests
489}
490
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -0700491sysctl_test_0003()
492{
493 TARGET="${SYSCTL}/int_0002"
494 reset_vals
495 ORIG=$(cat "${TARGET}")
496 TEST_STR=$(( $ORIG + 1 ))
497
498 run_numerictests
499 run_limit_digit
500 run_limit_digit_int
501}
502
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -0700503sysctl_test_0004()
504{
505 TARGET="${SYSCTL}/uint_0001"
506 reset_vals
507 ORIG=$(cat "${TARGET}")
508 TEST_STR=$(( $ORIG + 1 ))
509
510 run_numerictests
511 run_limit_digit
512 run_limit_digit_uint
513}
514
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700515list_tests()
516{
517 echo "Test ID list:"
518 echo
519 echo "TEST_ID x NUM_TEST"
520 echo "TEST_ID: Test ID"
521 echo "NUM_TESTS: Number of recommended times to run the test"
522 echo
523 echo "0001 x $(get_test_count 0001) - tests proc_dointvec_minmax()"
524 echo "0002 x $(get_test_count 0002) - tests proc_dostring()"
Luis R. Rodriguezeb965ed2017-07-12 14:33:52 -0700525 echo "0003 x $(get_test_count 0003) - tests proc_dointvec()"
Luis R. Rodriguez2920fad2017-07-12 14:33:55 -0700526 echo "0004 x $(get_test_count 0004) - tests proc_douintvec()"
Luis R. Rodriguez64b67122017-07-12 14:33:46 -0700527}
528
529test_reqs
530
531usage()
532{
533 NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .)
534 let NUM_TESTS=$NUM_TESTS+1
535 MAX_TEST=$(printf "%04d\n" $NUM_TESTS)
536 echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |"
537 echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
538 echo " [ all ] [ -h | --help ] [ -l ]"
539 echo ""
540 echo "Valid tests: 0001-$MAX_TEST"
541 echo ""
542 echo " all Runs all tests (default)"
543 echo " -t Run test ID the number amount of times is recommended"
544 echo " -w Watch test ID run until it runs into an error"
545 echo " -c Run test ID once"
546 echo " -s Run test ID x test-count number of times"
547 echo " -l List all test ID list"
548 echo " -h|--help Help"
549 echo
550 echo "If an error every occurs execution will immediately terminate."
551 echo "If you are adding a new test try using -w <test-ID> first to"
552 echo "make sure the test passes a series of tests."
553 echo
554 echo Example uses:
555 echo
556 echo "$TEST_NAME.sh -- executes all tests"
557 echo "$TEST_NAME.sh -t 0002 -- Executes test ID 0002 number of times is recomended"
558 echo "$TEST_NAME.sh -w 0002 -- Watch test ID 0002 run until an error occurs"
559 echo "$TEST_NAME.sh -s 0002 -- Run test ID 0002 once"
560 echo "$TEST_NAME.sh -c 0002 3 -- Run test ID 0002 three times"
561 echo
562 list_tests
563 exit 1
564}
565
566function test_num()
567{
568 re='^[0-9]+$'
569 if ! [[ $1 =~ $re ]]; then
570 usage
571 fi
572}
573
574function get_test_count()
575{
576 test_num $1
577 TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
578 LAST_TWO=${TEST_DATA#*:*}
579 echo ${LAST_TWO%:*}
580}
581
582function get_test_enabled()
583{
584 test_num $1
585 TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}')
586 echo ${TEST_DATA#*:*:}
587}
588
589function run_all_tests()
590{
591 for i in $ALL_TESTS ; do
592 TEST_ID=${i%:*:*}
593 ENABLED=$(get_test_enabled $TEST_ID)
594 TEST_COUNT=$(get_test_count $TEST_ID)
595 if [[ $ENABLED -eq "1" ]]; then
596 test_case $TEST_ID $TEST_COUNT
597 fi
598 done
599}
600
601function watch_log()
602{
603 if [ $# -ne 3 ]; then
604 clear
605 fi
606 date
607 echo "Running test: $2 - run #$1"
608}
609
610function watch_case()
611{
612 i=0
613 while [ 1 ]; do
614
615 if [ $# -eq 1 ]; then
616 test_num $1
617 watch_log $i ${TEST_NAME}_test_$1
618 ${TEST_NAME}_test_$1
619 else
620 watch_log $i all
621 run_all_tests
622 fi
623 let i=$i+1
624 done
625}
626
627function test_case()
628{
629 NUM_TESTS=$DEFAULT_NUM_TESTS
630 if [ $# -eq 2 ]; then
631 NUM_TESTS=$2
632 fi
633
634 i=0
635 while [ $i -lt $NUM_TESTS ]; do
636 test_num $1
637 watch_log $i ${TEST_NAME}_test_$1 noclear
638 RUN_TEST=${TEST_NAME}_test_$1
639 $RUN_TEST
640 let i=$i+1
641 done
642}
643
644function parse_args()
645{
646 if [ $# -eq 0 ]; then
647 run_all_tests
648 else
649 if [[ "$1" = "all" ]]; then
650 run_all_tests
651 elif [[ "$1" = "-w" ]]; then
652 shift
653 watch_case $@
654 elif [[ "$1" = "-t" ]]; then
655 shift
656 test_num $1
657 test_case $1 $(get_test_count $1)
658 elif [[ "$1" = "-c" ]]; then
659 shift
660 test_num $1
661 test_num $2
662 test_case $1 $2
663 elif [[ "$1" = "-s" ]]; then
664 shift
665 test_case $1 1
666 elif [[ "$1" = "-l" ]]; then
667 list_tests
668 elif [[ "$1" = "-h" || "$1" = "--help" ]]; then
669 usage
670 else
671 usage
672 fi
673 fi
674}
675
676test_reqs
677allow_user_defaults
678check_production_sysctl_writes_strict
679load_req_mod
680
681trap "test_finish" EXIT
682
683parse_args $@
684
685exit 0