2017-07-13 00:33:46 +03:00
#!/bin/bash
# Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or at your option any
# later version; or, when distributed separately from the Linux kernel or
# when incorporated into other software packages, subject to the following
# license:
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of copyleft-next (version 0.3.1 or later) as published
# at http://copyleft-next.org/.
# This performs a series tests against the proc sysctl interface.
2018-06-13 03:11:37 +03:00
# Kselftest framework requirement - SKIP code is 4.
ksft_skip = 4
2017-07-13 00:33:46 +03:00
TEST_NAME = "sysctl"
TEST_DRIVER = " test_ ${ TEST_NAME } "
TEST_DIR = $( dirname $0 )
TEST_FILE = $( mktemp)
# This represents
#
# TEST_ID:TEST_COUNT:ENABLED
#
# TEST_ID: is the test id number
# TEST_COUNT: number of times we should run the test
# ENABLED: 1 if enabled, 0 otherwise
#
# Once these are enabled please leave them as-is. Write your own test,
# we have tons of space.
ALL_TESTS = "0001:1:1"
ALL_TESTS = " $ALL_TESTS 0002:1:1 "
2017-07-13 00:33:52 +03:00
ALL_TESTS = " $ALL_TESTS 0003:1:1 "
2017-07-13 00:33:55 +03:00
ALL_TESTS = " $ALL_TESTS 0004:1:1 "
2017-07-13 00:33:58 +03:00
ALL_TESTS = " $ALL_TESTS 0005:3:1 "
2017-07-13 00:33:46 +03:00
test_modprobe( )
{
if [ ! -d $DIR ] ; then
echo " $0 : $DIR not present " >& 2
echo "You must have the following enabled in your kernel:" >& 2
cat $TEST_DIR /config >& 2
2018-06-13 03:11:37 +03:00
exit $ksft_skip
2017-07-13 00:33:46 +03:00
fi
}
function allow_user_defaults( )
{
if [ -z $DIR ] ; then
DIR = "/sys/module/test_sysctl/"
fi
if [ -z $DEFAULT_NUM_TESTS ] ; then
DEFAULT_NUM_TESTS = 50
fi
if [ -z $SYSCTL ] ; then
SYSCTL = "/proc/sys/debug/test_sysctl"
fi
if [ -z $PROD_SYSCTL ] ; then
PROD_SYSCTL = "/proc/sys"
fi
if [ -z $WRITES_STRICT ] ; then
WRITES_STRICT = " ${ PROD_SYSCTL } /kernel/sysctl_writes_strict "
fi
}
function check_production_sysctl_writes_strict( )
{
echo -n "Checking production write strict setting ... "
if [ ! -e ${ WRITES_STRICT } ] ; then
echo "FAIL, but skip in case of old kernel" >& 2
else
old_strict = $( cat ${ WRITES_STRICT } )
if [ " $old_strict " = "1" ] ; then
echo "ok"
else
echo "FAIL, strict value is 0 but force to 1 to continue" >& 2
echo "1" > ${ WRITES_STRICT }
fi
fi
2017-07-13 00:33:49 +03:00
if [ -z $PAGE_SIZE ] ; then
PAGE_SIZE = $( getconf PAGESIZE)
fi
if [ -z $MAX_DIGITS ] ; then
MAX_DIGITS = $(( $PAGE_SIZE / 8 ))
fi
2017-07-13 00:33:52 +03:00
if [ -z $INT_MAX ] ; then
INT_MAX = $( getconf INT_MAX)
fi
2017-07-13 00:33:55 +03:00
if [ -z $UINT_MAX ] ; then
UINT_MAX = $( getconf UINT_MAX)
fi
2017-07-13 00:33:46 +03:00
}
test_reqs( )
{
uid = $( id -u)
if [ $uid -ne 0 ] ; then
echo $msg must be run as root >& 2
2018-06-13 03:11:37 +03:00
exit $ksft_skip
2017-07-13 00:33:46 +03:00
fi
if ! which perl 2> /dev/null > /dev/null; then
echo " $0 : You need perl installed "
2018-06-13 03:11:37 +03:00
exit $ksft_skip
2017-07-13 00:33:46 +03:00
fi
2017-07-13 00:33:49 +03:00
if ! which getconf 2> /dev/null > /dev/null; then
echo " $0 : You need getconf installed "
2018-06-13 03:11:37 +03:00
exit $ksft_skip
2017-07-13 00:33:49 +03:00
fi
2017-07-13 00:33:58 +03:00
if ! which diff 2> /dev/null > /dev/null; then
echo " $0 : You need diff installed "
2018-06-13 03:11:37 +03:00
exit $ksft_skip
2017-07-13 00:33:58 +03:00
fi
2017-07-13 00:33:46 +03:00
}
function load_req_mod( )
{
if [ ! -d $DIR ] ; then
2018-06-13 03:11:37 +03:00
if ! modprobe -q -n $TEST_DRIVER ; then
echo " $0 : module $TEST_DRIVER not found [SKIP] "
exit $ksft_skip
fi
2017-07-13 00:33:46 +03:00
modprobe $TEST_DRIVER
if [ $? -ne 0 ] ; then
exit
fi
fi
}
2017-07-13 00:33:49 +03:00
reset_vals( )
{
VAL = ""
TRIGGER = $( basename ${ TARGET } )
case " $TRIGGER " in
int_0001)
VAL = "60"
; ;
2017-07-13 00:33:52 +03:00
int_0002)
VAL = "1"
; ;
2017-07-13 00:33:55 +03:00
uint_0001)
VAL = "314"
; ;
2017-07-13 00:33:49 +03:00
string_0001)
VAL = "(none)"
; ;
*)
; ;
esac
echo -n $VAL > $TARGET
}
2017-07-13 00:33:46 +03:00
set_orig( )
{
if [ ! -z $TARGET ] ; then
echo " ${ ORIG } " > " ${ TARGET } "
fi
}
set_test( )
{
echo " ${ TEST_STR } " > " ${ TARGET } "
}
verify( )
{
local seen
seen = $( cat " $1 " )
if [ " ${ seen } " != " ${ TEST_STR } " ] ; then
return 1
fi
return 0
}
2017-07-13 00:33:58 +03:00
verify_diff_w( )
{
echo " $TEST_STR " | diff -q -w -u - $1
return $?
}
2017-07-13 00:33:46 +03:00
test_rc( )
{
if [ [ $rc != 0 ] ] ; then
echo " Failed test, return value: $rc " >& 2
exit $rc
fi
}
test_finish( )
{
set_orig
rm -f " ${ TEST_FILE } "
if [ ! -z ${ old_strict } ] ; then
echo ${ old_strict } > ${ WRITES_STRICT }
fi
exit $rc
}
run_numerictests( )
{
echo " == Testing sysctl behavior against ${ TARGET } == "
rc = 0
echo -n "Writing test file ... "
echo " ${ TEST_STR } " > " ${ TEST_FILE } "
if ! verify " ${ TEST_FILE } " ; then
echo "FAIL" >& 2
exit 1
else
echo "ok"
fi
echo -n "Checking sysctl is not set to test value ... "
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
exit 1
else
echo "ok"
fi
echo -n "Writing sysctl from shell ... "
set_test
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
exit 1
else
echo "ok"
fi
echo -n "Resetting sysctl to original value ... "
set_orig
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
exit 1
else
echo "ok"
fi
# Now that we've validated the sanity of "set_test" and "set_orig",
# we can use those functions to set starting states before running
# specific behavioral tests.
echo -n "Writing entire sysctl in single write ... "
set_orig
dd if = " ${ TEST_FILE } " of = " ${ TARGET } " bs = 4096 2>/dev/null
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Writing middle of sysctl after synchronized seek ... "
set_test
dd if = " ${ TEST_FILE } " of = " ${ TARGET } " bs = 1 seek = 1 skip = 1 2>/dev/null
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Writing beyond end of sysctl ... "
set_orig
dd if = " ${ TEST_FILE } " of = " ${ TARGET } " bs = 20 seek = 2 2>/dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Writing sysctl with multiple long writes ... "
set_orig
( perl -e 'print "A" x 50;' ; echo " ${ TEST_STR } " ) | \
dd of = " ${ TARGET } " bs = 50 2>/dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
2017-07-13 00:33:49 +03:00
test_rc
}
# Your test must accept digits 3 and 4 to use this
run_limit_digit( )
{
echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..."
reset_vals
2017-07-13 00:33:46 +03:00
2017-07-13 00:33:49 +03:00
LIMIT = $(( MAX_DIGITS - 1 ))
TEST_STR = "3"
( perl -e 'print " " x ' $LIMIT ';' ; echo " ${ TEST_STR } " ) | \
dd of = " ${ TARGET } " 2>/dev/null
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Checking passing PAGE_SIZE of spaces fails on write ..."
reset_vals
LIMIT = $(( MAX_DIGITS))
TEST_STR = "4"
( perl -e 'print " " x ' $LIMIT ';' ; echo " ${ TEST_STR } " ) | \
dd of = " ${ TARGET } " 2>/dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
2017-07-13 00:33:46 +03:00
test_rc
}
2017-07-13 00:33:52 +03:00
# You are using an int
run_limit_digit_int( )
{
echo -n "Testing INT_MAX works ..."
reset_vals
TEST_STR = " $INT_MAX "
echo -n $TEST_STR > $TARGET
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing INT_MAX + 1 will fail as expected..."
reset_vals
let TEST_STR = $INT_MAX +1
echo -n $TEST_STR > $TARGET 2> /dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing negative values will work as expected..."
reset_vals
TEST_STR = "-3"
echo -n $TEST_STR > $TARGET 2> /dev/null
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
}
2017-07-13 00:33:58 +03:00
# You used an int array
run_limit_digit_int_array( )
{
echo -n "Testing array works as expected ... "
TEST_STR = "4 3 2 1"
echo -n $TEST_STR > $TARGET
if ! verify_diff_w " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing skipping trailing array elements works ... "
# Do not reset_vals, carry on the values from the last test.
# If we only echo in two digits the last two are left intact
TEST_STR = "100 101"
echo -n $TEST_STR > $TARGET
# After we echo in, to help diff we need to set on TEST_STR what
# we expect the result to be.
TEST_STR = "100 101 2 1"
if ! verify_diff_w " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing PAGE_SIZE limit on array works ... "
# Do not reset_vals, carry on the values from the last test.
# Even if you use an int array, you are still restricted to
# MAX_DIGITS, this is a known limitation. Test limit works.
LIMIT = $(( MAX_DIGITS - 1 ))
TEST_STR = "9"
( perl -e 'print " " x ' $LIMIT ';' ; echo " ${ TEST_STR } " ) | \
dd of = " ${ TARGET } " 2>/dev/null
TEST_STR = "9 101 2 1"
if ! verify_diff_w " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing exceeding PAGE_SIZE limit fails as expected ... "
# Do not reset_vals, carry on the values from the last test.
# Now go over limit.
LIMIT = $(( MAX_DIGITS))
TEST_STR = "7"
( perl -e 'print " " x ' $LIMIT ';' ; echo " ${ TEST_STR } " ) | \
dd of = " ${ TARGET } " 2>/dev/null
TEST_STR = "7 101 2 1"
if verify_diff_w " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
}
2017-07-13 00:33:55 +03:00
# You are using an unsigned int
run_limit_digit_uint( )
{
echo -n "Testing UINT_MAX works ..."
reset_vals
TEST_STR = " $UINT_MAX "
echo -n $TEST_STR > $TARGET
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing UINT_MAX + 1 will fail as expected..."
reset_vals
TEST_STR = $(( $UINT_MAX + 1 ))
echo -n $TEST_STR > $TARGET 2> /dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
echo -n "Testing negative values will not work as expected ..."
reset_vals
TEST_STR = "-3"
echo -n $TEST_STR > $TARGET 2> /dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
}
2017-07-13 00:33:46 +03:00
run_stringtests( )
{
echo -n "Writing entire sysctl in short writes ... "
set_orig
dd if = " ${ TEST_FILE } " of = " ${ TARGET } " bs = 1 2>/dev/null
if ! verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Writing middle of sysctl after unsynchronized seek ... "
set_test
dd if = " ${ TEST_FILE } " of = " ${ TARGET } " bs = 1 seek = 1 2>/dev/null
if verify " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n " Checking sysctl maxlen is at least $MAXLEN ... "
set_orig
perl -e 'print "A" x (' " ${ MAXLEN } " '-2), "B";' | \
dd of = " ${ TARGET } " bs = " ${ MAXLEN } " 2>/dev/null
if ! grep -q B " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Checking sysctl keeps original string on overflow append ... "
set_orig
perl -e 'print "A" x (' " ${ MAXLEN } " '-1), "B";' | \
dd of = " ${ TARGET } " bs = $(( MAXLEN - 1 )) 2>/dev/null
if grep -q B " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Checking sysctl stays NULL terminated on write ... "
set_orig
perl -e 'print "A" x (' " ${ MAXLEN } " '-1), "B";' | \
dd of = " ${ TARGET } " bs = " ${ MAXLEN } " 2>/dev/null
if grep -q B " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
echo -n "Checking sysctl stays NULL terminated on overwrite ... "
set_orig
perl -e 'print "A" x (' " ${ MAXLEN } " '-1), "BB";' | \
dd of = " ${ TARGET } " bs = $(( $MAXLEN + 1 )) 2>/dev/null
if grep -q B " ${ TARGET } " ; then
echo "FAIL" >& 2
rc = 1
else
echo "ok"
fi
test_rc
}
sysctl_test_0001( )
{
TARGET = " ${ SYSCTL } /int_0001 "
2017-07-13 00:33:49 +03:00
reset_vals
2017-07-13 00:33:46 +03:00
ORIG = $( cat " ${ TARGET } " )
TEST_STR = $(( $ORIG + 1 ))
run_numerictests
2017-07-13 00:33:49 +03:00
run_limit_digit
2017-07-13 00:33:46 +03:00
}
sysctl_test_0002( )
{
TARGET = " ${ SYSCTL } /string_0001 "
2017-07-13 00:33:49 +03:00
reset_vals
2017-07-13 00:33:46 +03:00
ORIG = $( cat " ${ TARGET } " )
TEST_STR = "Testing sysctl"
# Only string sysctls support seeking/appending.
MAXLEN = 65
run_numerictests
run_stringtests
}
2017-07-13 00:33:52 +03:00
sysctl_test_0003( )
{
TARGET = " ${ SYSCTL } /int_0002 "
reset_vals
ORIG = $( cat " ${ TARGET } " )
TEST_STR = $(( $ORIG + 1 ))
run_numerictests
run_limit_digit
run_limit_digit_int
}
2017-07-13 00:33:55 +03:00
sysctl_test_0004( )
{
TARGET = " ${ SYSCTL } /uint_0001 "
reset_vals
ORIG = $( cat " ${ TARGET } " )
TEST_STR = $(( $ORIG + 1 ))
run_numerictests
run_limit_digit
run_limit_digit_uint
}
2017-07-13 00:33:58 +03:00
sysctl_test_0005( )
{
TARGET = " ${ SYSCTL } /int_0003 "
reset_vals
ORIG = $( cat " ${ TARGET } " )
run_limit_digit_int_array
}
2017-07-13 00:33:46 +03:00
list_tests( )
{
echo "Test ID list:"
echo
echo "TEST_ID x NUM_TEST"
echo "TEST_ID: Test ID"
echo "NUM_TESTS: Number of recommended times to run the test"
echo
echo " 0001 x $( get_test_count 0001) - tests proc_dointvec_minmax() "
echo " 0002 x $( get_test_count 0002) - tests proc_dostring() "
2017-07-13 00:33:52 +03:00
echo " 0003 x $( get_test_count 0003) - tests proc_dointvec() "
2017-07-13 00:33:55 +03:00
echo " 0004 x $( get_test_count 0004) - tests proc_douintvec() "
2017-07-13 00:33:58 +03:00
echo " 0005 x $( get_test_count 0005) - tests proc_douintvec() array "
2017-07-13 00:33:46 +03:00
}
test_reqs
usage( )
{
NUM_TESTS = $( grep -o ' ' <<< " $ALL_TESTS " | grep -c .)
let NUM_TESTS = $NUM_TESTS +1
MAX_TEST = $( printf "%04d\n" $NUM_TESTS )
echo " Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] | "
echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>"
echo " [ all ] [ -h | --help ] [ -l ]"
echo ""
echo " Valid tests: 0001- $MAX_TEST "
echo ""
echo " all Runs all tests (default)"
echo " -t Run test ID the number amount of times is recommended"
echo " -w Watch test ID run until it runs into an error"
echo " -c Run test ID once"
echo " -s Run test ID x test-count number of times"
echo " -l List all test ID list"
echo " -h|--help Help"
echo
echo "If an error every occurs execution will immediately terminate."
echo "If you are adding a new test try using -w <test-ID> first to"
echo "make sure the test passes a series of tests."
echo
echo Example uses:
echo
echo " $TEST_NAME .sh -- executes all tests "
echo " $TEST_NAME .sh -t 0002 -- Executes test ID 0002 number of times is recomended "
echo " $TEST_NAME .sh -w 0002 -- Watch test ID 0002 run until an error occurs "
echo " $TEST_NAME .sh -s 0002 -- Run test ID 0002 once "
echo " $TEST_NAME .sh -c 0002 3 -- Run test ID 0002 three times "
echo
list_tests
exit 1
}
function test_num( )
{
re = '^[0-9]+$'
if ! [ [ $1 = ~ $re ] ] ; then
usage
fi
}
function get_test_count( )
{
test_num $1
TEST_DATA = $( echo $ALL_TESTS | awk '{print $' $1 '}' )
LAST_TWO = ${ TEST_DATA #* : * }
echo ${ LAST_TWO % : * }
}
function get_test_enabled( )
{
test_num $1
TEST_DATA = $( echo $ALL_TESTS | awk '{print $' $1 '}' )
echo ${ TEST_DATA #* : * : }
}
function run_all_tests( )
{
for i in $ALL_TESTS ; do
TEST_ID = ${ i % : * : * }
ENABLED = $( get_test_enabled $TEST_ID )
TEST_COUNT = $( get_test_count $TEST_ID )
if [ [ $ENABLED -eq "1" ] ] ; then
test_case $TEST_ID $TEST_COUNT
fi
done
}
function watch_log( )
{
if [ $# -ne 3 ] ; then
clear
fi
date
echo " Running test: $2 - run # $1 "
}
function watch_case( )
{
i = 0
while [ 1 ] ; do
if [ $# -eq 1 ] ; then
test_num $1
watch_log $i ${ TEST_NAME } _test_$1
${ TEST_NAME } _test_$1
else
watch_log $i all
run_all_tests
fi
let i = $i +1
done
}
function test_case( )
{
NUM_TESTS = $DEFAULT_NUM_TESTS
if [ $# -eq 2 ] ; then
NUM_TESTS = $2
fi
i = 0
while [ $i -lt $NUM_TESTS ] ; do
test_num $1
watch_log $i ${ TEST_NAME } _test_$1 noclear
RUN_TEST = ${ TEST_NAME } _test_$1
$RUN_TEST
let i = $i +1
done
}
function parse_args( )
{
if [ $# -eq 0 ] ; then
run_all_tests
else
if [ [ " $1 " = "all" ] ] ; then
run_all_tests
elif [ [ " $1 " = "-w" ] ] ; then
shift
watch_case $@
elif [ [ " $1 " = "-t" ] ] ; then
shift
test_num $1
test_case $1 $( get_test_count $1 )
elif [ [ " $1 " = "-c" ] ] ; then
shift
test_num $1
test_num $2
test_case $1 $2
elif [ [ " $1 " = "-s" ] ] ; then
shift
test_case $1 1
elif [ [ " $1 " = "-l" ] ] ; then
list_tests
elif [ [ " $1 " = "-h" || " $1 " = "--help" ] ] ; then
usage
else
usage
fi
fi
}
test_reqs
allow_user_defaults
check_production_sysctl_writes_strict
2018-06-13 03:11:37 +03:00
test_modprobe
2017-07-13 00:33:46 +03:00
load_req_mod
trap "test_finish" EXIT
parse_args $@
exit 0